Menu
Who Do Is
  • Home
  • What
  • How
  • Is
  • Can
  • Are
  • Does
  • Do
  • Why
  • Who
  • Where
  • Which
  • Which
  • Should
  • Will
  • When
  • What’s
  • Did
Who Do Is

[ANSWERED] unity3d – Can’t get broadcasts working with a native android module (AAR file) in Unity

Posted on November 14, 2022

Solution 1 :

I never thought i would end up with an answer myself. Apparantly you have to use the full name in the receiver for both the xml in unity as well as the one in android studio, like so:
<receiver android_name="com.BrabantWater.mynativemodulegps.GeofenceBroadcastReceiver" />

Problem :

The idea was that I needed the GPS in the background for a unity project. It is for finding out if a user is near an ‘information point’ while hiking certain routes. For that we figured (since we needed to get decently frequent GPS updates) to use a geofence. I created a native module in Android studio and placed the AAR file in unity. however the broadcastreceiver doesn’t ever trigger.
I started with the background broadcast tutorial of Android as a basic template (https://developer.android.com/training/location/geofencing.html).
The code ended up looking like this:

Bridge.java

package com.BrabantWater.mynativemodulegps;

//import needed packages / classes.
import android.annotation.SuppressLint;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;

public class Bridge extends Application
{
    public static Context context;
    public static String notificationContentTitle = "";
    public static String notificationContentText = "";
    public static String notificationDescription = "";

    private GeofencingClient geofencingClient;
    private ArrayList<Geofence> geofenceList;
    GeofencingRequest.Builder builder;
    PendingIntent geofencePendingIntent;
    GeofencingRequest geofencingRequest;

    public Bridge(Context ctx)
    {
        context = ctx;
        geofencingClient = new GeofencingClient(context);
        geofenceList = new ArrayList<>();
        builder = new GeofencingRequest.Builder();
    }

    public Bridge(Context ctx, String contentTitle, String contentText, String description)
    {
        context = ctx;
        notificationContentTitle = contentTitle;
        notificationContentText = contentText;
        notificationDescription = description;

        geofencingClient = new GeofencingClient(context);
        geofenceList = new ArrayList<>();
        builder = new GeofencingRequest.Builder();
    }

    public void addGeoFence(String id, double latitude, double longitude)
    {
        geofenceList.add(new Geofence.Builder()
                // Set the request ID of the geofence. This is a string to identify this
                // geofence.
                .setRequestId(id)
                .setCircularRegion(
                        latitude,
                        longitude,
                        300
                )
                .setNotificationResponsiveness(5000)
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                        Geofence.GEOFENCE_TRANSITION_EXIT |
                        Geofence.GEOFENCE_TRANSITION_DWELL)
                .setLoiteringDelay(1)
                .build());
        Log.i("TESTTAG", "GEOFENCE toegevoegd aan lijst");
        Log.i("TESTTAG", geofenceList.toString());
    }

    @SuppressLint("MissingPermission")
    public void GeoFenceCompleted()
    {
        Log.i("TESTTAG", "Completing Geofence");
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL);
        builder.addGeofences(geofenceList);
        geofencingRequest =  builder.build();

        geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent())
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        // Geofences added
                        // ...
                        Log.i("TESTTAG", "Geofences added");
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        // Failed to add geofences
                        // ...
                        Log.e("TESTTAG", "Couldn't add geofences");
                    }
                });
    }

    public void removeAllGeoFences()
    {
        geofencingClient.removeGeofences(getGeofencePendingIntent())
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        // Geofences removed
                        // ...
                        Log.i("TESTTAG", "removed all geofences");
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        // Failed to remove geofences
                        // ...
                        Log.e("TESTTAG", "Couldn't remove all geofences");
                    }
                });
    }

    public void removeGeoFences(List<String> ids) {
        geofencingClient.removeGeofences(ids);
    }

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            Log.i("TESTTAG", "used old intent for GeofenceBroadcastReceiver");
            return geofencePendingIntent;
        }
        Log.i("TESTTAG", "Created new intent for GeofenceBroadcastReceiver");
        Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }
}

GeofenceBroadcastReceiver.java

    package com.BrabantWater.mynativemodulegps;

    import android.app.NotificationChannel;
    import android.app.NotificationManager;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Build;
    import android.util.Log;

    import com.google.android.gms.location.Geofence;
    import com.google.android.gms.location.GeofenceStatusCodes;
    import com.google.android.gms.location.GeofencingEvent;

    import java.util.List;

    import androidx.core.app.NotificationCompat;

    import static android.content.ContentValues.TAG;
    import static androidx.core.content.ContextCompat.getSystemService;

    public class GeofenceBroadcastReceiver extends BroadcastReceiver {
        // ...
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i("TESTTAG", "GEOFENCE received");
            GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
            if (geofencingEvent.hasError()) {
                String errorMessage = GeofenceStatusCodes.getStatusCodeString(geofencingEvent.getErrorCode());
                Log.e(TAG, errorMessage);
                return;
            }

            // Get the transition type.
            int geofenceTransition = geofencingEvent.getGeofenceTransition();

            // Test that the reported transition was of interest.
            if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                    geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) {

                // Get the geofences that were triggered. A single event can trigger
                // multiple geofences.
                List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

                // Get the transition details as a String.
                String geofenceTransitionDetails = triggeringGeofences.toString();

                // Send notification and log the transition details.
                sendNotification(geofenceTransitionDetails, context);
                Log.i(TAG, geofenceTransitionDetails);
            } else {
                // Log the error.
                Log.e(TAG, "Transition Error");
            }
        }

        private void sendNotification(String geofenceTransitionDetails, Context context)
        {
            Log.i("TESTTAG", "GEOFENCE triggered");
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "Brabant_Water_GO")
                    .setContentTitle(Bridge.notificationContentTitle)
                    .setContentText(Bridge.notificationContentText)
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);

            // Create the NotificationChannel, but only on API 26+ because
            // the NotificationChannel class is new and not in the support library
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                CharSequence name = "Brabant_Water_GO";
                String description = Bridge.notificationDescription;
                int importance = NotificationManager.IMPORTANCE_DEFAULT;
                NotificationChannel channel = new NotificationChannel("Brabant_Water_GO", name, importance);
                channel.setDescription(description);
                // Register the channel with the system; you can't change the importance
                // or other notification behaviors after this
                NotificationManager notificationManager = getSystemService(context, NotificationManager.class);
                notificationManager.createNotificationChannel(channel);

                notificationManager.notify(10000, builder.build());
                Log.i("TESTTAG", "notification Shown");
            }
        }
    }

The manifest looks like this:

READ  [ANSWERED] android - Different Color Background in my AlertDialog in Kotlin
Powered by Inline Related Posts

AndroidManifest.xml

    <manifest xmlns_android="http://schemas.android.com/apk/res/android"
        package="com.BrabantWater.mynativemodulegps">
    
        <uses-permission android_name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android_name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
        <application>
            <receiver android_name=".GeofenceBroadcastReceiver" />
        </application>
    </manifest>

On the Unity side of things i made a class that calls the methods of bridge and modified the manifest to ensure i got access to the location.

AndroidCaller.cs

#if UNITY_ANDROID
//basic imports.
using UnityEngine;

public class AndroidCaller : MonoBehaviour
{
    AndroidJavaClass unityPlayerClass;
    AndroidJavaObject unityActivity;
    AndroidJavaObject bridge;
    object[] parameters;

    void Start()
    {
        //just done for testing purposes
        CallNativePlugin();
    }
    //method that calls our native plugin.
    public void CallNativePlugin()
    {
        // Retrieve the UnityPlayer class.
        unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

        // Retrieve the UnityPlayerActivity object ( a.k.a. the current context )
        unityActivity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");

        // Setup the parameters we want to send to our native plugin.    
        parameters = new object[4];
        parameters[0] = unityActivity;
        parameters[1] = "Title";
        parameters[2] = "ContentText";
        parameters[3] = "Description";

        // Retrieve the "Bridge" from our native plugin.
        // ! Notice we define the complete package name.              
        bridge = new AndroidJavaObject("com.BrabantWater.mynativemodulegps.Bridge", parameters);

        // Call addGeoFence in bridge, with our parameters.
        bridge.Call("addGeoFence", "id", 51.461059, 4.310962);
        bridge.Call("addGeoFence", "id2", 51.451459, 4.329905);
        bridge.Call("GeoFenceCompleted");
    }
}
#endif

AndroidManifest.xml(in Unity project)

<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
    xmlns_android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns_tools="http://schemas.android.com/tools">
    <application>
        <receiver android_name="com.BrabantWater.mynativemodulegps.GeofenceBroadcastReceiver" />
        <activity android_name="com.unity3d.player.UnityPlayerActivity"
                  android_theme="@style/UnityThemeSelector">
            <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>
    
    <uses-permission android_name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android_name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android_name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android_name="android.permission.CAMERA" />   
</manifest>

After alot of testing by using logcat i couldn’t figure out why the onReceive was never triggered. So that’s how i got stuck.

Comments

Comment posted by Lotan

if it’s correct, mark your own answer so everyone can see that the question is “solved” ^^

READ  [ANSWERED] java - Does the UniqueId you get from Android change sometime?
Powered by Inline Related Posts

Comment posted by knowndead

thanks for the reminder. I couldn’t mark it yet since i barely created the post an hour before i fixed it

Recent Posts

  • How can I play with my cat without toys?
  • What is a bag pipe band called?
  • Are Honda Civics actually fast?
  • Are Yankee candles toxic?
  • How do I pair my Michael Kors smartwatch with my Android?

Recent Comments

No comments to show.

Archives

  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022

Categories

  • ¿Cómo
  • ¿Cuál
  • ¿Cuántas
  • ¿Cuánto
  • ¿Que
  • ¿Quién
  • 90” and 108” so you may have to round up to the nearest size.
  • and delete any Spotify folders from it. Once this is done
  • Android
  • Are
  • At
  • Bei
  • blink
  • C'est
  • Can
  • carbs
  • Comment
  • Did
  • Do
  • Does
  • During
  • For
  • Has
  • How
  • In
  • Is
  • Ist
  • Kann
  • Können
  • nouveau
  • On
  • or 108 inches.2020-08-03
  • Où
  • owning
  • Pourquoi
  • Puis-je
  • Quand
  • Quante
  • Quel
  • Quelle
  • Quelles
  • Quels
  • Qui
  • Should
  • Sind
  • Sollte
  • spiritual
  • tap the downward-facing arrow on the top left. A downward-facing arrow will appear underneath each song in the album; they'll turn green as the download completes.2020-07-28
  • Uncategorized
  • Wann
  • Warum
  • Was
  • Welche
  • Welcher
  • Welches
  • Welke
  • Wer
  • Were
  • What
  • What's
  • When
  • Where
  • Which
  • Who
  • Whose
  • Why
  • Wie
  • Will
  • Wo
  • Woher
  • you will receive two curtains each with the same measurements of width 66"" (168cm) x drop 54""(137cm).
  • you'll see a green downward-facing arrow next to each song.2021-02-26
©2023 Who Do Is | Powered by SuperbThemes & WordPress