From 5a4bbb5dc9ce4ebc0597d2c03c09c60931b0eebf Mon Sep 17 00:00:00 2001 From: rachytski Date: Tue, 29 Nov 2011 20:49:58 +0400 Subject: [PATCH] implemented LocationManager C++->Java binding. --- android/jni/Android.mk | 2 + android/jni/com/mapswithme/jni/jni_method.cpp | 42 +++++++ android/jni/com/mapswithme/jni/jni_method.hpp | 67 +++++++++++ android/jni/com/mapswithme/jni/jni_thread.cpp | 63 +++++++++++ android/jni/com/mapswithme/jni/jni_thread.hpp | 18 +++ .../mapswithme/location/LocationService.cpp | 71 +++++++++++- .../mapswithme/location/LocationService.hpp | 32 ++++++ android/jni/com/mapswithme/maps/Framework.cpp | 37 ++---- android/jni/com/mapswithme/maps/Framework.hpp | 6 +- .../jni/com/mapswithme/maps/MWMActivity.cpp | 3 + .../src/com/mapswithme/maps/MWMActivity.java | 23 +++- .../maps/location/LocationService.java | 107 ++++++++++++------ platform/location.hpp | 3 +- 13 files changed, 400 insertions(+), 74 deletions(-) create mode 100644 android/jni/com/mapswithme/jni/jni_method.cpp create mode 100644 android/jni/com/mapswithme/jni/jni_method.hpp create mode 100644 android/jni/com/mapswithme/jni/jni_thread.cpp create mode 100644 android/jni/com/mapswithme/jni/jni_thread.hpp create mode 100644 android/jni/com/mapswithme/location/LocationService.hpp diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 07f370c7fd..963bd88387 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -41,6 +41,8 @@ LOCAL_SRC_FILES := \ com/mapswithme/maps/Lifecycle.cpp \ com/mapswithme/platform/Platform.cpp \ com/mapswithme/platform/http_thread_android.cpp \ + com/mapswithme/jni/jni_thread.cpp \ + com/mapswithme/jni/jni_method.cpp \ nv_thread/nv_thread.cpp \ nv_event/nv_event_queue.cpp \ nv_event/nv_event.cpp \ diff --git a/android/jni/com/mapswithme/jni/jni_method.cpp b/android/jni/com/mapswithme/jni/jni_method.cpp new file mode 100644 index 0000000000..46069075e9 --- /dev/null +++ b/android/jni/com/mapswithme/jni/jni_method.cpp @@ -0,0 +1,42 @@ +/* + * method_ref.cpp + * + * Created on: Nov 27, 2011 + * Author: siarheirachytski + */ + +#include "jni_thread.hpp" +#include "jni_method.hpp" +#include "../../../../../base/assert.hpp" + +namespace jni +{ + Method::Method(jclass klass, + char const * name, + char const * signature) + : m_name(name), + m_signature(signature) + { + JNIEnv * env = GetCurrentThreadJNIEnv(); + m_index = env->GetMethodID(klass, m_name, m_signature); + CHECK(m_index, ("Error: No valid function pointer in ", m_name)); + } + + bool Method::CallBoolean(jobject self) + { + JNIEnv* jniEnv = GetCurrentThreadJNIEnv(); + + CHECK(jniEnv, ("Error: No valid JNI env in ", m_name)); + + return jniEnv->CallBooleanMethod(self, m_index); + } + + bool Method::CallInt(jobject self) + { + JNIEnv* jniEnv = GetCurrentThreadJNIEnv(); + + CHECK(jniEnv, ("Error: No valid JNI env in ", m_name)); + + return (int)jniEnv->CallIntMethod(self, m_index); + } +} diff --git a/android/jni/com/mapswithme/jni/jni_method.hpp b/android/jni/com/mapswithme/jni/jni_method.hpp new file mode 100644 index 0000000000..80c83e60f5 --- /dev/null +++ b/android/jni/com/mapswithme/jni/jni_method.hpp @@ -0,0 +1,67 @@ +/* + * method_ref.hpp + * + * Created on: Nov 27, 2011 + * Author: siarheirachytski + */ + +#pragma once + +#include +#include "jni_thread.hpp" + +namespace jni +{ + class Method + { + private: + + const char* m_name; + const char* m_signature; + jmethodID m_index; + + public: + + Method(jclass klass, + const char* name, + const char* signature); + + void CallVoid(jobject self) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index); + }; + + template + void CallVoid(jobject self, A1 a1) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index, a1); + } + + template + void CallVoid(jobject self, A1 a1, A2 a2) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index, a1, a2); + } + + template + void CallVoid(jobject self, A1 a1, A2 a2, A3 a3) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index, a1, a2, a3); + } + + template + void CallVoid(jobject self, A1 a1, A2 a2, A3 a3, A4 a4) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index, a1, a2, a3, a4); + } + + template + void CallVoid(jobject self, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) + { + GetCurrentThreadJNIEnv()->CallVoidMethod(self, m_index, a1, a2, a3, a4, a5); + } + + bool CallBoolean(jobject self); + bool CallInt(jobject self); + }; +} diff --git a/android/jni/com/mapswithme/jni/jni_thread.cpp b/android/jni/com/mapswithme/jni/jni_thread.cpp new file mode 100644 index 0000000000..bc6b8d2e0b --- /dev/null +++ b/android/jni/com/mapswithme/jni/jni_thread.cpp @@ -0,0 +1,63 @@ +/* + * jni_thread.cpp + * + * Created on: Nov 27, 2011 + * Author: siarheirachytski + */ + +#include "jni_thread.hpp" +#include +#include "../../../../../base/logging.hpp" + +namespace jni +{ + JavaVM * s_jvm; + + void SetCurrentJVM(JavaVM * jvm) + { + s_jvm = jvm; + } + + JavaVM * GetCurrentJVM() + { + return s_jvm; + } + + static pthread_key_t s_jniEnvKey = 0; + + JNIEnv* GetCurrentThreadJNIEnv() + { + JNIEnv* env = NULL; + if (s_jniEnvKey) + env = (JNIEnv*)pthread_getspecific(s_jniEnvKey); + else + pthread_key_create(&s_jniEnvKey, NULL); + + if (!env) + { + if (!GetCurrentJVM()) + { + LOG(LINFO, ("Error - could not find JVM!")); + return 0; + } + + // Hmm - no env for this thread cached yet + int error = GetCurrentJVM()->AttachCurrentThread(&env, 0); + + LOG(LINFO, ("AttachCurrentThread: ", error, ", ", env)); + if (error || !env) + { + LOG(LINFO, ("Error - could not attach thread to JVM!")); + return 0; + } + + pthread_setspecific(s_jniEnvKey, env); + } + + return env; + } + +} + + + diff --git a/android/jni/com/mapswithme/jni/jni_thread.hpp b/android/jni/com/mapswithme/jni/jni_thread.hpp new file mode 100644 index 0000000000..853bf4883e --- /dev/null +++ b/android/jni/com/mapswithme/jni/jni_thread.hpp @@ -0,0 +1,18 @@ +/* + * jni_thread.hpp + * + * Created on: Nov 27, 2011 + * Author: siarheirachytski + */ + +#pragma once + +#include + +namespace jni +{ + void SetCurrentJVM(JavaVM * jvm); + JavaVM * GetCurrentJVM(); + + JNIEnv * GetCurrentThreadJNIEnv(); +} diff --git a/android/jni/com/mapswithme/location/LocationService.cpp b/android/jni/com/mapswithme/location/LocationService.cpp index 9b27718e9b..ec5a9d7534 100644 --- a/android/jni/com/mapswithme/location/LocationService.cpp +++ b/android/jni/com/mapswithme/location/LocationService.cpp @@ -6,26 +6,89 @@ */ #include +#include "../jni/jni_thread.hpp" #include "../maps/Framework.hpp" +#include "LocationService.hpp" + +android::LocationService * g_locationService = 0; + +namespace android +{ + LocationService::LocationService(location::LocationObserver & locationObserver, + jobject observer) + : location::LocationService(locationObserver), + m_javaObserver(observer) + { + jclass k = jni::GetCurrentThreadJNIEnv()->GetObjectClass(m_javaObserver); + m_onLocationChanged.reset(new jni::Method(k, "onLocationChanged", "(JDDF)V")); + m_onStatusChanged.reset(new jni::Method(k, "onStatusChanged", "(J)V")); + } + + void LocationService::Start() + { + m_observer.OnLocationStatusChanged(location::EStarted); + m_onStatusChanged->CallVoid(m_javaObserver, location::EStarted); + } + + void LocationService::Stop() + { + m_observer.OnLocationStatusChanged(location::EStopped); + m_onStatusChanged->CallVoid(m_javaObserver, location::EStopped); + } + + void LocationService::Disable() + { + m_observer.OnLocationStatusChanged(location::EDisabledByUser); + m_onStatusChanged->CallVoid(m_javaObserver, location::EDisabledByUser); + } + + void LocationService::OnLocationUpdate(location::GpsInfo const & info) + { + m_observer.OnGpsUpdated(info); + m_onLocationChanged->CallVoid(m_javaObserver, info.m_timestamp, info.m_latitude, info.m_longitude, info.m_horizontalAccuracy); + } +} /////////////////////////////////////////////////////////////////////////////////// // LocationService /////////////////////////////////////////////////////////////////////////////////// + extern "C" { JNIEXPORT void JNICALL - Java_com_mapswithme_maps_location_LocationService_nativeEnableLocationService(JNIEnv * env, jobject thiz, - jboolean enable) + Java_com_mapswithme_maps_location_LocationService_nativeStartUpdate(JNIEnv * env, jobject thiz, jobject observer) { - g_framework->EnableLocation(enable); + g_locationService = new android::LocationService(*g_framework, observer); + g_locationService->Start(); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_location_LocationService_nativeStopUpdate(JNIEnv * env, jobject thiz) + { + g_locationService->Stop(); + delete g_locationService; } JNIEXPORT void JNICALL Java_com_mapswithme_maps_location_LocationService_nativeLocationChanged(JNIEnv * env, jobject thiz, jlong time, jdouble lat, jdouble lon, jfloat accuracy) { - g_framework->UpdateLocation(time, lat, lon, accuracy); + location::GpsInfo info; + + info.m_horizontalAccuracy = static_cast(accuracy); + info.m_latitude = lat; + info.m_longitude = lon; + info.m_timestamp = time; + info.m_source = location::EAndroidNative; + + g_locationService->OnLocationUpdate(info); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_location_LocationService_nativeDisable(JNIEnv * env, jobject thiz) + { + g_locationService->Disable(); } JNIEXPORT void JNICALL diff --git a/android/jni/com/mapswithme/location/LocationService.hpp b/android/jni/com/mapswithme/location/LocationService.hpp new file mode 100644 index 0000000000..040cb70687 --- /dev/null +++ b/android/jni/com/mapswithme/location/LocationService.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "../../../../../platform/location_service.hpp" +#include "../../../../../std/scoped_ptr.hpp" +#include "../jni/jni_method.hpp" + +namespace android +{ + class LocationService : public location::LocationService + { + private: + + jobject m_javaObserver; + + scoped_ptr m_onLocationChanged; + scoped_ptr m_onStatusChanged; + + public: + + LocationService(location::LocationObserver & locationObserver, + jobject observer); + + void Start(); + void Stop(); + void Disable(); + void OnLocationUpdate(location::GpsInfo const & info); + }; +} + +extern android::LocationService * g_locationService; diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 9786fcbaed..726ea805fd 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -57,6 +57,16 @@ namespace android delete m_videoTimer; } + void Framework::OnLocationStatusChanged(location::TLocationStatus newStatus) + { + m_work.OnLocationStatusChanged(newStatus); + } + + void Framework::OnGpsUpdated(location::GpsInfo const & info) + { + m_work.OnGpsUpdate(info); + } + void Framework::DeleteRenderPolicy() { LOG(LINFO, ("clearing current render policy.")); @@ -241,33 +251,6 @@ namespace android } - void f() - { - // empty location stub - } - - void Framework::EnableLocation(bool enable) - { -// if (enable) -// m_work.StartLocationService(bind(&f)); -// else -// m_work.StopLocationService(); - } - - void Framework::UpdateLocation(uint64_t timestamp, double lat, double lon, float accuracy) - { - location::GpsInfo info; - info.m_timestamp = static_cast(timestamp); - info.m_latitude = lat; - info.m_longitude = lon; - info.m_horizontalAccuracy = accuracy; -// info.m_status = location::EAccurateMode; -// info.m_altitude = 0; -// info.m_course = 0; -// info.m_verticalAccuracy = 0; - m_work.OnGpsUpdate(info); - } - void Framework::UpdateCompass(uint64_t timestamp, double magneticNorth, double trueNorth, float accuracy) { location::CompassInfo info; diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index d1868aff8a..63db73e079 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -13,10 +13,11 @@ #include "../../../../../map/window_handle.hpp" #include "../../../../../map/feature_vec_model.hpp" #include "../../../nv_event/nv_event.hpp" +#include "../../../../../platform/location_service.hpp" namespace android { - class Framework + class Framework : public location::LocationObserver { private: ::Framework m_work; @@ -47,6 +48,9 @@ namespace android storage::Storage & Storage(); + void OnLocationStatusChanged(location::TLocationStatus newStatus); + void OnGpsUpdated(location::GpsInfo const & info); + void Invalidate(); bool InitRenderPolicy(); diff --git a/android/jni/com/mapswithme/maps/MWMActivity.cpp b/android/jni/com/mapswithme/maps/MWMActivity.cpp index 9395f47b0a..734a7238c8 100644 --- a/android/jni/com/mapswithme/maps/MWMActivity.cpp +++ b/android/jni/com/mapswithme/maps/MWMActivity.cpp @@ -16,6 +16,7 @@ #include "Framework.hpp" #include "../platform/Platform.hpp" #include "../../../nv_event/nv_event.hpp" +#include "../jni/jni_thread.hpp" JavaVM * g_jvm; @@ -24,6 +25,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * jvm, void * reserved) { + jni::SetCurrentJVM(jvm); InitNVEvent(jvm); g_jvm = jvm; jni::InitSystemLog(); @@ -36,6 +38,7 @@ extern "C" JNI_OnUnload(JavaVM * vm, void * reserved) { delete g_framework; + jni::SetCurrentJVM(0); } JNIEXPORT void JNICALL diff --git a/android/src/com/mapswithme/maps/MWMActivity.java b/android/src/com/mapswithme/maps/MWMActivity.java index 141e260806..8416409c41 100644 --- a/android/src/com/mapswithme/maps/MWMActivity.java +++ b/android/src/com/mapswithme/maps/MWMActivity.java @@ -15,14 +15,16 @@ import android.os.Environment; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.util.Log; -public class MWMActivity extends NvEventQueueActivity +public class MWMActivity extends NvEventQueueActivity implements LocationService.Observer { VideoTimer m_timer; private static String TAG = "MWMActivity"; private final static String PACKAGE_NAME = "com.mapswithme.maps"; private boolean m_locationEnabled = false; + private LocationService m_locationService = null; private String getAppBundlePath() throws NameNotFoundException { @@ -57,6 +59,17 @@ public class MWMActivity extends NvEventQueueActivity } m_timer = new VideoTimer(); + m_locationService = new LocationService(this); + } + + public void onStatusChanged(long status) + { + Log.d(TAG, "onStatusChanged"); + } + + public void onLocationChanged(long time, double latitude, double longitude, float accuracy) + { + Log.d(TAG, "onLocationChanged"); } /* @Override @@ -83,7 +96,7 @@ public class MWMActivity extends NvEventQueueActivity MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); // temprorarily disable downloader in the menu - menu.removeItem(R.id.download_maps); + //menu.removeItem(R.id.download_maps); return true; } @@ -94,10 +107,10 @@ public class MWMActivity extends NvEventQueueActivity switch (item.getItemId()) { case R.id.my_position: - if (m_locationEnabled) - LocationService.stop(); + if (m_locationService.isActive()) + m_locationService.stopUpdate(); else - LocationService.start(this); + m_locationService.startUpdate(this); m_locationEnabled = !m_locationEnabled; return true; case R.id.download_maps: diff --git a/android/src/com/mapswithme/maps/location/LocationService.java b/android/src/com/mapswithme/maps/location/LocationService.java index a7fbdaef86..9c7652295b 100644 --- a/android/src/com/mapswithme/maps/location/LocationService.java +++ b/android/src/com/mapswithme/maps/location/LocationService.java @@ -16,51 +16,75 @@ import android.view.WindowManager; public class LocationService implements LocationListener, SensorEventListener { + + public interface Observer + { + public void onLocationChanged(long time, double lat, double lon, float accuracy); + public void onStatusChanged(long status); + }; + + private boolean m_isActive = false; private static String TAG = "Location"; - private static LocationService m_self; private LocationManager m_locationManager; private SensorManager m_sensorManager; private Sensor m_compassSensor; - // To calculate true north for compass private GeomagneticField m_field; - public static void start(Context c) + public LocationService(Context c) { - if (m_self == null) + // Acquire a reference to the system Location Manager + m_locationManager = (LocationManager) c.getSystemService(Context.LOCATION_SERVICE); + m_sensorManager = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE); + m_compassSensor = m_sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); + m_field = null; + } + + public boolean isActive() + { + return m_isActive; + } + + public void startUpdate(Observer observer) + { + m_isActive = false; + + /*if (m_locationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)) { - m_self = new LocationService(); - // Acquire a reference to the system Location Manager - m_self.m_locationManager = (LocationManager) c.getSystemService(Context.LOCATION_SERVICE); - m_self.m_sensorManager = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE); - m_self.m_compassSensor = m_self.m_sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); -// m_self.m_defaultDisplay = ((WindowManager)c.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - m_self.m_field = null; + m_locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this); + m_isActive = true; + }*/ + + if (m_locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) + { + m_locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); + m_isActive = true; + } + + if (m_locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) + { + m_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); + m_isActive = true; + } + + if (m_isActive) + { + m_sensorManager.registerListener(this, m_compassSensor, SensorManager.SENSOR_DELAY_NORMAL); + nativeStartUpdate(observer); + } + else + { + Log.d(TAG, "no locationProviders are found"); + // TODO : callback into gui to show the "providers are not enabled" messagebox } - m_self.startUpdate(); } - public static void stop() - { - if (m_self != null) - m_self.stopUpdate(); - } - - private void startUpdate() - { - // Register the listener with the Location Manager to receive location updates - m_locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this); - m_locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); - m_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); - m_sensorManager.registerListener(this, m_compassSensor, SensorManager.SENSOR_DELAY_NORMAL); - nativeEnableLocationService(true); - } - - private void stopUpdate() + public void stopUpdate() { m_locationManager.removeUpdates(this); m_sensorManager.unregisterListener(this); - nativeEnableLocationService(false); + m_isActive = false; + nativeStopUpdate(); } //@Override @@ -77,12 +101,26 @@ public class LocationService implements LocationListener, SensorEventListener public void onProviderDisabled(String provider) { Log.d(TAG, "onProviderDisabled " + provider); + if (m_isActive) + { + m_isActive = m_locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + || m_locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + if (!m_isActive) + { + Log.d(TAG, "to receive a location data please enable some of the location providers"); + nativeDisable(); + stopUpdate(); + /// TODO : callback into GUI to set the button into the "disabled" state + } + } } //@Override public void onProviderEnabled(String provider) { Log.d(TAG, "onProviderEnabled " + provider); + if (m_isActive) + m_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); } //@Override @@ -107,12 +145,9 @@ public class LocationService implements LocationListener, SensorEventListener nativeCompassChanged(event.timestamp, event.values[0], event.values[0] + m_field.getDeclination(), m_field.getDeclination()); } - private native void nativeEnableLocationService(boolean enable); + private native void nativeStartUpdate(Observer observer); + private native void nativeStopUpdate(); + private native void nativeDisable(); private native void nativeLocationChanged(long time, double lat, double lon, float accuracy); - // screenOrientation: - // 0 = 0 - // 1 = 90 - // 2 = 180 - // 3 = 270 private native void nativeCompassChanged(long time, double magneticNorth, double trueNorth, float accuracy); } diff --git a/platform/location.hpp b/platform/location.hpp index 5080acaeaf..46b8417351 100644 --- a/platform/location.hpp +++ b/platform/location.hpp @@ -11,7 +11,7 @@ namespace location enum TLocationStatus { - EStopped, + EStopped = 0, EStarted, EFirstEvent, //!< Sent when first valid coorinate is received ENotSupported, @@ -22,6 +22,7 @@ namespace location { EAppleNative, EWindowsNative, + EAndroidNative, EGoogle };