AndroidJavaClass/AndroidJavaObject causing built application to crash - c#

I have an Android application that builds and runs find before the introduction of the following code to an object in my scene. My goal was to vibrate at different lengths for my application instead of just using the default Handheld.Vibrate() function. Once this code is added, my application still builds to my device just fine, but crashes on startup.
I am using Unity 2019.2.9f1, and my minimum API level is set to 25, using Mono and .NET 2.0.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HapticController : Singleton<HapticController> {
#if UNITY_ANDROID && !UNITY_EDITOR
public static AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
public static AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
public static AndroidJavaObject vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator");
#else
public static AndroidJavaClass unityPlayer;
public static AndroidJavaObject currentActivity;
public static AndroidJavaObject vibrator;
#endif
bool isVibrating;
...
// Update is called once per frame
void Update()
{
if(!isVibrating && ( *other condition* ) ){
isVibrating = true; //this will later be changed, just trying to get it to vibrate once
Vibrate(200);
}
}
public static void Vibrate(long milliseconds)
{
if(isAndroid() && vibrator != null){
print("vibrating");
vibrator.Call("vibrate", milliseconds);
} else {
Handheld.Vibrate();
}
}
private static bool isAndroid()
{
#if UNITY_ANDROID && !UNITY_EDITOR
return true;
#else
return false;
#endif
}
}
I know that it is the AndroidJavaClass/AndroidJavaObject calls at the top that are causing the crash. I have checked the logs in logcat in Android Studio, but there is no notable output that would have anything to do with the crash. Is there something I am not accounting for that would cause this?
EDIT: I should add that I am using an SDK (Mapbox) that has its own AndroidManifest.xml. It instructs you to rename it and move it to the Plugins/Android folder. You can see it here:
<!--
Android Manifest for UniAndroid Permission (2016/03/19 sanukin39)
--- if already have AndroidManifest file at Assets/Plugins/Android/ ----
Copy the activity and meta-data sentence to your AndroidManifest.xml
--- if not ---
Rename this file to AndroidManifest.xml and add permission you want to add And move the file to Assets/Plugins/Android
-->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
<application android:icon="#drawable/app_icon" android:label="#string/app_name">
<activity android:name="net.sanukin.OverrideUnityActivity"
android:label="#string/app_name"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />
</application>
</manifest>

For anyone else struggling with this, I was able to solve this with a combination of Farhan's answer and also doing the following:
Instead of initializing the AndroidJavaClass and AndroidJavaObjects at the top, I created an initializer function responsible for doing that. I added the line
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
above of it in order to force it to initialize them before the scene has loaded. I am not sure why this is required, but it fixed it for me. You can see the full function below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HapticController : Singleton<HapticController> {
public static AndroidJavaClass unityPlayer = null;
public static AndroidJavaObject currentActivity = null;
public static AndroidJavaObject vibrator = null;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize(){
#if UNITY_ANDROID && !UNITY_EDITOR
if (Application.platform == RuntimePlatform.Android) {
unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator");
}
#endif
}
...
}

You should add following vibration permission in {Project location}\Assets\Plugins\Android\AndroidManifest.xml
<uses-permission android:name="android.permission.VIBRATE"/>
So your final AndroidManifest becomes.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.VIBRATE"/>
<application
android:theme="#style/UnityThemeSelector"
android:icon="#drawable/app_icon"
android:label="#string/app_name"
android:debuggable="true">
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>

Related

C# SerialPort with Java Android phone

This bounty has ended. Answers to this question are eligible for a +50 reputation bounty. Bounty grace period ends in 1 hour.
obeid_s is looking for an answer from a reputable source.
what i'm trying to do:
Create an App for windows to connect android phone.
Send and receive data.
Windows (C#) is the host device --> android UsbAccessory.
C#:
now i can select the COM5 (Android phone) and connect to it, using this code:
serialPort1.PortName = this.devicesList.Text; // COM5
serialPort1.Open();
addToList("SerialPort is opened", Color.Green);
Java (Android Phone):
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessories = manager.getAccessoryList();
accessories is Always Null, i tried every code i find, but i could not do it.
what i'm doing wrong?
Thanks.
::: EDIT :::
Android Studio Code (Java)
package com.example.camerausb;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class UsbConnector extends Activity {
private static final String ACTION_USB_PERMISSION = "com.example.camerausb.UsbConnector.USB_PERMISSION";
public UsbManager usbManager;
public UsbAccessory usbAccessory;
public PendingIntent mPermissionIntent;
public Context global_context;
private IntentFilter filter;
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Toast.makeText(global_context, "Allow Usb Permission", Toast.LENGTH_LONG).show();
// TODO:: open Accessory method
} else {
Toast.makeText(global_context, "Deny Usb Permission", Toast.LENGTH_LONG).show();
}
}
}
else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
Toast.makeText(global_context, "Usb detached", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(global_context, "something else", Toast.LENGTH_LONG).show();
}
}
};
public UsbConnector(Context context) {
super();
global_context = context;
Toast.makeText(global_context, "started", Toast.LENGTH_LONG).show();
usbManager = (UsbManager) global_context.getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(global_context, 0, new Intent(ACTION_USB_PERMISSION), 0);
filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
usbManager.requestPermission(usbAccessory, mPermissionIntent);
global_context.registerReceiver(mUsbReceiver, filter);
}
public BroadcastReceiver getReceiver() {
return this.mUsbReceiver;
}
public IntentFilter getFilter() {
return this.filter;
}
public void resumeAccessory(Intent intent) {
// Intent intent = getIntent();
String action = intent.getAction();
Toast.makeText(global_context, "resume: " + action, Toast.LENGTH_LONG).show();
UsbAccessory[] accessories = usbManager.getAccessoryList();
if (accessories != null) {
Toast.makeText(global_context, "Accessory not NULL", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(global_context, "Accessory is NULL", Toast.LENGTH_LONG).show();
}
}
}
Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.camerausb">
<uses-sdk android:minSdkVersion="12" />
<uses-feature android:name="android.hardware.usb.accessory" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.CameraUsb">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="#xml/accessory_filter" />
</activity>
</application>
</manifest>
xml/accessory_filter :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory model="DemoKit" manufacturer="Google" version="1.0" />
</resources>
someone told me to add usbManager.requestPermission(usbAccessory, mPermissionIntent);
the phone screen goes to black and luck it self!!

How can I load a JSON from an URL using unity for android?

I'm doing a Unity app for android that scans a QR code and displays a model with some buttons. The buttons displays the info of an event, said info is stored in a website. The way the app request the info is:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;
using UnityEngine.Networking;
using UnityEngine.UI;
public class WebTextLoader : MonoBehaviour
{
// Start is called before the first frame update
[Serializable]
public class PlaceInfo
{
public string Titulo = "";
public string Texto = "";
}
public string URL;
public Text TituloUI;
public Text TextoUI;
public PlaceInfo placeInfo;
public void Start()
{
if (Debug.isDebugBuild)
{
StartCoroutine(GetRequest(URL));
}
}
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string jsonForm = uri;
if (webRequest.isNetworkError)
{
Debug.Log("Error loading");
}
else
{
try
{
placeInfo = JsonUtility.FromJson<PlaceInfo>(webRequest.downloadHandler.text);
TituloUI.text = placeInfo.Titulo;
TextoUI.text = placeInfo.Texto;
}
catch
{
Debug.Log("Error in connection");
}
}
}
}
}
With this, the app works perfectly on the computer, using the editor, but when I build the apk and install it on my phone...it no longer works. The text never loads, I checked the Android manifest and everything seems ok:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player" android:installLocation="preferExternal" android:versionCode="5" android:versionName="5.5">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
<application android:icon="#drawable/app_icon" android:label="#string/app_name" android:debuggable="true">
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="#string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
Also in player settings, I checked "internet" as "require"
I don't know that i'm missing here, specially since the host i'm using is a paid one so I shouldn't have problems with limitations or stuff.
I'm doing something wrong?

Read Android intent extra data on Unity app launch

I am launching an Unity application from another Android application using a custom implicit intent. This is working fine, but I cannot figure out how to read the intent extra data in Unity?
ANDROID INTENT TO LAUNCH UNITY APP
i=new Intent();
i.setAction("com.company.unityapp.MyMethod");
i.putExtra("KEY","This is the message string");
startActivity(i);
UNITY APP AndroidManifest.xml
<intent-filter>
<action android:name="com.company.unityapp.MyMethod" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
I have a GameObject in my scene with a script attached. Inside the start method I have this code to try and read the extra data that was passed along with the intent
AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject>("getIntent");
bool hasExtra = intent.Call<bool> ("hasExtra", "arguments");
if (hasExtra) {
AndroidJavaObject extras = intent.Call<AndroidJavaObject> ("getExtras");
arguments = extras.Call<string> ("getString", "arguments");
}
This is not working and arguments is always empty. Any help would be appreciated.
It took me quite some time to figure this out. All solutions found online were only partly complete. Below is the full solution for launching a Unity application from another android application using a custom implicit Intent and also how to access the extra data sent with the Intent inside Unity.
To accomplish this you need to create a Android plugin that will be used by Unity to access the Intent extra data.
ANDROID PLUGIN:
You need to copy the classes.jar from Unity installation folder to the android plugin folder /lib/classes.jar
public class MainActivity extends UnityPlayerActivity {
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleNewIntent(intent);
}
private void handleNewIntent(Intent intent){
String text = intent.getStringExtra("KEY");
UnityPlayer.UnitySendMessage("AccessManager","OnAccessToken", text);
}
}
AndroidManifest.xml
Important here is the package name used: com.company.plugin
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.plugin">
<application
android:allowBackup="true" android:icon="#mipmap/ic_launcher" android:label="#string/app_name"
android:supportsRtl="true" android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Gradle build file:
Add the following to the app gradle build file to be able to create a .jar to be used with Unity
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
sourceSets {
main {
java {
srcDir 'src/main/java'
}
}
}
...
...
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'
compile files('libs/classes.jar')
}
//task to delete the old jar
task deleteOldJar(type: Delete) {
delete 'release/AndroidPlugin.jar'
}
//task to export contents as jar
task exportJar(type: Copy) {
from('build/intermediates/bundles/release/')
into('release/')
include('classes.jar')
///Rename the jar
rename('classes.jar', 'AndroidPlugin.jar')
}
exportJar.dependsOn(deleteOldJar, build)
Copy the created AndroidPlugin.jar to Unity Assets/Plugins/Android
UNITY APP:
Set the bundle identifier in PlayerSettings to be the same as set in the Android Plugin - com.company.plugin
Create custom AndroidManifest.xml file in Assets/Plugins/Android
Important here is to use the same package name as used in the plugin.
Also note the Intent name: com.company.plugin.do
AndroidManifest.XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.plugin"
android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" />
<application android:label="#string/app_name">
<activity android:name=".MainActivity" android:label="#string/app_name"
android:launchMode="singleTask" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="sensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.company.plugin.do" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
</application>
</manifest>
Create a unity script named AccessManager and attach the script to a game object in the scene. OnAccessToken is the method that will receive the message sent from the android plugin and will contain the extra data sent from the intent.
public class accessManager : MonoBehaviour {
public void OnAccessToken(string accessToken)
{
Debug.Log("Message Received!!!! :" + accessToken);
}
}
ANDROID APP:
Create a standard Android application that will launch the Unity Application and send the Intent extra data
public void LaunchUnityApp(){
Intent i=new Intent();
i.setAction("com.company.plugin.do");
i.setType("text/plain");
i.putExtra("KEY","This is the text message sent from Android");
startActivity(i);
}
You don't need a plugin to achieve this. Do your intent from Android like this:
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.package.game");
launchIntent.putExtra("my_text", "Some data params");
if(launchIntent != null){
startActivity(launchIntent);
}else{
Log.d("Unity", "Couldnt start unity game");
}
Then in your unity Monobehaviour Class, receive it like this
private void Awake () {
getIntentData ();
}
private bool getIntentData () {
#if (!UNITY_EDITOR && UNITY_ANDROID)
return CreatePushClass (new AndroidJavaClass ("com.unity3d.player.UnityPlayer"));
#endif
return false;
}
public bool CreatePushClass (AndroidJavaClass UnityPlayer) {
#if UNITY_ANDROID
AndroidJavaObject currentActivity = UnityPlayer.GetStatic<AndroidJavaObject> ("currentActivity");
AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject> ("getIntent");
AndroidJavaObject extras = GetExtras (intent);
if (extras != null) {
string ex = GetProperty (extras, "my_text");
return true;
}
#endif
return false;
}
private AndroidJavaObject GetExtras (AndroidJavaObject intent) {
AndroidJavaObject extras = null;
try {
extras = intent.Call<AndroidJavaObject> ("getExtras");
} catch (Exception e) {
Debug.Log (e.Message);
}
return extras;
}
private string GetProperty (AndroidJavaObject extras, string name) {
string s = string.Empty;
try {
s = extras.Call<string> ("getString", name);
} catch (Exception e) {
Debug.Log (e.Message);
}
return s;
}
Credit: https://wenrongdev.com/get-android-intent-data-for-unity/
(updated) https://wenrongdev.com/posts/get-android-intent-data-for-unity/

accessing android jar in unity3d

I have an android project for camera flashlight, which when deployed from eclipse works fine. I am trying to access the flashlight function from my C# code in unity3d but it doesn't work. To verify if I am calling the android method correctly, I created a string function in the same activity and it is returning the string correctly. I am not familiar to native android coding. It would be great if you could have a look at the code and help me out.
I know there are some threads in unity forum and stackoverflow explaining the same, I tried to find a solution on these threads but no luck! So, posted this thread..
Below is android MainActivity.java (which I converted into a jar file from eclipse and copied in unity project, ~Assets/Plugins/Android/bin/),
package com.example.newflash;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.os.Bundle;
import android.app.Activity;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
private static Camera camera;
private static Parameters parameters;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
public static String dummyString()
{
return "dummy string";
}
public static void setFlashOn()
{
if (camera == null)
camera = Camera.open();
parameters = camera.getParameters();
parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
}
public static void setFlashOff()
{
parameters = camera.getParameters();
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
}
}
Below is my unity C# code,
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
public class testJar : MonoBehaviour
{
bool torchon;
AndroidJavaClass testClasslight;
void Start ()
{
torchon = false;
}
void OnGUI ()
{
string teststring = "empty";
AndroidJavaClass testClass = new AndroidJavaClass("com.example.glassplaces.MainActivity");
teststring = testClass.CallStatic<string>("dummyString");
GUI.Label (new Rect (20, 20, 100, 60), teststring);
if(GUI.Button(new Rect (20, 100, 100, 60), "ON"))
{
torchon = true;
}
if(torchon == true)
{
GUI.Label(new Rect(200, 20,100,60), "torch ON");
testClass.CallStatic<AndroidJavaObject>("setFlashOn");
}
}
}
The permissions to access camera in AndroidManifest.xml when added, the app doesn't start at all. On excluding the xml file from the project, the "dummyString" method still returns the string.
Below is the AndroidManifest.xml,
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.newflash"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:allowBackup="true"
android:icon="#drawable/app_icon"
android:label="#string/app_name">
<activity
android:name="com.example.newflash.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Below is the warning which unity shows in console with the above xml included during Build & Run,
Unable to find unity activity in manifest. You need to make sure orientation attribut is set to sensor manually.
UnityEditor.BuildPlayerWindow:BuildPlayerAndRun()
It would be great if someone could help me out. Any help is much appreciated.
Thank you in advance!
I am not 100% sure the above response by CaffeineCoder is accurate. It is certain possible to not extend UnityPlayerActivity and I've done it. You need to specify these special Unity activities in your Manifest along with your own Activity.
Unity will start UnityPlayerProxyActivity at first and proceed from there. Your Java code is only called when you call/initialize it.
Following are some of the activities you can specify:
<activity
android:name="com.unity3d.player.UnityPlayerProxyActivity"
android:label="AngryBots Gamme"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:screenOrientation="landscape"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="com.unity3d.player.UnityPlayerActivity"
android:label="AngryBots Gamme"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:screenOrientation="landscape"
/>
<activity
android:name="com.unity3d.player.UnityPlayerNativeActivity"
android:label="AngryBots Gamme"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:screenOrientation="landscape"
/>
Got it working, thanks to Stevethorne's answer (on unityAnswers). On extending my java activity to "UnityPlayerActivity", I got it working. That is, com.unity3d.player.UnityPlayerActivity. Here are further details about UnityPlayerActivity.

Receiving SMS with MonoDroid

Edit: This is now resolved - posting solution here in case someone needs it in future or anyone can suggest a better way of doing this. I removed the intent stuff from my manifest and just setup the BroadcastReceiver in my SmsReceiver class. This now works.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Util;
using Android.Telephony;
namespace dummyAndroid
{
[BroadcastReceiver(Enabled = true, Label = "SMS Receiver")]
[IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED" })]
public class SmsReceiver : Android.Content.BroadcastReceiver
{
public static readonly string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == INTENT_ACTION)
{
StringBuilder buffer = new StringBuilder();
Bundle bundle = intent.Extras;
if (bundle != null)
{
Java.Lang.Object[] pdus = (Java.Lang.Object[])bundle.Get("pdus");
SmsMessage[] msgs;
msgs = new SmsMessage[pdus.Length];
for (int i = 0; i < msgs.Length; i++)
{
msgs[i] = SmsMessage.CreateFromPdu((byte[])pdus[i]);
Log.Info("SmsReceiver", "SMS Received from: " + msgs[i].OriginatingAddress);
Log.Info("SmsReceiver", "SMS Data: " + msgs[i].MessageBody.ToString());
}
Log.Info("SmsReceiver", "SMS Received");
}
}
}
}
}
I'm writing an app that sends/receives SMS messages and have got sending working via the SMS Mananger.
I'm now trying to receive SMS in Mono for Android and am new to Android development so am probably doing something wrong!
I have added the following to my manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="com.me.dummyAndroid" android:versionCode="1" android:versionName="1">
<uses-sdk android:targetSdkVersion="8" />
<application
android:label="meAndroidSMS"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen">
<receiver android:name=".SmsReceiver">
<intent-filter>
<action android:name=
"android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
</manifest>
I then created a new class called SmsReceiver.cs which I've added the function onReceive into but there doesn't appear to be the getExtras function within intent which according to the online tutorial I read I would need (http://www.techques.com/question/1-3542320/IPhone-Android-SMS-intercept-and-redirection-to-an-application.).
namespace dummyAndroid
{
class SmsReceiver
{
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
}
}
}
I appreciate I'm in a bit over my head on Android and MonoDroid for sure but maybe someone can point me in the right direction!
I have some working code here (I can't remember the source, but I think it was someone associated with Xamarin who wrote this sample), which displays a Toast when an SMS is recieved.
using System.Text;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using Android.Telephony;
using Environment = System.Environment;
namespace MonoDroid.SMSFun
{
[BroadcastReceiver(Enabled = true, Label = "SMS Receiver")]
[IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" })]
public class SMSBroadcastReceiver : BroadcastReceiver
{
private const string Tag = "SMSBroadcastReceiver";
private const string IntentAction = "android.provider.Telephony.SMS_RECEIVED";
public override void OnReceive(Context context, Intent intent)
{
Log.Info(Tag, "Intent received: " + intent.Action);
if (intent.Action != IntentAction) return;
var bundle = intent.Extras;
if (bundle == null) return;
var pdus = bundle.Get("pdus");
var castedPdus = JNIEnv.GetArray<Java.Lang.Object>(pdus.Handle);
var msgs = new SmsMessage[castedPdus.Length];
var sb = new StringBuilder();
for (var i = 0; i < msgs.Length; i++)
{
var bytes = new byte[JNIEnv.GetArrayLength(castedPdus[i].Handle)];
JNIEnv.CopyArray(castedPdus[i].Handle, bytes);
msgs[i] = SmsMessage.CreateFromPdu(bytes);
sb.Append(string.Format("SMS From: {0}{1}Body: {2}{1}", msgs[i].OriginatingAddress,
Environment.NewLine, msgs[i].MessageBody));
}
Toast.MakeText(context, sb.ToString(), ToastLength.Long).Show();
}
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="monodroid.smsfun" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:targetSdkVersion="8" />
<application android:label="SMSFun">
</application>
<uses-permission android:name="android.permission.RECEIVE_SMS" />
</manifest>

Categories