-
Notifications
You must be signed in to change notification settings - Fork 28
6. Best Practice
This page contains a set of best practice advice and tips on using CARP Mobile Sensing. It is in particular related to discussing differences between the two mobile platforms -- Android and iOS.
To keep the app running in the background, the carp_context_package
needs to be included. Please follow the instructions on how to use this package.
On Android version >= 9, battery management has been significantly improved. There are repeatedly reported issues with some Android devices not working in the background. Check if your device model is on dontkillmyapp list before you report new issue. This means that an app will be put to sleep when in the background. This is less useful, if the app is using CAMS for passive data collection.
The dontkillmyapp site provides suggestions for the different types of phone and OS versions. But a general approach is the following:
- enable 'Developer Options' in Android
- go to 'Developers options' in Settings
- scroll down to 'Apps'
- make sure that 'Don't keep activities' is disabled
- make sure that 'Background process limit' is 'Standard limit'
- go to 'Apps' in Settings
- click on your app
- click on 'Battery'
- on Android 11:
- click on 'Optimise battery usage'
- make sure to show 'all' apps
- make sure your app is disabled, i.e. that it is not optimized for battery usage.
- on Android 12:
- use the 'Unrestricted' option
- go to 'Battery and device care'
- click the 'dot menu' in the top right corner and select 'Automation'
- turn off "Auto optimise daily' and 'Adaptive power saving'
From version 0.30.0
the CAMS carp_context_package
makes use of the location
plugin, which supports the collection of location "in the background". If you follow the instructions on how to modify the Android Manifest.xml
file, this will help keep the app running in the background. Furthermore, the user needs to allow the app to access location "Always".
See Issue #97 for a discussion on background sensing.
To keep the app running in the background, the carp_context_package
needs to be included. It is important to follow the instruction of the package, including modifying the info.plist
and AppDelegate.swift
files. Also make sure that "Allow location tracking always" is enabled for the app on the phone.
iOS tends to aggressively kill apps (especially after iOS v. 15). There are a few things to do about this:
-
Low Power Mode (Settings App - > Battery - > Low Power Mode) needs to be OFF.
-
Background App Refresh (Settings App -> Hushed - > Background App Refresh) needs to be ON. If Background refresh is greyed out in the ON position, go To Settings App - > General - > Background App Refresh - > Turn on the option for the system, and then you can turn on / off by app.
You can also force the app to always be in the foreground by using the "Guide Access" feature in iOS. This feature forces the app to be the only app running in the foreground always and is hence not useful for background sensing. But it may be useful, if the phone is dedicated to run an app for a specific study. For example, if a dedicated study phone is handed to a user to be used only for a specific study.
In a new Flutter project, the iOS Podfile
is auto-generated in the ios
folder. To make things work, there are a few edits to this file that need to be done:
- Specify the platform version, like adding
platform :ios, '12.0'
(should be later than 9.3).- Note that you might need to do this also manually in XCode also - see this issue.
- In the
post_install
part, you should make sure that theconfig.build_settings
is also set to the same platform version (see here and here). - Specify the Swift version by adding
ENV['SWIFT_VERSION'] = '5'
- In the
target 'RUNNER'
part, add the following linesuse_frameworks!
anduse_modular_headers!
(if not already added by Flutter) - In the
target 'RUNNER'
part, also remove thetarget 'RunnerTests'
part (this may cause build issues).
An example of the edited Podfile
would look like:
# Uncomment this line to define a global platform for your project
platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
ENV['SWIFT_VERSION'] = '5'
...
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# target 'RunnerTests' do
# inherit! :search_paths
# end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
If you want to store data using the FileDataManager
, and want to grant the user of your app (e.g. yourself during development and debugging) access to it on the phone or via the iTunes app, add the following two entries to Info.plist
.
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
Note that on iOS>=12, collection of WiFi info has been restricted – please see
- connectivity
- CNCopyCurrentNetworkInfo
- Access WiFi Information Entitlement
- How To Enable the WiFi Information Entitlement in iOS 13
You may get this error:
Building for iOS, but the linked and embedded framework 'App.framework' was built for iOS Simulator.
or vice-versa. This is a well-known issue on iOS / XCode 11.4 for Flutter version less than v1.15.3. See the Flutter Xcode 11.4 Support page.
The default notification controller used in CAMS is the FlutterLocalNotificationController, which again used the flutter_local_notifications plugin. This plugin is pretty powerful and supports - amongst other things - scheduling and receiving notifications when the app is in the background or even killed. However, to make this happen, there is quite a lot of configuration needed. This is all very well explained in the flutter_local_notifications plugin README on how to set up on Android and iOS. Please configure your app according to these guidelines.
In order to initialize the Flutter platform channels, you would need to call WidgetsFlutterBinding.ensureInitialized()
in you main()
method, before creating the app. For example - this is the main method of the CAMS demo app:
void main() async {
// makes sure to have an instance of the WidgetsBinding, which is required
// to use platform channels to call the native code.
WidgetsFlutterBinding.ensureInitialized();
await bloc.initialize();
runApp(App());
}
There is a StackOverflow description of What Does WidgetsFlutterBinding.ensureInitialized()
do?.
When building your app, the AndroidManifest.xml
files from all packages are merged.
When you want to publish your app, which uses various CARP package, to the Google Play Store, it can be rejected because it tries to collect SMS texts and call logs, which Google considers to be a restricted group of permissions for which you need a valid reason to access those. Unfortunately, research is not in the list of exceptions. Hence, we would like to remove collection of SMS texts and call logs while still keeping collection of calendar information (all in the carp_communication_package
).
Illegal permissions can be omitted by specifying that they should not be merged from a lower level manifest. This is done by adding the following lines to the AndroidManifest.xml
file of your app, which prevents the restricted permissions from being added:
<uses-permission android:name="android.permission.READ_CALL_LOG" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_SMS" tools:node="remove"/>
<uses-permission android:name="android.permission.SEND_SMS" tools:node="remove"/>
<uses-permission android:name="android.permission.RECEIVE_SMS" tools:node="remove"/>
See also Issue #183. Thanks to koenniem for this update.
CAMS uses the permission_handler
plugin for handling permissions. Please see their description on how to configure permissions in both iOS and Android. Note in particular to change both the Info.plist
and Podfile
files on iOS. Othervise you may have problems getting your app approved by Apple.
If you're experiencing any build issue, try these steps:
- Run this on your project and try again:
flutter clean
rm -rf build
rm -rf ~/.pub-cache
flutter pub get
On Android, you might also want to run the ./gradlew clean
command. And on iOS, you might want to remove the .symlink
folder.
- Update Xcode, Android Studio, VS Studio, and Android SDKs, to the latest versions.
- If the issue persists, clone the carp_mobile_sensing repo and run the
example
app on the platform you are having issues. If it works, then there is something wrong with your project, and you can compare it to theexample
app to see what the problem is. - If the problem still persists, and no existing (open or closed) issue on this repo, then you can open an issue. But you must follow the issue template and refer to the problem on the example app (or start with its code and make only the necessary modifications to trigger the issue), not on your own app, which we don't have access (because since step 2 the error must be reproducible on the example app).
- Again, only open an issue if you reach step 3 and follow the issue template closely. Build issues that do not follow these steps will be closed.