I have an app that do BLE scan on the background and alert the user by playing a sound and shows up a popup with a button to snooze. So if the user click on that button the app will stop playing the alert sound and dismiss the popup for a while (default duration is one minute)
So if the app in background mode it still works and can play the alert sound and then when I reopen the app by clicking on the service notification it shows the popup
So I'm calculating the spent time after the user click on dismiss button on a separate thread using some variables in the viewModel because I need them in other place.
Here is my code
Thread {
while (true) {
val previous = tagDetailsAlertsViewModel.stopAlertingAt.value
val durationMin = PreferenceManager.getDefaultSharedPreferences(this).getString("dismiss_duration", "1")!!.toInt()
val diff = (Date().time - previous!!.time) / 1000 / 60
if ( diff >= durationMin && tagDetailsAlertsViewModel.isStopAlerting() ) {
tagDetailsAlertsViewModel.setStopAlerting(false)
}
}
}.start()
The code above is to check always if the duration past to alert again the user
binding.btnDismiss.setOnClickListener {
tagDetailsAlertsViewModel.stopAlertingAt.value = Date()
tagDetailsAlertsViewModel.setStopAlerting(true)
}
So the problem here is when I close the app and open it again the line code below will point always on the last value before I close the app
val previous = tagDetailsAlertsViewModel.stopAlertingAt.value
But here it change correct the value
tagDetailsAlertsViewModel.stopAlertingAt.value = Date()
So if I understood correctly is that the background thread still use the mode ViewModel with the old value and I can't figure out how to fix that.
I'm using Koin Module for my viewModels
Related
I am trying to write some UI tests with UIAutomator on Android. I need to use UIAutomator to perform the following actions:
Start the app, wait for the page to be fully loaded.
Click the button to go to another activity. --> This is where I am stuck, I am trying to make it wait until another activity finish rendering (and wait for step 3).
Perform UI tests on the designated activity.
Could anyone please give me an example ? Thanks!
UiDevice & UiObject2 tests classes offer arbitrary timeout wait period associated to the matching condition before proceeding in tests.
Notice that the timeout value is the maximum amount of time to wait in milliseconds before declaring that the condition is not met; so it doesn't sleep your test until the timeout period expires; instead it tries to match the condition (finding a matched component for instance) and once the condition is met, the test continues without waiting the expiration of the timeout value.
So, you can assign an arbitrary value that can make sure that the test succeeds. So, it's safe if you want to set it to Long.MAX_VALUE; but make sure that should succeed to avoid ANR. In the below example, I am using 500 milliseconds.
For instance to launch an app, and wait for it to appear as per documentation:
var device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
....
// Wait for the app to appear
device.wait(
Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), // condition
LAUNCH_TIMEOUT // timeout
)
}
Similarly, you can wait/timeout until you can find the target component at the second activity, before proceeding in the test; something like:
val someView = mDevice.wait(
Until.findObject(
By.res( // find object by resource id
BASIC_SAMPLE_PACKAGE, // application package name
"myViewId" // id of the view in the second activity
)
),
500) /* wait 500ms */
So, in your test that you are trying to do, you need to:
Use the #Before test method (that precedes any test) to make sure the app is launched and its main actvivity is shown:
private lateinit var mDevice: UiDevice
private val BASIC_SAMPLE_PACKAGE = "com.example.android......" // change this to your app's package name
private val LAUNCH_TIMEOUT = 5000L
#Before
fun startMainActivityFromHomeScreen() {
// Initialize UiDevice instance
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Start from the home screen
mDevice.pressHome()
// Wait for launcher
val launcherPackage = getLauncherPackageName()
assertThat(launcherPackage, CoreMatchers.notNullValue())
mDevice.wait(
Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT
)
// Launch the blueprint app
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager
.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE)
intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) // Clear out any previous instances
context.startActivity(intent)
// Wait for the app to appear
mDevice.wait(
Until.hasObject(
By.pkg(BASIC_SAMPLE_PACKAGE)
.depth(0)
),
LAUNCH_TIMEOUT
)
}
Then to press on a button to launch the second activity:
// searching for a UI component with a resource Id btn_goto_second
val secondActivityButton = mDevice.wait(
Until.findObject(
By.res(
BASIC_SAMPLE_PACKAGE,
"btn_goto_second" // change to your button id
)
),
500 /* wait 500ms */
)
// Perform a click on the button to load the second activity.
secondActivityButton.click()
Then to enter some text to an EditText at the second activity; you can just wait until you find this view; and then type some text:
// searching for the EditText component with a resource Id edit_text
val editText = mDevice.wait(
Until.findObject(
By.res(
BASIC_SAMPLE_PACKAGE,
"edit_text" // change this to yoru editText id
)
),
500 /* wait 500ms */
)
// Set the text to the EditText
editText.text = "some text"
And to verify the text result, use one of the assertion methods:
assertThat(
editText.text,
CoreMatchers.`is`(equalTo("some text"))
)
You can check the documentation for further help; or refer to their sample app. Also this is a nice repo that would help.
Do you have any ideas on how to solve in an android long time of waiting on server response?
I am making a request to the API saving profile.
This request is fast
But the server is processing it rather long 10sec to 3min (like scanning cheap flight on some tickets sites)
After the response, I need to redirect the user to a confirmation screen or home screen of the app.
I solved this by setting the longer timeout to 45 sec and then always redirecting to confirmation + alert that it takes longer.
On the home screen, I am displaying "Processing..." label until the server finishes
But this solution has some problems like:
what about user going out by home button if it takes longer, or switching applications, and if just display goes to sleep while untouched more than 30sec? Then activity/fragment is recreated and response seems to not arrive.
I consider adding push notification when processing is done this could help a little. Is there any way to solve such an issue? Maybe some background Service? But isn't Android Services deprecated? I think only Foreground Services are valid to use, or maybe new WorkManagers (but this doesn't seem to fit this scenario). And how from then wakeup screen and move it to the next page.
Code sample:
// Fragment
viewModel.saveData(data)
// View Model
fun saveData(data: Data) : LiveData<Resource<DataResponse>> {
_dataEvent.postValue(Event(Resource.loading(null)))
val apiSource = dataRepo.saveData(data)
_dataEvent.addSource(apiSource) { resource ->
_dataEvent.removeSource(apiSource)
val resource = resource ?: Resource.error(null, null)
_dataEvent.postValue(Event(resource))
}
return apiSource
}
// Observing Data Event
viewModel.dataEvent.observe(this,
Observer { event ->
if(event?.peekContent()?.status == Resource.Status.LOADING) {
showProgressAlert(context)
event.getContentIfNotHandled() // consume loading event
}
val resource = event?.getContentIfNotHandled()
if(resource != null) {
hideProgressAlert()
if (resource.status == Resource.Status.SUCCESS) {
showSuccessAlert(context)
navigateToConfirmPage()
} else if (resource.status == Resource.Status.ERROR) {
if (throwable is SocketTimeoutException) {
showTimeoutAlert(context)
navigateToConfirmPage()
} else {
showErrorAlert(context)
}
}
}
If this request is taking a long time then you should perform it in a background thread, Since android oreo background services became very restricted if you want to implement it you can use JobIntentService or JobScheduler or make it a ForegroundService but in this case you will have to show a notification to the user while the service is running, You can read more about it here https://developer.android.com/about/versions/oreo/background
Another approach is to use RXjava which handles threading and perform background services very smoothly
want create app cordova and ionic for android to alaram in some times like 2:50 and i want program run even close the program
i use katzer/cordova-plugin-background-mode plugin , but when i close program , it be close and dont still run in background this is code
// Run when the device is ready
document.addEventListener('deviceready', function () {
// Android customization
// To indicate that the app is executing tasks in background and being paused would disrupt the user.
// The plug-in has to create a notification while in background - like a download progress bar.
cordova.plugins.backgroundMode.setDefaults({
title: 'TheTitleOfYourProcess',
text: 'Executing background tasks.'
});
// Enable background mode
cordova.plugins.backgroundMode.enable();
// Called when background mode has been activated
cordova.plugins.backgroundMode.onactivate = function () {
// Set an interval of 3 seconds (3000 milliseconds)
setInterval(function () {
var dd = new date();
var h = dd.getHours();
var m = dd.getMinutes();
if(h == 2 && m ==50){
alert("2:50");
}
}, 3000);
}
}, false);
how can i make app still run even user close the program like , alarm clock or some program reminder ?
Have you tried:
http://developer.android.com/guide/components/services.html
this might be helpful
I'm trying to implement a convenient-to-use system for handling status bar notifications for android, and i was thinking about the following:
Create a database, where i store when and what to show
Create a service what runs in the background using the 'interval' Service, what the API provides
In that service check if any notification needs to be shown according to the database, then show it.
The only problem is, that, i cannot detect, if i need to start the service or not. I tried these things, but none of them worked well so far:
1.) Save if the service was already started on the local storage:
// Do this on application startup
var isRunning = Ti.App.Properties.getBool("service_running", false);
if(!isRunning)
{
var service = Titanium.Android.createService(...);
service.addEventListener('start', function()
{
Ti.App.Properties.setBool("service_running", true);
});
service.addEventListener('stop', function()
{
Ti.App.Properties.setBool("service_running", false);
});
service.start();
}
This obviously won't work, because the android systems native onStop and onDestroy events will not be dispatched, if the Service doesn't terminates unusually (like the user force stops the app), so the stop event also won't be fired.
2.) Try to access any active service via Titanium.Android.getCurrentService(), but i got an error saying Titanium.Android has no method called getCurrentService(). This is pretty strange, because the IDEs code completion offered me this method.
3.) Use an Intent to clear the previously running Service
var intent = Titanium.Android.createServiceIntent
(
{
url : 'notification/NotificationService.js'
}
);
intent.putExtra('interval', 1000 * 60);
//Stop if needed
Titanium.Android.stopService(intent);
//Then start it
Titanium.Android.startService(intent);
But it seems like i need to have the same instance of Intent, that started the service to stop it, because doing this on application startup, then exiting and restaring it results in multiple Services to run.
At this point i ran out of ideas, on how to check for running services. Please if you know about any way to do this, let me know! Thanks for any hints!
EDIT
Here are the source materials which gave me the idea to try the above methods (maybe only i use them incorrectly):
The local storage: Titanium.App.Properties
The method for accessing running services: Titanium.Android.getCurrentService
The method for stoping a service with an Intent: Titanium.Android.stopService
And the full source for the NotificationHandler "class" and NotificationService.js that I wrote, and their usage: link
Use Bencoding AlarmManager and it will provide all you need to schedule an alarm notification : https://github.com/benbahrenburg/benCoding.AlarmManager
This module provides what you need. It's really easy - just set repeat to daily when sheduling a Notification or Service.
Refer https://gist.github.com/itsamiths/6248106 for fully functional code
I am checking if the service is started then show daily notification or else start service and then show daily notification
var isRunning = Ti.App.Properties.getBool("service_running", false);//get service running bool status
if (isRunning) {
Ti.API.info('service is running');
} else {
Ti.API.info('service is not running');
alarmManager.addAlarmService({
service : 'com.mkamithkumar.whatstoday.DailyEventNotificatoinService',
hour : "08",
repeat : 'daily'
});
}
I come one year late, but maybe this can help others in the future.
We had the same idea: run the service forever and do the checks on every cycle (I must check 20 different communications).
And I had the same problem: how to detect that the service is running, to don't run again to don't duplicate the checks.
To solve that problem, what I did is get the current time on every cycle and save it to store.
Then, before launch a new service, I check if the last execution was to far in time: if true, then the service was stopped, else is running.
Not very elegant, but was the only way I found to avoid the problem of the user killing the app (and the service).
This is my code for the "launcher" of the service. In my case, I test 30 seconds far away:
exports.createAndroidServiceForNotifications = function(seconds) {
var moment = require('alloy/moment');
var diffSeconds = moment().diff(Ti.App.Properties.getString('serviceLastRun', new Date().getTime() - 60000), 'second');
if (diffSeconds > 30) {
var now = new Date().getTime();
var delta = new Date(now + (seconds * 1000));
var deltaMS = delta - now;
var intent = Ti.Android.createServiceIntent({
url : 'notificationsService.js'
});
intent.putExtra('interval', deltaMS);
Ti.Android.startService(intent);
}
};
I have a jQuery Mobile web app which targets iOS and Android devices. A component of the application is a background task, which periodically checks for a.) changes to local data and b.) connectivity to the server. If both are true, the task pushes the changes.
I'm using a simple setTimeout()-based function to execute this task. Each failure or success condition calls setTimeout() on the background task, ensuring that it runs on 30 second intervals. I update a status div with the timestamp of the last task runtime for debugging purposes.
In any desktop browser, this works just fine; however, on iOS or Android, after some period of time, the task stops executing. I'm wondering if this is related to the power conservation settings of the devices--when iOS enters stand-by, does it terminate JavaScript execution? That is what appears to happen.
If so, what is the best way to resume? Is there an on-wake event which I can hook into? If not, what other options are there which don't involve hooking into events dependent on user interaction (I don't want to bind the entire page to a click event just to restart the background task).
Looks like Javascript execution is paused on MobileSafari when the browser page isn't focused. It also seems if setInterval() events are late, they are simply fired as soon as the browser is focused. This means we should be able to keep a setInterval() running, and assume the browser lost/regained focus if the setInterval function took much longer than usual.
This code alerts after switching back from a browser tab, after switching back from another app, and after resuming from sleep. If you set your threshold a bit longer than your setTimeout(), you can assume your timeout wouldn't finish if this fires.
If you wanted to stay on the safe side: you could save your timeout ID (returned by setTimeout) and set this to a shorter threshold than your timeout, then run clearTimeout() and setTimeout() again if this fires.
<script type="text/javascript">
var lastCheck = 0;
function sleepCheck() {
var now = new Date().getTime();
var diff = now - lastCheck;
if (diff > 3000) {
alert('took ' + diff + 'ms');
}
lastCheck = now;
}
window.onload = function() {
lastCheck = new Date().getTime();
setInterval(sleepCheck, 1000);
}
</script>
Edit: It appears this can sometimes trigger more than once in a row on resume, so you'd need to handle that somehow. (After letting my android browser sleep all night, it woke up to two alert()s. I bet Javascript got resumed at some arbitrary time before fully sleeping.)
I tested on Android 2.2 and the latest iOS - they both alert as soon as you resume from sleep.
When the user switches to another app or the screen sleeps, timers seem to pause until the user switches back to the app (or when the screen awakens).
Phonegap has a resume event you can listen to instead of polling for state (as well as a pause event if you want to do things before it is out of focus). You start listening to it after deviceReady fires.
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
I use angular with phonegap and I have a service implemented that manages a certain timeout for me but basically you could create an object that sets the timer, cancels the timer and most importantly, updates the timer (update is what is called during the 'resume' event).
In angular I have a scopes and root scope that I can attach data to, my timeout is global so I attach it to root scope but for the purpose of this example, I'll simply attach it to the document object. I don't condone that because you need should apply it to some sort of scope or namespace.
var timeoutManager = function () {
return {
setTimer: function (expiresMsecs) {
document.timerData = {
timerId: setTimeout(function () {
timeoutCallback();
},
expiresMsecs),
totalDurationMsecs: expiresMsecs,
expirationDate: new Date(Date.now() += expiresMsecs)
};
},
updateTimer: function () {
if (document.timerData) {
//
// Calculate the msecs remaining so it can be used to set a new timer.
//
var timerMsecs = document.timerData.expirationDate - new Date();
//
// Kill the previous timer because a new one needs to be set or the callback
// needs to be fired.
//
this.cancelTimer();
if (timerMsecs > 0) {
this.setTimer(timerMsecs);
} else {
timeoutCallback();
}
}
},
cancelTimer: function () {
if (document.timerData && document.timerData.timerId) {
clearTimeout(document.timerData.timerId);
document.timerData = null;
}
}
};
};
You could have the manager function take a millisecond parameter instead of passing it into set, but again this is modeled somewhat after the angular service I wrote. The operations should be clear and concise enough to do something with them and add them to your own app.
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
You will want to update the timer once you get the resume event in your event listener, like so:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
The timeout manager also has cancelTimer() which can be used to kill the timer at any time.
You can use this class github.com/mustafah/background-timer based on #jlafay answer , where you can use as follow:
coffeescript
timer = new BackgroundTimer 10 * 1000, ->
# This callback will be called after 10 seconds
console.log 'finished'
timer.enableTicking 1000, (remaining) ->
# This callback will get called every second (1000 millisecond) till the timer ends
console.log remaining
timer.start()
javascript
timer = new BackgroundTimer(10 * 1000, function() {
// This callback will be called after 10 seconds
console.log("finished");
});
timer.enableTicking(1000, function(remaining) {
// This callback will get called every second (1000 millisecond) till the timer ends
console.log(remaining);
});
timer.start();
Hope it helps, Thank you ...
You should use the Page Visibility API (MDN) which is supported just about everywhere. It can detect if a page or tab has become visible again and you can then resume your timeouts or carry out some actions.