1

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>
A.Crane
  • 435
  • 1
  • 3
  • 12

2 Answers2

1

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
    }
...
}
A.Crane
  • 435
  • 1
  • 3
  • 12
0

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>

Farhan
  • 442
  • 1
  • 6
  • 13
  • Thank you for this. 1) I was under the impression that including a line of code with Handheld.Vibrate() causes Unity to automatically include those permissions in its generated manifest. Is that not the case? 2) I don't actually have a Plugins/Android directory in my project. I am using an SDK (Mapbox) that seems to have its own AndroidManifest in its subfolders, which I have added to my original post now. I copied it and moved it to Plugins/Android (which it says you should do) and added the vibration permission line to it. Unfortunately, it still crashes once the app builds and starts. – A.Crane Nov 01 '19 at 17:41
  • For your question # 1: I'm not sure about that, may be unity does that automatically. Question # 2: If you don't have AndroidManifest.xml in Assets/Plugins/Android directory, than you can create Plugins/Android directory manually and place AndroidManifest.xml there (you can copy paste upper posted xml in any editor and save file with name AndroidManifest.xml). If you don't want to create Assets/Plugins/Android directory and Androidmanifest.xml manually, than you can also add permission in Mapbox's AndroidManifest.xml. – Farhan Nov 02 '19 at 21:06