Android Java USB serial communication plugin for Unity not running - java

I created a Java plugin that utilizes the UsbManager devices to communicate with a specified serial device. I'm using Android Studio to run the plugin and can write to the proper device, but I don't understand how to integrate my code with Unity. I pass the Context in the constructor so I can create the UsbManager, but I don't know how to this in Unity or if there's another way to get the Context.
What's the proper way to pass the Context from Unity to my plugin? I'm also not sure if my function is working in Unity, because I don't know if permissions are needed for USB as well in the manifest file.
Unity Code:
void Start()
{
ajc = new AndroidJavaObject("com.company.dg.USBController");
int connected = ajc.Call<int>("startUSB");
}
Java Code:
public class USBController {
private Context context;
private static final String ACTION_USB_PERMISSION = "com.company.dg.USB_PERMISSION";
private final int BAUD_RATE = 19200;
private int bytesRead;
private byte[] readBuffer;
private UsbManager usbManager;
private UsbDeviceConnection connection;
private UsbSerialDevice serial;
private UsbDevice dg = null;
public USBController(Context context){
this.context = context;
}
public int startUSB(){
//usbManager = (UsbManager) context.getSystemService(context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
UsbDevice dg = null;
if(deviceList.size() == 0){
return -2;
}
// 1st and only device
dg = deviceList.values().iterator().next();
if(dg != null){
PendingIntent pi = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(dataglove, pi);
UsbDeviceConnection connection = usbManager.openDevice(dataglove);
UsbSerialDevice serial = UsbSerialDevice.createUsbSerialDevice(dg, connection);
serial.open();
serial.setBaudRate(BAUD_RATE);
serial.setDataBits(UsbSerialInterface.DATA_BITS_8);
serial.setStopBits(UsbSerialInterface.STOP_BITS_1);
serial.setParity(UsbSerialInterface.PARITY_NONE);
serial.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
serial.read(callback);
} else {
return -1;
}
return 0;
}
private UsbSerialInterface.UsbReadCallback callback = new UsbSerialInterface.UsbReadCallback() {
#Override
public void onReceivedData(byte[] data) {
bytesRead = data.length;
readBuffer = data;
}
};

What's the proper way to pass the Context from Unity to my plugin?
C# side:
Your USBController class has a constructor that takes Context as an argument. Before calling the startUSB function, obtain the Unity Context then send it to the constructor when you're creating an instance of USBController.
Get Unity Context:
AndroidJavaClass unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject unityContext = unityActivity.Call<AndroidJavaObject>("getApplicationContext");
Send the Context to your Java code when creating an instance of it:
ajc = new AndroidJavaObject("com.bebopsensors.dg.USBController", unityContext);
Now, you can call your startUSB function:
int connected = ajc.Call<int>("startUSB");
Java Side:
In your startUSB function, you can now use the Context with the getSystemService. I noticed you commented that out. Note that the context.USB_SERVICE should be Context.USB_SERVICE. The c in Context should be capitalized.
public int startUSB()
{
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
....
....
}
Don't know if permissions are needed for USB as well in the manifest
file.
Not really sure about that but I believe that calling the requestPermission function which you did should be enough to handle that. If this is not working then I suggest you test your Java program in Android only and Without Unity to see if it's working. If it works there, the solution I suggested should also work.

Related

Changing the language manually not working on Samsung devices

I'm having trouble with changing the app language manually, in the app, I offer users the ability to change the app's language to their preferred, the code below works fine even in Android (Pixel 3 Emulator), but for some reason, it doesn't work on all Samsung devices
Context context = LocaleUtils.setLocale(getApplicationContext(), languageCode);
Resources resources = context.getResources();
Locale myLocale = new Locale(languageCode);
DisplayMetrics dm = resources.getDisplayMetrics();
Configuration conf = resources.getConfiguration();
conf.locale = myLocale;
resources.updateConfiguration(conf, dm);
Intent intent = getBaseContext().getPackageManager().getLaunchIntentForPackage(
getBaseContext().getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
Application class:
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
LocaleUtils.onAttach(base, Locale.getDefault().getLanguage());
MultiDex.install(this);
}
on each Activity:
#Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(ViewPumpContextWrapper.wrap(LocaleUtils.onAttach(newBase)));
}
I struggled with dynamic Locale change on Samsung devices even prior to Android 10.
There might be a better solution now,
but at the time in addition to what you have done,
I ended up retrieving all the strings by resource identifiers in the following way:
public static String getStringByIdentifier(final Context context, final String stringResIdName, final boolean forceRefresh) {
final Resources res = getResources(context, forceRefresh);
String result;
try {
result = res.getString(res.getIdentifier(stringResIdName, "string",
context.getPackageName()));
} catch (final Resources.NotFoundException e) {
result = stringResIdName; // TODO or here you may throw an Exception and handle it accordingly.
}
return result;
}
public static String getStringByIdentifier(final Context context, final String stringResIdName) {
return getStringByIdentifier(context, stringResIdName, false);
}
private static Resources getResources(final Context context, final boolean refreshLocale) {
if (!refreshLocale) {
return context.getResources();
} else {
final Configuration configuration = new Configuration(context.getResources().getConfiguration());
configuration.setLocale(Locale.getDefault());
return context.createConfigurationContext(configuration).getResources();
}
}
So you have to set text in the next way:
textView.setText(AndroidUtils.getStringByIdentifier(context, "string_res_name"));
Where the corresponding string resource is:
<string name="string_res_name">Some string</string>

Adobe Air ANE for android with java native extension trying to receive broadcast

I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}

Sounds working on Lollipop but not on kitkat or older versions

I made a method I'm using in onCreate, it's called loadSounds, and depending on the version I use the SoundPool Builder or the deprecated version if it's an older version of Android......But sounds only work on Lollipop devices......What could be wrong....the app runs in both versions.....The variable that holds the SoundPool is declared outside of onCreate as an object attribute private static SoundPool mySounds;
Thanks in advance!!!
private void loadSounds(){
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP) {
AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN).setUsage(AudioAttributes.USAGE_GAME).build();
mySounds = new SoundPool.Builder().
setMaxStreams(10).
setAudioAttributes(audioAttributes).
build();
correctAnswerID = mySounds.load(this, R.raw.correctanswer, 1);
incorrectAnswerID = mySounds.load(this, R.raw.incorrectanswer, 1);
}
else
{
mySounds= new SoundPool(10, AudioManager.USE_DEFAULT_STREAM_TYPE,1);
correctAnswerID = mySounds.load(this, R.raw.correctanswer, 1);
incorrectAnswerID = mySounds.load(this, R.raw.incorrectanswer, 1);
}
}
The method that uses the sounds is(if the answer is correct plays the correctanswer mp3, otherwise it plays the wronganswer mp3) :
if(selectedAnswer.equals(currentAnswer) || isEnharmonic ){
correctAnswers+=1;
reproduceAnswerSound("Correct");
}
else
{
incorrectAnswers+=1;
reproduceAnswerSound("Incorrect");
}
and the method that reproduce the sounds is like this:
private void reproduceAnswerSound(String type){
if(type.equals("Correct")){
mySounds.play(correctAnswerID,1,1,1,0,1);
}else if(type.equals("Incorrect"))
{
mySounds.play(incorrectAnswerID,1,1,1,0,1);
}
}

AudioManager NullPointerException for Release version

When I run my code in Debug from Android Studio my app work good, but when I create the APK and install it on device and start my app, the app get NullPointerException error and get killed( using Android Debug Monitor). Here is the code:
public class MusicService extends IntentService {
boolean INTERNET_CONNECTION = false;
boolean COMMAND_DOWNALOAD_SDCARD = false;
String TABLE_NAME = "";
String COMMAND_ARGUMENT = "";
// Here throw sometimes the error - Line 42
AudioManager audio;
MediaPlayer mp = new MediaPlayer();
long TIME_ELAPSED = 0;
long TIME_STARTED = 0;
boolean ALIVE = true;
long SLEEP_TIME = 5000;
String SERVER_URL = "http://xxx.xxx";
public MusicService() {
super("Service");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("SERVICE", "STARTED");
initialize();
initializeTimer();
while (ALIVE) {
...
}
}
private void initialize() {
// Get phone number set the table name
TABLE_NAME = "t" + getPhoneNumber();
audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// Wait for internet connection
INTERNET_CONNECTION = waitInternetConnection();
Log.d("INITIALIZE", "COMPLETED");
}
...
}
I start my app when boot is completed. After some times of trying to run it I got error at line 42 and I changed the line from:
AudioManager audio = null;
to
AudioManager audio;
But still get the error but now don't show any more the line.
There is no difference between these: AudioManager audio = null; and AudioManager audio;.
Changing the line from explicitly assigning null to the object doesn't change any functionality at all; all it means is that audio now implicitly defaults to null.
Clearly there is a difference in what getSystemService() does, depending on whether you're in your debugging environment versus your production environment. To find out what's wrong, go into that service. We don't have enough information here to debug it for you.
audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

How to get the icon of other applications (Android)

What I'm doing is getting a list of all the current running processes on the phone. Which I have done by,
private List<RunningAppProcessInfo> process;
private ActivityManager activityMan;
...
activityMan = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
process = activityMan.getRunningAppProcesses();
this works fine. When I call the processName field like
process.get(i).processName;
I get a name like com.android.mail for example.
what I'm trying to do is use this to get access to that application so I can display its icon to the user, but I cant find anything that lets me do this. Is there something that can help me?
I'm testing this app on my hero so the api level is 3 (android 1.5).
Thanks.
Ok, I figured out how to do it. In case your curious this is what I did.
private PackageManager pk;
pk = getPackageManager();
....
pk.getApplicationIcon(process.get(i).processName)
Thanks.
try this way make a class called packageinformation:
public class PackageInformation{
private Context mContext;
public PackageInformation(Context context){
mContext=context;
}
class InfoObject {
public String appname = "";
public String pname = "";
public String versionName = "";
public int versionCode = 0;
public Drawable icon;
public void InfoObjectAggregatePrint() {//not used yet
Log.v(appname,appname + "\t" + pname + "\t" + versionName + "\t" + versionCode);
}
}
private ArrayList getPackages() {
ArrayList apps = getInstalledApps(false); /* false = no system packages */
final int max = apps.size();
for (int i=0; i
public ArrayList<InfoObject> getInstalledApps(boolean getSysPackages) {
ArrayList<InfoObject> res = new ArrayList<InfoObject>();
List<PackageInfo> packs = mContext.getPackageManager().getInstalledPackages(0);
for(int i=0;i<packs.size();i++) {
PackageInfo p = packs.get(i);
if ((!getSysPackages) && (p.versionName == null)) {
continue ;
}
InfoObject newInfo = new InfoObject();
newInfo.appname = p.applicationInfo.loadLabel(mContext.getPackageManager()).toString();
newInfo.pname = p.packageName;
newInfo.versionName = p.versionName;
newInfo.versionCode = p.versionCode;
newInfo.icon = p.applicationInfo.loadIcon(mContext.getPackageManager());
res.add(newInfo);
}
return res;
}
}
tuck this away somewhere and now to access the info from your working class do this:
PackageInformation androidPackagesInfo=new PackageInformation(this);
ArrayList<InfoObject> appsData=androidPackagesInfo.getInstalledApps(true);
for (InfoObject info : appsData) {
Toast.makeText(MainActivity.this, info.appname,2).show();
Drawable somedrawable=info.icon;
}
Since Android 3.0 you might want to get a bigger launcher icon that you can't get the way you described. If so, perhaps my answer to question below can help you: Getting App Icon in Android
While packagemanager.getApplicationIcon(pkagename) works for some apps but not for all.
Found better answer here:
how to get running applications icon on android programmatically
Uses:
Drawable ico=getApplicationInfo(packagemanager,PackageManager.GET_META_DATA).loadIcon(getPackageManager());

Categories

Resources