LocationActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.Manifest;
  4import android.annotation.TargetApi;
  5import android.content.Context;
  6import android.content.SharedPreferences;
  7import android.content.pm.PackageManager;
  8import android.graphics.Bitmap;
  9import android.graphics.BitmapFactory;
 10import android.location.Location;
 11import android.location.LocationListener;
 12import android.location.LocationManager;
 13import android.os.Build;
 14import android.os.Bundle;
 15import android.preference.PreferenceManager;
 16import android.provider.Settings;
 17import android.support.annotation.BoolRes;
 18import android.support.annotation.NonNull;
 19import android.util.Log;
 20import android.view.MenuItem;
 21
 22import org.osmdroid.api.IGeoPoint;
 23import org.osmdroid.api.IMapController;
 24import org.osmdroid.config.Configuration;
 25import org.osmdroid.config.IConfigurationProvider;
 26import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
 27import org.osmdroid.util.GeoPoint;
 28import org.osmdroid.views.CustomZoomButtonsController;
 29import org.osmdroid.views.MapView;
 30import org.osmdroid.views.overlay.Overlay;
 31
 32import java.io.IOException;
 33
 34import eu.siacs.conversations.BuildConfig;
 35import eu.siacs.conversations.Config;
 36import eu.siacs.conversations.R;
 37import eu.siacs.conversations.http.HttpConnectionManager;
 38import eu.siacs.conversations.services.QuickConversationsService;
 39import eu.siacs.conversations.ui.util.LocationHelper;
 40import eu.siacs.conversations.ui.widget.Marker;
 41import eu.siacs.conversations.ui.widget.MyLocation;
 42import eu.siacs.conversations.utils.ThemeHelper;
 43
 44public abstract class LocationActivity extends ActionBarActivity implements LocationListener {
 45	protected LocationManager locationManager;
 46	protected boolean hasLocationFeature;
 47
 48	public static final int REQUEST_CODE_CREATE = 0;
 49	public static final int REQUEST_CODE_FAB_PRESSED = 1;
 50	public static final int REQUEST_CODE_SNACKBAR_PRESSED = 2;
 51
 52	protected static final String KEY_LOCATION = "loc";
 53	protected static final String KEY_ZOOM_LEVEL = "zoom";
 54
 55	protected Location myLoc = null;
 56	private MapView map = null;
 57	protected IMapController mapController = null;
 58
 59	protected Bitmap marker_icon;
 60
 61	protected void clearMarkers() {
 62		synchronized (this.map.getOverlays()) {
 63			for (final Overlay overlay : this.map.getOverlays()) {
 64				if (overlay instanceof Marker || overlay instanceof MyLocation) {
 65					this.map.getOverlays().remove(overlay);
 66				}
 67			}
 68		}
 69	}
 70
 71	protected void updateLocationMarkers() {
 72		clearMarkers();
 73	}
 74	
 75	@Override
 76	protected void onCreate(final Bundle savedInstanceState) {
 77		super.onCreate(savedInstanceState);
 78		final Context ctx = getApplicationContext();
 79		setTheme(ThemeHelper.find(this));
 80
 81		final PackageManager packageManager = ctx.getPackageManager();
 82		hasLocationFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION) ||
 83				packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) ||
 84				packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_NETWORK);
 85		this.locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
 86		this.marker_icon = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.marker);
 87
 88		// Ask for location permissions if location services are enabled and we're
 89		// just starting the activity (we don't want to keep pestering them on every
 90		// screen rotation or if there's no point because it's disabled anyways).
 91		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) {
 92			requestPermissions(REQUEST_CODE_CREATE);
 93		}
 94
 95		final IConfigurationProvider config = Configuration.getInstance();
 96		config.load(ctx, getPreferences());
 97		config.setUserAgentValue(BuildConfig.APPLICATION_ID + "/" + BuildConfig.VERSION_CODE);
 98		if (QuickConversationsService.isConversations() && getBooleanPreference("use_tor", R.bool.use_tor)) {
 99			try {
100				config.setHttpProxy(HttpConnectionManager.getProxy());
101			} catch (IOException e) {
102				throw new RuntimeException("Unable to configure proxy");
103			}
104		}
105	}
106
107	@Override
108	protected void onSaveInstanceState(@NonNull final Bundle outState) {
109		super.onSaveInstanceState(outState);
110
111		final IGeoPoint center = map.getMapCenter();
112		outState.putParcelable(KEY_LOCATION, new GeoPoint(
113				center.getLatitude(),
114				center.getLongitude()
115		));
116		outState.putDouble(KEY_ZOOM_LEVEL, map.getZoomLevelDouble());
117	}
118
119	@Override
120	protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
121		super.onRestoreInstanceState(savedInstanceState);
122
123		if (savedInstanceState.containsKey(KEY_LOCATION)) {
124			mapController.setCenter(savedInstanceState.getParcelable(KEY_LOCATION));
125		}
126		if (savedInstanceState.containsKey(KEY_ZOOM_LEVEL)) {
127			mapController.setZoom(savedInstanceState.getDouble(KEY_ZOOM_LEVEL));
128		}
129	}
130
131	protected void setupMapView(MapView mapView, final GeoPoint pos) {
132		map = mapView;
133		map.setTileSource(TileSourceFactory.MAPNIK);
134		map.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
135		map.setMultiTouchControls(true);
136		map.setTilesScaledToDpi(false);
137		mapController = map.getController();
138		mapController.setZoom(Config.Map.INITIAL_ZOOM_LEVEL);
139		mapController.setCenter(pos);
140	}
141
142	protected void gotoLoc() {
143		gotoLoc(map.getZoomLevelDouble() == Config.Map.INITIAL_ZOOM_LEVEL);
144	}
145
146	protected abstract void gotoLoc(final boolean setZoomLevel);
147
148	protected abstract void setMyLoc(final Location location);
149
150	protected void requestLocationUpdates() {
151		if (!hasLocationFeature || locationManager == null) {
152			return;
153		}
154
155		Log.d(Config.LOGTAG, "Requesting location updates...");
156		final Location lastKnownLocationGps;
157		final Location lastKnownLocationNetwork;
158
159		try {
160			if (locationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER)) {
161				lastKnownLocationGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
162
163				if (lastKnownLocationGps != null) {
164					setMyLoc(lastKnownLocationGps);
165				}
166				locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, Config.Map.LOCATION_FIX_TIME_DELTA,
167						Config.Map.LOCATION_FIX_SPACE_DELTA, this);
168			} else {
169				lastKnownLocationGps = null;
170			}
171
172			if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
173				lastKnownLocationNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
174				if (lastKnownLocationNetwork != null && LocationHelper.isBetterLocation(lastKnownLocationNetwork,
175						lastKnownLocationGps)) {
176					setMyLoc(lastKnownLocationNetwork);
177				}
178				locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, Config.Map.LOCATION_FIX_TIME_DELTA,
179						Config.Map.LOCATION_FIX_SPACE_DELTA, this);
180			}
181
182			// If something else is also querying for location more frequently than we are, the battery is already being
183			// drained. Go ahead and use the existing locations as often as we can get them.
184			if (locationManager.getAllProviders().contains(LocationManager.PASSIVE_PROVIDER)) {
185				locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
186			}
187		} catch (final SecurityException ignored) {
188			// Do nothing if the users device has no location providers.
189		}
190	}
191
192	protected void pauseLocationUpdates() throws SecurityException {
193		if (locationManager != null) {
194			locationManager.removeUpdates(this);
195		}
196	}
197
198	@Override
199	public boolean onOptionsItemSelected(final MenuItem item) {
200		switch (item.getItemId()) {
201			case android.R.id.home:
202				finish();
203				return true;
204		}
205		return super.onOptionsItemSelected(item);
206	}
207
208	@Override
209	protected void onPause() {
210		super.onPause();
211		Configuration.getInstance().save(this, getPreferences());
212		map.onPause();
213		try {
214			pauseLocationUpdates();
215		} catch (final SecurityException ignored) {
216		}
217	}
218
219	protected abstract void updateUi();
220
221	protected boolean mapAtInitialLoc() {
222		return map.getZoomLevelDouble() == Config.Map.INITIAL_ZOOM_LEVEL;
223	}
224
225	@Override
226	protected void onResume() {
227		super.onResume();
228		Configuration.getInstance().load(this, getPreferences());
229		map.onResume();
230		this.setMyLoc(null);
231		requestLocationUpdates();
232		updateLocationMarkers();
233		updateUi();
234		map.setTileSource(TileSourceFactory.MAPNIK);
235		map.setTilesScaledToDpi(true);
236
237		if (mapAtInitialLoc()) {
238			gotoLoc();
239		}
240	}
241
242	@TargetApi(Build.VERSION_CODES.M)
243	protected boolean hasLocationPermissions() {
244		return (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
245				checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED);
246	}
247
248	@TargetApi(Build.VERSION_CODES.M)
249	protected void requestPermissions(final int request_code) {
250		if (!hasLocationPermissions()) {
251			requestPermissions(
252					new String[]{
253							Manifest.permission.ACCESS_FINE_LOCATION,
254							Manifest.permission.ACCESS_COARSE_LOCATION,
255					},
256					request_code
257			);
258		}
259	}
260
261	@Override
262	public void onRequestPermissionsResult(final int requestCode,
263										   @NonNull final String[] permissions,
264										   @NonNull final int[] grantResults) {
265		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
266		for (int i = 0; i < grantResults.length; i++) {
267			if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) ||
268					Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[i])) {
269				if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
270					requestLocationUpdates();
271				}
272			}
273		}
274	}
275
276	protected SharedPreferences getPreferences() {
277		return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
278	}
279
280	protected boolean getBooleanPreference(String name, @BoolRes int res) {
281		return getPreferences().getBoolean(name, getResources().getBoolean(res));
282	}
283
284	protected boolean isLocationEnabled() {
285		try {
286			final int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
287			return locationMode != Settings.Secure.LOCATION_MODE_OFF;
288		} catch( final Settings.SettingNotFoundException e ){
289			return false;
290		}
291	}
292}