~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/geolocation/gps_device_android.cc

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2008, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
6
//  1. Redistributions of source code must retain the above copyright notice,
 
7
//     this list of conditions and the following disclaimer.
 
8
//  2. Redistributions in binary form must reproduce the above copyright notice,
 
9
//     this list of conditions and the following disclaimer in the documentation
 
10
//     and/or other materials provided with the distribution.
 
11
//  3. Neither the name of Google Inc. nor the names of its contributors may be
 
12
//     used to endorse or promote products derived from this software without
 
13
//     specific prior written permission.
 
14
//
 
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
 
 
26
// TODO(cprince): remove platform-specific #ifdef guards when OS-specific
 
27
// sources (e.g. WIN32_CPPSRCS) are implemented.
 
28
#ifdef OS_ANDROID
 
29
 
 
30
#include "gears/geolocation/gps_device_android.h"
 
31
 
 
32
#include "gears/base/android/java_local_frame.h"
 
33
#include "gears/base/android/java_exception_scope.h"
 
34
#include "gears/base/android/webview_manager.h"
 
35
 
 
36
static const char *kGpsProviderClass =
 
37
    GEARS_JAVA_PACKAGE "/AndroidGpsLocationProvider";
 
38
 
 
39
static const char *kLocationClass = "android/location/Location";
 
40
 
 
41
static JavaClass::Method provider_methods[] = {
 
42
  { JavaClass::kNonStatic, "<init>", "(Landroid/webkit/WebView;J)V"},
 
43
  { JavaClass::kNonStatic, "shutdown", "()V"},
 
44
};
 
45
 
 
46
enum ProviderMethodID {
 
47
  METHOD_ID_INIT = 0,
 
48
  METHOD_ID_SHUTDOWN,
 
49
};
 
50
 
 
51
static const char16 *kGpsDisabledMessage =
 
52
    STRING16(L"GPS is disabled.");
 
53
static const char16 *kGpsErrorMessage =
 
54
    STRING16(L"GPS failed to get a position fix.");
 
55
 
 
56
JNINativeMethod AndroidGpsDevice::native_methods_[] = {
 
57
  {"nativeLocationChanged",
 
58
   "(Landroid/location/Location;J)V",
 
59
   reinterpret_cast<void*>(AndroidGpsDevice::OnLocationChanged)
 
60
  },
 
61
  {"nativeProviderError",
 
62
   "(ZJ)V",
 
63
   reinterpret_cast<void*>(AndroidGpsDevice::OnProviderError)
 
64
  },
 
65
};
 
66
 
 
67
// Local function
 
68
// This assumes that position_2 is a good fix.
 
69
static bool PositionsDiffer(const Position &position_1,
 
70
                            const Position &position_2);
 
71
static bool GetPositionFromJavaLocation(JNIEnv *env,
 
72
                                        const jobject &location,
 
73
                                        Position *position);
 
74
 
 
75
// GpsLocationProvider factory method for platform-dependent GPS devices.
 
76
// static
 
77
GpsDeviceBase *GpsLocationProvider::NewGpsDevice(
 
78
    GpsDeviceBase::ListenerInterface *listener) {
 
79
  return new AndroidGpsDevice(listener);
 
80
}
 
81
 
 
82
AndroidGpsDevice::AndroidGpsDevice(GpsDeviceBase::ListenerInterface *listener)
 
83
    : GpsDeviceBase(listener) {
 
84
  assert(listener);
 
85
  JavaLocalFrame frame;
 
86
  if (!provider_java_class_.FindClass(kGpsProviderClass)) {
 
87
    LOG(("Could not find the AndroidGpsDevice class.\n"));
 
88
    assert(false);
 
89
    return;
 
90
  }
 
91
 
 
92
  // Get the provider method IDs.
 
93
  if (!provider_java_class_.GetMultipleMethodIDs(provider_methods,
 
94
                                                 NELEM(provider_methods))) {
 
95
    LOG(("Could not find the AndroidGpsDevice methods.\n"));
 
96
    assert(false);
 
97
    return;
 
98
  }
 
99
 
 
100
  // Get the WebView pointer. Note that this MUST execute on either
 
101
  // the browser main thread or a worker thread. See WebView::RegisterWebView()
 
102
  // method comments for more details aboud this constraint.
 
103
  jobject webview;
 
104
  if (!WebViewManager::GetWebView(&webview)) {
 
105
    LOG(("Could not find the window object.\n"));
 
106
    assert(false);
 
107
    return;
 
108
  }
 
109
  webview_.Reset(webview);
 
110
  // We're now ready to go. Start the thread.
 
111
  Start();
 
112
}
 
113
 
 
114
void AndroidGpsDevice::BeforeLoop() {
 
115
  JavaExceptionScope scope;
 
116
  JavaLocalFrame frame;
 
117
  // Construct the Java Gps provider.
 
118
  jlong native_object_ptr = reinterpret_cast<jlong>(this);
 
119
  jobject object = JniGetEnv()->NewObject(provider_java_class_.Get(),
 
120
                                          provider_methods[METHOD_ID_INIT].id,
 
121
                                          webview_.Get(),
 
122
                                          native_object_ptr);
 
123
  if (scope.Occurred()) {
 
124
    // Something went wrong when instantiating the Java object.
 
125
    LOG(("Could not instantiate the Java object.\n"));
 
126
    position_.error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE;
 
127
    position_.error_message = kGpsErrorMessage;
 
128
    // No need to call 'scope.Clear();', as the exception
 
129
    // will be cleared by the destructor of scope.
 
130
  } else {
 
131
    provider_java_object_.MoveLocal(object);
 
132
    // Register the native callback.
 
133
    jniRegisterNativeMethods(JniGetEnv(),
 
134
                             kGpsProviderClass,
 
135
                             native_methods_,
 
136
                             NELEM(native_methods_));
 
137
  }
 
138
}
 
139
 
 
140
AndroidGpsDevice::~AndroidGpsDevice() {
 
141
  Stop();
 
142
}
 
143
 
 
144
void AndroidGpsDevice::AfterLoop() {
 
145
  // Call the shutdown method if the Java object was created
 
146
  // successfully.
 
147
  if (provider_java_object_.Get()) {
 
148
    JniGetEnv()->CallVoidMethod(provider_java_object_.Get(),
 
149
                                provider_methods[METHOD_ID_SHUTDOWN].id);
 
150
  }
 
151
}
 
152
 
 
153
// static
 
154
void AndroidGpsDevice::OnLocationChanged(JNIEnv *env,
 
155
                                         jclass cls,
 
156
                                         jobject location,
 
157
                                         jlong self) {
 
158
  assert(location);
 
159
  assert(self);
 
160
  AndroidGpsDevice *self_ptr =
 
161
      reinterpret_cast<AndroidGpsDevice*>(self);
 
162
  Position position;
 
163
  if (GetPositionFromJavaLocation(env, location, &position) &&
 
164
      position.IsGoodFix()) {
 
165
    self_ptr->PositionChanged(position);
 
166
  }
 
167
}
 
168
 
 
169
// static
 
170
void AndroidGpsDevice::OnProviderError(JNIEnv *env,
 
171
                                       jclass cls,
 
172
                                       jboolean is_disabled,
 
173
                                       jlong self) {
 
174
  assert(self);
 
175
  AndroidGpsDevice *self_ptr =
 
176
      reinterpret_cast<AndroidGpsDevice*>(self);
 
177
  self_ptr->ProviderError(is_disabled);
 
178
}
 
179
 
 
180
void AndroidGpsDevice::PositionChanged(const Position &new_position) {
 
181
  if (PositionsDiffer(position_, new_position)) {
 
182
    position_ = new_position;
 
183
    LOG(("Position changed, updating listener.\n"));
 
184
    listener_->GpsPositionUpdateAvailable(position_);
 
185
  }
 
186
}
 
187
 
 
188
void AndroidGpsDevice::ProviderError(bool is_disabled) {
 
189
  if (is_disabled) {
 
190
    LOG(("GPS disabled, updating listener.\n"));
 
191
    listener_->GpsFatalError(Position::ERROR_CODE_POSITION_UNAVAILABLE,
 
192
                             kGpsDisabledMessage);
 
193
  } else {
 
194
    LOG(("GPS error, updating listener.\n"));
 
195
    listener_->GpsFatalError(Position::ERROR_CODE_POSITION_UNAVAILABLE,
 
196
                             kGpsErrorMessage);
 
197
  }
 
198
}
 
199
 
 
200
static bool GetPositionFromJavaLocation(JNIEnv *env,
 
201
                                        const jobject &location,
 
202
                                        Position *position) {
 
203
  assert(position);
 
204
  JavaLocalFrame frame;
 
205
  JavaExceptionScope scope;
 
206
  JavaClass clazz(kLocationClass, env->GetObjectClass(location));
 
207
 
 
208
  JavaClass::Method methods[] = {
 
209
    { JavaClass::kNonStatic, "getLatitude", "()D"},
 
210
    { JavaClass::kNonStatic, "getLongitude", "()D"},
 
211
    { JavaClass::kNonStatic, "hasAltitude", "()Z"},
 
212
    { JavaClass::kNonStatic, "getAltitude", "()D"},
 
213
    { JavaClass::kNonStatic, "hasAccuracy", "()Z"},
 
214
    { JavaClass::kNonStatic, "getAccuracy", "()F"},
 
215
    { JavaClass::kNonStatic, "getTime", "()J"},
 
216
  };
 
217
 
 
218
  enum LocationMethods {
 
219
    LOCATION_METHODS_GET_LATITUDE,
 
220
    LOCATION_METHODS_GET_LONGITUDE,
 
221
    LOCATION_METHODS_HAS_ALTITUDE,
 
222
    LOCATION_METHODS_GET_ALTITUDE,
 
223
    LOCATION_METHODS_HAS_ACCURACY,
 
224
    LOCATION_METHODS_GET_ACCURACY,
 
225
    LOCATION_METHODS_GET_TIME,
 
226
  };
 
227
 
 
228
  if (!clazz.GetMultipleMethodIDs(methods, NELEM(methods))) {
 
229
    LOG(("Could not find the Location methods.\n"));
 
230
    assert(false);
 
231
    return false;
 
232
  }
 
233
 
 
234
  position->latitude = env->CallDoubleMethod(
 
235
      location,
 
236
      methods[LOCATION_METHODS_GET_LATITUDE].id);
 
237
 
 
238
  position->longitude = env->CallDoubleMethod(
 
239
      location,
 
240
      methods[LOCATION_METHODS_GET_LONGITUDE].id);
 
241
 
 
242
  if (env->CallBooleanMethod(location,
 
243
                             methods[LOCATION_METHODS_HAS_ALTITUDE].id)) {
 
244
    position->altitude = env->CallDoubleMethod(
 
245
        location,
 
246
        methods[LOCATION_METHODS_GET_ALTITUDE].id);
 
247
  }
 
248
 
 
249
  if (env->CallBooleanMethod(location,
 
250
                             methods[LOCATION_METHODS_HAS_ACCURACY].id)) {
 
251
    position->accuracy = env->CallFloatMethod(
 
252
       location,
 
253
       methods[LOCATION_METHODS_GET_ACCURACY].id);
 
254
  } else {
 
255
    // On the Android emulator valid position fixes do not have
 
256
    // the accuracy set. For debugging purposes, we set it to 0 here.
 
257
    position->accuracy = 0;
 
258
  }
 
259
 
 
260
  position->timestamp = env->CallLongMethod(
 
261
      location,
 
262
      methods[LOCATION_METHODS_GET_TIME].id);
 
263
 
 
264
  return true;
 
265
}
 
266
 
 
267
// Local function
 
268
// TODO(andreip/steveblock): move this to GpsDeviceBase.
 
269
// static
 
270
bool PositionsDiffer(const Position &position_1, const Position &position_2) {
 
271
  assert(position_2.IsGoodFix());
 
272
 
 
273
  if (!position_1.IsGoodFix()) {
 
274
    return true;
 
275
  }
 
276
  return position_1.latitude != position_2.latitude ||
 
277
         position_1.longitude != position_2.longitude ||
 
278
         position_1.accuracy != position_2.accuracy ||
 
279
         position_1.altitude != position_2.altitude;
 
280
}
 
281
 
 
282
 
 
283
 
 
284
#endif  // OS_ANDROID