I am creating a framework which will test the android application. In the android application they have implemented Activity Recognition to know the user activity. I want to test the functionality in a framework. Can anybody tell me how to implement mock activity recognition in android?
Thanks
I would advice you to subclass the ActivityRecognitionClient and reimplement methods which handle registration to updates and take there the reference to the pending intent which calls your subclassed ActivityRecognitionIntentService.
You can then periodically call your service with some artificial data..How to call it? I would just use a thread with handler for its simplicity.
EDIT: I have added a very abstract code snippet which should give you the idea..
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
private ActivityRecognitionClient mActivityRecognitionClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
mActivityRecognitionClient = MOCK ? new MockActivityRecognitionClient(getApplicationContext(), this, this) : new ActivityRecognitionClient(getApplicationContext(),
this, this);
}
...
/*
* Called by Location Services once the location client is connected.
*
* Continue by requesting activity updates.
*/
#Override
public void onConnected(Bundle dataBundle) {
/*
* Request activity recognition updates using the preset
* detection interval and PendingIntent. This call is
* synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
/*
* Since the preceding call is synchronous, turn off the
* in progress flag and disconnect the client
*/
mInProgress = false;
mActivityRecognitionClient.disconnect();
}
...
}
/**
* Service that receives ActivityRecognition updates. It receives
* updates in the background, even if the main Activity is not visible.
*/
public class ActivityRecognitionIntentService extends IntentService {
...
/**
* Called when a new activity detection update is available.
*/
#Override
protected void onHandleIntent(Intent intent) {
...
}
...
}
public class MockActivityRecognitionClient extends ActivityRecognitionClient {
private List<ConnectionCallbacks> mConnectionCallbacksList;
private List<OnConnectionFailedListener> mConnectionFailedListenerlist;
private final Handler mHandler;
private long interval = 1000; //default 1000ms
private PendingIntent mPendingIntent;
private Context mContext;
public MockActivityRecognitionClient(Context context, ConnectionCallbacks connectedListener, OnConnectionFailedListener connectionFailedListener) {
super(context, connectedListener, connectionFailedListener);
registerConnectionCallbacks(connectedListener);
registerConnectionFailedListener(connectionFailedListener);
mHandler = new Handler(Looper.getMainLooper());
mContext = context;
}
#Override
public void requestActivityUpdates(long detectionIntervalMillis, PendingIntent callbackIntent) {
interval = detectionIntervalMillis;
mPendingIntent = callbackIntent;
mHandler.postDelayed(postLocationUpdateRunnable, interval);
}
#Override
public void removeActivityUpdates(PendingIntent callbackIntent) {
mHandler.removeCallbacks(postLocationUpdateRunnable);
}
#Override
public void connect() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
postConnected();
}
}, 2000);
}
private void postConnected() {
for(ConnectionCallbacks connectionCallbacks : mConnectionCallbacksList){
connectionCallbacks.onConnected(new Bundle()); //insert mocku bundle data
}
}
#Override
public boolean isConnected() {
}
#Override
public boolean isConnecting() {
}
#Override
public void registerConnectionCallbacks(ConnectionCallbacks listener) {
if(mConnectionCallbacksList == null){
mConnectionCallbacksList = new LinkedList<>();
}
mConnectionCallbacksList.add(listener);
}
#Override
public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) {
return mConnectionCallbacksList.contains(listener);
}
#Override
public void unregisterConnectionCallbacks(ConnectionCallbacks listener) {
mConnectionCallbacksList.remove(listener);
}
#Override
public void registerConnectionFailedListener(OnConnectionFailedListener listener) {
...
}
#Override
public boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener) {
...
}
#Override
public void unregisterConnectionFailedListener(OnConnectionFailedListener listener) {
...
}
#Override
public void disconnect() {
}
private final Runnable postLocationUpdateRunnable = new Runnable() {
#Override
public void run() {
try {
mPendingIntent.send(mContext, 1234, new Intent()); // in the intent should probably be the mocked activity data - I dont know the exact structure, it should be documented
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
mHandler.postDelayed(this, interval);
}
};
}
You can check this code, This is also include how you can get location of user when particular activity occurs.
MainActivity
class MainActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
override fun onConnected(p0: Bundle?) {
requestActivityUpdates()
}
override fun onConnectionSuspended(p0: Int) {
Log.e("location", "connection suspended, retry to connect")
apiClient.connect()
}
override fun onConnectionFailed(p0: ConnectionResult) {
Log.e("location", "connection error ${p0.errorMessage}")
}
lateinit var apiClient: GoogleApiClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
}
override fun onStart() {
super.onStart()
apiClient.connect()
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION), 100)
}
}
override fun onStop() {
super.onStop()
if (apiClient.isConnected)
apiClient.disconnect()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
100 -> {
if (grantResults.isNotEmpty() && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "You must assign permission of your location", Toast.LENGTH_SHORT).show()
finish()
}
}
}
}
private fun init() {
apiClient = GoogleApiClient.Builder(this).addApi(ActivityRecognition.API).addConnectionCallbacks(this).addOnConnectionFailedListener(this).build()
}
fun requestActivityUpdates() {
if (!apiClient.isConnected) {
Toast.makeText(this, "GoogleApiClient not yet connected", Toast.LENGTH_SHORT).show()
} else {
ActivityRecognition.getClient(this).requestActivityUpdates(0, getActivityDetectionPendingIntent())
}
}
fun removeActivityUpdates() {
ActivityRecognition.getClient(this).removeActivityUpdates(getActivityDetectionPendingIntent())
}
private fun getActivityDetectionPendingIntent(): PendingIntent {
val intent = Intent(this, ActivityRecognizeService::class.java)
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
}
Service
class ActivityRecognizeService : IntentService("LjRecognize") {
lateinit var location: Location
private var fusedLocationClient: FusedLocationProviderClient? = null
override fun onHandleIntent(intent: Intent?) {
if (intent == null)
return
if (ActivityRecognitionResult.hasResult(intent)) {
val result = ActivityRecognitionResult.extractResult(intent)
val time = System.currentTimeMillis()
result.probableActivities.forEach {
when (it.type) {
DetectedActivity.IN_VEHICLE -> {
Log.e("Activity found", "on vehicle at " + time + " with confidence of " + it.confidence)
}
DetectedActivity.ON_BICYCLE -> {
Log.e("Activity found", "on bicycle at " + time + " with confidence of " + it.confidence)
}
DetectedActivity.ON_FOOT -> {
Log.e("Activity found", "on foot at " + time + " with confidence of " + it.confidence)
getLocation()
}
DetectedActivity.STILL -> {
Log.e("Activity found", "still at " + time + " with confidence of " + it.confidence)
}
DetectedActivity.UNKNOWN -> {
Log.e("Activity found", "unknown at " + time + " with confidence of " + it.confidence)
}
DetectedActivity.TILTING -> {
Log.e("Activity found", "tilted at " + time + " with confidence of " + it.confidence)
}
}
}
}
}
private fun getLocation() {
if (fusedLocationClient == null)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient!!.lastLocation.addOnSuccessListener {
if (it != null)
location = it
Log.e("location", "found -> lat: ${location.latitude}, lng: ${it.longitude}")
}
} else {
startActivity(Intent(this, MainActivity::class.java))
}
}
}
Don't forgot to add permissions
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Related
I'm trying to get location information in ViewModel.
So I called requestLocationUpdates() by getting LocationManager at View.
I'm not sure requestLocationUpdates() works in viewmodel because callback method onLocationChanged() is not working.
I'm new on android so I don't know what's wrong in here.
Because there was no error on this code.
I guess the error is occuring because callback method onLocationChanged() is not getting called at
ViewModel.
How can make this work??
This is my code.
ViewModel Class
public class MAgencyViewModel extends ViewModel implements LocationListener {
private final String TAG = "MAgencyViewModel";
//이 클래스에서는 Model과 통신하여서 날씨 정보를 받아온다.
private MutableLiveData<ShortWeather> sw;
private MAgencyRepo maRepo;
private GeoInfo gi;
private GpsTransfer gpt;
// public void init(GeoInfo gi) {
// if (sw != null) {
// return;
// }
//
// maRepo = MAgencyRepo.getInStance();
// sw = maRepo.getWeather(gi); // this part is calling a weather api
// Log.i(TAG, "API Connection finish");
// }
public LiveData<ShortWeather> getWeather() {
return sw;
}
#SuppressLint("MissingPermission")
//이부분은 권한 재확인 안하게 해주는 부분이다. 따로 재확인을 안하는 이유는 Activity단에서 이미 확인을 거쳤기 때문이다.
public void locationUpdate(LocationManager lm) {
Log.i(TAG, "locationUpdate()");
// lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0,this); //위치정보를 update하는 함수 이건 실제 기기용
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); //After this nothing is happening
}
//여기서는 이제 위치 정보가 변경 되었을때 진입하는 부분
#Override
public void onLocationChanged(Location location) { //This CallBack method is not working
Log.i(TAG, "onLocationChanged()");
}
//위치 정보 이용 권한 허가를 받지 못했을떄 호출 하는 부분
public void defaultLocation() {
//GpsTransfer 객체 생성
gpt = new GpsTransfer();
//GpsTransfer 위도와 경도를 원주로 설정
gpt.setxLat(76);
gpt.setyLon(122);
gpt.transfer(gpt, 1);
gi = new GeoInfo();
gi.setLon(gpt.getyLon());
gi.setLat(gpt.getxLat());
getTime();
if (sw != null) {
return;
}
//해당 정보를 API를 호출
maRepo = MAgencyRepo.getInStance();
sw = maRepo.getWeather(gi); // this part is calling a weather api
Log.i(TAG, "API Connection finish");
}
public void getTime() {
SimpleDateFormat dateSdf = new SimpleDateFormat("yyyyMMdd"); //년월일 받아오는 부분
SimpleDateFormat timeSdf = new SimpleDateFormat("HH"); //현재시간 받아오는 부분
Calendar cal = Calendar.getInstance(); //현재시간을 받아온다.
gi.setNowDate(dateSdf.format(cal.getTime())); //날짜 세팅
gi.setNowTime(timeSdf.format(cal.getTime())); //시간 세팅
/*
* 하루 전체의 기상예보를 받아오려면 전날 23시에 266개의 날씨정보를 호출해와야 한다.
* 따라서 호출 값은 현재 날짜보다 1일전으로 세팅해줘야 한다.
* */
cal.add(Calendar.DATE, -1); //1일전 날짜를 구하기 위해 현재 날짜에서 -1 시켜주는 부분
gi.setCallDate(dateSdf.format(cal.getTime())); //1일전 값으로 호출값 생성
Log.i(TAG, "DATE : " + gi.getNowDate());
Log.i(TAG, "TIME : " + gi.getNowTime());
Log.i(TAG, "CALL DATE : " + gi.getCallDate());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
In you view model
public class MAgencyViewModel extends AndroidViewModel {
private LocationCallback locationCallback;
public boolean requestingLocationUpdates;
public MAgencyViewModel(Application app) {
super(app);
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
// Update UI with location data
// ...
}
}
};
}
public void startLocationUpdates() {
requestingLocationUpdates = true
LocationServices.getFusedLocationProviderClient(getApplication())
.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper());
}
public void stopLocationUpdates() {
requestingLocationUpdates = false
LocationServices.getFusedLocationProviderClient(getApplication())
.removeLocationUpdates(locationCallback);
}
}
Now in your Fragment/Activity override on pause and onresume
Then call the startlocation and stoplction in your viewmodel
#Override
protected void onPause() {
super.onPause();
viewModel.stopLocationUpdates();
}
#Override
protected void onResume() {
super.onResume();
if (viewModel.requestingLocationUpdates) {
startLocationUpdates();
}
Notice setting and checking boolean requestingLocationUpdates if locationRequest is active
onDestroy() is called when press home button in video call using agora.io .
I have 2 project one is for user and other for astrologer. In User app everything in working fine but in astrologer app onDestroy is calling whenever I press the home button.
This is refernce for agora
https://github.com/AgoraIO/Basic-Video-Call/tree/master/One-to-One-Video/Agora-Android-Tutorial-1to1
public class VideoChatViewActivity extends AppCompatActivity {
private static final String TAG = VideoChatViewActivity.class.getSimpleName();
private static final int PERMISSION_REQ_ID = 22;
// Permission WRITE_EXTERNAL_STORAGE is not mandatory
// for Agora RTC SDK, just in case if you wanna save
// logs to external sdcard.
private static final String[] REQUESTED_PERMISSIONS = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private RtcEngine mRtcEngine;
private boolean mCallEnd;
private boolean mMuted;
private FrameLayout mLocalContainer;
private RelativeLayout mRemoteContainer;
private SurfaceView mLocalView;
private SurfaceView mRemoteView;
private ImageView mCallBtn;
private ImageView mMuteBtn;
private ImageView mSwitchCameraBtn;
// Customized logger view
/**
* Event handler registered into RTC engine for RTC callbacks.
* Note that UI operations needs to be in UI thread because RTC
* engine deals with the events in a separate thread.
*/
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
#Override
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("TAG", "Join channel success, uid: " + (uid & 0xFFFFFFFFL));
}
});
}
#Override
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("TAG", "First remote video decoded, uid: " + (uid & 0xFFFFFFFFL));
setupRemoteVideo(uid);
}
});
}
#Override
public void onUserOffline(final int uid, int reason) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("TAG", "User offline, uid: " + (uid & 0xFFFFFFFFL));
onRemoteUserLeft();
}
});
}
};
private void setupRemoteVideo(int uid) {
// Only one remote video view is available for this
// tutorial. Here we check if there exists a surface
// view tagged as this uid.
int count = mRemoteContainer.getChildCount();
View view = null;
for (int i = 0; i < count; i++) {
View v = mRemoteContainer.getChildAt(i);
if (v.getTag() instanceof Integer && ((int) v.getTag()) == uid) {
view = v;
}
}
if (view != null) {
return;
}
mRemoteView = RtcEngine.CreateRendererView(getBaseContext());
mRemoteContainer.addView(mRemoteView);
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid));
mRemoteView.setTag(uid);
}
private void onRemoteUserLeft() {
removeRemoteVideo();
}
private void removeRemoteVideo() {
if (mRemoteView != null) {
mRemoteContainer.removeView(mRemoteView);
}
mRemoteView = null;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_chat_view);
initUI();
// Ask for permissions at runtime.
// This is just an example set of permissions. Other permissions
// may be needed, and please refer to our online documents.
if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
initEngineAndJoinChannel();
}
}
private void initUI() {
mLocalContainer = findViewById(R.id.local_video_view_container);
mRemoteContainer = findViewById(R.id.remote_video_view_container);
mCallBtn = findViewById(R.id.btn_call);
mMuteBtn = findViewById(R.id.btn_mute);
mSwitchCameraBtn = findViewById(R.id.btn_switch_camera);
// Sample logs are optional.
showSampleLogs();
}
private void showSampleLogs() {
Log.e("TAG", "Welcome to Agora 1v1 video call");
}
private boolean checkSelfPermission(String permission, int requestCode) {
if (ContextCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQ_ID) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED ||
grantResults[2] != PackageManager.PERMISSION_GRANTED) {
showLongToast("Need permissions " + Manifest.permission.RECORD_AUDIO +
"/" + Manifest.permission.CAMERA + "/" + Manifest.permission.WRITE_EXTERNAL_STORAGE);
finish();
return;
}
// Here we continue only if all permissions are granted.
// The permissions can also be granted in the system settings manually.
initEngineAndJoinChannel();
}
}
private void showLongToast(final String msg) {
this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
});
}
private void initEngineAndJoinChannel() {
// This is our usual steps for joining
// a channel and starting a call.
initializeEngine();
setupVideoConfig();
setupLocalVideo();
joinChannel();
}
private void initializeEngine() {
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
}
private void setupVideoConfig() {
// In simple use cases, we only need to enable video capturing
// and rendering once at the initialization step.
// Note: audio recording and playing is enabled by default.
mRtcEngine.enableVideo();
// Please go to this page for detailed explanation
// https://docs.agora.io/en/Video/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_rtc_engine.html#af5f4de754e2c1f493096641c5c5c1d8f
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
VideoEncoderConfiguration.VD_640x360,
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
VideoEncoderConfiguration.STANDARD_BITRATE,
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT));
}
private void setupLocalVideo() {
// This is used to set a local preview.
// The steps setting local and remote view are very similar.
// But note that if the local user do not have a uid or do
// not care what the uid is, he can set his uid as ZERO.
// Our server will assign one and return the uid via the event
// handler callback function (onJoinChannelSuccess) after
// joining the channel successfully.
mLocalView = RtcEngine.CreateRendererView(getBaseContext());
mLocalView.setZOrderMediaOverlay(true);
mLocalContainer.addView(mLocalView);
mRtcEngine.setupLocalVideo(new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0));
}
private void joinChannel() {
// 1. Users can only see each other after they join the
// same channel successfully using the same app id.
// 2. One token is only valid for the channel name that
// you use to generate this token.
mRtcEngine.joinChannel(null, getIntent().getExtras().getString("channel_name"), "Extra Optional Data", 0);
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
if (!mCallEnd) {
leaveChannel();
}
RtcEngine.destroy();
}
#Override
protected void onPause() {
super.onPause();
Log.e(TAG, "onPause: ");
}
#Override
protected void onStop() {
super.onStop();
Log.e(TAG, "onStop: ");
}
private void leaveChannel() {
mRtcEngine.leaveChannel();
}
public void onLocalAudioMuteClicked(View view) {
mMuted = !mMuted;
mRtcEngine.muteLocalAudioStream(mMuted);
int res = mMuted ? R.drawable.btn_mute : R.drawable.btn_mute;
mMuteBtn.setImageResource(res);
}
public void onSwitchCameraClicked(View view) {
mRtcEngine.switchCamera();
}
public void onCallClicked(View view) {
if (mCallEnd) {
startCall();
mCallEnd = false;
mCallBtn.setImageResource(R.drawable.btn_end_call);
} else {
endCall();
mCallEnd = true;
mCallBtn.setImageResource(R.drawable.btn_end_call);
}
showButtons(!mCallEnd);
}
private void startCall() {
setupLocalVideo();
joinChannel();
}
private void endCall() {
removeLocalVideo();
removeRemoteVideo();
leaveChannel();
}
private void removeLocalVideo() {
if (mLocalView != null) {
mLocalContainer.removeView(mLocalView);
}
mLocalView = null;
}
private void showButtons(boolean show) {
int visibility = show ? View.VISIBLE : View.GONE;
mMuteBtn.setVisibility(visibility);
mSwitchCameraBtn.setVisibility(visibility);
}
}
Welcome to codding party.
you should implement
onSaveInstanceState
and
onRestoreInstanceState
properly. The framework may kill your Activity at any time it isn't foreground.
other way u can Use Foreground service for keeping alive your call.
I want to implement functionality when an app goes in the background the GPS should automatically Off and when an app in the foreground the GPS should automatically On. I've referred https://stackoverflow.com/a/44668999/9635628 for Enable GPS It's working fine but how can I disable when an app goes in the background?
Please, help me to solve it!
I've tried below code
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
Log.d("App", "App in background")
offGPS()
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
Log.d("App", "App in foreground")
enableLoc()
}
}
private fun enableLoc() {
if (googleApiClient == null) {
googleApiClient = GoogleApiClient.Builder(this#MainActivity)
.addApi(LocationServices.API)
.addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
override fun onConnected(bundle: Bundle?) {
}
override fun onConnectionSuspended(i: Int) {
googleApiClient!!.connect()
}
})
.addOnConnectionFailedListener { connectionResult -> Log.d("Location error", "Location error " + connectionResult.errorCode) }.build()
googleApiClient!!.connect()
val locationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_NO_POWER
locationRequest.interval = (30 * 1000).toLong()
locationRequest.fastestInterval = (5 * 1000).toLong()
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
result.setResultCallback(object : ResultCallback<LocationSettingsResult> {
override fun onResult(result: LocationSettingsResult) {
val status = result.status
when (status.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(this#MainActivity, REQUEST_LOCATION)
// finish()
} catch (e: IntentSender.SendIntentException) {
// Ignore the error.
}
}
}
})
}
}
fun offGPS() {
var provider = Settings.Secure.getString(contentResolver, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
if (provider.contains("gps")) { //if gps is enabled
var poke: Intent = Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
Second solution is
/**
* Method checks if the app is in background or not
*/
fun isAppIsInBackground(context : Context) : Boolean{
var isInBackground = true
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
val runningProcesses = am.runningAppProcesses
for (processInfo in runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (activeProcess in processInfo.pkgList) {
if (activeProcess.equals(context.packageName)) {
isInBackground = false
}
}
}
}
} else {
val taskInfo = am.getRunningTasks(1)
val componentInfo = taskInfo.get(0).topActivity
if (componentInfo.packageName.equals(context.packageName)) {
isInBackground = false
}
}
return isInBackground
}
Try this solution
private void turnGPSOff() {
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(provider.contains("gps")){ //if gps is enabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
Hope this will work for you.
First, in your Application class you should register a detector like this:
registerActivityLifecycleCallbacks(new Detector());
In the detector, which is an instance of Application.ActivityLifecycleCallbacks you should keep a count of activities resumed and paused like this:
/**Detector class**/
#Override
public void onActivityResumed(Activity activity) {
mActivitiesResumed++;
if (mInBackground) {
mInBackground = false;
// App is in background
}
}
#Override
public void onActivityPaused(Activity activity) {
mActivitiesResumed--;
}
#Override
public void onActivityStopped(Activity activity) {
if (mActivitiesResumed == 0) {
mInBackground = true;
// App is in foreground
}
}
And that should be it. I've this class working in production flawlessly.
This is weird. I mean, I have strong network, and GPS is turned On, still it asks for Wifi !!
I'm using new Location Api with class FusedLocationProviderClient.
This is how I am checking if Location Settings are enabled or not:
private void checkIfLocationSettingsAreEnabled() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
SettingsClient client = LocationServices.getSettingsClient(context);
Task<LocationSettingsResponse> locationSettingsResponseTask = client.checkLocationSettings(builder.build());
locationSettingsResponseTask.addOnSuccessListener(locationSettingsResponse -> {
getLastKnownLocation();
});
locationSettingsResponseTask.addOnFailureListener(e -> {
if (e instanceof ResolvableApiException) {
myLocationListener.onResolutionNeeded(e);
} else {
myLocationListener.onLocationFailure(e);
}
});
}
With the above code, I am able to get below image:
But then, after clicking OK, again I make call to checkIfSettingsAreEnabled() this shows another popup as below:
I wonder why enabling Wifi is mandatory, even if I am on desert safari, where there is no Wifi to connect to !!
Is there any way to Skip this Wifi option, and work as normal as Google Maps does ?
In fact, Google Maps uses priority PRIORITY_HIGH_ACCURACY and still once, the first settings dialog has been shown, it doesn't ask second time to turn on Wifi.
FusedLocationProvider uses a combo of GPS and WIFI to get your location. Especially when asking for balanced mode, it needs both or won't work (GPS takes power, so it won't rely just on it for balanced). Try max accuracy, its less likely to use it then. Or just use the GPS provider if you actually want GPS.
PRIORITY_BALANCED_POWER_ACCURACY
This priority will go with coarse level accuracy which fetch location only from wi-fi , cellular network or bluetooth and NOT from GPS.
and if device don't have SIM it needs to have wi-fi to get location or it will not call onLocationChanged till won't get location.
With the below solution, I am able to skip this Wifi Settings Dialog. This is not a perfect solution, but just a workaround till I find perfect solution.
Problem was, even after clicking "Ok" on Settings Dialog, it returned result as Activity.RESULT_CANCELLED. Reason was, Wifi was still turned off. So, as a quick fix, made a check in result Activity.RESULT_CANCELLED, that if I could get Location by checking LocationSettingStates.
You can use my MyLocationUtils.java and Base classes LocationActivity.java or LocationFragment.java. Happy Coding..!!
for getting location
MyLocationUtils.java:
public class MyLocationUtils {
public static final int REQUEST_CODE_LOCATION_SETTINGS = 123;
private static final int INTERVAL_IN_MS = 10000;
private static final int FASTEST_INTERVAL_IN_MS = 5000;
private static final int MAX_WAIT_TIME_IN_MS = 5000;
private static final int NUMBER_OF_UPDATES = 1;
private final MyLocationListener myLocationListener;
private final Context context;
private FusedLocationProviderClient mFusedLocationProviderClient;
private LocationCallback mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Location location = null;
if (locationResult.getLocations().size() > 0) {
location = locationResult.getLocations().get(0);
}
myLocationListener.onLocationSuccess(location);
stopContinuousLocation();
}
};
public MyLocationUtils(Context context, MyLocationListener myLocationListener) {
this.context = context;
this.myLocationListener = myLocationListener;
initializeFusedLocationProviderClient();
}
private boolean checkIfRequiredLocationSettingsAreEnabled(Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
}
private void initializeFusedLocationProviderClient() {
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
}
public void stopContinuousLocation() {
mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
}
public void getLocation() {
checkIfLocationSettingsAreEnabled();
}
private void checkIfLocationSettingsAreEnabled() {
if (checkIfRequiredLocationSettingsAreEnabled(context)) {
getLastKnownLocation();
} else {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(getLocationRequest());
builder.setAlwaysShow(true);
SettingsClient client = LocationServices.getSettingsClient(context);
Task<LocationSettingsResponse> locationSettingsResponseTask = client.checkLocationSettings(builder.build());
locationSettingsResponseTask.addOnSuccessListener(locationSettingsResponse -> {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
getLastKnownLocation();
});
locationSettingsResponseTask.addOnFailureListener(e -> {
if (e instanceof ResolvableApiException) {
myLocationListener.onResolutionNeeded(e);
} else {
myLocationListener.onLocationFailure(e);
}
});
}
}
#SuppressLint("MissingPermission")
private void getLastKnownLocation() {
Task<Location> locationTask = mFusedLocationProviderClient.getLastLocation();
locationTask.addOnSuccessListener(location -> {
// Got last known location. In some rare situations this can be null.
if (location != null) {
myLocationListener.onLocationSuccess(location);
} else {
startContinuousLocation();
}
});
locationTask.addOnFailureListener(myLocationListener::onLocationFailure);
}
#SuppressLint("MissingPermission")
private void startContinuousLocation() {
mFusedLocationProviderClient.requestLocationUpdates(getLocationRequest(), mLocationCallback, Looper.getMainLooper())
.addOnFailureListener(myLocationListener::onLocationFailure);
}
private LocationRequest getLocationRequest() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL_IN_MS);
locationRequest.setFastestInterval(FASTEST_INTERVAL_IN_MS);
locationRequest.setNumUpdates(NUMBER_OF_UPDATES);
locationRequest.setMaxWaitTime(MAX_WAIT_TIME_IN_MS);
return locationRequest;
}
public void resolveLocationSettings(FragmentActivity fragmentActivity, Exception exception) {
ResolvableApiException resolvable = (ResolvableApiException) exception;
try {
resolvable.startResolutionForResult(fragmentActivity, MyLocationUtils.REQUEST_CODE_LOCATION_SETTINGS);
} catch (IntentSender.SendIntentException e1) {
e1.printStackTrace();
}
}
public interface MyLocationListener {
void onLocationSuccess(Location location);
void onResolutionNeeded(Exception exception);
void onLocationFailure(Exception exception);
}
}
BaseLocationActivity.java:
public abstract class LocationActivity extends AppCompatActivity {
private MyLocationUtils myLocationUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myLocationUtils = new MyLocationUtils(this, new MyLocationUtils.MyLocationListener() {
#Override
public void onLocationSuccess(Location location) {
if (shouldBeAllowedToProceed())
LocationActivity.this.onLocationSuccess(location);
}
#Override
public void onResolutionNeeded(Exception exception) {
exception.printStackTrace();
if (shouldBeAllowedToProceed())
LocationActivity.this.onResolutionNeeded(exception);
}
#Override
public void onLocationFailure(Exception exception) {
exception.printStackTrace();
if (shouldBeAllowedToProceed())
LocationActivity.this.onLocationFailure(exception);
}
});
}
protected void getLocation() {
myLocationUtils.getLocation();
}
protected void resolveLocationSettings(Exception exception) {
myLocationUtils.resolveLocationSettings(this, exception);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MyLocationUtils.REQUEST_CODE_LOCATION_SETTINGS) {
final LocationSettingsStates locationSettingsStates = LocationSettingsStates.fromIntent(data);
switch (resultCode) {
case Activity.RESULT_OK:
onResolveLocationSettingOk();
break;
case Activity.RESULT_CANCELED:
// The user was asked to change settings, but chose not to,
// or on Android Oreo 8.1 Wifi Settings were not satisfied inspite of clicking OK, that results on Activity.RESULT_CANCELED
onResolveLocationSettingCancelled(locationSettingsStates);
break;
default:
break;
}
}
}
protected abstract void onResolveLocationSettingOk();
protected void onResolveLocationSettingCancelled(LocationSettingsStates locationSettingsStates) {
if (locationSettingsStates.isLocationPresent() && locationSettingsStates.isLocationUsable()) {
onResolveLocationSettingOk();
}
}
private boolean shouldBeAllowedToProceed() {
return getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED);
}
public abstract void onLocationSuccess(Location location);
public abstract void onResolutionNeeded(Exception exception);
public abstract void onLocationFailure(Exception exception);
#Override
public void onDestroy() {
super.onDestroy();
myLocationUtils.stopContinuousLocation();
}
}
LocationFragment.java:
public abstract class LocationFragment extends Fragment {
private MyLocationUtils myLocationUtils;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myLocationUtils = new MyLocationUtils(context, new MyLocationUtils.MyLocationListener() {
#Override
public void onLocationSuccess(Location location) {
if (shouldBeAllowedToProceed())
LocationFragment.this.onLocationSuccess(location);
}
#Override
public void onResolutionNeeded(Exception exception) {
exception.printStackTrace();
if (shouldBeAllowedToProceed())
LocationFragment.this.onResolutionNeeded(exception);
}
#Override
public void onLocationFailure(Exception exception) {
exception.printStackTrace();
if (shouldBeAllowedToProceed())
LocationFragment.this.onLocationFailure(exception);
}
});
}
protected void resolveLocationSettings(FragmentActivity appCompatActivity, Exception exception) {
myLocationUtils.resolveLocationSettings(appCompatActivity, exception);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MyLocationUtils.REQUEST_CODE_LOCATION_SETTINGS) {
final LocationSettingsStates locationSettingsStates = LocationSettingsStates.fromIntent(data);
switch (resultCode) {
case Activity.RESULT_OK:
onResolveLocationSettingOk();
break;
case Activity.RESULT_CANCELED:
// The user was asked to change settings, but chose not to
onResolveLocationSettingCancelled(locationSettingsStates);
break;
default:
break;
}
}
}
protected abstract void onResolveLocationSettingOk();
protected void onResolveLocationSettingCancelled(LocationSettingsStates locationSettingsStates) {
if (locationSettingsStates.isLocationPresent() && locationSettingsStates.isLocationUsable()) {
onResolveLocationSettingOk();
}
}
public void getLocation() {
myLocationUtils.getLocation();
}
private boolean shouldBeAllowedToProceed() {
return getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED);
}
public abstract void onLocationSuccess(Location location);
public abstract void onResolutionNeeded(Exception exception);
public abstract void onLocationFailure(Exception exception);
#Override
public void onDestroy() {
super.onDestroy();
myLocationUtils.stopContinuousLocation();
}
}
Does anyone know of an example of using the LocationServices.GeofencingApi?
All the android geofencing examples I find are using the deprecated LocationClient class.
From what I can see, the LocationServices class is the one to use, but there doesn't seem to be any working examples on how to use it.
The closest I've found is this post highlighting location update requests
UPDATE: The closest answer I've found is this git example project - but it still uses the deprecated LocationClient to get triggered fences.
I just migrated my code to the new API. Here is a working example:
A working project on GitHub based on this answer: https://github.com/androidfu/GeofenceExample
This helper class registers the geofences using the API. I use a callback interface to communicate with the calling activity/fragment. You can build a callback that fits your needs.
public class GeofencingRegisterer implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private Context mContext;
private GoogleApiClient mGoogleApiClient;
private List<Geofence> geofencesToAdd;
private PendingIntent mGeofencePendingIntent;
private GeofencingRegistererCallbacks mCallback;
public final String TAG = this.getClass().getName();
public GeofencingRegisterer(Context context){
mContext =context;
}
public void setGeofencingCallback(GeofencingRegistererCallbacks callback){
mCallback = callback;
}
public void registerGeofences(List<Geofence> geofences){
geofencesToAdd = geofences;
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
if(mCallback != null){
mCallback.onApiClientConnected();
}
mGeofencePendingIntent = createRequestPendingIntent();
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, geofencesToAdd, mGeofencePendingIntent);
result.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
// Successfully registered
if(mCallback != null){
mCallback.onGeofencesRegisteredSuccessful();
}
} else if (status.hasResolution()) {
// Google provides a way to fix the issue
/*
status.startResolutionForResult(
mContext, // your current activity used to receive the result
RESULT_CODE); // the result code you'll look for in your
// onActivityResult method to retry registering
*/
} else {
// No recovery. Weep softly or inform the user.
Log.e(TAG, "Registering failed: " + status.getStatusMessage());
}
}
});
}
#Override
public void onConnectionSuspended(int i) {
if(mCallback != null){
mCallback.onApiClientSuspended();
}
Log.e(TAG, "onConnectionSuspended: " + i);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if(mCallback != null){
mCallback.onApiClientConnectionFailed(connectionResult);
}
Log.e(TAG, "onConnectionFailed: " + connectionResult.getErrorCode());
}
/**
* Returns the current PendingIntent to the caller.
*
* #return The PendingIntent used to create the current set of geofences
*/
public PendingIntent getRequestPendingIntent() {
return createRequestPendingIntent();
}
/**
* Get a PendingIntent to send with the request to add Geofences. Location
* Services issues the Intent inside this PendingIntent whenever a geofence
* transition occurs for the current list of geofences.
*
* #return A PendingIntent for the IntentService that handles geofence
* transitions.
*/
private PendingIntent createRequestPendingIntent() {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
} else {
Intent intent = new Intent(mContext, GeofencingReceiver.class);
return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
}
This class is the base class for your geofence transition receiver.
public abstract class ReceiveGeofenceTransitionIntentService extends IntentService {
/**
* Sets an identifier for this class' background thread
*/
public ReceiveGeofenceTransitionIntentService() {
super("ReceiveGeofenceTransitionIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
if(event != null){
if(event.hasError()){
onError(event.getErrorCode());
} else {
int transition = event.getGeofenceTransition();
if(transition == Geofence.GEOFENCE_TRANSITION_ENTER || transition == Geofence.GEOFENCE_TRANSITION_DWELL || transition == Geofence.GEOFENCE_TRANSITION_EXIT){
String[] geofenceIds = new String[event.getTriggeringGeofences().size()];
for (int index = 0; index < event.getTriggeringGeofences().size(); index++) {
geofenceIds[index] = event.getTriggeringGeofences().get(index).getRequestId();
}
if (transition == Geofence.GEOFENCE_TRANSITION_ENTER || transition == Geofence.GEOFENCE_TRANSITION_DWELL) {
onEnteredGeofences(geofenceIds);
} else if (transition == Geofence.GEOFENCE_TRANSITION_EXIT) {
onExitedGeofences(geofenceIds);
}
}
}
}
}
protected abstract void onEnteredGeofences(String[] geofenceIds);
protected abstract void onExitedGeofences(String[] geofenceIds);
protected abstract void onError(int errorCode);
}
This class implements the abstract class and does all the handling of geofence transitions
public class GeofencingReceiver extends ReceiveGeofenceTransitionIntentService {
#Override
protected void onEnteredGeofences(String[] geofenceIds) {
Log.d(GeofencingReceiver.class.getName(), "onEnter");
}
#Override
protected void onExitedGeofences(String[] geofenceIds) {
Log.d(GeofencingReceiver.class.getName(), "onExit");
}
#Override
protected void onError(int errorCode) {
Log.e(GeofencingReceiver.class.getName(), "Error: " + i);
}
}
And in your manifest add:
<service
android:name="**xxxxxxx**.GeofencingReceiver"
android:exported="true"
android:label="#string/app_name" >
</service>
Callback Interface
public interface GeofencingRegistererCallbacks {
public void onApiClientConnected();
public void onApiClientSuspended();
public void onApiClientConnectionFailed(ConnectionResult connectionResult);
public void onGeofencesRegisteredSuccessful();
}