GLUON POSITION SERVICE IOS - gluon

I have tried getting the position service to work. i do not get the pop up like android notifying me to allow location tracking.
i have updated my plist based on solutions online see below.
label1.setText("Waiting for GPS signal");
Services.get(PositionService.class).ifPresent(service -> {
// if there is GPS service, disable button to avoid adding more
// listeners
// label1.setText("Waiting for GPS signal");
service.positionProperty().addListener((obs, ov, nv)
-> label1.setText(+nv.getLatitude() + ", " + nv.getLongitude()));
});
find the updated plist file
link
everything build fine just the location not working what could be wrong

Related

How to open a link in smartwatch from phone

I would like to know how to open a link from a simple app that I have made to push a link from within the app from phone to open on the watch for example a link to google play store app to get downloaded on the smartwatch.
Thank you.
See the docs here https://developer.android.com/reference/androidx/wear/remote/interactions/RemoteActivityHelper
and an example here https://github.com/android/wear-os-samples/blob/d18c489ff415aa0fbb25c260e3aacdf50f7716e3/WearVerifyRemoteApp/Application/src/main/java/com/example/android/wearable/wear/wearverifyremoteapp/MainMobileActivity.kt
Log.d(TAG, "Number of nodes without app: " + nodesWithoutApp.size)
val intent = Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(PLAY_STORE_APP_URI))
// In parallel, start remote activity requests for all wear devices that don't have the app installed yet.
nodesWithoutApp.forEach { node ->
lifecycleScope.launch {
try {
remoteActivityHelper
.startRemoteActivity(
targetIntent = intent,
targetNodeId = node.id
)
.await()

Why does Xcode simulator launch app with didEnterRegion call, while iPhone does not?

I'm troubleshooting a location-based app launch cycle that works fine on iOS simulator, but apparently does not launch the app on an actual device.
I've set up UNNotifications for both enter and exit events. Both the simulator and the device(s) register and display these notifications.
The next thing that is supposed to happen is that the app goes through a launch process, setting the app's state in such a way that I can tell without being connected to the debugger whether the app has launched or not when I open it from the home screen.
On simulator, the didEnterRegion code gets called and I can step through the subsequent launch code using the debugger - success.
However when I take the device out (for a walk) all I get are UNNotifications, and no app launch (which I can tell from the UI in the app on the real device)
I'm sure I need to improve my testing strategy (welcome to suggestions!), but at this point I should be able to expect that the app should behave the same on the simulator and the actual device - it is not.
Why is the expected outcome happening on the simulator but not on the device?
LocationService.swift
func handleRegionEnterEvent (for region: CLRegion) {
ApplicationController.sharedInstance.didEnterRegion(region)
}
func handleRegionExitEvent (for region: CLRegion) {
ApplicationController.sharedInstance.didExitRegion(region)
}
ApplicationController.swift
func didEnterRegion (_ region: CLRegion) {
// Present Alert
delegate?.handleUNEvent(note: "ENTER: \(region.identifier)")
// Start launch cycle
}
AppDelegate.swift
extension AppDelegate: AppControllerDelegate {
func handleUNEvent(note: String?) {
// Show an alert if application is active
if UIApplication.shared.applicationState == .active {
guard let message = note else { return }
Alert().showAlert(withMessage: "Application received UN while active: \(message)", title: "UNNotification")
} else {
// Otherwise present a local notification
guard let body = note else { return }
let notificationContent = UNMutableNotificationContent()
notificationContent.body = body
notificationContent.sound = UNNotificationSound.default
notificationContent.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "location_change",
content: notificationContent,
trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error: \(error)")
}
}
}
}
}
The simulator sadly has a lot of differences from a device both in terms of data reported (you will not get speed for example) and also as you saw call flow may be different...
Something to check is to make sure you have the descriptions for the specific location permissions you are asking for in relation to location set up. For entering regions you probably want to make sure you have "always" access.
It may also be worth enabling wireless debugging, and walking around with laptop tethered to the device to verify with the debugger the didEnterRegion is truly not called, and there is a bug in the code that is supposed to display the UI you are expecting. At the very least perhaps you could get better logging as to the status of various location callbacks.
What happened: Code that executed on simulator was indeed crashing on the device and I was not reviewing crash logs from the device. This question arose from an issue that is neither ios, or core-location specific.
It has everything to do with choosing appropriate testing strategies and using all available information at one's disposal before coming to what may turn out to be premature conclusions.
However when I take the device out (for a walk) all I get are UNNotifications, and no app launch (which I can tell from the UI in the app on the real device)
I was mistaken to think that a) User Notifications that appear when the App is not running are presented by the system (as banners) and b) the App launches in another process. In fact my own code specifically executes the code that makes those notifications appear. Face-to-palm: Copying code from tutorials to achieve quick test results without even trying to understand what the code does can lead to that I suppose.
In Xcode, plug in your device and hit command-shift-2 to open Devices & Simulators window. Select your device on the left and hit "View Logs" button to bring up your crash log.

Using 'Background location services' on iOS with gluon mobile

I just started using gluon mobile and I am developing a small iOS app. I managed to use the PositionService to update the users position on a label in the UI. Now I would like to get Location Updates even if the app is in background mode. Due to apple developers documentation this should work by adding the following key to the apps plist
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
When deploying the app on an iPhone I can see the updates as long as the App is active. When going to the home screen, the updating stops and in the terminal (gradle -> launchIOSDevice) the message "Stop updating location" is shown. Any idea why I don't get Location Updates in background mode?
Here the relevant code:
Services.get(PositionService.class).ifPresent(service -> {
service.positionProperty().addListener((obs, oldPos, newPos) -> {
posLbl.setText(String.format(" %.7f %.7f\nLast update: " + LocalDateTime.now().toString(),
newPos.getLatitude(), newPos.getLongitude()));
handleData();
});
});
Here is the relevant plist entry:
<key>NSLocationAlwaysUsageDescription</key>
<string>A good reason.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>An even better reason.</string>
The reason why the Position service is not working when the app goes into background mode can be found here:
Services.get(LifecycleService.class).ifPresent(l -> {
l.addListener(LifecycleEvent.PAUSE, IOSPositionService::stopObserver);
l.addListener(LifecycleEvent.RESUME, IOSPositionService::startObserver);
});
startObserver();
The lifecycle service is designed to prevent doing unnecessary stuff when the app is in background mainly to save battery. Many services, including Position or Battery, make use of it by default.
So far, there is no easy way to remove the listener, as there is no API to enable or disable its use. If you think this should be added, you can file an issue here.
You could fork the Charm Down repository, removing the related code, and build it again, using your own snapshot, but of course this is not a good long term solution.
For now, the only way I can think of, without modifying Down, is avoiding the inclusion of the Lifecycle service implementation for iOS.
Doing so, once you open the app and instantiate the Position service, startObserver will be called and never stopped (until you close the app).
In your build.gradle file, instead of using the downConfig block to include the position plugin, do it in the dependencies block, and remove the traversal dependency to lifecycle-ios:
dependencies {
compile 'com.gluonhq:charm:4.3.7'
compile 'com.gluonhq:charm-down-plugin-position:3.6.0'
iosRuntime('com.gluonhq:charm-down-plugin-position-ios:3.6.0') {
exclude group: 'com.gluonhq', module: 'charm-down-plugin-lifecycle-ios'
}
}
jfxmobile {
downConfig {
version = '3.6.0'
plugins 'display', 'statusbar', 'storage'
}
Now deploy it to your iOS device and check if the position service works on background mode.
EDIT
As pointed out, removing the lifecycle listener that stopped the observer is not enough: the location is not updated in background mode.
The solution to get this working requires modifying the Position service for iOS, and building a local snapshot.
These are the steps (for Mac only):
1. Clone/fork Charm Down
Charm Down is an open source library that can be found here.
2. Edit the Position Service for iOS
We need to comment out or remove the Lifecycle listeners from IOSPositionService (link):
public IOSPositionService() {
position = new ReadOnlyObjectWrapper<>();
startObserver();
}
(though a better approach will be adding API to allow background mode, and install the listeners based on it. Also a way to stop the observer should be required)
And now we have to modify the native implementation at Position.m (link):
- (void)startObserver
{
...
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
// try to save battery by using GPS only when app is used:
[self.locationManager requestWhenInUseAuthorization];
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)
{
// allow background mode
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
NSLog(#"Start updating location");
...
}
(again, this should be set based on a background mode API)
3. Build and install
At the root of Charm Down, and using a Mac, run this:
./gradlew clean install
(if the Android sdk is not installed, the android services can be commented out at settings.gradle).
This will install a snapshot of the Charm Down services (currently 3.7.0-SNAPSHOT).
4. Update the Gluon Mobile project
Edit the build.gradle file, and set the mavenLocal() repository, and the snapshot version:
repositories {
mavenLocal()
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
dependencies {
compile 'com.gluonhq:charm:4.3.7'
}
jfxmobile {
downConfig {
version = '3.7.0-SNAPSHOT'
plugins 'display', 'lifecycle', 'position', 'statusbar', 'storage'
}
Save and reload the project.
5. Use Position service in background mode
As pointed out in the comments, when running on background mode, iOS doesn't allow making changes in the UI.
This means that whenever a new position is retrieved from the service, we'll have to use a buffer to store it and only when user resumes the app, we'll make the necessary update to the UI with all those buffered locations.
This is a simple use case: with the Lifecycle service we know if we are on foreground or background, and we only update the ListView control (UI) when the app is running on foreground, or it just resumed from background.
private final BooleanProperty foreground = new SimpleBooleanProperty(true);
private final Map<String, String> map = new LinkedHashMap<>();
private final ObservableList<String> positions = FXCollections.observableArrayList();
public BasicView(String name) {
super(name);
Services.get(LifecycleService.class).ifPresent(l -> {
l.addListener(LifecycleEvent.PAUSE, () -> foreground.set(false));
l.addListener(LifecycleEvent.RESUME, () -> foreground.set(true));
});
ListView<String> listView = new ListView<>(positions);
Button button = new Button("Start GPS");
button.setGraphic(new Icon(MaterialDesignIcon.GPS_FIXED));
button.setOnAction(e -> {
Services.get(PositionService.class).ifPresent(service -> {
foreground.addListener((obs, ov, nv) -> {
if (nv) {
positions.addAll(map.values());
}
});
service.positionProperty().addListener((obs, oldPos, newPos) -> {
if (foreground.get()) {
positions.add(addPosition(newPos));
} else {
map.put(LocalDateTime.now().toString(), addPosition(newPos));
}
});
});
});
VBox controls = new VBox(15.0, button, listView);
controls.setAlignment(Pos.CENTER);
setCenter(controls);
}
private String addPosition(Position position) {
return LocalDateTime.now().toString() + " :: " +
String.format("%.7f, %.7f", position.getLatitude(), position.getLongitude()) +
" :: " + (foreground.get() ? "F" : "B");
}
Finally, as pointed out by the OP, make sure to add the required keys to the plist:
<key>NSLocationAlwaysUsageDescription</key>
<string>A good reason.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>An even better reason.</string>
6. Deploy and run
Allow location use, start the location service, and when entering background mode, a blue status bar on the iOS device will show that the App is using location.
Note that this could drain the battery really fast.

Closing Android Activity opened from Forms.Context.StartActivity

I am working on facebook authentication in a Xamarin forms app and want to use "native" login on each device.
The Facebook SDK (by Xamarin) is not designed for forms since it requires passing in an Android activity that implements some specific interfaces.
However; I was able to get the UI to display using a custom page renderer and then in that renderer calling StartActitivty with an activity that uses the exact implementation described in the Facebook SDK getting started. https://components.xamarin.com/view/facebookandroid
So far everything works perfect. The android app starts, xamarin forms kicks in, the custom page renderer is loaded the login android activity starts and the user logs in with facebook and we get to the console.writeline below.
So, how do I dismiss this intent or otherwise get back to xamarin forms?
Do I:
Dismiss this intent? - if so then what?
Inject something to "reset" the main page?
Other?
public void OnCompleted (Xamarin.Facebook.Model.IGraphUser user, Response response)
{
//TODO: show user details with welcome and button that takes them to main app.
//TODO: switch back to xam forms from here on out.
//TODO: figure out how to change the `main` activity to xam forms.
// 'Me' request callback
if (user != null) {
//How do I get back to Xamarin forms from here?
Console.WriteLine ("GOT USER: " + user.Name);
} else {
Console.WriteLine ("Failed to get 'me'!");
}
}
You should call Finish();
Anyway, if you want to see exactly how I did it, you can check it here:
https://github.com/IdoTene/XamarinFormsNativeFacebook

How can I test the background scan and launch the application in background with iBeacon-Android?

I am using the pro library.
But I just found doc for free library
I cannot find any doc for pro version.
Also, I don't know how to implement the background mode even using the pro sample.
Here are the steps:
Build the pro sample project
start the iBeacon source(using iPad) and it can be detected
start the application and then press home button the make it in
background
Turn off the iBeacon source
Turn on the iBeacon source
However, more than 5 minutes, the application does not launch
So, can anyone verify the step I did?
How can I test the background mode more easily?
Also, for the BootstrapNotifier, is it just work only first time when the device reboot?
After that, even I put application in background, the application will not launch when it detect iBeacon?
Your testing method sounds fine. I think the issue is that the reference app for the pro library only auto launches the app on the first detection after boot. After that, it sends a notification instead, and tapping on that notification launches the app.
This is purely for demonstration purposes. You can change it to auto launch on every detection if you wish. Simply alter the haveDetectedIBeaconsSinceBoot logic in this code:
#Override
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever an iBeacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
if (!haveDetectedIBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an iBeacon, we launch the
// MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: make sure to add android:launchMode="singleInstance" in the manifest
// to keep multiple copies of this activity from getting created if the user has
// already manually launched the app.
this.startActivity(intent);
haveDetectedIBeaconsSinceBoot = true;
} else {
// If we have already seen iBeacons and launched the MainActivity before, we simply
// send a notification to the user on subsequent detections.
Log.d(TAG, "Sending notification.");
sendNotification();
}
}
The javadoc link was missing from the main documentation page when you posted this question. That is fixed now.

Resources