Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

First time plugin creation problems

Discussion in 'Android' started by Tripwire, Jul 24, 2014.

  1. Tripwire

    Tripwire

    Joined:
    Oct 12, 2010
    Posts:
    442
    Hi,

    I'm having some problems creating my first Unity3D plugin for Android. I'm trying to create a plugin for an exisiting Android plugin which comes in a Jar file. I keep getting a fatal exception that the class can't be found, but I don't know what i'm doing wrong :(

    The Jar file from the exisiting plugin is placed in the libs folder in my eclipse folder. The same goes for classes.jar which I got from Unity3D. I've added the jar files to the build path in the eclipse project.

    Adroid Logcat error:
    Code (CSharp):
    1. 07-24 17:54:19.880: E/AndroidRuntime(26189): FATAL EXCEPTION: main
    2. 07-24 17:54:19.880: E/AndroidRuntime(26189): Process: com.unity3d.JavaPlugin, PID: 26189
    3. 07-24 17:54:19.880: E/AndroidRuntime(26189): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.unity3d.JavaPlugin/nl.banzaibandit.plugin.UnityBridge}: java.lang.ClassCastException: nl.banzaibandit.plugin.UnityBridge cannot be cast to android.app.Activity
    4. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2514)
    5. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2653)
    6. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread.access$800(ActivityThread.java:156)
    7. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1355)
    8. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.os.Handler.dispatchMessage(Handler.java:102)
    9. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.os.Looper.loop(Looper.java:157)
    10. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread.main(ActivityThread.java:5872)
    11. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at java.lang.reflect.Method.invokeNative(Native Method)
    12. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at java.lang.reflect.Method.invoke(Method.java:515)
    13. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:852)
    14. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:668)
    15. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at dalvik.system.NativeStart.main(Native Method)
    16. 07-24 17:54:19.880: E/AndroidRuntime(26189): Caused by: java.lang.ClassCastException: nl.banzaibandit.plugin.UnityBridge cannot be cast to android.app.Activity
    17. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.Instrumentation.newActivity(Instrumentation.java:1079)
    18. 07-24 17:54:19.880: E/AndroidRuntime(26189):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2505)
    19. 07-24 17:54:19.880: E/AndroidRuntime(26189):     ... 11 more
    20.  
    Android Manifest Eclipse project:
    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3.     package="nl.banzaibandit.plugin"
    4.     android:versionCode="1"
    5.     android:versionName="1.0" >
    6.  
    7.     <uses-permission android:name="android.permission.INTERNET"/>
    8.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    9.     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    10.     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    11.     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    12.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    13.       <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    14.       <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    15.  
    16.     <uses-sdk
    17.         android:minSdkVersion="14"
    18.         android:targetSdkVersion="18" />
    19.  
    20.     <application
    21.         android:icon="@drawable/ic_launcher"
    22.         android:label="@string/app_name" >
    23.         <activity
    24.             android:name="nl.banzaibandit.plugin.UnityBridge"
    25.             android:screenOrientation="portrait"
    26.             android:label="@string/app_name" >
    27.             <intent-filter>
    28.                 <action android:name="android.intent.action.MAIN" />
    29.  
    30.                 <category android:name="android.intent.category.LAUNCHER" />
    31.             </intent-filter>
    32.         </activity>
    33.  
    34.         <activity android:name="nl.banzaibandit.plugin.IndoorAtlasExample" />
    35.  
    36.     </application>
    37.  
    38. </manifest>
    UnityBridge.java code:
    Code (CSharp):
    1. package nl.banzaibandit.plugin;
    2.  
    3. import android.content.Context;
    4. import android.os.Bundle;
    5.  
    6. import com.indooratlas.android.IndoorAtlasListener;
    7. import com.indooratlas.android.ServiceState;
    8. import com.unity3d.player.UnityPlayerActivity;
    9.  
    10. public class UnityBridge
    11. {
    12.     public static Context mContext;
    13.  
    14.     private IndoorAtlasExample example;
    15.  
    16.     public int GetInt()
    17.     {
    18.         example = new IndoorAtlasExample();
    19.         return 120;
    20.     }
    21. }
    IndoorAtlasExample.java code:
    Code (CSharp):
    1. package nl.banzaibandit.plugin;
    2.  
    3. import com.indooratlas.android.*;
    4. import com.unity3d.player.UnityPlayerActivity;
    5.  
    6. import android.app.Activity;
    7. import android.os.Bundle;
    8. import android.os.Handler;
    9. import android.util.Log;
    10. import android.view.View;
    11. import android.view.View.OnClickListener;
    12. import android.widget.Button;
    13. import android.widget.ImageView;
    14. import android.widget.TextView;
    15. import android.graphics.Bitmap;
    16. import android.graphics.Canvas;
    17. import android.graphics.Paint;
    18.  
    19.  
    20. public class IndoorAtlasExample extends Activity implements IndoorAtlasListener {
    21.  
    22.     // set the TAG string title
    23.     private static final String TAG = "IndoorAtlasExample";
    24.  
    25.     // Declare the textview variable here
    26.     private TextView textView;
    27.  
    28.     // Declare the Handler variable here
    29.     private Handler handler = new Handler();
    30.  
    31.     // Declare the IndoorAtlas application variable here
    32.     private IndoorAtlas indoorAtlas;
    33.  
    34.     // Create the button variable
    35.     Button button;
    36.  
    37.     // Create the image variable
    38.     ImageView image;
    39.  
    40.     // API key
    41.     private final String apiKey = "";
    42.      
    43.     // Secret key
    44.     private final String secretKey = "!";
    45.      
    46.     // Building-id:
    47.     private final String building_ID = "";
    48.  
    49.     // Floor-id:
    50.     private final String floor_ID = "";
    51.  
    52.     // Graphics-id:
    53.     private final String graphics_ID = "";
    54.  
    55.  
    56.     private boolean calibrated = false;
    57.  
    58.     /** Called when the activity is first created. */
    59.     @Override
    60.     public void onCreate(Bundle savedInstanceState) {
    61.  
    62.         Log.d(TAG, "onCreate -State-");
    63.      
    64.         super.onCreate(savedInstanceState);
    65.      
    66.         try {
    67.             // Get handle to the IndoorAtlas API
    68.             // Throws exception when the cloud service cannot be reached
    69.             // Get your Apikey and Secret key from IndoorAtlas My Account
    70.             indoorAtlas = IndoorAtlasFactory.createIndoorAtlas(
    71.                     this.getApplicationContext(),
    72.                     this,
    73.                     apiKey,
    74.                     secretKey);
    75.  
    76.          
    77.         } catch (IndoorAtlasException ex) {
    78.             showMessageOnUI("Failed to connect to IndoorAtlas. Check your credentials.");
    79.             Log.e(TAG, "Failed to connect to IndoorAtlas. Check your credentials.");
    80.             this.finish();
    81.         }
    82.     }
    83.  
    84.     public int GetInt()
    85.     {
    86.         return 129;
    87.     }
    88.  
    89.     @Override
    90.     public void onStop() {
    91.  
    92.         Log.d(TAG, "onStop() -State- : calibrated = "+calibrated);
    93.  
    94.         try {
    95.             showMessageOnUI("onStop(): Stopping positioning.");
    96.  
    97.             // Stop positioning when not needed
    98.             indoorAtlas.stopPositioning();
    99.  
    100.         } catch (Exception e) {
    101.             e.printStackTrace();
    102.         }
    103.  
    104.         super.onStop();
    105.     }
    106.  
    107.     @Override
    108.     protected void onPause()
    109.     {
    110.         Log.d(TAG, "onPause() -State- : calibrated = "+calibrated);
    111.      
    112.         showMessageOnUI("onPause(): Stopping positioning.");
    113.      
    114.  
    115.         // Stop positioning when not needed
    116.         indoorAtlas.stopPositioning();
    117.  
    118.         super.onPause();
    119.      
    120.     }
    121.  
    122.     @Override
    123.     protected void onRestart()
    124.     {
    125.         Log.d(TAG, "onRestart() -State- : calibrated = "+calibrated);
    126.  
    127.         super.onRestart();
    128.      
    129.         showMessageOnUI("onRestart().");
    130.      
    131.     }
    132.  
    133.  
    134.     @Override
    135.     protected void onResume()
    136.     {
    137.         Log.d(TAG, "onResume() -State- : calibrated = "+calibrated);
    138.         super.onResume();
    139.      
    140.         if(calibrated == false)
    141.         {
    142.             // Prompts user to perform Figure "8" calibration motion
    143.             showMessageOnUI("onResume(): Calibrating... Figure \"8\" motion until onCalibrationFinished() or onCalibrationFailed() is called");
    144.  
    145.             // Starts the calibration, once completed the onCalibrationFinished() is called
    146.             indoorAtlas.calibrate();
    147.         }
    148.         else
    149.         {
    150.          
    151.             showMessageOnUI("onResume(): Starting positioning.");
    152.          
    153.             indoorAtlas.startPositioning(building_ID, floor_ID,
    154.                     graphics_ID, false);
    155.         }
    156.  
    157.     }
    158.  
    159.  
    160.  
    161.     // Called on every new location estimate
    162.     public void onServiceUpdate(ServiceState state) {
    163.  
    164.         Log.d(TAG, "onServiceUpdate()");
    165.      
    166.      
    167.         // Create application here...
    168.      
    169.      
    170.      
    171.         // Use location estimate
    172.         final String s = new String(
    173.                 "Roundtrip : " + state.getRoundtrip()+ "ms\n\n"
    174.                 + "Lat : "+ state.getGeoPoint().getLatitude() + "\n\n"
    175.                 + "Lon : "+ state.getGeoPoint().getLongitude() + "\n\n"
    176.                 + "X [meter] : "+ state.getMetricPoint().getX() + "\n\n"
    177.                 + "Y [meter] : "+ state.getMetricPoint().getY() + "\n\n"
    178.                 + "I [pixel] : "+ state.getImagePoint().getI() + "\n\n"
    179.                 + "J [pixel] : "+ state.getImagePoint().getJ()) + "\n\n"
    180.                 + "Heading : "+ state.getHeadingDegrees() + "\n\n"
    181.                 + "Probability : "+ state.getProbability();
    182.      
    183.         //final String XandY = new String("The X = " + state.getMetricPoint().getX() + "\n\n"
    184.         //        + " The Y = " +state.getMetricPoint().getY());
    185.      
    186.         // Show the values on screen
    187.         showMessageOnUI(s);
    188.      
    189.         Double probe = new Double(state.getProbability());
    190.      
    191.         if (probe == 1.0)
    192.         {
    193.          
    194.             showMessageOnUI("SET POINTER HERE");
    195.          
    196.      
    197.         // Load the pointer-image here
    198.      
    199.             //createBitMap(); // this could be changed
    200.      
    201.         // Update Pointer on X and Y position
    202.          
    203.          
    204.          
    205.         }
    206.      
    207.     }
    208.  
    209.  
    210.     private void createBitMap() {
    211.         // Create a mutable bitmap
    212.      
    213.         Bitmap bitMap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    214.  
    215.         bitMap = bitMap.copy(bitMap.getConfig(), true);
    216.         // Construct a canvas with the specified bitmap to draw into
    217.         Canvas canvas = new Canvas(bitMap);
    218.         // Create a new paint with default settings.
    219.         Paint paint = new Paint();
    220.         // smooths out the edges of what is being drawn
    221.         paint.setAntiAlias(true);
    222.         // set color
    223.       //  paint.setColor(Color.BLACK);
    224.         // set style
    225.         paint.setStyle(Paint.Style.STROKE);
    226.         // set stroke
    227.         paint.setStrokeWidth(4.5f);
    228.         // draw circle with radius 30
    229.         canvas.drawCircle(50, 50, 30, paint);
    230.         // set on ImageView or any other view
    231.         image.setImageBitmap(bitMap);
    232.  
    233.     }
    234.  
    235.  
    236.     // Request failed
    237.     public void onServiceFailure(final String reason) {
    238.         Log.d(TAG, "onServiceFailure(): reason : " + reason);
    239.         showMessageOnUI("onServiceFailure(): reason : " + reason);
    240.     }
    241.  
    242.     // Initializing location service
    243.     public void onServiceInitializing() {
    244.         Log.d(TAG, "onServiceInitializing()");
    245.         showMessageOnUI("onServiceInitializing()");
    246.     }
    247.  
    248.     // Initialization completed
    249.     public void onServiceInitialized() {
    250.         Log.d(TAG, "onServiceInitialized()");
    251.         showMessageOnUI("onServiceInitialized(): Walk to get location fix");
    252.     }
    253.  
    254.     // Location service initialization failed
    255.     public void onInitializationFailed(final String reason) {
    256.         Log.d(TAG, "onInitializationFailed()");
    257.         showMessageOnUI("onInitializationFailed(): "+ reason);
    258.     }
    259.  
    260.     // Positioning was stopped
    261.     public void onServiceStopped() {
    262.         Log.d(TAG, "onServiceStopped()");
    263.         showMessageOnUI("onServiceStopped(): IndoorAtlas Positioning Service is stopped.");
    264.     }
    265.  
    266.     // Calibration failed
    267.     public void onCalibrationFailed(String reason) {
    268.         Log.d(TAG, "onCalibrationFailed(), reason : "+reason);
    269.         showMessageOnUI("onCalibrationFailed(): Calibration failed, reason : "+reason);
    270.     }
    271.  
    272.     // Calibration successful, positioning can be started
    273.     public void onCalibrationFinished() {
    274.         Log.d(TAG, "onCalibrationFinished()");
    275.  
    276.         showMessageOnUI("onCalibrationFinished(): starting positioning.");
    277.  
    278.         calibrated = true;
    279.      
    280.         // Use Floor Plans tool to get IDs for building, level and floor plan
    281.      
    282.         // Setting the last parameter true tries to switch to Mobile network for
    283.         // the positioning session.
    284.         // Otherwise WiFi network will be used, if available.
    285.  
    286.         indoorAtlas.startPositioning(building_ID, floor_ID,
    287.                 graphics_ID, false);
    288.     }
    289.  
    290.     // Helper method
    291.     private void showMessageOnUI(final String message) {
    292.         handler.post(new Runnable() {
    293.             public void run() {
    294.                 textView.setText(message);
    295.             }
    296.         });
    297.  
    298.     }
    299.  
    300. }
    301.  
    Android Manifest in Unity3D project:
    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3.     package="nl.banzaibandit.plugin"
    4.     android:versionCode="1"
    5.     android:versionName="0.1.1" >
    6.  
    7.  
    8.     <uses-permission android:name="android.permission.INTERNET"/>
    9.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    10.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    11.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    12.  
    13.  
    14.       <application android:icon="@drawable/app_icon"
    15.                       android:label="@string/app_name"
    16.                       android:debuggable="true">
    17.    
    18.           <activity
    19.               android:name="nl.banzaibandit.plugin.UnityBridge"
    20.               android:label="@string/app_name" >
    21.            
    22.               <intent-filter>
    23.                 <action android:name="android.intent.action.MAIN" />
    24.                 <category android:name="android.intent.category.LAUNCHER" />
    25.                </intent-filter>
    26.              
    27.            </activity>
    28.  
    29.            <activity
    30.                android:name="com.indooratlas.android.IndoorAtlasListener"
    31.                android:label="@string/app_name" >
    32.            </activity>
    33.        
    34.           <activity
    35.               android:name="com.unity3d.player.UnityPlayerProxyActivity"
    36.               android:screenOrientation="portrait"
    37.               android:label="@string/app_name" >
    38.           </activity>
    39.            
    40.         <activity android:name="com.unity3d.player.UnityPlayerNativeActivity" android:label="@string/app_name" >
    41.               <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
    42.         </activity>
    43.  
    44.     </application>
    45. </manifest>
    Unity3D C# code for testing purposes:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. public class IndoorAtlasExample : MonoBehaviour {
    6.  
    7.  
    8.     public UILabel label;
    9.  
    10.     private void Start()
    11.     {
    12.         AndroidJNI.AttachCurrentThread();
    13.     }
    14.  
    15.     public void ButtonClick()
    16.     {
    17.         int number = GetCurrentActivity().Call<int>("GetInt");
    18.  
    19.         label.text = number.ToString();
    20.     }
    21.  
    22.     private AndroidJavaObject GetCurrentActivity()
    23.     {
    24.         AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    25.         AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    26.         return activity;
    27.     }
    28. }

    Can anyone help me get this first step working?
     
    Last edited: Jul 24, 2014
  2. Tripwire

    Tripwire

    Joined:
    Oct 12, 2010
    Posts:
    442
    Anyone?
     
  3. Tripwire

    Tripwire

    Joined:
    Oct 12, 2010
    Posts:
    442
    Still no answers :( I'm still trying to get this to work but i'm not one step closer to solving this one :(
     
  4. Helath

    Helath

    Joined:
    Jan 3, 2013
    Posts:
    9
    I'm no pro at this, but it seems like your manifest is trying to create "nl.banzaibandit.plugin.UnityBridge" as the main activity, when UnityBridge does not extend from "UnityPlayerActivity", or from any activity type for that matter.
    Try changing:

    <activity
    android:name="nl.banzaibandit.plugin.UnityBridge"
    android:label="@String/app_name" >

    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    </activity>


    To:

    <activity android:name="com.unity3d.player.UnityPlayerProxyActivity"
    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>

    This should have the manifest start up unity as the main activity.
     
  5. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,555
    That will probably run, but will not be what he wants, since the main activity won't have the getInt() method on it...
     
  6. Helath

    Helath

    Joined:
    Jan 3, 2013
    Posts:
    9
    It shouldn't need to be the main activity if you just want to call the function getInt().
    If it were to run without being the main activity, and the exported jar is correctly located in Plugins/Android folder you should be able to do something like this:

    AndroidJavaClass unityBridge =new AndroidJavaClass("nl.banzaibandit.plugin.UnityBridge");
    int number = unityBridge.Call<int>("GetInt");