Не удается заставить трансляции работать с собственным модулем android (файлом AAR) в Unity - PullRequest
1 голос
/ 07 августа 2020

Идея заключалась в том, что мне нужен был GPS в фоновом режиме для проекта Unity. Он предназначен для того, чтобы узнать, находится ли пользователь рядом с «информационной точкой» во время пеших походов по определенным маршрутам. Для этого мы решили (поскольку нам нужно было получать довольно частые обновления GPS) использовать геозону. Я создал собственный модуль в студии Android и поместил файл AAR в единое целое. однако широковещательный приемник никогда не запускается. Я начал с учебника по фоновой трансляции Android как базового c шаблона (https://developer.android.com/training/location/geofencing.html). Код в итоге выглядел так:

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");
            }
        }
    }

Манифест выглядит так:

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>

На стороне Unity я создал класс, который вызывает методы моста и изменил манифест, чтобы убедиться, что я получил доступ к местоположению.

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 (в 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>

После большого количества тестов с использованием logcat я не мог понять, почему onReceive никогда не запускался. Вот так я и застрял.

1 Ответ

0 голосов
/ 07 августа 2020

Никогда не думал, что сам получу ответ. Очевидно, вы должны использовать полное имя в приемнике как для xml in unity, так и для android studio, например: <receiver android:name="com.BrabantWater.mynativemodulegps.GeofenceBroadcastReceiver" />

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...