Removed Skyhook, updated compat lib, and old multishot service moethods.
For mulitshot services, use alarm manager repeating methods Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
This commit is contained in:
BIN
LocationLib/libs/android-support-v4-googlemaps-r10.jar
Normal file
BIN
LocationLib/libs/android-support-v4-googlemaps-r10.jar
Normal file
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
4.4.0.14
|
|
||||||
Binary file not shown.
@@ -1,318 +0,0 @@
|
|||||||
/**
|
|
||||||
* @author Twenty Codes, LLC
|
|
||||||
* @author ricky barrette
|
|
||||||
* @date Oct 2, 2010
|
|
||||||
*/
|
|
||||||
package com.TwentyCodes.android.SkyHook;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.debug.Debug;
|
|
||||||
import com.TwentyCodes.android.location.AndroidGPS;
|
|
||||||
import com.TwentyCodes.android.location.GeoPointLocationListener;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
import com.skyhookwireless.wps.WPSAuthentication;
|
|
||||||
import com.skyhookwireless.wps.WPSContinuation;
|
|
||||||
import com.skyhookwireless.wps.WPSLocation;
|
|
||||||
import com.skyhookwireless.wps.WPSPeriodicLocationCallback;
|
|
||||||
import com.skyhookwireless.wps.WPSReturnCode;
|
|
||||||
import com.skyhookwireless.wps.XPS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this calls will be used to create skyhook object that uses an listener
|
|
||||||
* interface to interact with the rest of location ringer
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public class SkyHook implements GeoPointLocationListener {
|
|
||||||
|
|
||||||
private class XPScallback implements WPSPeriodicLocationCallback {
|
|
||||||
@Override
|
|
||||||
public void done() {
|
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(DONE_MESSAGE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WPSContinuation handleError(final WPSReturnCode error) {
|
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(ERROR_MESSAGE, error));
|
|
||||||
return WPSContinuation.WPS_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WPSContinuation handleWPSPeriodicLocation(final WPSLocation location) {
|
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(LOCATION_MESSAGE, location));
|
|
||||||
return WPSContinuation.WPS_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String TAG = "Skyhook";
|
|
||||||
public static final String USERNAME = "cjyh95q32gsc";
|
|
||||||
public static final String USERNAME_FOR_TESTING = "twentycodes";
|
|
||||||
public static final String REALM = "TwentyCodes";
|
|
||||||
public static final int LOCATION_MESSAGE = 1;
|
|
||||||
public static final int ERROR_MESSAGE = 2;
|
|
||||||
public static final int DONE_MESSAGE = 3;
|
|
||||||
private final XPScallback mXPScallback = new XPScallback();
|
|
||||||
private final XPS mXps;
|
|
||||||
private final Context mContext;
|
|
||||||
private GeoPointLocationListener mListener;
|
|
||||||
private long mPeriod = 0l; // period is in milliseconds for periodic updates
|
|
||||||
private final int mIterations = 0;
|
|
||||||
private WPSAuthentication mWPSAuthentication;
|
|
||||||
private static Handler mHandler;
|
|
||||||
private boolean isPeriodicEnabled;
|
|
||||||
private boolean hasLocation;
|
|
||||||
protected AndroidGPS mSkyHookFallback = null;
|
|
||||||
protected long mFallBackDelay = 5000l;
|
|
||||||
private boolean isFallBackScheduled = false;
|
|
||||||
private boolean isEnabled = false;
|
|
||||||
private boolean isUnauthorized = false;
|
|
||||||
|
|
||||||
private boolean isFirstFix;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this runnable will be used to check if we have location from skyhook, if
|
|
||||||
* we dont, then we will us android's location services to fall back on.
|
|
||||||
*/
|
|
||||||
private final Runnable mFallBack = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mHandler.removeCallbacks(mFallBack);
|
|
||||||
Log.d(TAG, "skyhook, " + (hasLocation ? "is" : "isn't") + " working!");
|
|
||||||
|
|
||||||
if (!hasLocation && mSkyHookFallback == null && isEnabled) {
|
|
||||||
Log.d(TAG, "falling back on android");
|
|
||||||
mSkyHookFallback = new AndroidGPS(mContext);
|
|
||||||
mSkyHookFallback.enableLocationUpdates(SkyHook.this);
|
|
||||||
/*
|
|
||||||
* Schedule another check, if skyhook is still enabled
|
|
||||||
*/
|
|
||||||
if (mXps != null)
|
|
||||||
mHandler.postDelayed(mFallBack, mFallBackDelay);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "already fell back on android");
|
|
||||||
if (mSkyHookFallback != null) {
|
|
||||||
Log.d(TAG, "got location, picking up the slack");
|
|
||||||
mSkyHookFallback.disableLocationUpdates();
|
|
||||||
mSkyHookFallback = null;
|
|
||||||
}
|
|
||||||
isFallBackScheduled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this runnable keeps skyhook working!
|
|
||||||
*/
|
|
||||||
private final Runnable mPeriodicUpdates = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (Debug.DEBUG)
|
|
||||||
Log.d(TAG, "geting location");
|
|
||||||
mXps.getXPSLocation(mWPSAuthentication, mIterations, XPS.EXACT_ACCURACY, mXPScallback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructors a new skyhook object
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public SkyHook(final Context context) {
|
|
||||||
mXps = new XPS(context);
|
|
||||||
mContext = context;
|
|
||||||
// initialize the Handler which will display location data
|
|
||||||
// in the text view. we use a Handler because UI updates
|
|
||||||
// must occur in the UI thread
|
|
||||||
setUIHandler();
|
|
||||||
isFirstFix = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructors a new skyhook object
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param period
|
|
||||||
* between location updates in milliseconds
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public SkyHook(final Context context, final long period) {
|
|
||||||
this(context);
|
|
||||||
mPeriod = period;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* request current user location, note that the listeners
|
|
||||||
* onLocationChanged() will be call multiple times. updates will stop once
|
|
||||||
* an accurate location is determined.
|
|
||||||
*
|
|
||||||
* @author Ricky Barrette
|
|
||||||
*/
|
|
||||||
public void getLoctaion() {
|
|
||||||
Log.d(TAG, "getLocation()");
|
|
||||||
if (mListener != null) {
|
|
||||||
mWPSAuthentication = new WPSAuthentication(SkyHookRegistration.getUserName(mContext), REALM);
|
|
||||||
mHandler.post(mPeriodicUpdates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to register the the listener for periodic updates
|
|
||||||
*
|
|
||||||
* @author Ricky Barrette
|
|
||||||
*/
|
|
||||||
public void getUpdates() {
|
|
||||||
Log.d(TAG, "getUpdates()");
|
|
||||||
if (mListener != null) {
|
|
||||||
|
|
||||||
if (Debug.DEBUG)
|
|
||||||
Log.i(TAG, "username: " + SkyHookRegistration.getUserName(mContext));
|
|
||||||
|
|
||||||
mWPSAuthentication = new WPSAuthentication(SkyHookRegistration.getUserName(mContext), REALM);
|
|
||||||
isPeriodicEnabled = true;
|
|
||||||
mHandler.post(mPeriodicUpdates);
|
|
||||||
isEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true is skyhook is enabled
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return isEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFirstFix(final boolean firstFix) {
|
|
||||||
if (mListener != null)
|
|
||||||
mListener.onFirstFix(firstFix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called from our skyhook to android fall back class (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.location.GeoPointLocationListener#onLocationChanged(com.google.android.maps.GeoPoint,
|
|
||||||
* int)
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(final GeoPoint point, final int accuracy) {
|
|
||||||
if (!hasLocation)
|
|
||||||
if (mListener != null)
|
|
||||||
mListener.onLocationChanged(point, accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes any current registration for location updates of the current
|
|
||||||
* activity with the given LocationListener. Following this call, updates
|
|
||||||
* will no longer occur for this listener.
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void removeUpdates() {
|
|
||||||
Log.d(TAG, "removeUpdates()");
|
|
||||||
mHandler.removeCallbacks(mFallBack);
|
|
||||||
mListener = null;
|
|
||||||
isPeriodicEnabled = false;
|
|
||||||
if (mXps != null)
|
|
||||||
mXps.abort();
|
|
||||||
if (mSkyHookFallback != null) {
|
|
||||||
Log.d(TAG, "disabling fallback");
|
|
||||||
mSkyHookFallback.disableLocationUpdates();
|
|
||||||
mSkyHookFallback = null;
|
|
||||||
isEnabled = false;
|
|
||||||
}
|
|
||||||
isFirstFix = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for receiving notifications from SkyHook when the location has
|
|
||||||
* changed. These methods are called if the LocationListener has been
|
|
||||||
* registered with the location manager service using the method.
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void setLocationListener(final GeoPointLocationListener listener) {
|
|
||||||
Log.d(TAG, "setLocationListener()");
|
|
||||||
if (mListener == null)
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUIHandler() {
|
|
||||||
mHandler = new Handler() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(final Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case LOCATION_MESSAGE:
|
|
||||||
if (msg.obj instanceof WPSLocation) {
|
|
||||||
final WPSLocation location = (WPSLocation) msg.obj;
|
|
||||||
if (mListener != null && location != null) {
|
|
||||||
|
|
||||||
if (Debug.DEBUG)
|
|
||||||
Log.d(TAG, "got location " + location.getLatitude() + ", " + location.getLongitude() + " +- " + location.getHPE() + "m");
|
|
||||||
|
|
||||||
mListener.onLocationChanged(new GeoPoint((int) (location.getLatitude() * 1e6), (int) (location.getLongitude() * 1e6)), location.getHPE());
|
|
||||||
mListener.onFirstFix(isFirstFix);
|
|
||||||
hasLocation = true;
|
|
||||||
isFirstFix = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ERROR_MESSAGE:
|
|
||||||
if (msg.obj instanceof WPSReturnCode) {
|
|
||||||
final WPSReturnCode code = (WPSReturnCode) msg.obj;
|
|
||||||
if (code != null)
|
|
||||||
Log.w(TAG, code.toString());
|
|
||||||
hasLocation = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check to see if the error returned is an
|
|
||||||
* WPS_ERROR_UNAUTHORIZED then check to see if this is
|
|
||||||
* the second occurrence of WPS_ERROR_UNAUTHORIZED, if
|
|
||||||
* so we will stop skyhook's services to cut down the
|
|
||||||
* work load
|
|
||||||
*/
|
|
||||||
if (code == WPSReturnCode.WPS_ERROR_UNAUTHORIZED) {
|
|
||||||
if (isUnauthorized) {
|
|
||||||
isPeriodicEnabled = false;
|
|
||||||
mXps.abort();
|
|
||||||
// mXps = null;
|
|
||||||
}
|
|
||||||
isUnauthorized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check to see if we already have a fall back Scheduled
|
|
||||||
* if we dont, and there is not fallback already in
|
|
||||||
* place, then schedule one
|
|
||||||
*/
|
|
||||||
if (!isFallBackScheduled && mSkyHookFallback == null && isEnabled) {
|
|
||||||
Log.d(TAG, "scheduling fallback");
|
|
||||||
postDelayed(mFallBack, mFallBackDelay);
|
|
||||||
isFallBackScheduled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case DONE_MESSAGE:
|
|
||||||
if (isPeriodicEnabled) {
|
|
||||||
postDelayed(mPeriodicUpdates, mPeriod);
|
|
||||||
Log.d(TAG, "done getting location");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* @author Twenty Codes, LLC
|
|
||||||
* @author ricky barrette
|
|
||||||
* @date Oct 26, 2010
|
|
||||||
*/
|
|
||||||
package com.TwentyCodes.android.SkyHook;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.debug.Debug;
|
|
||||||
import com.TwentyCodes.android.debug.LocationLibraryConstants;
|
|
||||||
import com.skyhookwireless.wps.RegistrationCallback;
|
|
||||||
import com.skyhookwireless.wps.WPSAuthentication;
|
|
||||||
import com.skyhookwireless.wps.XPS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this class will be used to register new users with skyhook
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public class SkyHookRegistration {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the users username
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @return
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public static String getUserName(final Context context) {
|
|
||||||
|
|
||||||
switch (LocationLibraryConstants.DEFAULT_REGISTRATION_BEHAVIOR) {
|
|
||||||
case NORMAL:
|
|
||||||
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
|
||||||
if (tm == null)
|
|
||||||
Log.v(SkyHook.TAG, "TelephonyManager is null");
|
|
||||||
return tm.getLine1Number();
|
|
||||||
|
|
||||||
case RETURN_NULL:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case USE_TESTING_USERNAME:
|
|
||||||
return SkyHook.USERNAME_FOR_TESTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final XPS mXps;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
public SkyHookRegistration(final Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mXps = new XPS(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* attempts to register the user by their cell #
|
|
||||||
*
|
|
||||||
* TODO hash cell number for privacy
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
* for call back methods
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void registerNewUser(final RegistrationCallback listener) {
|
|
||||||
if (mXps != null) {
|
|
||||||
final TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
|
||||||
if (tm == null)
|
|
||||||
Log.v(SkyHook.TAG, "TelephonyManager is null");
|
|
||||||
final String newUser = tm.getLine1Number();
|
|
||||||
|
|
||||||
if (Debug.DEBUG)
|
|
||||||
Log.v(SkyHook.TAG, "newUser = " + newUser);
|
|
||||||
|
|
||||||
if (newUser == null)
|
|
||||||
Log.e(SkyHook.TAG, "users number is null");
|
|
||||||
mXps.registerUser(new WPSAuthentication(SkyHook.USERNAME, SkyHook.REALM), new WPSAuthentication(newUser, SkyHook.REALM), listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
/**
|
|
||||||
* @author Twenty Codes, LLC
|
|
||||||
* @author Ricky Barrette barrette
|
|
||||||
* @date Oct 6, 2010
|
|
||||||
*/
|
|
||||||
package com.TwentyCodes.android.SkyHook;
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.location.Location;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.debug.LocationLibraryConstants;
|
|
||||||
import com.TwentyCodes.android.location.GeoPointLocationListener;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
import com.skyhookwireless.wps.RegistrationCallback;
|
|
||||||
import com.skyhookwireless.wps.WPSContinuation;
|
|
||||||
import com.skyhookwireless.wps.WPSReturnCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This service class will be used broadcast the users location either one time,
|
|
||||||
* or periodically. To use as a one shot location service: <blockquote>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* PendingIntent pendingIntent = PendingIntent.getService(context, 0, SkyHookService.startService(context), 0);
|
|
||||||
* or
|
|
||||||
* Intent service = new Intent(context, SkyHookService.class);
|
|
||||||
* context.startService(service);
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* </bloackquote>
|
|
||||||
* To use as a recurring service:
|
|
||||||
* <blockquote>SkyHookService.startService(this, (60000 * Integer.parseInt(ringer.getString(UPDATE_INTVERVAL , "5")))).run();</bloackquote>
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public class SkyHookService extends Service implements GeoPointLocationListener, RegistrationCallback {
|
|
||||||
|
|
||||||
public static final String TAG = "SkyHookService";
|
|
||||||
public static final int REQUEST_CODE = 32741942;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a convince method for getting an intent to start the service
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @return a intent that will be used to start the service
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public static Intent getStartServiceIntent(final Context context) {
|
|
||||||
return new Intent(context, SkyHookService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a convince method for stopping the service and removing its que from the
|
|
||||||
* alarm manager
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @return a runnable that will stop the service
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public static Runnable stopService(final Context context) {
|
|
||||||
return new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
context.stopService(new Intent(context, SkyHookService.class));
|
|
||||||
((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel(PendingIntent.getService(context, REQUEST_CODE, new Intent(context,
|
|
||||||
SkyHookService.class), 0));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private SkyHook mSkyhook;
|
|
||||||
protected long mPeriod = -1;
|
|
||||||
private GeoPoint mLocation;
|
|
||||||
private int mStartID;
|
|
||||||
private int mRequiredAccuracy;
|
|
||||||
|
|
||||||
private Intent mIntent;
|
|
||||||
|
|
||||||
private int mAccuracy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* broadcasts location to anything listening for updates
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private void braodcastLocation() {
|
|
||||||
if (mLocation != null) {
|
|
||||||
final Intent locationUpdate = new Intent();
|
|
||||||
if (mIntent.getAction() != null)
|
|
||||||
locationUpdate.setAction(mIntent.getAction());
|
|
||||||
else
|
|
||||||
locationUpdate.setAction(LocationLibraryConstants.INTENT_ACTION_UPDATE);
|
|
||||||
locationUpdate.putExtra(LocationManager.KEY_LOCATION_CHANGED, convertLocation());
|
|
||||||
sendBroadcast(locationUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* converts skyhook's location object into android's location object
|
|
||||||
*
|
|
||||||
* @return converted location
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public Location convertLocation() {
|
|
||||||
final Location location = new Location("location");
|
|
||||||
location.setLatitude(mLocation.getLatitudeE6() / 1e6);
|
|
||||||
location.setLongitude(mLocation.getLongitudeE6() / 1e6);
|
|
||||||
location.setAccuracy(mAccuracy);
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void done() {
|
|
||||||
// unused
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* I believe that this method is no longer needed as we are not supporting
|
|
||||||
* pre 2.1
|
|
||||||
*/
|
|
||||||
// /**
|
|
||||||
// * To keep backwards compatibility we override onStart which is the
|
|
||||||
// equivalent of onStartCommand in pre android 2.x
|
|
||||||
// * @author ricky barrette
|
|
||||||
// */
|
|
||||||
// @Override
|
|
||||||
// public void onStart(Intent intent, int startId) {
|
|
||||||
// Log.i(SkyHook.TAG, "onStart.Service started with start id of: " +
|
|
||||||
// startId);
|
|
||||||
// parseIntent(intent);
|
|
||||||
// this.mSkyhook.getUpdates();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WPSContinuation handleError(final WPSReturnCode arg0) {
|
|
||||||
// unused
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSuccess() {
|
|
||||||
// unused
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see android.app.Service#onBind(android.content.Intent)
|
|
||||||
* @param arg0
|
|
||||||
* @return
|
|
||||||
* @author Ricky Barrette barrette
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(final Intent arg0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
mSkyhook = new SkyHook(this);
|
|
||||||
mSkyhook.setLocationListener(this);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fail safe this will stop the service after the maximum running time,
|
|
||||||
* if location has not been reported
|
|
||||||
*/
|
|
||||||
new Handler().postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
stopSelfResult(mStartID);
|
|
||||||
}
|
|
||||||
}, LocationLibraryConstants.MAX_LOCATION_SERVICE_RUN_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* aborts location services (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see android.app.Service#onDestroy()
|
|
||||||
* @author Ricky Barrette
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
mSkyhook.removeUpdates();
|
|
||||||
braodcastLocation();
|
|
||||||
// ask android to restart service if mPeriod is set
|
|
||||||
if (mPeriod > -1)
|
|
||||||
registerWakeUp();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFirstFix(final boolean isFistFix) {
|
|
||||||
// unused
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(final GeoPoint point, final int accuracy) {
|
|
||||||
mLocation = point;
|
|
||||||
mAccuracy = accuracy;
|
|
||||||
/*
|
|
||||||
* fail safe if the accuracy is greater than the minimum required
|
|
||||||
* accuracy then continue else stop to report location
|
|
||||||
*/
|
|
||||||
if (accuracy < (mRequiredAccuracy > -1 ? mRequiredAccuracy : LocationLibraryConstants.MINIMUM_REQUIRED_ACCURACY)
|
|
||||||
|| LocationLibraryConstants.REPORT_FIRST_LOCATION)
|
|
||||||
this.stopSelf(mStartID);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when startService is called. only used in 2.x
|
|
||||||
* android.
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
|
||||||
Log.i(SkyHook.TAG, "onStartCommand.Service started with start id of: " + startId);
|
|
||||||
mStartID = startId;
|
|
||||||
parseIntent(intent);
|
|
||||||
mSkyhook.getUpdates();
|
|
||||||
return START_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the incoming intent for the service options
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private void parseIntent(final Intent intent) {
|
|
||||||
|
|
||||||
mIntent = intent;
|
|
||||||
|
|
||||||
if (intent != null) {
|
|
||||||
if (intent.hasExtra(LocationLibraryConstants.INTENT_EXTRA_PERIOD_BETWEEN_UPDATES))
|
|
||||||
mPeriod = intent.getLongExtra(LocationLibraryConstants.INTENT_EXTRA_PERIOD_BETWEEN_UPDATES, LocationLibraryConstants.FAIL_SAFE_UPDATE_INVERVAL);
|
|
||||||
|
|
||||||
if (intent.hasExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY))
|
|
||||||
mRequiredAccuracy = intent.getIntExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY, LocationLibraryConstants.MINIMUM_REQUIRED_ACCURACY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* registers our Receiver the starts the service with the alarm manager
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private void registerWakeUp() {
|
|
||||||
final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
|
||||||
am.set(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + mPeriod, PendingIntent.getService(this, REQUEST_CODE, mIntent, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -67,12 +67,6 @@ public final class LocationLibraryConstants {
|
|||||||
|
|
||||||
public static final String INTENT_EXTRA_LOCATION_CHANGED = LocationManager.KEY_LOCATION_CHANGED;
|
public static final String INTENT_EXTRA_LOCATION_CHANGED = LocationManager.KEY_LOCATION_CHANGED;
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to tell the service how frequently it needs to run. This is required
|
|
||||||
* if you want a multishot service
|
|
||||||
*/
|
|
||||||
public static final String INTENT_EXTRA_PERIOD_BETWEEN_UPDATES = "period_beween_updates";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to tell the service how accurate of a location you want reported
|
* Used to tell the service how accurate of a location you want reported
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
/**
|
|
||||||
* UserOverlayMapFragmentBase.java
|
|
||||||
* @date Jan 12, 2012
|
|
||||||
* @author ricky barrette
|
|
||||||
* @author Twenty Codes, LLC
|
|
||||||
*/
|
|
||||||
package com.TwentyCodes.android.fragments;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.location.CompassSensor.CompassListener;
|
|
||||||
import com.TwentyCodes.android.location.GeoPointLocationListener;
|
|
||||||
import com.TwentyCodes.android.location.MapView;
|
|
||||||
import com.TwentyCodes.android.overlays.SkyHookUserOverlay;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a MapFragment that maintains the SkyHookUserOverlay
|
|
||||||
*
|
|
||||||
* TODO acquiring gps dialog
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public class SkyHoookUserOverlayMapFragment extends BaseMapFragment implements GeoPointLocationListener, CompassListener {
|
|
||||||
|
|
||||||
private SkyHookUserOverlay mUserOverlay;
|
|
||||||
private GeoPointLocationListener mGeoPointLocationListener;
|
|
||||||
private CompassListener mCompassListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new UserOverlayMapFragment
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public SkyHoookUserOverlayMapFragment() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the useroverlay to pan the map to follow the user
|
|
||||||
*
|
|
||||||
* @param followUser
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void followUser(final boolean followUser) {
|
|
||||||
mUserOverlay.followUser(followUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return return the current destination
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public GeoPoint getDestination() {
|
|
||||||
return mUserOverlay.getDestination();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the users current location
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public GeoPoint getUserLocation() {
|
|
||||||
return mUserOverlay.getUserLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the compass is updated (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.location.CompassListener#onCompassUpdate(float)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onCompassUpdate(final float bearing) {
|
|
||||||
if (mCompassListener != null)
|
|
||||||
mCompassListener.onCompassUpdate(bearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFirstFix(final boolean isFistFix) {
|
|
||||||
if (mGeoPointLocationListener != null)
|
|
||||||
mGeoPointLocationListener.onFirstFix(isFistFix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when has a location to report
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(final GeoPoint point, final int accuracy) {
|
|
||||||
if (mGeoPointLocationListener != null)
|
|
||||||
mGeoPointLocationListener.onLocationChanged(point, accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.fragments.BaseMapFragment#onMapViewCreate(com.TwentyCodes.android.location.MapView)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onMapViewCreate(final MapView map) {
|
|
||||||
mUserOverlay = new SkyHookUserOverlay(map, getActivity().getApplicationContext());
|
|
||||||
mUserOverlay.registerListener(this);
|
|
||||||
mUserOverlay.setCompassListener(this);
|
|
||||||
mUserOverlay.enableCompass();
|
|
||||||
mUserOverlay.followUser(true);
|
|
||||||
|
|
||||||
map.getOverlays().add(mUserOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.fragments.BaseMapFragment#onPause()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
mUserOverlay.disableMyLocation();
|
|
||||||
removeOverlay(mUserOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.fragments.BaseMapFragment#onResume()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
if (mUserOverlay != null) {
|
|
||||||
mUserOverlay.enableMyLocation();
|
|
||||||
addOverlay(mUserOverlay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reorders the overlays to the UserOverlay always on top
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void reorderOverlays() {
|
|
||||||
getMap().getOverlays().remove(mUserOverlay);
|
|
||||||
getMap().getOverlays().add(mUserOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param needleResId
|
|
||||||
* @param backgroundResId
|
|
||||||
* @param x
|
|
||||||
* @param y
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void setCompassDrawables(final int needleResId, final int backgroundResId, final int x, final int y) {
|
|
||||||
mUserOverlay.setCompassDrawables(needleResId, backgroundResId, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param listener
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void setCompassListener(final CompassListener listener) {
|
|
||||||
mCompassListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the destination for the compass to point to
|
|
||||||
*
|
|
||||||
* @param destination
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void setDestination(final GeoPoint destination) {
|
|
||||||
mUserOverlay.setDestination(destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param listener
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void setGeoPointLocationListener(final GeoPointLocationListener listener) {
|
|
||||||
mGeoPointLocationListener = listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
|
|
||||||
* AndroidGPS.java
|
* AndroidGPS.java
|
||||||
* @date Feb 3, 2011
|
* @date Feb 3, 2011
|
||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
@@ -13,7 +14,6 @@ import android.location.LocationManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.TwentyCodes.android.SkyHook.SkyHook;
|
|
||||||
import com.TwentyCodes.android.debug.Debug;
|
import com.TwentyCodes.android.debug.Debug;
|
||||||
import com.google.android.maps.GeoPoint;
|
import com.google.android.maps.GeoPoint;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ public class AndroidGPS implements LocationListener {
|
|||||||
*/
|
*/
|
||||||
public void enableLocationUpdates(final GeoPointLocationListener listener) {
|
public void enableLocationUpdates(final GeoPointLocationListener listener) {
|
||||||
if (Debug.DEBUG)
|
if (Debug.DEBUG)
|
||||||
Log.d(SkyHook.TAG, "enableLocationUpdates()");
|
Log.d(TAG, "enableLocationUpdates()");
|
||||||
if (mListener == null) {
|
if (mListener == null) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
requestUpdates();
|
requestUpdates();
|
||||||
@@ -76,7 +76,7 @@ public class AndroidGPS implements LocationListener {
|
|||||||
*/
|
*/
|
||||||
public void enableLocationUpdates(final LocationListener listener) {
|
public void enableLocationUpdates(final LocationListener listener) {
|
||||||
if (Debug.DEBUG)
|
if (Debug.DEBUG)
|
||||||
Log.d(SkyHook.TAG, "enableLocationUpdates()");
|
Log.d(TAG, "enableLocationUpdates()");
|
||||||
if (mLocationListener == null) {
|
if (mLocationListener == null) {
|
||||||
mLocationListener = listener;
|
mLocationListener = listener;
|
||||||
requestUpdates();
|
requestUpdates();
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.TwentyCodes.android.location;
|
package com.TwentyCodes.android.location;
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@@ -65,7 +63,6 @@ public class LocationService extends Service implements LocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private WakeLock mWakeLock;
|
private WakeLock mWakeLock;
|
||||||
protected long mPeriod = -1;
|
|
||||||
private Location mLocation;
|
private Location mLocation;
|
||||||
private int mStartId;
|
private int mStartId;
|
||||||
private AndroidGPS mLocationManager;
|
private AndroidGPS mLocationManager;
|
||||||
@@ -153,8 +150,6 @@ public class LocationService extends Service implements LocationListener {
|
|||||||
mLocationManager.disableLocationUpdates();
|
mLocationManager.disableLocationUpdates();
|
||||||
if (mWakeLock.isHeld())
|
if (mWakeLock.isHeld())
|
||||||
mWakeLock.release();
|
mWakeLock.release();
|
||||||
if (mPeriod > -1)
|
|
||||||
registerwakeUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -214,23 +209,8 @@ public class LocationService extends Service implements LocationListener {
|
|||||||
} else {
|
} else {
|
||||||
mIntent = intent;
|
mIntent = intent;
|
||||||
|
|
||||||
if (intent.hasExtra(LocationLibraryConstants.INTENT_EXTRA_PERIOD_BETWEEN_UPDATES))
|
|
||||||
mPeriod = intent.getLongExtra(LocationLibraryConstants.INTENT_EXTRA_PERIOD_BETWEEN_UPDATES, LocationLibraryConstants.FAIL_SAFE_UPDATE_INVERVAL);
|
|
||||||
|
|
||||||
if (intent.hasExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY))
|
if (intent.hasExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY))
|
||||||
mRequiredAccuracy = intent.getIntExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY, LocationLibraryConstants.MINIMUM_REQUIRED_ACCURACY);
|
mRequiredAccuracy = intent.getIntExtra(LocationLibraryConstants.INTENT_EXTRA_REQUIRED_ACCURACY, LocationLibraryConstants.MINIMUM_REQUIRED_ACCURACY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* registers this service to be waken up by android's alarm manager
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private void registerwakeUp() {
|
|
||||||
Log.d(TAG, "registerwakeUp()");
|
|
||||||
final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
|
||||||
am.set(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + mPeriod, PendingIntent.getService(this, REQUEST_CODE, mIntent, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* @author Twenty Codes, LLC
|
|
||||||
* @author ricky barrette
|
|
||||||
* @date Oct 2, 2010
|
|
||||||
*/
|
|
||||||
package com.TwentyCodes.android.overlays;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.SkyHook.SkyHook;
|
|
||||||
import com.google.android.maps.MapView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this class will be used to display the users location on the map using
|
|
||||||
* skyhook's call back methods
|
|
||||||
*
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public class SkyHookUserOverlay extends BaseUserOverlay {
|
|
||||||
|
|
||||||
private final SkyHook mSkyHook;
|
|
||||||
|
|
||||||
public SkyHookUserOverlay(final MapView mapView, final Context context) {
|
|
||||||
super(mapView, context);
|
|
||||||
mSkyHook = new SkyHook(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new SkyHookUserOverlay
|
|
||||||
*
|
|
||||||
* @param mapView
|
|
||||||
* @param context
|
|
||||||
* @param followUser
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public SkyHookUserOverlay(final MapView mapView, final Context context, final boolean followUser) {
|
|
||||||
super(mapView, context, followUser);
|
|
||||||
mSkyHook = new SkyHook(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFirstFix(final boolean isFistFix) {
|
|
||||||
// unused
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the location provider needs to be disabled (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.overlays.BaseUserOverlay#onMyLocationDisabled()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onMyLocationDisabled() {
|
|
||||||
mSkyHook.removeUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the location provider needs to be enabled (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see com.TwentyCodes.android.overlays.BaseUserOverlay#onMyLocationEnabled()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onMyLocationEnabled() {
|
|
||||||
mSkyHook.setLocationListener(this);
|
|
||||||
mSkyHook.getUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user