Browse Source

Add settings

isundil 4 years ago
parent
commit
9c9f1f4efe

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 36 - 0
app/build.gradle

@@ -0,0 +1,36 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 29
+    buildToolsVersion "29.0.3"
+
+    defaultConfig {
+        applicationId "info.knacki.prometheusandroidexporter"
+        minSdkVersion 16
+        targetSdkVersion 29
+        versionCode 3
+        versionName "1.0.2"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    implementation 'androidx.gridlayout:gridlayout:1.0.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+    implementation 'androidx.work:work-runtime:2.5.0'
+}

+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 2 - 1
app/src/main/AndroidManifest.xml

@@ -15,7 +15,8 @@
         android:roundIcon="@mipmap/ic_launcher_foreground"
         android:logo="@mipmap/ic_launcher_foreground"
         android:supportsRtl="true"
-        android:theme="@style/AppTheme">
+        android:theme="@style/AppTheme"
+        android:fullBackupContent="@xml/backup_descriptor">
         <activity android:name=".MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />

+ 28 - 8
app/src/main/java/info/knacki/prometheusandroidexporter/MainActivity.java

@@ -9,11 +9,14 @@ import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.View;
 import android.widget.Button;
+import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SwitchCompat;
 
+import java.util.Collections;
 import java.util.List;
 
 import info.knacki.prometheusandroidexporter.configuration.ConfigurationManager;
@@ -25,7 +28,9 @@ public class MainActivity extends AppCompatActivity implements NetworkReceiver.A
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
+        fConfig = ConfigurationManager.GetInstance(this).GetConfiguration();
 
+        // Start / kill service buttons
         findViewById(R.id.bt_killservice).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -41,21 +46,23 @@ public class MainActivity extends AppCompatActivity implements NetworkReceiver.A
             }
         });
 
-        fConfig = ConfigurationManager.GetInstance(this).GetConfiguration();
-
+        // Save config button
         final Button bt_save = findViewById(R.id.bt_saveconfiguration);
         bt_save.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 ConfigurationManager.Save(MainActivity.this, fConfig);
-                MainService.StopService(MainActivity.this);
-                MainService.StartService(MainActivity.this);
+                if (IsServiceRunning()) {
+                    MainService.StopService(MainActivity.this);
+                    MainService.StartService(MainActivity.this);
+                }
                 bt_save.setEnabled(!fConfig.equals(ConfigurationManager.GetInstance(MainActivity.this).GetConfiguration()));
             }
         });
 
+        // Port input
         EditText input_port = findViewById(R.id.input_port);
-        input_port.setText(Short.toString(fConfig.GetPort()));
+        input_port.setText(("" + fConfig.GetPort()));
         input_port.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@@ -76,13 +83,25 @@ public class MainActivity extends AppCompatActivity implements NetworkReceiver.A
                 bt_save.setEnabled(!fConfig.equals(ConfigurationManager.GetInstance(MainActivity.this).GetConfiguration()));
             }
         });
-        UpdateUiState();
+
+        // Start on boot input
+        // FIXME check app permission
+        SwitchCompat input_startonboot = findViewById(R.id.input_startonboot);
+        input_startonboot.setChecked(fConfig.IsStartOnBootEnabled());
+        input_startonboot.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                fConfig.SetStartOnBootEnabled(isChecked);
+                bt_save.setEnabled(!fConfig.equals(ConfigurationManager.GetInstance(MainActivity.this).GetConfiguration()));
+            }
+        });
     }
 
     @Override
     protected void onResume() {
         super.onResume();
         NetworkReceiver.SetAdditionalReceiver(this);
+        UpdateUiState();
     }
 
     @Override
@@ -93,7 +112,8 @@ public class MainActivity extends AppCompatActivity implements NetworkReceiver.A
 
     private boolean IsServiceRunning() {
         final ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
-        final List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
+        @SuppressWarnings("unchecked")
+        final List<ActivityManager.RunningServiceInfo> services = (activityManager == null) ? ((List<ActivityManager.RunningServiceInfo>)Collections.EMPTY_LIST) : activityManager.getRunningServices(Integer.MAX_VALUE);
         final String serviceClassName = MainService.class.getName();
 
         for (ActivityManager.RunningServiceInfo runningServiceInfo : services) {
@@ -122,7 +142,7 @@ public class MainActivity extends AppCompatActivity implements NetworkReceiver.A
                     public void run() {
                         findViewById(R.id.bt_killservice).setEnabled(running);
                         findViewById(R.id.bt_startservice).setEnabled(!running);
-                        ((TextView) findViewById(R.id.txt_servicestate)).setText(running ? ("running" + ip) : "stopped");
+                        ((TextView) findViewById(R.id.txt_servicestate)).setText(running ? (" running" + ip) : " stopped");
                     }
                 });
             }

+ 27 - 17
app/src/main/java/info/knacki/prometheusandroidexporter/MainService.java

@@ -30,23 +30,28 @@ import info.knacki.prometheusandroidexporter.collector.StorageCollector;
 import info.knacki.prometheusandroidexporter.receiver.NetworkReceiver;
 
 public class MainService extends Service {
+    private enum eStatus {
+        eStopped,
+        eBooting,
+        ePaused,
+        eRunning
+    }
+
     public final static Logger log = Logger.getLogger(MainService.class.getName());
     private final Binder fLocalBinder = this.new Binder();
     private NotificationCompat.Builder fNotif;
-    private boolean fPaused;
+    private eStatus fStatus = eStatus.eStopped;
     private static final int gNotifId = 1;
 
     public MainService() {
     }
 
     public static void StartService(Context ctx) {
-        Intent i = new Intent(ctx, MainService.class);
-        ctx.startService(i);
+        ctx.startService(new Intent(ctx, MainService.class));
     }
 
     public static void StopService(Context ctx) {
-        Intent i = new Intent(ctx, MainService.class);
-        ctx.stopService(i);
+        ctx.stopService(new Intent(ctx, MainService.class));
     }
 
     @Override
@@ -60,15 +65,18 @@ public class MainService extends Service {
         collectorManager.tick(this);
         InitCollectors(collectorManager);
         NotifyIcon();
-        fPaused = true;
+        fStatus = eStatus.eBooting;
         NetworkReceiver.Register(this);
     }
 
     @Override
     public void onDestroy() {
         Pause();
+        fStatus = eStatus.eStopped;
         super.onDestroy();
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(gNotifId);
+        NotificationManager notifMan = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+        if (notifMan != null)
+            notifMan.cancel(gNotifId);
     }
 
     public class Binder extends android.os.Binder {
@@ -98,10 +106,9 @@ public class MainService extends Service {
     }
 
     public void Resume() {
-        if (fPaused) {
+        if (fStatus == eStatus.ePaused || fStatus == eStatus.eBooting) {
             synchronized (this) {
-                if (fPaused) {
-                    log.warning("RESUME!~~~~");
+                if (fStatus == eStatus.ePaused || fStatus == eStatus.eBooting) {
                     try {
                         HttpService.Register(this);
                     }
@@ -111,18 +118,17 @@ public class MainService extends Service {
                     }
                     ScheduleCollection();
                     UpdateIcon(R.mipmap.ic_launcher, "Service is running");
-                    fPaused = false;
+                    fStatus = eStatus.eRunning;
                 }
             }
         }
     }
 
     public void Pause() {
-        if (!fPaused) {
+        if (fStatus == eStatus.eRunning || fStatus == eStatus.eBooting) {
             synchronized (this) {
-                if (!fPaused) {
-                    log.warning("PAUSE!~~~~");
-                    fPaused = true;
+                if (fStatus == eStatus.eRunning || fStatus == eStatus.eBooting) {
+                    fStatus = eStatus.ePaused;
                     HttpService.Stop();
                     WorkManager.getInstance(this).cancelAllWork();
                     UpdateIcon(R.mipmap.ic_launcher_idle, "Service waiting for wifi");
@@ -144,14 +150,18 @@ public class MainService extends Service {
                 .build();
 
         notif.flags = Notification.FLAG_ONGOING_EVENT;
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(gNotifId, notif);
+        NotificationManager notifMan = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+        if (notifMan != null)
+            notifMan.notify(gNotifId, notif);
     }
 
     private void NotifyIcon() {
         String notifChannelId = getPackageName();
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
             NotificationChannel channel = new NotificationChannel(notifChannelId, MainService.class.getName(), NotificationManager.IMPORTANCE_DEFAULT);
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).createNotificationChannel(channel);
+            NotificationManager notifMan = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+            if (notifMan != null)
+                notifMan.createNotificationChannel(channel);
         }
         fNotif = new NotificationCompat.Builder(this, notifChannelId)
             .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT))

+ 24 - 16
app/src/main/java/info/knacki/prometheusandroidexporter/collector/NetworkCollector.java

@@ -20,34 +20,42 @@ public class NetworkCollector implements ICollector {
     private final CollectorType netWifiAddr = new CollectorType("net_wifi_addr", "Wifi address", CollectorType.Type.GAUGE);
     private final CollectorType netHostname = new CollectorType("net_wifi_Hostname", "Network hostname", CollectorType.Type.GAUGE);
 
-    public static String GetWifiIPAddress(Context ctx) {
+    private static WifiInfo GetWifiInfos(Context ctx) {
+        WifiManager wman = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+        if (wman != null && wman.getConnectionInfo() != null)
+            return wman.getConnectionInfo();
+        return null;
+    }
+
+    private static byte[] GetWifiIp(Context ctx) {
         WifiInfo winfo = GetWifiInfos(ctx);
         if (winfo != null) {
+            byte[] ipBytes = BigInteger.valueOf(winfo.getIpAddress()).toByteArray();
+            if (ipBytes != null && ipBytes.length >= 4)
+                return new byte[]{ipBytes[3], ipBytes[2], ipBytes[1], ipBytes[0]};
+        }
+        return null;
+    }
+
+    public static String GetWifiIPAddress(Context ctx) {
+        byte[] ipAddr = GetWifiIp(ctx);
+        if (ipAddr != null) {
             try {
-                byte[] ipBytes = BigInteger.valueOf(winfo.getIpAddress()).toByteArray();
-                return InetAddress.getByAddress(new byte[]{ipBytes[3], ipBytes[2], ipBytes[1], ipBytes[0]}).getHostAddress();
+                return InetAddress.getByAddress(ipAddr).getHostAddress();
             } catch (UnknownHostException e) {
-                return "Unknown";
+                // Failed to reverse ip
             }
         }
         return "Unknown";
     }
 
-    private static WifiInfo GetWifiInfos(Context ctx) {
-        WifiManager wman = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
-        if (wman != null && wman.getConnectionInfo() != null)
-            return wman.getConnectionInfo();
-        return null;
-    }
-
     public static String GetWifiHostname(Context ctx) {
-        WifiInfo winfo = GetWifiInfos(ctx);
-        if (winfo != null) {
+        byte[] ipAddr = GetWifiIp(ctx);
+        if (ipAddr != null) {
             try {
-                byte[] ipBytes = BigInteger.valueOf(winfo.getIpAddress()).toByteArray();
-                return InetAddress.getByAddress(new byte[]{ipBytes[3], ipBytes[2], ipBytes[1], ipBytes[0]}).getHostName();
+                return InetAddress.getByAddress(ipAddr).getHostName();
             } catch (UnknownHostException e) {
-                return "Unknown";
+                // Failed to reverse ip
             }
         }
         return "Unknown";

+ 3 - 3
app/src/main/java/info/knacki/prometheusandroidexporter/collector/ProcessCollector.java

@@ -17,9 +17,9 @@ public class ProcessCollector implements ICollector {
 
         Data(Context ctx) {
             final ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
-            fProcessCount = activityManager.getRunningServices(Integer.MAX_VALUE).size();
-            fRunningAppProcess = activityManager.getRunningAppProcesses().size();
-            fTaskCount = activityManager.getRunningTasks(Integer.MAX_VALUE).size();
+            fProcessCount = activityManager == null ? -1 : activityManager.getRunningServices(Integer.MAX_VALUE).size();
+            fRunningAppProcess = activityManager == null ? -1 : activityManager.getRunningAppProcesses().size();
+            fTaskCount = activityManager == null ? -1 : activityManager.getRunningTasks(Integer.MAX_VALUE).size();
         }
     }
 

+ 9 - 1
app/src/main/java/info/knacki/prometheusandroidexporter/configuration/ConfigurationManager.java

@@ -8,30 +8,36 @@ import androidx.annotation.NonNull;
 public class ConfigurationManager {
     public static class Configuration {
         private short fPort = 9100;
+        private boolean fStartOnBootEnabled = true;
 
         Configuration() {
         }
 
         public short GetPort() { return fPort; }
         public void SetPort(short value) { fPort = value; }
+        public boolean IsStartOnBootEnabled() { return fStartOnBootEnabled; }
+        public void SetStartOnBootEnabled(boolean value) { fStartOnBootEnabled = value; }
 
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Configuration that = (Configuration) o;
-            return fPort == that.fPort;
+            return fPort == that.fPort
+                    && fStartOnBootEnabled == that.fStartOnBootEnabled;
         }
 
         @NonNull
         protected Configuration Clone() {
             Configuration result = new Configuration();
             result.fPort = fPort;
+            result.fStartOnBootEnabled = fStartOnBootEnabled;
             return result;
         }
     }
 
     public static final String CONFIG_PORT = "port";
+    public static final String CONFIG_START_ON_BOOT_ENABLED = "startOnBootEnabled";
 
     private static ConfigurationManager gInstance;
     private Configuration fSavedConfiguration = new Configuration();
@@ -39,6 +45,7 @@ public class ConfigurationManager {
     private ConfigurationManager(Context ctx) {
         SharedPreferences prefs = ctx.getSharedPreferences(ConfigurationManager.class.getName(), Context.MODE_PRIVATE);
         fSavedConfiguration.fPort = (short) prefs.getInt(CONFIG_PORT, fSavedConfiguration.fPort);
+        fSavedConfiguration.fStartOnBootEnabled = prefs.getBoolean(CONFIG_START_ON_BOOT_ENABLED, fSavedConfiguration.fStartOnBootEnabled);
     }
 
     public static ConfigurationManager GetInstance(Context ctx) {
@@ -60,6 +67,7 @@ public class ConfigurationManager {
         GetInstance(ctx);
         SharedPreferences.Editor prefs = ctx.getSharedPreferences(ConfigurationManager.class.getName(), Context.MODE_PRIVATE).edit();
         prefs.putInt(CONFIG_PORT, config.fPort);
+        prefs.putBoolean(CONFIG_START_ON_BOOT_ENABLED, config.fStartOnBootEnabled);
         prefs.apply();
         GetInstance(ctx).fSavedConfiguration = config.Clone();
     }

+ 4 - 1
app/src/main/java/info/knacki/prometheusandroidexporter/receiver/BootReceiver.java

@@ -5,10 +5,13 @@ import android.content.Context;
 import android.content.Intent;
 
 import info.knacki.prometheusandroidexporter.MainService;
+import info.knacki.prometheusandroidexporter.configuration.ConfigurationManager;
 
 public class BootReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
-        MainService.StartService(context);
+        if (ConfigurationManager.GetInstance(context.getApplicationContext()).GetConfiguration().IsStartOnBootEnabled() &&
+                (Intent.ACTION_BOOT_COMPLETED.equalsIgnoreCase(intent.getAction())))
+            MainService.StartService(context);
     }
 }

+ 12 - 6
app/src/main/java/info/knacki/prometheusandroidexporter/receiver/NetworkReceiver.java

@@ -40,23 +40,29 @@ public class NetworkReceiver extends BroadcastReceiver {
         synchronized (NetworkReceiver.class) {
             if (gInstance == null) {
                 synchronized (NetworkReceiver.class) {
-                    IntentFilter intentFilter = new IntentFilter();
-                    intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
-                    intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
                     gInstance = new NetworkReceiver();
-                    ctx.registerReceiver(gInstance, intentFilter);
                 }
             }
         }
+        try {
+            ctx.unregisterReceiver(gInstance);
+        }
+        catch (IllegalArgumentException e) {
+            // Service not registered yet
+        }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+        intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+        ctx.registerReceiver(gInstance, intentFilter);
         gInstance.Tick(ctx);
     }
 
     private static NetworkInfo GetFirstNetworkAvailable(Context ctx) {
         ConnectivityManager manager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo netInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        NetworkInfo netInfo = manager == null ? null : manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
         if (netInfo != null && netInfo.isConnected())
             return netInfo;
-        netInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
+        netInfo = manager == null ? null : manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
         return (netInfo != null && netInfo.isConnected()) ? netInfo : null;
     }
 

+ 58 - 55
app/src/main/res/layout/activity_main.xml

@@ -1,65 +1,78 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context=".MainActivity">
 
-    <TableLayout
+    <androidx.gridlayout.widget.GridLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <TextView
-                android:id="@+id/textView"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="TextView" />
-
-            <EditText
-                android:id="@+id/input_port"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ems="10"
-                android:inputType="numberSigned" />
-
-        </TableRow>
-
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <Button
-                android:id="@+id/bt_saveconfiguration"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="Save" />
-        </TableRow>
-
-        <TableRow
+        android:layout_height="match_parent"
+        app:columnCount="2"
+        app:rowCount="5">
+
+        <TextView
+            android:id="@+id/textView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/port"
+            app:layout_gravity="fill_horizontal"
+            app:layout_column="0"
+            app:layout_row="0" />
+
+        <EditText
+            android:id="@+id/input_port"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ems="5"
+            android:inputType="numberSigned"
+            app:layout_column="1"
+            app:layout_row="0" />
+
+        <androidx.appcompat.widget.SwitchCompat
+            android:id="@+id/input_startonboot"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/startonboot"
+            app:layout_column="0"
+            app:layout_columnSpan="2"
+            app:layout_row="1" />
+
+        <Button
+            android:id="@+id/bt_saveconfiguration"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/save"
+            app:layout_gravity="fill_horizontal"
+            app:layout_column="1"
+            app:layout_row="2" />
+
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
+            android:layout_height="wrap_content"
+            app:layout_column="0"
+            app:layout_columnSpan="2"
+            app:layout_row="3">
 
             <TextView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_weight="1"
                 android:text="@string/service_is" />
 
             <TextView
                 android:id="@+id/txt_servicestate"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1" />
+                android:layout_height="wrap_content" />
+        </LinearLayout>
 
-            <Space
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1" />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_column="0"
+            app:layout_columnSpan="2"
+            app:layout_row="4">
 
             <Button
                 android:id="@+id/bt_killservice"
@@ -76,17 +89,7 @@
                 android:layout_weight="1"
                 android:enabled="false"
                 android:text="@string/start" />
+        </LinearLayout>
+    </androidx.gridlayout.widget.GridLayout>
 
-        </TableRow>
-
-    </TableLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        tools:layout_editor_absoluteX="36dp"
-        tools:layout_editor_absoluteY="106dp">
-
-    </LinearLayout>
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -3,4 +3,7 @@
     <string name="service_is">Service is</string>
     <string name="kill">kill</string>
     <string name="start">Start</string>
+    <string name="save">Save</string>
+    <string name="port">Port</string>
+    <string name="startonboot">Start service on boot</string>
 </resources>

+ 4 - 0
app/src/main/res/xml/backup_descriptor.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<full-backup-content>
+    <!-- Exclude specific shared preferences that contain GCM registration Id -->
+</full-backup-content>

+ 29 - 0
build.gradle

@@ -0,0 +1,29 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    
+    repositories {
+        google()
+        jcenter()
+        
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:4.1.3'
+        
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 20 - 0
gradle.properties

@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+

+ 2 - 0
settings.gradle

@@ -0,0 +1,2 @@
+rootProject.name='Prometheus android exporter'
+include ':app'