I'm trying to make an Android app with the Unity editor in order to read from an USB device. I'm using Unity 2019.4.12f1 and the android device I'm building to is a Xiaomi Mi Box S, running Android 9.
What I've done so far is to use Android Studio to compile some java code in order to gain access to the USB port on Xiaomi.
I'm using this repo: https://github.com/mik3y/usb-serial-for-android as the base functionality of the library I'm trying to build.
This is the code I wrote in my Java library:
public class Plugin extends Activity {
private static UsbSerialPort port;
private static final int READ_WAIT_MILLIS = 2000;
public static String Initialize(Context unityContext) throws IOException {
UsbManager manager = (UsbManager) unityContext.getSystemService(Context.USB_SERVICE);
ProbeTable customTable = new ProbeTable();
customTable.addProduct(0x067b, 0x2303, CdcAcmSerialDriver.class);
UsbSerialProber prober = new UsbSerialProber(customTable);
List<UsbSerialDriver> availableDrivers = prober.findAllDrivers(manager);
if (availableDrivers.isEmpty()) {
return "Drivers list is empty";
}
// Open a connection to the first available driver.
UsbSerialDriver driver = availableDrivers.get(0);
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
return "Connection is null";
}
port = driver.getPorts().get(0); // Most devices have just one port (port 0)
port.open(connection);
port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
return "Connection Successful";
}
public static String ReadPort() throws IOException {
if(port == null || !port.isOpen())
return "Null";
byte[] buffer = new byte[8192];
int len = port.read(buffer, READ_WAIT_MILLIS);
return buffer.toString();
}
public static void CloseSerialPort() throws IOException {
if(port != null && port.isOpen())
port.close();
}
And this is the C# code i'm using on the Unity side:
void Start()
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
AndroidJavaClass pluginClass = new AndroidJavaClass("com.hoho.android.usbserial.Plugin");
string response = pluginClass.CallStatic<string>("Initialize", context);
Debug.Log(response);
if (response.Contains("Successful"))
{
string read = pluginClass.CallStatic<string>("ReadPort");
Debug.Log(read);
}
else
{
Debug.Log("Init failed");
}
}
You can see that I've created a custom "Prober" for my device with the it's Vendor and Product ID's, so in theory the Java side should know what to look for. Unfortunately the collection "availableDrivers" is empty after the code executes.
On the C# side, I'm sending my unity activity for the Java to get it's context from. I see that the USB manager needs that context in order to be instanced. I did check and the variable "unityContext" is not null after is passed into the Java library from C#. Right now I'm investigating if maybe the context I'm sending to Java is not the right one, causing the USB manager not being able to access the USB devices.
The return String that I get from the Java code to Unity is: "Drivers list is empty", hence the conclusion that the USB drivers list is empty.
Any help or any kind of leading in the right direction is going to be helpful.
PS. Things I've checked:
I know that the USB device is able to communicate with the Android device because I've used an app to check and everything seemed fine. (https://play.google.com/store/apps/details?id=de.kai_morich.serial_usb_terminal&hl=en)
I'm answering my own question here. Maybe someone else will stumble upon this.
There were a couple of issues that I was not aware of:
The Xiaomi USB port will not continue to communicate with the sensor if it is set to USB Debugging Mode;
Again, even if the Xiaomi USB Debugging mode was turned off, it will not work. To make it work I had to restart the device.
The conclusion being that if you are working with Xiaomi Mi Box S make sure the hardware connection is working before you start changing anything on your code.
To make all of this work:
Make sure you understand how Java <-> C# communication works;
You will need to be a little bit familiar with Android Studio library builds;
Follow the instructions on the repo I posted.
You should be good to go at this point. Let me know if you need any further help.
Related
I need to create an Android application to set carrier configuration(VoLte e.g.). The application should fetch configs from our Back-End and apply them on the phone.
In Android documentation I found the following article: This article says, that I can create my own application and override CarrierService.
public class SampleCarrierConfigService extends CarrierService {
private static final String TAG = "SampleCarrierConfigService";
public SampleCarrierConfigService() {
Log.d(TAG, "Service created");
}
#Override
public PersistableBundle onLoadConfig(CarrierIdentifier id) {
Log.d(TAG, "Config being fetched");
PersistableBundle config = new PersistableBundle();
config.putBoolean(
CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL, true);
config.putBoolean(
CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
config.putInt(CarrierConfigManager.KEY_VOLTE_REPLACEMENT_RAT_INT, 6);
// Check CarrierIdentifier and add more config if needed…
return config;
}
}
I created an app with this service, but the method onLoadConfig(CarrierIdentifier id) is never called by the system.
So what I want from the system to call my overridden method, not system's. What should I do?
I found your question when researching how to do something similar.
In the article you linked it says:
The carrier app in question must be signed with the same certificate found on the SIM card, as documented in UICC Carrier Privileges.
Since we can't get the certificate from your carrier (they will never give it to you) I think we can't implement our own flavour sadly :-(
In our test case: I need to disable Wi-Fi at some specific point/action.
I have verified:
driver[deviceIndex].setConnection(Connection.NONE);
assertEquals(Connection.ALL, driver[deviceIndex].getConnection());
But it's not doing anything.
My expectation is "turning OFF wi-fi" and keeping Mobile Data ON.
Current code is doing no action.
You need to add Apache common langs to your project.
https://mvnrepository.com/artifact/org.apache.commons/commons-lang3/3.9
In appium you can only toggle the wifi network using driver.toggleWifi() method. That means it will change wifi to off state if it is in on state and vice versa.
To use toggleWifi() method you must use androidDriver. If you are using AppiumDriver you can typecast it to AndroidDriver like following.
((AndroidDriver) driver).toggleWifi();
You can also use setNetworkConnection() method but it also has certain limitaions. The limitations are:
What is the type of driver you are using? You should use AndroidDriver if you are working on Android. Here are the working code for me.
// turn on all (data and wi-fi)
public void turnOnAllData() {
driver.setConnection(Connection.ALL);
}
// turn off all (data and wi-fi)
public void turnOffAllData() {
driver.setConnection(Connection.NONE);
}
// turn on airplane
public void turnOnAirplaneMode() {
driver.setConnection(Connection.AIRPLANE);
}
// turn on data
public void turnOnMobileData() {
driver.setConnection(Connection.DATA);
}
// turn on wi-fi
public void turnOnWiFi() {
driver.setConnection(Connection.WIFI);
}
I'm in the middle of creating an application for Wear that has a phone companion app to do some of the heavier processes and make API calls.
So far in regards to communication between the two devices, I have been following this tutorial - http://android-wear-docs.readthedocs.io/en/latest/data.html
This has left me with the phone being able to communicate with the watch, but now I'm wondering how I replicate it working the other way.
The current code I have on the Wear app is like this (a seperate listener class is also present on the phone and watch):
public void sendTestMessage(View view)
{
String WEARABLE_DATA_PATH = "/wearable_data";
DataMap dataMap = new DataMap();
dataMap.putString("WATCH2PHONE TEST MESSAGE EVENT!", "1");
new SendToDataLayerThread(WEARABLE_DATA_PATH, dataMap).start();
}
class SendToDataLayerThread extends Thread
{
String path;
DataMap dataMap;
SendToDataLayerThread(String p, DataMap data)
{
path = p;
dataMap = data;
}
public void run()
{
PutDataMapRequest putDMR = PutDataMapRequest.create(path);
putDMR.getDataMap().putAll(dataMap);
PutDataRequest request = putDMR.asPutDataRequest();
DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleClient, request).await();
if (result.getStatus().isSuccess())
{
Log.v("myTag", "DataMap: " + dataMap + " sent successfully to data layer ");
}
else
{
Log.v("myTag", "ERROR: failed to send DataMap to data layer");
}
}
}
Listener Class (for watch, the phone version is identical):
public class ListenerService extends WearableListenerService
{
#Override
public void onDataChanged(DataEventBuffer dataEvents)
{
DataMap dataMap;
for (DataEvent event : dataEvents)
{
Log.v("myTag", "DataMap received on device: " + DataMapItem.fromDataItem(event.getDataItem()).getDataMap());
}
}
}
So sending the test message sends the messages to the layer and they send, but are received by the watch's listener service.
I'm assuming the "String WEARABLE_DATA_PATH = "/wearable_data";" is what's causing it to end up being received by the watch but I'm unsure what to change it to in order to get it to send properly.
The Data API works the same in both directions. The specific architecture to use on the handheld depends on your app's structure, but there is a good overview of the options in the documentation at https://developer.android.com/training/wearables/data-layer/events.html#Listen.
That page also discusses using a path to filter the data events received. Again, this depends on your architecture, but it might be reasonable to use paths like "/data_from_handheld" and "/data_from_wearable" to differentiate the DataItems going in the different directions. Or you can use the same path, and then the data events will appear on all connected devices - which may or may not be what you want.
One other common "gotcha": Data API events only fire when the data has actually changed. This is an efficient way for the API to handle real-world communications, but can make development difficult. To force it, simply include an item that is always different, such as:
dataMap.putLong("timestamp", System.nanoTime());
...but be sure to take that out before deploying the app!
Intro to me and my application school project
Hi,
iam pretty new with android and for some school project iam building an application where users can configure regions to recieve alerts from. The app need also make it posible to recieve alerts around the current location of the app user.
The app gets its info from a xml feed and sorts the data by the configured regions. The workflow is 1. to get the alerts which are in the configured regions. 2. When gps alerts are enabled the app need to get the location and when it is known it needs to do the first step again but this time the gps region is included. (i need to optimize this proces LATER)
(questions bellow)
intro to my app and problem
I'm using a asynctask in my application to download some xml feed. When the asynctask is ready i need to call 3 places for do something with the result.
1 class saves the result in the local database (alertmanager)
2 fragments (in a tabview) needs to show the results (1 in a map an one in a listview)
Now i use weakreferences for giving the call back "references" to the asynctask. in the onPostExecute() i use theWeakReference.get().updateMethod(result); for updating the class/fragments.
The alertmanager (the class who needs to recieve the updates) also calls a gps manager in the same method where it calls the asynctask to get the gps location. When i comment out (in my case with a if) the line what calls the gps manager the weak reference of the alertmanager will go to null in the asynctask between the constructor (all references are filled) and the doInBackground (the alertmanager reference is null, the other 2 still filled) which results in a crashing app.
When i dont comment out the if the app works fine.....
Alertmanager information
This is the method in the alertmanager who calls the async task. The references are filled on this place.
public void GetAlerts(List<WeakReference<e_Alerts>> callbackReferences, Context context) {
//Update the alerts in the listview and mapview with the local alerts.
List<Alert> localAlerts = internalDc.GetAllAlerts();
try {
for (WeakReference<e_Alerts> callback : callbackReferences) {
callback.get().UpdateAlerts(localAlerts);
}
} catch (Exception e) {
Log.e("AlertManager", e.getMessage());
}
//If connected to the internet then update the local db and the views
if (isConnectingToInternet(context)) {
WeakReference<e_Alerts> wr = new WeakReference<e_Alerts>(this);
callbackReferences.add(wr);
// Update the alerts where no location is needed for so the user has a quick result
externalDc.getAlerts(callbackReferences, areaManager.GetActiveAreas(false));
// If gps region is enabled then find the phones location and update the alerts
if (areaManager.GetGpsArea().IsActive()) {
new GpsManager(this.context, this, callbackReferences);
}
}
}
The GpsManager extends the LocationListener:
public class GpsManager extends Service implements LocationListener {
The listener is implemented by the Alertmanager
// This method is caled by the GPS Manager when the GPS location is changed
#Override
public void OnLocationChanged(Location location, List<WeakReference<e_Alerts>> references) {Area gpsArea = areaManager.GetGpsArea();
gpsArea.SetLocation(location);
areaManager.SaveArea(gpsArea);
externalDc.getAlerts(references, areaManager.GetActiveAreas(true));
}
Asynctask information
This are the asynctask methods:
Asynctask constructor:
Here the list callbackReferences contains 3 weakrefrences and all of them are filled (2x fragment reference 1x alertmanager reference)
public At_allAlerts(List<WeakReference<e_Alerts>> callbackReferences, List<Area> areas) {
this.mCallbackReferences = callbackReferences;
this.mAreas = areas;
}
doInBackground code:
The XmlDownloader: Downloads an xml feed an parses the xml to objects with a library
The AlertConverter: converts the xml object to the object i use in my app
Both classes can work without the asynctask class and don't use the references.
#Override
protected String doInBackground(String... inputUrl) {
Log.i("At_allAlerts", "Asynctask for downloading and parsing mAlerts is started");
try {
//Downloads the alert XMLs from the internet and parses it to xmlAlerts
this.mAlerts = new XmlDownloader().DownloadAlerts(inputUrl);
// Filters the mXml mAlerts so only the mAlerts where the enduser is interessed in will remain
this.mAlerts = filterAlerts(this.mAlerts);
// Converts the remaining xmlAlerts to Alerts;
this.mResult = new AlertConverter().Convert(this.mAlerts);
}catch (Exception e) {
Log.e("At_allAlerts",e.getMessage());
}
return null;
}
The onPostExecute method:
When the programm comes in this method the this.references.get(2) reference (alertmanager reference) = null, the other 2 references are still filed
#Override
protected void onPostExecute(String xml){
for (WeakReference<e_Alerts> reference : activityWeakReferences)
{
reference.get().UpdateAlerts(this.result);
}
}
filterAlerts Method:
private List<Item> filterAlerts(List<Item> alerts) {
List<Item> filteredXmlAlerts = new ArrayList<>();
for (Item alert : alerts)
{
Location alertLocation = new Location("");
alertLocation.setLatitude(alert.getGeometries().get(0).getLocations().get(0).getLat());
alertLocation.setLongitude(alert.getGeometries().get(0).getLocations().get(0).getLng());
for(Area area : this.mAreas)
{
if (area.IsOrganization() && alert.getCountryCode().toLowerCase().equals(area.getOrganizationcode().toLowerCase())){
filteredXmlAlerts.add(alert);
break;
}
else if(!area.IsOrganization() && isAlertInRegion(alertLocation, area)) {
filteredXmlAlerts.add(alert);
break;
}
}
}
return filteredXmlAlerts;
}
My Question(s)
I think Weakreference are the right way for giving references to asynctask is this correct or do i need to give it as an other object? (class or object or whatever?).
Why goes my reference to null? and only one of the 3? and only when i dont use the gps location class? and how to solve this?
I read something about the garbage collector what can be the cause of this problem, is this true and when yes how can i solve this?
It would be fine when the answere are simple to understand since android is pretty new for me.
I am begginer in Android App and using Java as when I add this code :
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
} else {
Log.v(TAG, "Already registered");
}
I had error on :
SENDER_ID
Log
TAG
the error "cannot be resolved to available "
As I commented if you're a beginner you shouldn't start with GCM, but seems that you're a beginner not only on Android but also in Java (this is not bad, everybody was a beginner, and I'm not that advanced).
I suggest you to start following some basic tutorials on Java, then start with some basic tutorial for Android and so on.
GCM requires also a server side, so this is going to be quite advanced.
Anyway, the error is simple. SENDER_ID is a field that you haven't defined anywhere.
You have to declare it somewhere, like:
String SENDER_ID = "mySenderId";
or at the top of your class:
public class MyClass {
private static final SENDER_ID = "mySenderID";
}