diff --git a/.gitignore b/.gitignore
index e511255..c737d54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,17 +41,7 @@ captures/
# IntelliJ
*.iml
-.idea/workspace.xml
-.idea/tasks.xml
-.idea/gradle.xml
-.idea/assetWizardSettings.xml
-.idea/dictionaries
-.idea/libraries
-# Android Studio 3 in .gitignore file.
-.idea/caches
-.idea/modules.xml
-# Comment next line if keeping position of elements in Navigation Editor is relevant for you
-.idea/navEditor.xml
+.idea/
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index e0e4c70..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-USB Gadget Tool
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 681f41a..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index fb7f4a8..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index a5f05cd..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 860da66..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index e497da9..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 910f1c5..16fccf7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion 33
@@ -23,6 +24,11 @@ android {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
+
+ buildFeatures {
+ viewBinding true
+ }
+
}
dependencies {
@@ -34,6 +40,10 @@ dependencies {
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
+
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85cd48e..7a9a710 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
+
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/usbFunctionProfiles/CCID b/app/src/main/assets/usbFunctionProfiles/CCID
index bf18547..da0d9f5 100644
--- a/app/src/main/assets/usbFunctionProfiles/CCID
+++ b/app/src/main/assets/usbFunctionProfiles/CCID
@@ -1,6 +1,5 @@
#!/bin/sh
-GADGET="ccid"
GADGET_PATH="____gadgetPath____"
cd $GADGET_PATH/configs/
diff --git a/app/src/main/assets/usbFunctionProfiles/CTAP b/app/src/main/assets/usbFunctionProfiles/CTAP
index e931146..18f1f8c 100644
--- a/app/src/main/assets/usbFunctionProfiles/CTAP
+++ b/app/src/main/assets/usbFunctionProfiles/CTAP
@@ -1,6 +1,5 @@
#!/bin/sh
-GADGET="ctap"
GADGET_PATH="____gadgetPath____"
cd $GADGET_PATH/configs/
diff --git a/app/src/main/assets/usbFunctionProfiles/Keyboard b/app/src/main/assets/usbFunctionProfiles/Keyboard
index 6283238..18cf54e 100644
--- a/app/src/main/assets/usbFunctionProfiles/Keyboard
+++ b/app/src/main/assets/usbFunctionProfiles/Keyboard
@@ -1,6 +1,5 @@
#!/bin/sh
-GADGET="keyboard"
GADGET_PATH="____gadgetPath____"
cd $GADGET_PATH/configs/
diff --git a/app/src/main/assets/usbFunctionProfiles/Mouse b/app/src/main/assets/usbFunctionProfiles/Mouse
index 736976b..627a1ad 100644
--- a/app/src/main/assets/usbFunctionProfiles/Mouse
+++ b/app/src/main/assets/usbFunctionProfiles/Mouse
@@ -1,6 +1,5 @@
#!/bin/sh
-GADGET="mouse"
GADGET_PATH="____gadgetPath____"
cd $GADGET_PATH/configs/
diff --git a/app/src/main/assets/usbFunctionProfiles/UVC b/app/src/main/assets/usbFunctionProfiles/UVC
index 6b60f42..9557d17 100644
--- a/app/src/main/assets/usbFunctionProfiles/UVC
+++ b/app/src/main/assets/usbFunctionProfiles/UVC
@@ -1,6 +1,5 @@
#!/bin/sh
-GADGET="camera"
GADGET_PATH="____gadgetPath____"
cd $GADGET_PATH/configs/
diff --git a/app/src/main/assets/usbGadgetProfiles/CCID b/app/src/main/assets/usbGadgetProfiles/CCID
index d0f5ce6..1066cd7 100644
--- a/app/src/main/assets/usbGadgetProfiles/CCID
+++ b/app/src/main/assets/usbGadgetProfiles/CCID
@@ -3,7 +3,7 @@
CONFIGFS_DIR="/config"
GADGETS_PATH="${CONFIGFS_DIR}/usb_gadget"
-GADGET="ccid3"
+GADGET="____gadgetName____"
GADGET_PATH=${GADGETS_PATH}/${GADGET}
CONFIG_PATH="$GADGET_PATH/configs/c.1/"
@@ -27,6 +27,7 @@ echo "42" > serialnumber
cd $CONFIG_PATH
echo 30 > MaxPower
+mkdir -p strings/0x409
echo "Configuration" > strings/0x409/configuration
ln -s ${GADGET_PATH}/functions/ccid.usb0 $CONFIG_PATH/ccid.usb0
diff --git a/app/src/main/assets/usbGadgetProfiles/CTAP b/app/src/main/assets/usbGadgetProfiles/CTAP
index ac5278a..5582648 100644
--- a/app/src/main/assets/usbGadgetProfiles/CTAP
+++ b/app/src/main/assets/usbGadgetProfiles/CTAP
@@ -3,7 +3,7 @@
CONFIGFS_DIR="/config"
GADGETS_PATH="${CONFIGFS_DIR}/usb_gadget"
-GADGET="ctap3"
+GADGET="____gadgetName____"
GADGET_PATH=${GADGETS_PATH}/${GADGET}
CONFIG_PATH="$GADGET_PATH/configs/c.1/"
@@ -42,6 +42,7 @@ echo "42" > serialnumber
cd $CONFIG_PATH
echo 30 > MaxPower
+mkdir -p strings/0x409
echo "Configuration" > strings/0x409/configuration
ln -s ${GADGET_PATH}/functions/hid.usb0 $CONFIG_PATH/hid.usb0
\ No newline at end of file
diff --git a/app/src/main/assets/usbGadgetProfiles/Empty b/app/src/main/assets/usbGadgetProfiles/Empty
index b3be8b2..96c133c 100644
--- a/app/src/main/assets/usbGadgetProfiles/Empty
+++ b/app/src/main/assets/usbGadgetProfiles/Empty
@@ -3,7 +3,7 @@
CONFIGFS_DIR="/config"
GADGETS_PATH="${CONFIGFS_DIR}/usb_gadget"
-GADGET="gadget_${RANDOM}"
+GADGET="____gadgetName____"
GADGET_PATH=${GADGETS_PATH}/${GADGET}
CONFIG_PATH="$GADGET_PATH/configs/c.1/"
@@ -24,4 +24,5 @@ echo "42" > serialnumber
cd $CONFIG_PATH
echo 120 > MaxPower
+mkdir -p strings/0x409
echo "Configuration" > strings/0x409/configuration
diff --git a/app/src/main/assets/usbGadgetProfiles/Mouse & Keyboard b/app/src/main/assets/usbGadgetProfiles/MouseKeyboard
similarity index 97%
rename from app/src/main/assets/usbGadgetProfiles/Mouse & Keyboard
rename to app/src/main/assets/usbGadgetProfiles/MouseKeyboard
index e47b1d8..2d9d3b2 100644
--- a/app/src/main/assets/usbGadgetProfiles/Mouse & Keyboard
+++ b/app/src/main/assets/usbGadgetProfiles/MouseKeyboard
@@ -3,7 +3,7 @@
CONFIGFS_DIR="/config"
GADGETS_PATH="${CONFIGFS_DIR}/usb_gadget"
-GADGET="keyboard"
+GADGET="____gadgetName____"
GADGET_PATH=${GADGETS_PATH}/${GADGET}
CONFIG_PATH="$GADGET_PATH/configs/c.1/"
@@ -50,6 +50,7 @@ echo "42" > serialnumber
cd $CONFIG_PATH
echo 120 > MaxPower
+mkdir -p strings/0x409
echo "Configuration" > strings/0x409/configuration
ln -s ${GADGET_PATH}/functions/hid.keyboard $CONFIG_PATH/hid.keyboard
diff --git a/app/src/main/assets/usbGadgetProfiles/UVC b/app/src/main/assets/usbGadgetProfiles/UVC
index c20ce7b..7bc7574 100644
--- a/app/src/main/assets/usbGadgetProfiles/UVC
+++ b/app/src/main/assets/usbGadgetProfiles/UVC
@@ -3,7 +3,7 @@
CONFIGFS_DIR="/config"
GADGETS_PATH="${CONFIGFS_DIR}/usb_gadget"
-GADGET="uvc"
+GADGET="____gadgetName____"
GADGET_PATH=${GADGETS_PATH}/${GADGET}
CONFIG_PATH="$GADGET_PATH/configs/c.1/"
diff --git a/app/src/main/java/net/tjado/usbgadget/BootConfiguration.kt b/app/src/main/java/net/tjado/usbgadget/BootConfiguration.kt
new file mode 100644
index 0000000..6daa34f
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/BootConfiguration.kt
@@ -0,0 +1,44 @@
+package net.tjado.usbgadget
+
+import android.app.Application
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+class BootConfiguration : BroadcastReceiver() {
+ private val TAG = "DEVICE_BOOT"
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
+ Log.i(TAG, "Intent ACTION_LOCKED_BOOT_COMPLETED")
+ val gmPreferences = GadgetManagerPreferences(context)
+
+ val shellApi = GadgetShellApi()
+ if(!shellApi.hasRootSync()) {
+ Log.e(TAG, "No root permissions... aborting")
+ return
+ }
+ Log.i(TAG, "Seems root is available... proceeding...")
+
+ val assets = GadgetAssetProfiles(context.applicationContext as Application)
+ val activeGadget = gmPreferences.getActiveBootGadget()
+
+ for(profile in assets.allGadgetProfiles) {
+ if( gmPreferences.isAddedDuringBoot(profile) ) {
+ Log.i(TAG, "Adding gadget $profile")
+
+ shellApi.exec(assets.getProfileGadget(profile)!!) { response ->
+ if (response != null && response.first == true && profile == activeGadget) {
+ Log.i(TAG, "Activating gadget $profile")
+ shellApi.activate("/config/usb_gadget/${profile}", null)
+ } else if (response != null && response.first == false) {
+ Log.e(TAG, "Activation failed: ${response.second}")
+ }
+ }
+ }
+ }
+
+ Log.i(TAG, "Synchronous processing finished. ")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/CoroutineTask.kt b/app/src/main/java/net/tjado/usbgadget/CoroutineTask.kt
new file mode 100644
index 0000000..772c362
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/CoroutineTask.kt
@@ -0,0 +1,39 @@
+package net.tjado.usbgadget
+
+import kotlinx.coroutines.*
+
+// gist by Shanmuga Santhosh A
+// https://gist.github.com/shanmugasanthosh7/417f413f359d10c8f4581542a11b3f91/
+abstract class CoroutineTask {
+
+ protected open fun onPreExecute() {}
+
+ protected abstract fun doInBackground(vararg params: Params): APair
+
+ protected open fun onPostExecute(result: APair) {}
+
+ private val mUiScope by lazy { CoroutineScope(Dispatchers.Main) }
+
+ private lateinit var mJob: Job
+
+ fun execute(vararg params: Params) {
+ mJob = mUiScope.launch {
+ // run on UI/Main thread
+ onPreExecute()
+
+ // execute on worker thread
+ val result = async(Dispatchers.IO) { doInBackground(*params) }
+
+ // return result on UI/Main thread
+ onPostExecute(result.await())
+ }
+ }
+
+ fun cancel() {
+ if (::mJob.isInitialized && mJob.isActive) mJob.cancel()
+ }
+
+ fun cancelAll() {
+ if (mUiScope.isActive) mUiScope.cancel()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.java b/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.java
deleted file mode 100644
index c0d7076..0000000
--- a/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package net.tjado.usbgadget;
-
-import android.graphics.Typeface;
-import android.os.Bundle;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-import java.io.BufferedReader;
-import java.io.StringReader;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class DeviceInfoFragment extends Fragment {
-
- private MutableLiveData> deviceData;
- private View v;
-
- public DeviceInfoFragment() {}
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- this.v = inflater.inflate(R.layout.fragment_device_info, container, false);
-
- deviceData = new MutableLiveData<>();
- deviceData.setValue(new TreeMap<>(new DeviceInfoMapComparator()));
-
- deviceData.observe(getViewLifecycleOwner(), item -> {
- this.loadData(item);
- });
-
- GadgetShellApi gsa = new GadgetShellApi();
- gsa.updateDeviceInfo(deviceData);
-
- return v;
- }
-
- private void loadData(Map deviceData) {
-
- LinearLayout list = this.v.findViewById(R.id.list_device_data);
- list.removeAllViews();
-
- View viHead = getLayoutInflater().inflate(R.layout.row_device_info, null);
-
- TextView tvHeadName = viHead.findViewById(R.id.name);
- tvHeadName.setText("Kernel Config Parameter");
- tvHeadName.setTypeface(null, Typeface.BOLD);
-
- TextView tvHeadValue = viHead.findViewById(R.id.value);
- tvHeadValue.setText("Value");
- tvHeadValue.setTypeface(null, Typeface.BOLD);
- list.addView(viHead);
-
- for (Map.Entry entry : deviceData.entrySet()) {
- View vi = getLayoutInflater().inflate(R.layout.row_device_info, null);
-
- TextView tvName = vi.findViewById(R.id.name);
- tvName.setText(entry.getKey().toUpperCase());
-
- TextView tvValue = vi.findViewById(R.id.value);
- String value = entry.getValue();
-
- String color;
- switch (value) {
- case "y":
- value = "Yes";
- color = "#008000";
- break;
- case "n":
- value = "No";
- color = "#ff0000";
- break;
- case "NOT_SET":
- value = "Not set";
- color = "#ff0000";
- break;
- default:
- color = "#000000";
- }
-
- tvValue.setText(Html.fromHtml(String.format("%s", color, value), Html.FROM_HTML_MODE_LEGACY));
-
- list.addView(vi);
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.kt b/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.kt
new file mode 100644
index 0000000..fd307cd
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/DeviceInfoFragment.kt
@@ -0,0 +1,79 @@
+package net.tjado.usbgadget
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.os.Bundle
+import android.graphics.Typeface
+import android.text.Html
+import android.view.View
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.MutableLiveData
+import net.tjado.usbgadget.databinding.FragmentDeviceInfoBinding
+import net.tjado.usbgadget.databinding.RowDeviceInfoBinding
+import java.util.*
+
+
+class DeviceInfoFragment : Fragment() {
+ private var _binding: FragmentDeviceInfoBinding? = null
+ private val binding get() = _binding!!
+
+ private var deviceData: MutableLiveData?>? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentDeviceInfoBinding.inflate(inflater, container, false)
+ val view = binding.root
+
+ deviceData = MutableLiveData()
+ deviceData!!.value = TreeMap(DeviceInfoMapComparator())
+ deviceData!!.observe(viewLifecycleOwner) { item: TreeMap? -> loadData(item) }
+ val shellAPi = GadgetShellApi()
+ shellAPi.updateDeviceInfo(deviceData!!)
+
+ return view
+ }
+
+ private fun loadData(deviceData: Map?) {
+ binding.listDeviceData.removeAllViews()
+
+ val rowHead = RowDeviceInfoBinding.inflate(layoutInflater)
+ rowHead.name.text = "Kernel Config Parameter"
+ rowHead.name.setTypeface(null, Typeface.BOLD)
+ rowHead.value.text = "Value"
+ rowHead.value.setTypeface(null, Typeface.BOLD)
+
+ binding.listDeviceData.addView(rowHead.root)
+
+ for (entry in deviceData!!.entries) {
+ val rowEntry = RowDeviceInfoBinding.inflate(layoutInflater)
+ rowEntry.name.text = entry.key.uppercase(Locale.getDefault())
+
+ var value = entry.value
+ var color: String
+ when (value) {
+ "y" -> {
+ value = "Yes"
+ color = "#008000"
+ }
+ "n" -> {
+ value = "No"
+ color = "#ff0000"
+ }
+ "NOT_SET" -> {
+ value = "Not set"
+ color = "#ff0000"
+ }
+ else -> color = "#000000"
+ }
+ rowEntry.value.text = Html.fromHtml(
+ String.format("%s", color, value),
+ Html.FROM_HTML_MODE_LEGACY
+ )
+
+ binding.listDeviceData.addView(rowEntry.root)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.java b/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.java
deleted file mode 100644
index e6aeae9..0000000
--- a/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.tjado.usbgadget;
-
-import java.util.Comparator;
-import java.util.TreeMap;
-
-public class DeviceInfoMapComparator implements Comparator {
-
- @Override
- public int compare(String s1, String s2) {
- if (s1.equalsIgnoreCase("KERNEL_VERSION")) {
- return -1;
- } else if (s2.equalsIgnoreCase("KERNEL_VERSION")) {
- return 1;
- } else {
- return s1.compareTo(s2);
- }
- }
-}
diff --git a/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.kt b/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.kt
new file mode 100644
index 0000000..4dada15
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/DeviceInfoMapComparator.kt
@@ -0,0 +1,15 @@
+package net.tjado.usbgadget
+
+import java.util.Comparator
+
+class DeviceInfoMapComparator : Comparator {
+ override fun compare(s1: String, s2: String): Int {
+ return if (s1.equals("KERNEL_VERSION", ignoreCase = true)) {
+ -1
+ } else if (s2.equals("KERNEL_VERSION", ignoreCase = true)) {
+ 1
+ } else {
+ s1.compareTo(s2)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.java b/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.java
deleted file mode 100644
index b389d49..0000000
--- a/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * Authorizer
- *
- * Copyright 2016 by Tjado Mäcke
- * Licensed under GNU General Public License 3.0.
- *
- * @license GPL-3.0
- */
-package net.tjado.usbgadget;
-
-import android.util.Pair;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-/*
- * Class by muzikant
- *
- * Reference:
- * http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html
- * http://stackoverflow.com/a/7102780
- */
-
-public class ExecuteAsRootUtil
-{
-
- public static boolean canRunRootCommands()
- {
- boolean retval = false;
- Process suProcess;
-
- try
- {
- suProcess = Runtime.getRuntime().exec("su");
-
- DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
- DataInputStream osRes = new DataInputStream(suProcess.getInputStream());
-
- if (null != os && null != osRes)
- {
- // Getting the id of the current user to check if this is root
- os.writeBytes("id\n");
- os.flush();
-
- String currUid = osRes.readLine();
- boolean exitSu = false;
- if (null == currUid)
- {
- retval = false;
- exitSu = false;
- Log.d("ROOT", "Can't get root access or denied by user");
- }
- else if (currUid.contains("uid=0"))
- {
- retval = true;
- exitSu = true;
- Log.d("ROOT", "Root access granted");
- }
- else
- {
- retval = false;
- exitSu = true;
- Log.d("ROOT", "Root access rejected: " + currUid);
- }
-
- if (exitSu)
- {
- os.writeBytes("exit\n");
- os.flush();
- }
- }
- }
- catch (Exception e)
- {
- // Can't get root !
- // Probably broken pipe exception on trying to write to output stream (os) after su failed, meaning that the device is not rooted
-
- retval = false;
- Log.d("ROOT", "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
- }
-
- return retval;
- }
-
- public static Pair execute(String command) {
- return execute(new String[]{ command });
- }
-
- public static Pair execute(String[] commands)
- {
- Boolean retval = false;
- String output = null;
-
- try
- {
- if (commands != null && commands.length > 0)
- {
- Process suProcess = Runtime.getRuntime().exec("su -");
-
- DataOutputStream stdin = new DataOutputStream(suProcess.getOutputStream());
- InputStream stdout = suProcess.getInputStream();
- InputStream stderr = suProcess.getErrorStream();
-
- for (String cmd: commands) {
- Log.d("ROOT", String.format("Execute command: %s", cmd));
- stdin.writeBytes(cmd);
- stdin.flush();
- }
-
- stdin.writeBytes("exit\n");
- stdin.flush();
-
- stdin.close();
-
- StringBuffer sbOut = new StringBuffer();
- Scanner scanner = new Scanner(stdout);
- while (scanner.hasNext()) {
- sbOut.append(scanner.nextLine());
- sbOut.append(System.lineSeparator());
- }
-
- StringBuffer sbErr = new StringBuffer();
- Scanner scannerErr = new Scanner(stderr);
- while (scannerErr.hasNext()) {
- sbErr.append(scannerErr.nextLine());
- if (scannerErr.hasNext())
- sbErr.append(System.lineSeparator());
- }
-
- output = sbOut.toString();
- Log.d("ROOT (stdout)", output);
-
- String error = sbErr.toString();
- Log.d("ROOT (stderr)", error);
-
- retval = ! error.equals("Permission denied");
- }
- }
- catch (IOException | SecurityException ex)
- {
- Log.w("ROOT", "Can't get root access", ex);
- }
- catch (Exception ex)
- {
- Log.w("ROOT", "Error executing internal operation", ex);
- }
-
- return new Pair(retval, output);
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.kt b/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.kt
new file mode 100644
index 0000000..efae1c2
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/ExecuteAsRootUtil.kt
@@ -0,0 +1,120 @@
+/**
+ * Authorizer
+ *
+ * Copyright 2016 by Tjado Mäcke @maecke.de>
+ * Licensed under GNU General Public License 3.0.
+ *
+ * @license GPL-3.0 //opensource.org/licenses/GPL-3.0>
+ */
+package net.tjado.usbgadget
+
+import androidx.core.util.Pair
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.IOException
+import java.lang.Exception
+import java.util.*
+
+/*
+ * Class by muzikant
+ *
+ * Reference:
+ * http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html
+ * http://stackoverflow.com/a/7102780
+ */
+object ExecuteAsRootUtil {
+ fun canRunRootCommands(): Boolean {
+ var retval = false
+ val suProcess: Process
+ try {
+ suProcess = Runtime.getRuntime().exec("su")
+ val os = DataOutputStream(suProcess.outputStream)
+ val osRes = DataInputStream(suProcess.inputStream)
+
+ if (null != os && null != osRes) {
+ // Getting the id of the current user to check if this is root
+ os.writeBytes("id\n")
+ os.flush()
+ val currUid = osRes.readLine()
+ val exitSu: Boolean
+
+ if (null == currUid) {
+ retval = false
+ exitSu = false
+ Log.d("ROOT", "Can't get root access or denied by user")
+ } else if (currUid.contains("uid=0")) {
+ retval = true
+ exitSu = true
+ Log.d("ROOT", "Root access granted")
+ } else {
+ retval = false
+ exitSu = true
+ Log.d("ROOT", "Root access rejected: $currUid")
+ }
+
+ if (exitSu) {
+ os.writeBytes("\nexit\n")
+ os.flush()
+ }
+ }
+ } catch (e: Exception) {
+ // Can't get root !
+ // Probably broken pipe exception on trying to write to output stream (os) after su failed, meaning that the device is not rooted
+ retval = false
+ Log.d("ROOT", "Root access rejected [" + e.javaClass.name + "] : " + e.message)
+ }
+
+ return retval
+ }
+
+ fun execute(command: String): Pair<*, *> {
+ return execute(arrayOf(command))
+ }
+
+ fun execute(commands: Array?): Pair<*, *> {
+ var retval = false
+ var output: String? = null
+
+ try {
+ if (commands != null && commands.isNotEmpty()) {
+ val suProcess = Runtime.getRuntime().exec("su -")
+ val stdin = DataOutputStream(suProcess.outputStream)
+ val stdout = suProcess.inputStream
+ val stderr = suProcess.errorStream
+ for (cmd in commands) {
+ Log.d("ROOT", String.format("Execute command: %s", cmd))
+ stdin.writeBytes(cmd)
+ stdin.flush()
+ }
+ stdin.writeBytes("\nexit\n")
+ stdin.flush()
+ stdin.close()
+ val sbOut = StringBuffer()
+ val scanner = Scanner(stdout)
+ while (scanner.hasNext()) {
+ sbOut.append(scanner.nextLine())
+ sbOut.append(System.lineSeparator())
+ }
+ val sbErr = StringBuffer()
+ val scannerErr = Scanner(stderr)
+ while (scannerErr.hasNext()) {
+ sbErr.append(scannerErr.nextLine())
+ if (scannerErr.hasNext()) sbErr.append(System.lineSeparator())
+ }
+ output = sbOut.toString()
+ Log.d("ROOT (stdout)", output)
+ val error = sbErr.toString()
+ Log.d("ROOT (stderr)", error)
+ retval = error != "Permission denied"
+ }
+ } catch (ex: IOException) {
+ Log.w("ROOT", "Can't get root access", ex)
+ } catch (ex: SecurityException) {
+ Log.w("ROOT", "Can't get root access", ex)
+ } catch (ex: Exception) {
+ Log.w("ROOT", "Error executing internal operation", ex)
+ }
+
+ return Pair(retval, output)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.java b/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.java
deleted file mode 100644
index 0da62ca..0000000
--- a/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package net.tjado.usbgadget;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.cardview.widget.CardView;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-public class GadgetAdapter extends RecyclerView.Adapter {
- private Activity context;
- private LayoutInflater inflater;
- private final GadgetAdapterClickInterface listener;
- List data;
-
- public GadgetAdapter(Activity context, List data, GadgetAdapterClickInterface clickListener) {
- this.context = context;
- this.data = data;
- listener = clickListener;
- }
-
- // Inflate the layout when viewholder created
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-
- View view = inflater.from(parent.getContext()).
- inflate(R.layout.cardview_gadget, parent, false);
- return new DefaultViewHolder(view, listener);
- }
-
- @Override
- public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
-
- initLayoutDefault((DefaultViewHolder) holder, position);
- }
-
-
- @Override
- public int getItemCount() {
- return data.size();
- }
-
- private void initLayoutDefault(DefaultViewHolder holder, int pos) {
- holder.gadget = data.get(pos);
- holder.manufacturer.setText(holder.gadget.getValue("manufacturer"));
- holder.product.setText(holder.gadget.getValue("product"));
- holder.serialnumber.setText(holder.gadget.getValue("serialnumber"));
- holder.path.setText(holder.gadget.getValue("gadget_path"));
- holder.functions.setText(Html.fromHtml(holder.gadget.getFormattedFunctions(), Html.FROM_HTML_MODE_LEGACY));
-
- holder.status.setChecked(holder.gadget.isActivated());
-
- String udc = holder.gadget.getValue("udc");
- holder.udc.setText(udc);
- if (holder.gadget.isActivated()) {
- holder.card.setCardBackgroundColor(Color.WHITE);
- } else {
- holder.card.setCardBackgroundColor(Color.LTGRAY);
- }
-
- }
-
- // Static inner class to initialize the views of rows
- static class DefaultViewHolder extends RecyclerView.ViewHolder {
- GadgetObject gadget;
- CardView card;
- TextView manufacturer;
- TextView product;
- TextView serialnumber;
- TextView udc;
- TextView functions;
- TextView path;
- Switch status;
-
- private WeakReference listenerRef;
-
- public DefaultViewHolder(View itemView, GadgetAdapterClickInterface clickInterface) {
- super(itemView);
- card = itemView.findViewById(R.id.cv_gadget);
- manufacturer = itemView.findViewById(R.id.tv_gadget_manufacturer);
- product = itemView.findViewById(R.id.tv_gadget_product);
- serialnumber = itemView.findViewById(R.id.tv_gadget_sn);
- udc = itemView.findViewById(R.id.tv_gadget_udc);
- path = itemView.findViewById(R.id.tv_gadget_path);
- functions = itemView.findViewById(R.id.tv_gadget_functions);
- status = itemView.findViewById(R.id.tv_gadget_status);
-
- status.setOnClickListener((buttonView) -> {
- listenerRef.get().onStatusChange(gadget, status.isChecked());
- });
-
- listenerRef = new WeakReference<>(clickInterface);
-
- itemView.setOnClickListener(view -> {
- listenerRef.get().onGadgetClicked( gadget );
- });
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.kt b/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.kt
new file mode 100644
index 0000000..f8a37ca
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/GadgetAdapter.kt
@@ -0,0 +1,76 @@
+package net.tjado.usbgadget
+
+import android.app.Activity
+import android.graphics.Color
+import android.text.Html
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import net.tjado.usbgadget.databinding.CardviewGadgetBinding
+import java.lang.ref.WeakReference
+
+
+class GadgetAdapter(
+ private val context: Activity,
+ private var data: List,
+ private val listener: GadgetAdapterClickInterface
+) : RecyclerView.Adapter() {
+ private val inflater: LayoutInflater? = null
+
+ private var _binding: CardviewGadgetBinding? = null
+ private val binding get() = _binding!!
+
+ interface GadgetAdapterClickInterface {
+ fun onGadgetClicked(gadget: GadgetObject)
+ fun onStatusChange(gadget: GadgetObject, newStatus: Boolean)
+ }
+
+ // Inflate the layout when viewholder created
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DefaultViewHolder {
+ _binding = CardviewGadgetBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+
+ return DefaultViewHolder(binding, listener)
+ }
+
+ override fun onBindViewHolder(holder: DefaultViewHolder, position: Int) {
+ with(holder) {
+ gadget = data[position]
+ binding.tvGadgetManufacturer.text = gadget?.getValue("manufacturer")
+ binding.tvGadgetProduct.text = gadget?.getValue("product")
+ binding.tvGadgetSn.text = gadget?.getValue("serialnumber")
+ binding.tvGadgetPath.text = gadget?.getValue("gadget_path")
+ binding.tvGadgetFunctions.text = Html.fromHtml(holder.gadget?.formattedFunctions, Html.FROM_HTML_MODE_LEGACY)
+ binding.tvGadgetStatus.isChecked = gadget?.isActivated == true
+
+ val udc = gadget!!.getValue("udc")
+ binding.tvGadgetUdc.text = udc
+ if (gadget?.isActivated == true) {
+ binding.cvGadget.setCardBackgroundColor(Color.WHITE)
+ } else {
+ binding.cvGadget.setCardBackgroundColor(Color.LTGRAY)
+ }
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return data.size
+ }
+
+ // Static inner class to initialize the views of rows
+ inner class DefaultViewHolder(val binding: CardviewGadgetBinding, clickInterface: GadgetAdapterClickInterface) : RecyclerView.ViewHolder(binding.root) {
+ var gadget: GadgetObject? = null
+ private val listenerRef: WeakReference
+
+ init {
+ listenerRef = WeakReference(clickInterface)
+
+ binding.tvGadgetStatus.setOnClickListener {
+ gadget?.let { listenerRef.get()?.onStatusChange(it, binding.tvGadgetStatus.isChecked) }
+ }
+
+ itemView.setOnClickListener {
+ gadget?.let { listenerRef.get()?.onGadgetClicked(it) }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetAdapterClickInterface.java b/app/src/main/java/net/tjado/usbgadget/GadgetAdapterClickInterface.java
deleted file mode 100644
index 4141292..0000000
--- a/app/src/main/java/net/tjado/usbgadget/GadgetAdapterClickInterface.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.tjado.usbgadget;
-
-public interface GadgetAdapterClickInterface {
-
- void onGadgetClicked(GadgetObject gadget);
- void onStatusChange( GadgetObject gadget, Boolean newStatus);
-}
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.java b/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.java
deleted file mode 100644
index 9f7699c..0000000
--- a/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package net.tjado.usbgadget;
-
-import android.app.Application;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
-
-public class GadgetAssetProfiles {
-
- private Application app;
-
- public GadgetAssetProfiles(Application app) {
- this.app = app;
- }
-
- public String[] getAllGadgetProfiles() {
- String [] gadgetProfileList;
-
- try {
- gadgetProfileList = this.app.getAssets().list("usbGadgetProfiles/");
- } catch (IOException e) {
- gadgetProfileList = null;
- }
-
- return gadgetProfileList;
- }
-
- public String[] getAllFunctionProfiles() {
- String [] gadgetProfileList;
-
- try {
- gadgetProfileList = this.app.getAssets().list("usbFunctionProfiles/");
- } catch (IOException e) {
- gadgetProfileList = null;
- }
-
- return gadgetProfileList;
- }
-
- public String getProfileFromAsset(String profileFolder, String assetFile) {
- try {
- InputStream is = this.app.getAssets().open(String.format("%s/%s", profileFolder, assetFile));
- Scanner scanner = new Scanner(is);
-
- StringBuffer sb = new StringBuffer();
- while (scanner.hasNext()) {
- sb.append(scanner.nextLine()).append("\n");
- }
-
- return sb.toString();
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public String getProfileGadget(String assetFile) {
- return getProfileFromAsset("usbGadgetProfiles", assetFile);
- }
-
- public String getProfileFunction(String assetFile, String gadgetPath) {
- if (!GadgetShellApi.isValidGadgetPath(gadgetPath)) {
- return null;
- }
-
- Map tokens = new HashMap<>();
- tokens.put("gadgetPath", gadgetPath);
- String profile = getProfileFromAsset("usbFunctionProfiles", assetFile);
-
- return parseAsset(profile, tokens);
- }
-
- public String parseAsset(String profile, Map tokens) {
- if (profile == null || profile.equals("")) {
- return "";
- }
-
- if (tokens != null && tokens.size() > 0) {
- for (Map.Entry token : tokens.entrySet()) {
- profile = profile.replace("____" + token.getKey() + "____", token.getValue());
- }
- }
-
- return profile;
- }
-}
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.kt b/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.kt
new file mode 100644
index 0000000..c7af33c
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/GadgetAssetProfiles.kt
@@ -0,0 +1,64 @@
+package net.tjado.usbgadget
+
+import android.app.Application
+import android.text.TextUtils
+
+
+class GadgetAssetProfiles(private val app: Application) {
+
+ val allGadgetProfiles: Array
+ get() {
+ return app.assets.list("usbGadgetProfiles/") as Array
+ }
+
+ val allFunctionProfiles: Array
+ get() {
+ return app.assets.list("usbFunctionProfiles/") as Array
+ }
+
+ fun getProfileFromAsset(profileFolder: String, assetFile: String): String {
+ val inputStream = app.assets.open("$profileFolder/$assetFile")
+ return inputStream.bufferedReader().use { it.readText() }
+ }
+
+ fun getProfileGadget(assetFile: String): String? {
+ return getProfileGadget(assetFile, assetFile)
+ }
+
+ fun getProfileGadget(assetFile: String, gadgetName: String): String? {
+ if (TextUtils.isEmpty(gadgetName)) {
+ return null
+ }
+
+ val tokens: MutableMap = HashMap()
+ tokens["gadgetName"] = gadgetName
+ val profile = getProfileFromAsset("usbGadgetProfiles", assetFile)
+ return parseAsset(profile, tokens)
+ }
+
+ fun getProfileFunction(assetFile: String, gadgetPath: String): String? {
+ if (!GadgetShellApi.isValidGadgetPath(gadgetPath)) {
+ return null
+ }
+
+ val tokens: MutableMap = HashMap()
+ tokens["gadgetPath"] = gadgetPath
+ val profile = getProfileFromAsset("usbFunctionProfiles", assetFile)
+ return parseAsset(profile, tokens)
+ }
+
+ fun parseAsset(profile: String, tokens: Map): String {
+ if (TextUtils.isEmpty(profile)) {
+ return ""
+ }
+
+ var parsedProfile = profile
+ if (tokens.isNotEmpty()) {
+ for ((key, value) in tokens) {
+ parsedProfile = parsedProfile.replace("____" + key + "____", value)
+ }
+ }
+
+ return parsedProfile
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetFragment.java b/app/src/main/java/net/tjado/usbgadget/GadgetFragment.java
deleted file mode 100644
index 9e9c813..0000000
--- a/app/src/main/java/net/tjado/usbgadget/GadgetFragment.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package net.tjado.usbgadget;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-import android.widget.ViewFlipper;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
-
-import java.util.ArrayList;
-
-public class GadgetFragment extends Fragment {
-
- private GadgetViewModel gadgetViewModel;
- private ViewFlipper rootFlipper;
- private GadgetObject gadget;
- private String[] functionProfileList;
- private View view;
-
- public GadgetFragment(GadgetObject g) {
- this.gadget = g;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- view = inflater.inflate(R.layout.fragment_gadget, container, false);
- gadgetViewModel = new ViewModelProvider(getActivity()).get(GadgetViewModel.class);
-
- rootFlipper = view.findViewById(R.id.flipper);
-
- gadgetViewModel.getGadgets().observe(getViewLifecycleOwner(), item -> {
- for (GadgetObject g : item) {
- if (g.getValue("gadget_path").equals(this.gadget.getValue("gadget_path"))) {
- this.gadget = g;
- refreshData();
- }
- }
- });
-
- gadgetViewModel.hasRootPermissions().observe(getViewLifecycleOwner(), item -> {
- rootFlipper.setDisplayedChild(item ? 0 : 1);
- });
-
- GadgetAssetProfiles gap = new GadgetAssetProfiles(getActivity().getApplication());
- functionProfileList = gap.getAllFunctionProfiles();
-
- Button addFunction = view.findViewById(R.id.btn_function_add);
- addFunction.setOnClickListener(v -> {
- final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getContext());
- alertBuilder.setTitle("Add Function to Gadget");
- alertBuilder.setCancelable(true);
-
- alertBuilder.setItems(functionProfileList, (dialog, which) -> {
- gadgetViewModel.loadFunctionProfile(gadget, functionProfileList[which]);
- });
-
- alertBuilder.show();
- });
-
-
- refreshData();
-
- return view;
- }
-
- private void refreshData() {
- View v = view;
- TextView manufacturer = v.findViewById(R.id.tv_gadget_manufacturer);
- TextView product = v.findViewById(R.id.tv_gadget_product);
- TextView serialnumber = v.findViewById(R.id.tv_gadget_sn);
- TextView udc = v.findViewById(R.id.tv_gadget_udc);
- TextView path = v.findViewById(R.id.tv_gadget_path);
- Switch status = v.findViewById(R.id.tv_gadget_status);
-
- manufacturer.setText(gadget.getValue("manufacturer"));
- product.setText(gadget.getValue("product"));
- serialnumber.setText(gadget.getValue("serialnumber"));
- path.setText(gadget.getValue("gadget_path"));
- udc.setText(gadget.getValue("udc"));
-
- status.setChecked(gadget.isActivated());
- status.setOnClickListener((buttonView) -> {
- if(status.isChecked()) {
- gadget.activate(response -> {
- gadgetViewModel.updateGadgetData();
- });
- } else {
- gadget.deactivate(response -> {
- gadgetViewModel.updateGadgetData();
- });
- }
- });
-
- LinearLayout list = v.findViewById(R.id.list_functons);
- list.removeAllViews();
-
- ArrayList functions = gadget.getFunctions();
- for(String functionName : functions) {
- View vi = getLayoutInflater().inflate(R.layout.row_function, null);
-
- TextView tv = vi.findViewById(R.id.name);
- tv.setText(functionName);
-
- Switch f_status = vi.findViewById(R.id.status);
- f_status.setChecked( gadget.getActiveFunctions().contains(functionName) );
- f_status.setOnClickListener((buttonView) -> {
- if (f_status.isChecked()){
- gadget.activateFunction(functionName, false);
- } else {
- gadget.deactivateFunction(functionName, true);
- }
- gadgetViewModel.reloadGadgetData();
- });
-
- list.addView(vi);
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/tjado/usbgadget/GadgetFragment.kt b/app/src/main/java/net/tjado/usbgadget/GadgetFragment.kt
new file mode 100644
index 0000000..98be221
--- /dev/null
+++ b/app/src/main/java/net/tjado/usbgadget/GadgetFragment.kt
@@ -0,0 +1,101 @@
+package net.tjado.usbgadget
+
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.os.Bundle
+
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AlertDialog
+import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.Fragment
+import net.tjado.usbgadget.databinding.FragmentGadgetBinding
+import net.tjado.usbgadget.databinding.RowFunctionBinding
+
+
+class GadgetFragment(private var gadget: GadgetObject) : Fragment() {
+ private var _binding: FragmentGadgetBinding? = null
+ private val binding get() = _binding!!
+
+ private lateinit var gadgetViewModel: GadgetViewModel
+ private lateinit var functionProfileList: Array
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentGadgetBinding.inflate(inflater, container, false)
+ val view = binding.root
+
+ gadgetViewModel = ViewModelProvider(requireActivity()).get(GadgetViewModel::class.java)
+ gadgetViewModel.gadgets.observe(viewLifecycleOwner) { item: List ->
+ for (g in item) {
+ if (g.getValue("gadget_path") == gadget.getValue("gadget_path")) {
+ gadget = g
+ refreshData()
+ }
+ }
+ }
+
+ gadgetViewModel.hasRootPermissions().observe(viewLifecycleOwner) {
+ item -> binding.flipper.displayedChild = if (item) 0 else 1
+ }
+
+ val assets = GadgetAssetProfiles(requireActivity().application)
+ functionProfileList = assets.allFunctionProfiles
+
+ val addFunction = view.findViewById