This repository has been archived by the owner on May 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathMainActivity.java
394 lines (328 loc) · 14.6 KB
/
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
package com.tomjanson.wifilocationlogger;
import android.app.Activity;
import android.content.Context;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.net.wifi.WifiManager;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.text.DateFormat;
import java.util.Date;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
/*
* Location update code based on:
* https://developer.android.com/training/location/receive-location-updates.html
* Wifi scan code loosely based on:
* http://www.tutorialspoint.com/android/android_wi_fi.htm
* https://github.com/Skarbo/WifiMapper
*/
public class MainActivity extends Activity implements
ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
// Logback loggers, see https://github.com/tony19/logback-android
Logger log; // regular log/debug messages
Logger dataLog; // sensor data (geo, wifi) for debugging (verbose)
Logger diskLog; // pretty CSV output, i.e., the "product" of this app
Logger remoteLog; // sends output to remote server (see logback.xml for server details)
// unique ID sent to server to distinguish clients
// changes everytime logging is enabled
static String sessionId;
// Location update intervals
// it seems the updates take at least 5s; setting it lower doesn't seem to work
static final long LOCATION_UPDATE_INTERVAL_MILLIS = 3000;
static final long FASTEST_LOCATION_UPDATE_INTERVAL_MILLIS = 1000;
// Wifi scan delay (i.e., wait $delay between completion of scan and start of next scan)
static final long WIFI_SCAN_DELAY_MILLIS = 2000;
// TODO: try different delays
// Instance state Bundle keys,
// see https://developer.android.com/training/basics/activity-lifecycle/recreating.html
private final static String LOCATION_KEY = "location-key";
private final static String LAST_LOCATION_UPDATE_TIME_STRING_KEY = "last-location-update-time-string-key";
private final static String LAST_WIFI_SCAN_TIME_STRING_KEY = "last-wifi-scan-time-string-key";
// Used to access Fused Location API,
// see https://developer.android.com/google/play-services/location.html
private GoogleApiClient googleApiClient;
private LocationRequest locationRequest;
Location currentLocation;
// Wifi scan stuff
static WifiManager wifiManager;
static IntentFilter wifiIntentFilter;
static WifiBroadcastReceiver wifiBroadcastReceiver;
// toggles logging location+wifi to file
// debug logs may be created regardless of this
boolean loggingEnabled = false;
// wake-lock to (hopefully) continue logging while screen is off
PowerManager.WakeLock wakeLock;
// UI Elements
Button loggingButton;
CheckBox remoteLogCB;
TextView locationTV;
TextView locationAccuracyTV;
TextView locationUpdateTV;
Date lastLocationUpdateTime;
TextView wifiTV;
EditText wifiFilterET;
TextView wifiUpdateTV;
String wifiListString;
Date lastWifiScanTime;
private final static String SSID_FILTER_PREFERENCE_KEY = "ssid-filter-preference-key";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
log = LoggerFactory.getLogger(MainActivity.class);
dataLog = LoggerFactory.getLogger("data");
diskLog = LoggerFactory.getLogger("disk");
remoteLog = LoggerFactory.getLogger("remote");
log.info("Started; " + Build.VERSION.RELEASE + ", " + Build.ID + ", " + Build.MODEL);
setContentView(R.layout.activity_main);
assignUiElements();
// restore saved preferences
SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
wifiFilterET.setText(sharedPref.getString(SSID_FILTER_PREFERENCE_KEY, getString(R.string.ssid_filter_default)));
// restore state on Activity recreation
updateValuesFromBundle(savedInstanceState);
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag");
buildGoogleApiClient();
initWifiScan();
wifiManager.startScan();
}
private void assignUiElements() {
loggingButton = (Button) findViewById(R.id.loggingButton);
remoteLogCB = (CheckBox) findViewById(R.id.remoteLogCheckbox);
locationTV = (TextView) findViewById(R.id.locationTextView);
locationAccuracyTV = (TextView) findViewById(R.id.locationAccuracyTextView);
locationUpdateTV = (TextView) findViewById(R.id.locationUpdateTextView);
wifiTV = (TextView) findViewById(R.id.wifiTextView);
wifiFilterET = (EditText) findViewById(R.id.wifiFilterEditText);
wifiUpdateTV = (TextView) findViewById(R.id.wifiUpdateTextView);
}
void updateUI() {
if (currentLocation != null) {
locationTV.setText(currentLocation.getLatitude() + ", " + currentLocation.getLongitude());
locationAccuracyTV.setText(currentLocation.getAccuracy() + " m");
}
if (lastLocationUpdateTime != null) {
locationUpdateTV.setText(DateFormat.getTimeInstance().format(lastLocationUpdateTime));
}
if (lastWifiScanTime != null) {
wifiUpdateTV.setText(DateFormat.getTimeInstance().format(lastWifiScanTime));
}
if (wifiListString != null) {
wifiTV.setText(wifiListString);
}
loggingButton.setText(loggingEnabled ? R.string.logging_stop : R.string.logging_start);
// remoteLogCB.setChecked(allowRemoteLogging);
}
/**
* Toggles logging data points to file and aquires wake-lock to do so while screen is off.
*/
public void toggleLogging(View view) {
loggingEnabled = !loggingEnabled;
log.info((loggingEnabled ? "En" : "Dis") + "abled logging to disk");
if (loggingEnabled) {
sessionId = UUID.randomUUID().toString();
log.info("SessionID for remote logging: " + sessionId);
wakeLock.acquire();
log.debug("Acquired wake-lock");
} else if (wakeLock.isHeld()) {
wakeLock.release();
log.debug("Released wake-lock");
}
updateUI();
}
// public void toggleRemoteLogPermission(View view) {
// allowRemoteLogging = !allowRemoteLogging;
// log.info((allowRemoteLogging ? "En" : "Dis") + "abled remote logging");
// updateUI();
// }
private void initWifiScan() {
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiBroadcastReceiver = new WifiBroadcastReceiver(this);
wifiIntentFilter = new IntentFilter();
wifiIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
this.registerReceiver(wifiBroadcastReceiver, wifiIntentFilter);
log.debug("Registered WifiBroadcastReceiver");
}
private void updateValuesFromBundle(Bundle savedInstanceState) {
log.debug("Updating values from Bundle");
if (savedInstanceState != null) {
if (savedInstanceState.keySet().contains(LOCATION_KEY)) {
currentLocation = savedInstanceState.getParcelable(LOCATION_KEY);
}
if (savedInstanceState.keySet().contains(LAST_LOCATION_UPDATE_TIME_STRING_KEY)) {
lastLocationUpdateTime = new Date(savedInstanceState.getLong(LAST_LOCATION_UPDATE_TIME_STRING_KEY));
}
if (savedInstanceState.keySet().contains(LAST_WIFI_SCAN_TIME_STRING_KEY)) {
lastWifiScanTime = new Date(savedInstanceState.getLong(LAST_WIFI_SCAN_TIME_STRING_KEY));
}
updateUI();
}
}
synchronized void buildGoogleApiClient() {
log.trace("Building GoogleApiClient");
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
void createLocationRequest() {
locationRequest = new LocationRequest();
// Sets the desired interval for active location updates. This interval is
// inexact. You may not receive updates at all if no location sources are available, or
// you may receive them slower than requested. You may also receive updates faster than
// requested if other applications are requesting location at a faster interval.
locationRequest.setInterval(LOCATION_UPDATE_INTERVAL_MILLIS);
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
locationRequest.setFastestInterval(FASTEST_LOCATION_UPDATE_INTERVAL_MILLIS);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Requests location updates from the FusedLocationApi.
*/
void startLocationUpdates() {
// The final argument to {@code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
log.trace("Requesting GoogleApiClient location updates");
}
void stopLocationUpdates() {
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
log.trace("Stopped location updates");
} else {
log.warn("Attempted to stop location updates, but not connected");
}
}
@Override
public void onLocationChanged(Location location) {
dataLog.trace("Location: {}", location);
currentLocation = location;
lastLocationUpdateTime = new Date();
updateUI();
}
@Override
protected void onStart() {
super.onStart();
googleApiClient.connect();
log.trace("Connecting GoogleApiClient ...");
}
@Override
protected void onResume() {
super.onResume();
if (googleApiClient.isConnected()) {
startLocationUpdates();
}
if (!loggingEnabled) {
this.registerReceiver(wifiBroadcastReceiver, wifiIntentFilter);
log.trace("Registered WifiBroadcastReceiver");
wifiManager.startScan();
}
}
@Override
protected void onPause() {
super.onPause();
if (!loggingEnabled) {
this.unregisterReceiver(wifiBroadcastReceiver);
log.debug("Unregistered WifiBroadcastReceiver");
stopLocationUpdates();
} else {
log.debug("onPause called, but logging to file; we'll keep going");
}
}
@Override
protected void onStop() {
super.onStop();
if (!loggingEnabled) {
disconnectGoogleApiClient();
}
// save preferences or other persisting stuff
SharedPreferences.Editor prefEditor = this.getPreferences(Context.MODE_PRIVATE).edit();
prefEditor.putString(SSID_FILTER_PREFERENCE_KEY, wifiFilterET.getText().toString());
prefEditor.apply();
}
@Override
protected void onDestroy() {
super.onDestroy();
disconnectGoogleApiClient();
if (loggingEnabled) {
this.unregisterReceiver(wifiBroadcastReceiver);
log.trace("Unregistered WifiBroadcastReceiver");
}
// assume SLF4J is bound to logback-classic in the current environment
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
}
private void disconnectGoogleApiClient() {
if (googleApiClient.isConnected()) {
googleApiClient.disconnect();
log.trace("GoogleApiClient disconnected");
}
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
@Override
public void onConnected(Bundle connectionHint) {
log.info("Connected to GoogleApiClient");
if (currentLocation == null) {
currentLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
lastLocationUpdateTime = new Date();
updateUI();
}
startLocationUpdates();
}
@Override
public void onConnectionFailed(ConnectionResult result) {
log.warn("GoogleApiClient connection failed: ConnectionResult.getErrorCode() = {}", result.getErrorCode());
}
@Override
public void onConnectionSuspended(int cause) {
log.info("GoogleApiClient connection suspended, attempting reconnect");
googleApiClient.connect();
}
/**
* Stores activity data in the Bundle.
*/
@Override
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putParcelable(LOCATION_KEY, currentLocation);
if (lastLocationUpdateTime != null) {
savedInstanceState.putLong(LAST_LOCATION_UPDATE_TIME_STRING_KEY, lastLocationUpdateTime.getTime());
}
if (lastWifiScanTime != null) {
savedInstanceState.putLong(LAST_WIFI_SCAN_TIME_STRING_KEY, lastWifiScanTime.getTime());
}
super.onSaveInstanceState(savedInstanceState);
}
/**
* Clears focus from EditText (the SSID filter).
* This is needed because EditText grabs focus and opens the keyboard, which is annoying.
* See GridLayout's focussing attributes, which prevents ET focus grab at startup.
*
* https://stackoverflow.com/questions/1555109/stop-edittext-from-gaining-focus-at-activity-startup/
*/
public void removeFocusFromEditText(View view) {
wifiFilterET.clearFocus();
}
}