-
Notifications
You must be signed in to change notification settings - Fork 105
/
MapperActivity.java
276 lines (248 loc) · 8.6 KB
/
MapperActivity.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
/*
* Copyright 2013 Thomas Schöps
* Copyright 2014 Thomas Schöps, Kai Pastor
*
* This file is part of OpenOrienteering.
*
* OpenOrienteering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenOrienteering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
*/
package org.openorienteering.mapper;
// Until Qt Creator and androiddeployqt fully support per-flavor app IDs,
// we must change the ID in AndroidManifest.xml, and thus the BuildConfig
// and R classes are generated in the MAPPER_APP_ID namespace.
import @MAPPER_APP_ID@.BuildConfig;
import @MAPPER_APP_ID@.R;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.location.LocationListener;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.view.Gravity;
import android.view.Surface;
/**
* Contains Android Java code for Mapper.
*/
public class MapperActivity extends org.qtproject.qt5.android.bindings.QtActivity
{
private static MapperActivity instance;
private String yes_string;
private String no_string;
private String gps_disabled_string;
private static boolean service_started = false;
private static boolean optimization_request_done = false;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
instance = this;
}
/** Call setIntent, as recommended for singleTask launch mode. */
@Override
public void onNewIntent(Intent intent)
{
setIntent(intent);
}
/** Returns the data string from the intent, and resets the intent. */
public String takeIntentPath()
{
String result = "";
Intent intent = getIntent();
if (intent != null)
{
String action = intent.getAction();
if (action == Intent.ACTION_EDIT || action == Intent.ACTION_VIEW)
{
result = intent.getDataString();
}
setIntent(null);
}
return result;
}
/**
* Request android to disable battery optimization for Mapper.
*
* "An app holding the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission can
* trigger a system dialog to let the user add the app to the whitelist
* directly, without going to settings."
*
* "Google Play policies prohibit apps from requesting direct exemption from
* Power Management features in Android 6.0+ (Doze and App Standby) unless
* the core function of the app is adversely affected."
*
* @see https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
*/
void requestIgnoreBatteryOptimizations()
{
if (Build.VERSION.SDK_INT < 23)
{
return;
}
if (optimization_request_done)
{
return;
}
String app_id = BuildConfig.APPLICATION_ID;
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm != null && !pm.isIgnoringBatteryOptimizations(app_id))
{
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + app_id));
startActivity(intent);
}
optimization_request_done = true; // until app is restarted
}
// Static methods to be called from C++
/** Checks if GPS is enabled in the Android settings and if not, prompts the user to enable it.
* The dialog box works asynchronously, so the method cannot return the result. */
static void checkGPSEnabled()
{
LocationManager locationManager = (LocationManager) instance.getSystemService(LOCATION_SERVICE);
boolean enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!enabled)
{
instance.runOnUiThread(new Runnable() {
public void run() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
instance.startActivity(intent);
break;
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(instance);
builder.setMessage(instance.gps_disabled_string)
.setPositiveButton(instance.yes_string, dialogClickListener)
.setNegativeButton(instance.no_string, dialogClickListener)
.show();
}
});
}
}
public static void setTranslatableStrings(String yes_string, String no_string, String gps_disabled_string)
{
instance.yes_string = yes_string;
instance.no_string = no_string;
instance.gps_disabled_string = gps_disabled_string;
}
/** Locks the current display orientation.
* While a native "locked" mode comes in API level 18,
* this method tries to determine and lock the current orientation
* even on devices with lower API level. On these devices, the screen
* may be temporary in reverse orientation.
*/
public static void lockOrientation()
{
// ActivityInfo.SCREEN_ORIENTATION_LOCKED == 14 comes with API level 18
if (Build.VERSION.SDK_INT >= 18)
{
instance.setRequestedOrientation(14);
return;
}
int orientation = instance.getResources().getConfiguration().orientation;
int rotation = instance.getWindowManager().getDefaultDisplay().getRotation();
if (orientation == Configuration.ORIENTATION_PORTRAIT)
{
if (rotation == Surface.ROTATION_180)
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
else
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
else if (orientation == Configuration.ORIENTATION_LANDSCAPE)
{
if (rotation == Surface.ROTATION_180)
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
else
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
// If we read another value now, then we must reverse the rotation.
// Maybe this can occasionally return the old value (i.e. the value
// before the requested rotation takes effect).
int new_rotation = instance.getWindowManager().getDefaultDisplay().getRotation();
if (new_rotation != rotation)
{
// first try didn't lock the original rotation, retry reverse.
if (orientation == Configuration.ORIENTATION_PORTRAIT)
{
if (new_rotation == Surface.ROTATION_180)
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
else
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
}
else if (orientation == Configuration.ORIENTATION_LANDSCAPE)
{
if (new_rotation == Surface.ROTATION_180)
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
else
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
}
}
/** Unlocks the display orientation
* by setting the requested orientation to unspecified.
*/
public static void unlockOrientation()
{
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
/** Returns the display's current rotation.
*/
public static int getDisplayRotation()
{
return instance.getWindowManager().getDefaultDisplay().getRotation();
}
/** Starts a foreground service with the given notification message.
*/
public static void startService(String message)
{
if (!service_started)
{
service_started = true;
Intent intent = new Intent(instance, MapperService.class);
intent.putExtra("message", message);
instance.startService(intent);
instance.requestIgnoreBatteryOptimizations();
}
}
/** Stops the foreground service.
*/
public static void stopService()
{
if (service_started)
{
Intent intent = new Intent(instance, MapperService.class);
instance.stopService(intent);
service_started = false;
}
}
}