Why LocationManager is null after initialization? I'm trying to removeUpdates() - android

I have a class "OldLocationService" (generally I'm using fused location from GoogleApiClient but I keep that class in case of old Google Play):
public class OldLocationService {
static Location loc;
private static final String TAG = MainActivity.class.getSimpleName();
protected LocationManager service;
private final LocationListener mLocationListener = new LocationListener() {
#Override
public void onLocationChanged(final Location location) {
Log.d(TAG, "New location: " + location.toString());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
public void EnableGPS(String provider, Context ctx) {
service = (LocationManager) ctx.getSystemService(ctx.LOCATION_SERVICE);
boolean enabled = service.isProviderEnabled(provider);
if (enabled) {
service.requestLocationUpdates(provider, 10000, 0, mLocationListener, Looper.getMainLooper());
}
else
{
Log.d(TAG, "GPS is not enabled");
}
}
public void DisableGPS() {
try {
if (!(service==null)) {
service.removeUpdates(mLocationListener);
} else {
Log.d(TAG, "service is null");
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
Somewhere in another class I'm calling:
OldLocationService OLS = new OldLocationService();
OLS.EnableGPS(LocationManager.NETWORK_PROVIDER, mContext);
And I'm getting locations correctly. However, when I'm trying to disable GPS:
OldLocationService ols = new OldLocationService();
ols.DisableGPS();
Then in logcat I get:
service is null
Why I can't remove GPS updates? How to do that?
Sorry for my English errors,
Defozo

First of all don't make 2 instances of your OldLocationService class.. you must remove updates from the same instance from which you started them.. because each instance has its own copy of variables/fields, in your case protected LocationManager service;
OldLocationService ols = new OldLocationService(); // declare it globally
// in middle of some code
OLS.EnableGPS(LocationManager.NETWORK_PROVIDER, mContext);
// more code here
If you're removing Location updates from a Service then you must put the code in it's onDestroy() method from the same instance:
public void onDestroy(){
ols.DisableGPS();
super.onDestroy();
}

Related

The requestLocationUpdates to get GPS not work

I am new to Android and try to have one example to get GPS location.
I surf through the web and have following code.
The program will crash once I click the button to get a GPS.
I found it seems something wrong when I call locationManager.requestLocationUpdates(commandStr, 1000, 0, locationListener);
I trace it into requestLocationUpdates and found it crashed at Looper class
public final class Looper {
Looper(boolean quitAllowed) {
throw new RuntimeException("Stub!");
}
public static void prepare() {
throw new RuntimeException("Stub!");
}
public static void prepareMainLooper() {
throw new RuntimeException("Stub!");
}
public static Looper getMainLooper() {
throw new RuntimeException("Stub!");
}
public static void loop() {
throw new RuntimeException("Stub!");
}
Following are the complete code, ,dose anyone know which part of program is wrong
public class MainActivity extends AppCompatActivity {
private TextView tvLocation;
private Button btGetLocation;
private LocationManager locationManager;
private String commandStr;
private static final int MY_PERMISSION_ACCESS_COARSE_LOCATION = 11;
public LocationListener locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
tvLocation.setText("經度" + location.getLongitude() + "\n緯度" + location.getLatitude());
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
commandStr = LocationManager.GPS_PROVIDER;
// LocationManager.GPS_PROVIDER //使用GPS定位
// LocationManager.NETWORK_PROVIDER //使用網路定位
tvLocation = (TextView) findViewById(R.id.textView);
btGetLocation = (Button) findViewById(R.id.button);
btGetLocation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// LocationManager可以用來獲取當前的位置,追蹤設備的移動路線
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
MY_PERMISSION_ACCESS_COARSE_LOCATION);
return;
}
locationManager.requestLocationUpdates(commandStr, 1000, 0, locationListener);
// 事件的條件設定為時間間隔1秒,距離改變0米,設定監聽位置變化
Location location = locationManager.getLastKnownLocation(commandStr);
if (location != null)
tvLocation.setText("經度"+location.getLongitude()+"\n緯度"+location.getLatitude());
else
tvLocation.setText("定位中");
// location.getLongitude() //取得經度
// location.getLatitude() //取得緯度
// location.getAltitude() //取得海拔高度
}
});
}
}
Here some code example:
public class Activity extends AppCompatActivity{
//variables for GPS data
private LocationManager locationManager;
private Location location;
private int minTime = 1000 //=1s
private int minDistance = 5 //=5m
private List<String> providers;
private final ActivityLocationListener locationListener = new ActivityLocationListener();
#Override
protected void onCreate(){
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout_file);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
providers = locationManager.getProviders(true);
//this will print you the available providers in your LogCat
//in some virtual devices network is not available
Log.d(NoteMapActivity.this.getClass().getSimpleName(), "available providers:\n" + providers);
}
public void onButtClick(View view){
this.requestLocationUpdates();
}
public void requestLocationUpdates(){
locationManager = (LocationManager) getSystemService(LOCATON_SERVICE);
//with this following code you will call onLocationChanged()
if (providers.contains("gps")){
//"permisson check"
try {
locationManager.requestLocationUpdates("gps", minTime, minDistance, locationListener);
} catch (SecurityException ex){
Log.e(getClass().getSimpleName(), "no permisson: " + ex.toString());
}
//if gps is not available you can use "network" or "passive" instead, maybe with if-else conditions
}
//create a class inside of this one!
class ActivityLocationListener implements LocationListener{
#Override
public void onLocationChanged(Location location) {
currentLocation = location;
Log.d(NoteMapActivity.this.getClass().getSimpleName(), "received location:\n" + currentLocation.getLatitude() + ", " + currentLocation.getLongitude());
addCurrentLocationMarkerToMap();
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
}

How to get time out exception for fetch location from Location service

public class LocationUtils {
private static final long LOCATION_REFRESH_TIME = 5000L;
private static final float LOCATION_REFRESH_DISTANCE = 300;
LocationListener mLocationListener;
public void fetchLocation(Context context, LocationCallback callback) {
final LocationManager locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
try {
Criteria criteria = new Criteria();
String provider = locationManager.getBestProvider(criteria, false);
#SuppressLint("MissingPermission")
Location location = locationManager.getLastKnownLocation(provider);
if (callback != null && location != null) {
String locationString = getLocationString(location);
Timber.i("LocationUtils = " + locationString);
callback.onLocationLocked(locationString);
removeListener(locationManager);
return;
}
if (mLocationListener == null) {
mLocationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
removeListener(locationManager);
if (callback != null) {
String locationString = getLocationString(location);
if (locationString != null) {
Timber.i("LocationUtils = " + locationString);
callback.onLocationLocked(locationString);
} else {
callback.onLocationLockFailed(new LocationNotFoundException());
}
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
locationManager.requestLocationUpdates(provider, LOCATION_REFRESH_TIME,
LOCATION_REFRESH_DISTANCE, mLocationListener);
}
} catch (Exception e) {
removeListener(locationManager);
if (callback != null) {
callback.onLocationLockFailed(e);
}
}
}
private void removeListener(LocationManager locationManager){
try {
locationManager.removeUpdates(mLocationListener);
}catch (Exception e){
}
mLocationListener = null;
}
private String getLocationString(Location location) {
if (location != null) {
return location.getLatitude() + "," + location.getLongitude();
}
return null;
}
public interface LocationCallback {
void onLocationLocked(String location);
void onLocationLockFailed(Exception e);
}
public class LocationNotFoundException extends Exception {
}
}
This is my code i want to get one more method time out exception so that if Location Service will not able to fetch location then that timeout exception should be invoked and accordingly we can handle it but there is not overrides method for timeout in location service please suggest me how we will implement this using time or timer class in android
please try following it will help you
public void onStatusChanged(final String provider, final int status, final Bundle extras) {
switch( status ) {
case LocationProvider.AVAILABLE:
// ....show message
break;
case LocationProvider.OUT_OF_SERVICE:
// ...show message
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
// ...show message
break;
}
}
and show message accordingly
The answer from Ganesh looks good and the intended way of doing it, but if you want to configure your own timeout, maybe you can use this answer to this question: How to timeout a thread
So you create an independent thread to call fetch location and if the timeout expires you can do whatever you want (probably you need to remove the listener, as well)

Location in Android always returned null

I am a begginer in Android programming and I encountered a problem with displaying the current location of my phone using the NETWORK PROVIDER, with which I need some help.
Here is the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtLat = (TextView) findViewById(R.id.txtLat);
txtLon = (TextView) findViewById(R.id.txtLon);
Log.d("ADebugTag", "WTF");
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
txtLat.setText(String.valueOf(location.getLatitude()));
txtLon.setText(String.valueOf(location.getLongitude()));
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
Log.d("ADebugTag", "WTF END");
}
I try to set the latitude and longitude of the location into the TextViews, but my app crashes when i try tu run it.
Also, I tried to print a full stack trace, but I couldn't find a method to make it work..
UPDATE: Thanks for all the replies :D - After posting this I realized I should have also posted the layout of the main activity, just in case.
<TextView
android:id = "#+id/txtLat"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="LATITUDINE" />
<TextView
android:id = "#+id/txtLon"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_below="#+id/txtLat"
android:text="LONGITUDINE" />
Also, I have tried to print something in logcat when the onLocationChanged method gets called, but the message is not displayed when I run the app, meaning that the method is not called.
I am running this app on an older device, using Android 2.3.6, maybe that is useful information as well.
It's highly possible that you are lacking the corresponding app rights. Make sure you added following lines to your manifest file:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
I am not sure about the first 2, but for sure you must have the coarse and fine location permissions.
Here's an example that I reuse in m apps, using a custom class to wrap the LocationListener:
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;
public class MyLocationListener implements LocationListener {
// GLOBALS
float currentLatitude = 0;
float currentLongitude = 0;
float currentAltitude = 0;
float currentAccuracy = 0;
float currentSpeed = 0;
float currentBearing = 0;
String currentProvider = "";
public MyLocationListener(Context context) {
super();
}
// Define all LocationListener methods
public void onLocationChanged(Location location) {
currentLatitude = (float)location.getLatitude();
currentLongitude = (float)location.getLongitude();
currentAltitude = (float)location.getAltitude();
currentAccuracy = (float)location.getAccuracy();
currentSpeed = (float)location.getSpeed();
currentBearing = (float)location.getBearing();
currentProvider = location.getProvider();
}
public void onProviderDisabled (String provider) {
//currentProvider = "";
Log.v(TAG, "Provider is " + provider);
}
public void onProviderEnabled (String provider) {
//currentProvider = provider;
Log.v(TAG, "Provider is " + provider);
}
public void onStatusChanged (String provider, int status, Bundle extras) {
Log.v(TAG, "Status is " + status);
}
// Custom Methods
public String getCurrentLatitude() {
String lat = Float.toString(currentLatitude);
return lat;
}
public String getCurrentLongitude() {
String lon = Float.toString(currentLongitude);
return lon;
}
public String getCurrentAltitude() {
String alt = Float.toString(currentAltitude);
return alt;
}
public String getCurrentAccuracy() {
String acc = Float.toString(currentAccuracy);
return acc;
}
public String getCurrentSpeed() {
String spd = Float.toString(currentSpeed);
return spd;
}
}
... and then in onCreate
// GPS
locationListener = new MyLocationListener(this); // instance of above class
// Runnable
handler = new Handler();
runnable = new Runnable() {
public void run() {
handler.postDelayed(this, REFRESH_RATE);
// Get Data
float lat = Float.parseFloat(locationListener.getCurrentLatitude());
float lon = Float.parseFloat(locationListener.getCurrentLongitude());
// Update Text
ActualLatitude.setText(locationListener.getCurrentLatitude());
ActualLongitude.setText(locationListener.getCurrentLongitude());
}
};
handler.postDelayed(runnable, REFRESH_RATE);
.. and then in onResume
#Override
protected void onResume() {
super.onResume();
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, REFRESH_RATE, 5, locationListener);
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
.. and then in onDestroy
#Override
public void onDestroy() {
super.onDestroy();
// Kill Runnable
handler.removeCallbacks(runnable);
// Stop GPS
locationManager.removeUpdates(locationListener);
locationManager = null;
}
Some other notes:
make sure you have requested the COARSE_LOCATION and/or FINE_LOCATION in your AndroidManifest
double check the id's of your textviews - you said it was crashing and without seeing a stacktrace, for all we know the location stuff is fine but its crashing on a NPE!
Here is my current working code for getting user location in android:
public class MyApp extends Application{
private LocationManager locationManager;
private LocationListener listener;
#Override public void onCreate(){
super.onCreate();
}
public void acquireUserCoordinates(){
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
listener = new GpsListener();
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, listener);
}else {
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
listener = new GpsListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
}
}
private class GpsListener implements LocationListener {
public void onLocationChanged(Location location) {
if (location != null) {
double mLatitude = location.getLatitude();
double mLongitude = location.getLongitude();
EventBus.getDefault().post(new UserLocationAcquiredEvent(true, mLatitude, mLongitude));
try {
if (listener != null)
locationManager.removeUpdates(listener);
} catch (Exception e) {
}
locationManager = null;
} else {
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
}
Now, you notice that I use EventBus library here to post results to the respective activity/fragment.
In your activity or fragment, you get the application instance (getApplication()) and call the acquireUserCoordinates() method then you can override onEvent(EventClassHere event) and then easily update the UI respectively (like dismiss dialog).
You can find EventBus here: EventBus
I hope this helps you.
Good luck and happy coding!

Not getting GPS location, though Google map is showing current location

I am not getting GPS location in my code, while Google maps is showing current location and even its updating, I am starting a service, inside service registering locationListener to the locationManager, handling onLocationChanged callback method, doing entry in the AndroidManifest.xml even. The logs onCreate methods is showing.
below is my code, could you guys please let me know where i am doing wrong...
public class LocationService extends Service implements LocationListener {
private static final long MIN_TIME_INTERVAL_FOR_GPS_LOCATION = 100;
private static final float MIN_DISTANCE_INTERVAL_FOR_GPS_LOCATION = 1.0f;
private static String TAG = LocationService.class.getSimpleName();
private LocationManager locationManager;
private static Location mCurrentLocation;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate...");
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_INTERVAL_FOR_GPS_LOCATION, MIN_DISTANCE_INTERVAL_FOR_GPS_LOCATION, this);
mCurrentLocation = getBestLocation();
}
private Location getBestLocation() {
Log.i(TAG, "getBestLocation...");
Location location_gps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Location location_network = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
// If both are available, get the most recent
if (location_gps != null && location_network != null) {
return (location_gps.getTime() > location_network.getTime()) ? location_gps : location_network;
} else if (location_gps == null && location_network == null) {
return null;
} else {
return (location_gps == null) ? location_network : location_gps;
}
}
#Override
public void onLocationChanged(Location location) {
Log.i(TAG, "onLocationChanged...");
Toast.makeText(LocationService.this, "Location Found", Toast.LENGTH_SHORT).show();
mCurrentLocation = location;
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
public static Location getmCurrentLocation() {
return mCurrentLocation;
}
public static void setmCurrentLocation(Location mCurrentLocation) {
LocationService.mCurrentLocation = mCurrentLocation;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy...");
locationManager.removeUpdates(this);
}
}
Your locationManager called requestLocationUpdates using GPS provider only.
I think your device could not catch GPS satellite signal, in-door sate.
So, you should call requestLocationUpdates using Network provider too.
Next two links are may helpful to you.
http://developer.android.com/guide/topics/location/strategies.html
http://developer.android.com/training/location/receive-location-updates.html

Starting LocationManager as Service Android

What I'm attempting to do is when receiving a c2dm message, start a service that asks for location for 'x' amount of time and then hands that location off to our server. The c2dm message starts the service correctly, and the GPS location turns on, but it never updates. It just sits there for the length of time I specify (currently 12 seconds) in the thread and does nothing. I'm using the exact same code somewhere else in my app (not as a service) and it works perfectly. What am I doing wrong?
This starts the service when receiving a c2dm message.
context.startService(new Intent(context, ServicePingLocation.class));
This is the code for the service itself. All that ever gets called, is "onCreate" and "onStart".
public class ServicePingLocation extends Service implements LocationListener {
private final String DEBUG_TAG = "[GPS Ping]";
private boolean xmlSuccessful = false;
private boolean locationTimeExpired = false;
private LocationManager lm;
private double latitude;
private double longitude;
private double accuracy;
#Override
public void onLocationChanged(Location location) {
Log.d(DEBUG_TAG, "onLocationChanged");
latitude = location.getLatitude();
longitude = location.getLongitude();
accuracy = location.getAccuracy();
}
#Override
public void onProviderDisabled(String provider) {
Log.d(DEBUG_TAG, "onProviderDisabled");
Toast.makeText(
getApplicationContext(),
"Attempted to ping your location, and GPS was disabled.",
Toast.LENGTH_LONG).show();
}
#Override
public void onProviderEnabled(String provider) {
Log.d(DEBUG_TAG, "onProviderEnabled");
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10f, this);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d(DEBUG_TAG, "onStatusChanged");
}
#Override
public void onCreate() {
Log.d(DEBUG_TAG, "onCreate");
}
#Override
public void onDestroy() {
Log.d(DEBUG_TAG, "onDestroy");
}
#Override
public IBinder onBind(Intent intent) {
Log.d(DEBUG_TAG, "onBind");
return null;
}
#Override
public void onStart(Intent intent, int startid) {
Log.d(DEBUG_TAG, "onStart");
lm = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10f, this);
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000,
300f, this);
Log.d(DEBUG_TAG, lm.toString());
new SubmitLocationTask(ServicePingLocation.this).execute();
}
private void locationTimer() {
new Handler().postDelayed(new Runnable() {
// #Override
#Override
public void run() {
locationTimeExpired = true;
}
}, 12000);
}
private class SubmitLocationTask extends AsyncTask<String, Void, Boolean> {
/** application context. */
private Context context;
private Service service;
public SubmitLocationTask(Service service) {
this.service = service;
context = service;
}
#Override
protected void onPreExecute() {
locationTimer(); // Start 12 second timer
}
#Override
protected void onPostExecute(final Boolean success) {
if (success && xmlSuccessful) {
lm.removeUpdates(ServicePingLocation.this);
onDestroy();
} else {
if (!GlobalsUtil.DEBUG_ERROR_MSG.equals(""))
Toast.makeText(getBaseContext(),
GlobalsUtil.DEBUG_ERROR_MSG, Toast.LENGTH_SHORT)
.show();
GlobalsUtil.DEBUG_ERROR_MSG = "";
}
}
#Override
protected Boolean doInBackground(final String... args) {
try {
DateFormat df = null;
df = new SimpleDateFormat("M/d/yy h:mm a");
Date todaysDate = new Date();// get current date time with
// Date()
String currentDateTime = df.format(todaysDate);
while ((accuracy > 100f || accuracy == 0.0)
&& !locationTimeExpired) {
// We just want it to sit here and wait.
}
return xmlSuccessful = SendToServerUtil.submitGPSPing(
0, longitude,
latitude, accuracy, currentDateTime);
} catch (Exception e) {
return false;
}
}
}
}
[Edit]
Fixed the issue I was having. Code was actually working. I added the network provider, adjusted the onDestroy() method to stop the service, and tweaked the time used to grab GPS signal.
Thank you for the advice, CommonsWare
Fixed the issue I was having. Code was actually working. I added the network provider, adjusted the onDestroy() method to stop the service, and tweaked the time used to grab GPS signal.
Thank you for the advice, CommonsWare

Categories

Resources