Use CMSensorRecorder to continuously collect Accelerometer data in Apple Watch - background

I want to use CMSensorRecorder to continuously collect the Accelerometer data, also if user didn't open my app on watch\phone.
What I want to do is - "whenever possible" (i.e. watch is awake and I can execute code), do the following:
call recordAccelerometerForDuration to tell watch to keep collecting data for as long as possible
call accelerometerDataFromDate to get data collected so far (or from last time I got the data)
My question is - how to implement the "whenever possible", i.e. how can I cause my watch application to wake up and execute these apis whenever the watch itself wakes up ?

CMSensorRecorder- to recored data continuously use ExtentionDelegate to triger CMSensorRecorder to invoke start recording and reading data.
func applicationDidBecomeActive() {
print("Active")
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
AccelorometerDataReader.sharedReader.sessionEndDate = Date()
AccelorometerDataReader.sharedReader.getRecordedData()
}
func applicationWillResignActive() {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, etc.
print("inactive")
AccelorometerDataReader.sharedReader.startReadingAccelorometerData()
AccelorometerDataReader.sharedReader.sessionStartDate = Date()
}
//AccelorometerReaderCode
func startReadingAccelorometerData()
{
if CMSensorRecorder.isAccelerometerRecordingAvailable()
{
if CMSensorRecorder.isAuthorizedForRecording()
{
print("Authorized.......")
DispatchQueue.global(qos: .background).async
{
self.recorder?.recordAccelerometer(forDuration: 3 * 60) // Record for 3 minutes
}
}
else
{
print("not authorized")
}
}
else
{
print("NOt available for recording")
}
}
func getRecordedData()
{
DispatchQueue.global(qos: .background).async
{
if self.sessionStartDate < self.sessionEndDate
{
if let list = self.recorder?.accelerometerData(from: self.sessionStartDate, to:self.sessionEndDate)
{
for record in list
{
let data = record as! CMRecordedAccelerometerData
print("x: \(data.acceleration.x) y: \(data.acceleration.y) z: \(data.acceleration.z) time :\(data.startDate.getFormattedDate())")
}
}
}
}
}
}

It doesn't seem you can. I have tried the following but none of these really work.
Register for backgroundApplicationRefresh (watchOS 3) and make sure the app is saved to the Dock. Dock apps can rely on getting one call an hour to update themselves.
On completion of the period query for the accelerometer data and archive the result to file and then transfer the file to the iOS companion app. The file transfer will occur in the background independent of the watch kit app assuming you get enough CPU time to write the accelerometer data to file.
Try using ProcessInfo API to keep the process running.
To date the only success I have had is in running an async thread to extract the data and keeping the watch kit app active by tapping on the screen each time the screen blanks.
Hope this helps and please post any better solution you find.

Related

Suspend `withTimeout`

My app starts from executing coroutine:
model.viewModelScope.launch(Dispatchers.IO) {
val timeout = withTimeoutOrNull(TIMEOUT) {
//send some initialization server requests
true //timeout=true
}
if (timeout!=null){
// app started ok
} else {
// app freezed on start
}
}
The problem is one of the initialization step - obtain user consent to share his personal data(GDPR) Consent form is a modal popup that waits for user input.But the timer is ticking all this time. Therefore after gdpr user may see app starts too long error.
Is there way to suspend withTimeoutOrNull timer and resume it after some time?
It is not supported out of the box, but you can write your own version of withTimeoutOrNull that would support a pauseable timer. The key idea is to run your block in a separate coroutineScope { ... } and to launch a secondary helper coroutine that would cancel this scope after timeout. Now if you keep a reference to this helper cancellationJob, then you can cancel it when you need to pause your time and restart it when you need to resume timer.
That would give you essentially the same behavior that basic withTimeout has but with an added pauseability. A bit of extra code is needed to implement a withTimeoutOrNull variant. You'll need to catch your cancellation exception and replace it with null result.
A worked-out implementation can be found in this gist: https://gist.github.com/elizarov/c5b0fde43ca14efbb8bcab13ad43c6ca

Issue with syncing data between watch and phone

I have developed an Android App which runs on both a smartphone and a smartwatch in parallel. On both devices, (let's say) it reads certain sensor data, processes that data (calculate its mean), and then store that results. The watch sends this result to the phone so all storing takes place on the phone. I used buffer writer to write a number into a text file every 5 seconds.
Now after every 320 data items exchanges from watch to the phone, my app on the phone gets killed and I get "the name of the app" is unfortunately stopped as a message. I can't figure it what why they stop exactly after this time? The app running on the watch continues to work fine. However, I cannot store its data because it cannot communicate to the phone version so I get this message "the app is unfortunately stopped as a message" every time the watch sends a number to phone for storing. The app has one activity which has a service (foreground).
Could it be that there is a limit on the amount of data being shared?
The code on watch:
// Create a data map and put data in it
private void increaseCounter() {
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count");
putDataMapReq.getDataMap().putInt(COUNT_KEY, count++); // I add current time here as well
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
}
Code on phone (possible problematic area):
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
// DataItem changed
DataItem item = event.getDataItem();
if (item.getUri().getPath().compareTo("/count") == 0) {
DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
updateCount(dataMap.getInt(COUNT_KEY));
}
} else if (event.getType() == DataEvent.TYPE_DELETED) {
// DataItem deleted
}
}
}
You have to use Service with StartForeground notification to be sure app is always working.
and try to use START_STICKY flag while staring.
UPDATE
You have to dealloc memory of dataevent:
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
try{
for(DataEvent dataEvent: dataEvents){
if(dataEvent.getType() != DataEvent.TYPE_CHANGED){
continue;
}
////... code
dataEvents.release();
}catch (Exception e){
Log.v("SunshineWatchFace",e.getMessage());
}
}

Notification when display gets connected or disconnected

I'm working on an OS X application that displays custom windows on all available spaces of all the connected displays.
I can get an array of the available display objects by calling [NSScreen screens].
What I'm currently missing is a way of telling if the user connects a display to or disconnects a screen from their system.
I have searched the Cocoa documentation for notifications that deal with a scenario like that without much luck, and I refuse to believe that there isn't some sort of system notification that gets posted when changing the number of displays connected to the system.
Any suggestions on how to solve this problem?
There are several ways to achieve that:
You could implement applicationDidChangeScreenParameters: in your app delegate (the method is part of the NSApplicationDelegateProtocol).
Another way is to listen for the NSApplicationDidChangeScreenParametersNotification sent by the default notification center [NSNotificationCenter defaultCenter].
Whenever your delegate method is called or you receive the notification, you can iterate over [NSScreen screens] and see if a display got connected or removed (you have to maintain a display list you can check against at program launch).
A non-Cocoa approach would be via Core Graphics Display services:
You have to implement a reconfiguration function and register it with CGDisplayRegisterReconfigurationCallback(CGDisplayReconfigurationCallBack cb, void* obj);
In your reconfiguration function you can query the state of the affected display. E.g.:
void DisplayReconfigurationCallBack(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void* userInfo)
{
if(display == someDisplayYouAreInterestedIn)
{
if(flags & kCGDisplaySetModeFlag)
{
...
}
if(flags & kCGDisplayRemoveFlag)
{
...
}
if(flags & kCGDisplayDisabledFlag)
{
...
}
}
if(flags & kCGDisplaySetModeFlag || flags & kCGDisplayDisabledFlag || flags & kCGDisplayRemoveFlag)
{
...
}
}
in swift 3.0:
let nc = NotificationCenter.default
nc.addObserver(self,
selector: #selector(screenDidChange),
name: NSNotification.Name.NSApplicationDidChangeScreenParameters,
object: nil)
NC call back:
final func screenDidChange(notification: NSNotification){
let userInfo = notification.userInfo
print(userInfo)
}

Detect screen on/off from iOS service

I am developing a network monitor app that runs in background as a service. Is it possible to get a notification/call when the screen is turned on or off?
It exists in Android by using the following code:
private void registerScreenOnOffReceiver()
{
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenOnOffReceiver, filter);
}
screenOnOffReceiver is then called when screen is turned on/off. Is there a similar solution for iOS?
Edit:
The best I've found so far is UIApplicationProtectedDataWillBecomeUnavailable ( Detect if iPhone screen is on/off ) but it require the user to enable Data Protection (password protection) on the device.
You can use Darwin notifications, to listen for the events. I'm not 100% sure, but it looks to me, from running on a jailbroken iOS 5.0.1 iPhone 4, that one of these events might be what you need:
com.apple.iokit.hid.displayStatus
com.apple.springboard.hasBlankedScreen
com.apple.springboard.lockstate
Update: also, the following notification is posted when the phone locks (but not when it unlocks):
com.apple.springboard.lockcomplete
To use this, register for the event like this (this registers for just one event, but if that doesn't work for you, try the others):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.iokit.hid.displayStatus"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
where displayStatusChanged is your event callback:
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSLog(#"event received!");
// you might try inspecting the `userInfo` dictionary, to see
// if it contains any useful info
if (userInfo != nil) {
CFShow(userInfo);
}
}
If you really want this code to run in the background as a service, and you're jailbroken, I would recommend looking into iOS Launch Daemons. As opposed to an app that you simply let run in the background, a launch daemon can start automatically after a reboot, and you don't have to worry about iOS rules for apps running tasks in the background.
Let us know how this works!
Using the lower-level notify API you can query the lockstate when a notification is received:
#import <notify.h>
int notify_token;
notify_register_dispatch("com.apple.springboard.lockstate", &notify_token, dispatch_get_main_queue(), ^(int token) {
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
NSLog(#"com.apple.springboard.lockstate = %llu", state);
});
Of course your app will have to start a UIBackgroundTask in order to get the notifications, which limits the usefulness of this technique due to the limited runtime allowed by iOS.
While iPhone screen is locked appdelegate method
"- (void)applicationWillResignActive:(UIApplication *)application"
will be called you can check that. Hope it may help you.

Objective C: Get notifications about a user's idle state

My cocoa app runs background tasks, which I would like to stop when the user becomes idle (no keyboard/mouse input) and then resume when the user becomes active again. Is there a way to register for idle-state notifications?
In case you can't link to Carbon (ie. you want to compile x86_64 bit binary) you can wrap this function (which returns current idle time in seconds resolution as double - CFTimeInterval) in a timer:
#include <IOKit/IOKitLib.h>
CFTimeInterval CFDateGetIdleTimeInterval() {
mach_port_t port;
io_iterator_t iter;
CFTypeRef value = kCFNull;
uint64_t idle = 0;
CFMutableDictionaryRef properties = NULL;
io_registry_entry_t entry;
IOMasterPort(MACH_PORT_NULL, &port);
IOServiceGetMatchingServices(port, IOServiceMatching("IOHIDSystem"), &iter);
if (iter) {
if ((entry = IOIteratorNext(iter))) {
if (IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties) {
if (CFDictionaryGetValueIfPresent(properties, CFSTR("HIDIdleTime"), &value)) {
if (CFGetTypeID(value) == CFDataGetTypeID()) {
CFDataGetBytes(value, CFRangeMake(0, sizeof(idle)), (UInt8 *) &idle);
} else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
CFNumberGetValue(value, kCFNumberSInt64Type, &idle);
}
}
CFRelease(properties);
}
IOObjectRelease(entry);
}
IOObjectRelease(iter);
}
return idle / 1000000000.0;
}
You'll need to link your code to IOKit.framework
There's a Carbon API that will send a notification when there hasn't been a user event after a certain duration called EventLoopIdleTimer. Uli Kusterer has written a Cocoa wrapper for here (look for UKIdleTimer).
If you want something lower level, you may be able to implement the behavior you want with a combination of timers and the CoreGraphics function CGEventSourceSecondsSinceLastEventType (available in <CoreGraphics/CGEventSource.h>).
Apple's Technical Q&A QA1340 Registering and unregistering for sleep and wake notifications may be what you are looking for.
If you need more control than NSWorkspaceWillSleepNotification (Listing 1), use I/O Kit and register to receive power notifications (Listing 3).
I used a different approach.
Subclassing UIApplication I override the sendEvent method filtering touches (actually you can filter any kind of event, acceleration, touches, etc.).
Using a shared variable and a background timer I managed the "idle".
Every time the user touch the screen the variable is set with current timeInterval (current time).
The timer fire method checks for the elapsed time since last touch, if greater than the threshold (in my case was around 90seconds) you can POST your own notification.
I used this simple approach to create a custom set of apps that after some idle time automatically call the "screensaver" app.
Nothing clever, it just do the job.
Hope that helps.