Launch Collect from a browser, with feature pre-selected

Hello everyone, I am currently working as the lead developer for the FMTM project at HOTOSM.

Looking to revive the discussion from @Ivangayton Launching Collect from a browser

Requirement

Initially we planned to use the 'select from map' feature from ODKCollect as part of our workflow:

  1. QR code for features & XLSForm generated in FMTM and downloaded.
  2. QR code imported into ODKCollect.
  3. Building selected on the map.
  4. Form filled out and submitted.

After field testing with various users, we determined it would be better to:

  1. Navigate to features within FMTM (allowing for integrated functionality such as route navigation without needing to modify ODKCollect).
  2. User selects a feature and clicks a button to open ODKCollect.
  3. ODKCollect XLSForm is opened directly from the link, with feature id pre-selected.
  4. Form filled out and submitted.

The difference here is that it saves the user having to mess with QR codes and generally simplifies the data collection flow.

What we are asking for specifically: a way to open ODKCollect directly from a link, with pre-populated data.

Suggested solution

Some more research needs to be done for the exact implementation here (cc our mobile dev @Nishon).

But I assume the approach using intent filters should still be valid:

It would be great to organise a phone call for a quick discussion on the preferred implementation.

Then we would plan for the development and make a PR in Github.

1 Like

We've developed an app using Android's Accessibility API to automate ODK Collect. It checks if ODK Collect is installed, opens a form, and fills the first question's answer. This approach may misuse the Accessibility API. We're seeking a more direct method to pass parameters to ODK Collect, such as project configuration URL, auto-download forms, and specify the field to fill answers.

This feels like a reasonable approach to meet your specific needs. Is there a concrete issue you're experiencing with it?

This is pretty far outside of our current priorities (https://getodk.org/roadmap). We generally would prefer to reduce rather than increase the integration surface area of Collect because it has historically been unapproachable for most users, brittle, and time-consuming to maintain, especially as Android changes.

For something significant like this to be considered for the core, it would need to come with consideration of edge cases (especially around Android life cycle), tests, and documentation. Is that something you would have the capacity to include? We've had bad experiences in the past with community members contributing narrow solutions without tests that then resulted in a significant maintenance burden.

That said, we are excited about an alternative to QR code configuration using deep linking. The idea would be that instead of scanning a QR code, a user could tap on one of these links. The behavior would then be identical (if a project with the same config exists, prompt to switch to it, if it doesn't create it, then request forms from server, etc). That could possibly be a first step that we work on together.

Using an external app to launch a specific form with values filled in does not fit in well with where we are going. We are working towards Collect having entity-centric views, including map-based ones, in addition to the form-centric presentation it has now. To do that, we'll establish a way to launch forms from individual entities and for those forms to access data for the entity/ies it's about. What you describe is similar but with an external app doing the entity listing. If we take that on now, we're likely to end up with two similar but different ways to launch forms with values. Given this information and @Nishon's findings, if you still feel like this should be part of the core, I think a good next step would be for you to do a spike around how it could be structured so there's something concrete to consider.

Deep linking as an alternative to a QR code is an answer to many of our hopes and dreams!
We are very excited about this and grateful that you are considering implementing it. Let us know if we can help!

Indeed, reducing the integration surface makes sense.
I think what we are getting at is not injecting random stuff into a form.
It's launching ODK with an entity targeted. The entity concept, once fully implemented in ODK, pretty much answers all of our needs. We just need a way to target one of the entities, which may be a fairly small integration surface.

I think it makes sense for ODK to have a basic map view that's integrated, in the same way, that makes for Central to have some basic visualization analytics and serve us some CSV. But just as some users would be using OData to access their data in PowerBI, it might make sense for people with really sophisticated mobile map and navigation needs to use something beyond the basic map view that would be a sensible core feature of Collect. Just as it might not make sense to implement PowerBI functionality within Central, it may not be optimal to implement sophisticated map and navigation functionality within Collect; users who want that might be better served by something external that has a nice but limited integration into Collect (i.e. targeting an entity when launching Collect from a deep link or QR Code).

That's great! It's not currently work that we have scheduled. If you want to try to speed it along, some exploration and design would be much appreciated. For example, here are some kinds of questions that would be helpful to answer:

  • Are there any likely issues with very long urls? A minimal config would be 400 chars which should not be a problem. Long ones could be multiple thousands of chars.
  • Do all deep links have to be registered with the Play Store?
  • When we looked at URLs like collect://settings?config=IAogICJnZW5lcmFsIiA6IHsKICAgICJzZXJ2ZXJfdXJsIjogaHR0cDovL2xvY2FsaG9zdDo4OTg5L3YxL2tleS8ybFdhNGMhdzQxYUtURnRSQzFYQVVNYThnc0N2ZUVQdXdhZVJocHdHVUIwanFsb21nbmpJa1hDZVVQNFdrTzNjL3Byb2plY3RzLzEsCiAgICAidXNlcm5hbWUiOiBmb28sCiAgICAicGFzc3dvcmQiOiBiYXJhbmRzb21lbW9yZXN0dWZmeWVhLAogICJtYXRjaF9leGFjdGx5IjogdHJ1ZQoKCn0= in the past, we found that apps like WhatsApp didn't recognize them as links. That might be a reason to use e.g. https://getodk.org URLs. Would those work offline if Collect is installed?
  • Are there any considerations that have to be made up front if we'd like the same links to eventually be deferred deep links that open the Play Store if collect is not installed? Or maybe supporting these links offline is not important because forms have to be downloaded for any work to be possible anyway.

Yes, the ideal progression would be to first design and build out Collect's way of listing entities and launching forms from them and then making that mechanism available externally.

If you can't wait for that, it currently seems to me like adding the targeted functionality that you need in a fork for now would be ideal. But again, if you have a concrete proposal on approach, that might change that impression.

Have you considered using Enketo? The biggest advantage of Collect is that it helps manage workflows across multiple forms and maintains state when offline for a while. It doesn't sound like these are as relevant in your context if you want to jump directly into a form. I'm guessing you'll say that the geo widgets and offline mode in Enketo are not sufficiently usable. Those are two areas that we would like to work on sooner than later and that we might be able to consider with you on a timeline that works for you.

Thanks for the productive discussion so far!

Apologies for the slow feedback, I don't have notifications for the forum :sweat_smile:

You have asked some very good questions @LN that we will investigate and suggest possible answers to.

As for immediate goals:

  • Deep linking to forms (as an alternative to QR code) seems like it would be desirable for both parties. This is the main thing we need. We will prototype possible solutions and create a PR in our fork for feedback / consideration.

  • Launching with data pre-populated is very much a nice to have, but it would significantly improve our workflow in the field. If a solution to deep linking is agreed upon, then I think we can address this afterwards, using entities.

As for your point around maintenance: ODK Collect is an integral tool for one of HOT's flagship products, FMTM.
The precursor tool to this, Tasking Manager, has been around in various forms for nearly 10yrs now. I imagine there should be bandwidth for supporting this feature within HOT, NAXA, or another team for the foreseeable future. We would also try to include some comprehensive testing within the PR.

1 Like

Fantastic!

Thank you so much, that sounds great.

Hi @LN !

We finally got around to prototyping this during a tech team meeting with NAXA.
@zestyping was able to pull together a quick working solution for loading entities via an intent.

To answer your specific questions:

Do all deep links have to be registered with the Play Store?

They only have to be registered on the Play Store if they are HTTP links.
If using the URL protocol as collect://xxx, this isn't an issue.
If the requirement is to have the links identified and hyperlinked within other apps (like whatsapp), then we probably need to use http:// as the protocol and register a verified link on the Play Store, but this is more involved.

Are there any considerations that have to be made up front if we'd like the same links to eventually be deferred deep links that open the Play Store if collect is not installed? Or maybe supporting these links offline is not important because forms have to be downloaded for any work to be possible anyway.

  • I don't think this is an issue if using a custom protocol collect://.
  • Further research may need to be done around loading the Play Store ODK Collect listing via an intent, if this is required.
  • The URL example links to an entity, but to better align with the Central API convention, we may want to use a structure like: /project/ID/entity/ID.

Additional Considerations

  • The PR allows us to build a custom ODK Collect APK and launch directly with an entity pre-loaded.
  • We could potentially do what we need without using the entities feature, but think it is best to integrate with the existing entity-based solution (I assume this would be useful to a wider range of users).

Discussion

  • Are other users potentially interested in this feature?
  • If we fleshed out the PR and added test cases etc, would this be something you are willing to support in Collect?

@Ivangayton @Nishon @varun2948 @Niraj_Adhikari

2 Likes

It sounds like you've decided links for configuring Collect are not your priority but launching with data pre-populated is, right?

@bs has recently also been in touch with interest in setting values from an external app. In his case it would likely be more than one value.

I'll discuss with the Collect team in the next few weeks and share some thoughts.

I know you may be thinking this is a really simple thing that should be a no-brainer to add given how simple @zestyping's proof of concept is. Here are some examples of questions that we'll be considering:

  • If we introduce links with a custom scheme, we'll then have two URL types if we want to add http links later. HTTP links are not important to your use case because you're linking from an external app but they can enable workflows like tasking over WhatsApp. They also make more sense for links to configure Collect which we're interested in. Again, those would likely be distributed by email, text message, etc.
  • Is setting a default value by navigating through the form the best approach or should we consider doing something like using passed in values to generate a filled form instance and opening that?
  • Should setting multiple values be in scope?
  • What should the user experience be if Collect isn't configured or doesn't have a local copy of the form?
2 Likes

I think at first we didn't realise that launching directly to a loaded form & pre-populating information were different problems!

Attempting to load Entities made that clearer for us.
Although it would be nice to load the form via a link, preselecting an entity / feature is the much more valuable user experience for us.

You raise many important questions!

If HTTP links are used and must be verified, I wonder if this would prevent usage via a sideloaded APK.

Restarting this conversation!

This is the main target in our sights for assistance on ODK Collect (we potentially have some mobile dev time to put to use) :smiley:

  • For the custom links, I think this was only done as I think https scheme links need to be registered with the Google Play Store. Is my assumption correct here? We are more than happy to use https links if possible - it would also be very useful to pass links around via messaging apps.
  • You are probably right that generating a filled out form instance would be better. Do you think passing the fields as URL params is an acceptable approach?
  • If we pass each field as a URL param keys, then this would allow us to set any field from the value. We could definitely make the approach used more generic to match any URL param key to a field of the same name.
  • Currently if the form is not loaded locally, an error is shown with title: Error Occured and message: Sorry, unabled to parse form.. This is fine for now, but in a polished version the message could be updated:
    • If the project is not loaded: Please load the project via QRCode first.
    • If the project is loaded, but the form does not exist: The form does not exist in your project, or appuser does not have sufficient permissions.

If we start with supporting loading multiple fields by url param in our demo, do you think this a good approach?

Let us know your thoughts / if you are happy for us to continue work on this :smile:

That's exciting!

We're about to release Collect v2024.2 (probably the first week of July) and then want to spend some time laser-focused on offline Entities until we get a production-ready implementation ready.

That means we'll do our best to engage with high-level design discussions around this but won't review code for a little while.

Once offline Entities are complete, we'll start looking at having an interface to select an Entity and then launch a form in the context of that Entity as I described above. I think that will be a great time to engage in detail with what you'll have explored in support of an external app doing something similar. We can then figure out what overlap, if any, there is between the two.

Yes, that's my understanding. Finding out more about that process and how devs usually test these implementations would be very helpful. For example, is it reasonable to have an odk:// URL for testing and forks that uses the same code paths as a https:// URL that does get registered through the Play Store? Is there some other common practice?

I don't think a sideloaded APK would be an issue as long as it's signed with the ODK key. I think it's APKs signed with other keys that wouldn't work. Again, super helpful if you're able to verify that!

I think so. A decision point if taking this approach would be whether to use full paths like /data/entity_id, /data/my_group/my_question as keys or paths starting at the first form-defined node (entity_id, my_group/my_question) I'm a bit torn -- I like how explicit full paths are but if they're going to be user-visible it may be friendlier to be as concise as possible. Luckily this should be an easy one to change one way or the other. The one precedent we have is Enketo's way of doing defaults which does use full paths.

In fact, it may make sense to match Enketo's URL structure entirely: d[/path/to/node]=value&d[/path/to/node]=value. Using a d attribute with indexes clearly indicates which attributes are intended to be default keys and allows for other attributes to be specified, for example to configure a project. Square brackets used like this in URLs are not explicitly allowed in the spec, sadly, but are in common usage.

Some requirements:

  • Messages should be as neutral with respect to workflow and server as possible. For example, not everyone uses projects or QR Codes. (something like: "ODK Collect is not configured yet, please configure it to get access to the "My Cool Form" form" -- could the primary action go to the Collect landing screen?)
  • Messages should be as specific as possible. For example, it would be helpful to know the title of the form that was not found because maybe it hasn't been manually downloaded and the user can solve the problem.

Unfortunately, we don't currently have a way to to identify a Collect project from outside the app. I believe that means this functionality will either have to use the current Collect project and if it doesn't find the form, suggest that it may be in another project, or scan all active projects for the form_id and provide a picker if there's ambiguity.

Thanks for digging into this and engaging with us to find a way to get it in the core! I think we'll be able to figure something good out. :rocket:

1 Like

Thanks for the update and detailed feedback!

All very good questions and suggestions - we will start prototyping and reply on this thread if there is progress :smile:

1 Like

Progress

We have made some good progress on this!

We first did the simplest approach, but will later refine to better align with your proposals above.

The Android devs (Amit (@itheamc) & Samir) have made a release that allows:

  • Loading a project via intent using base64 encoded settings data in the URL (I know this is a separate issue to the one discussed above, so we could move this to another forum post if preferred). This is instead of loading via QRCode, which can be a bit of a hassle for an individual user having to download / upload the QR.

    Example:

    <a
      href="odkcollect://project/configuration?data=eJxNkEtvwjAQhP8KtTiiWJGgQhxTckCUR6uUilysxV4Sgx1HfgQo4r/XkThw3NlvR7NzJxU2aEGR2Z04tB1aFmycSO1962aUGnFOBHbJUXud1MYbpxNjK9ql9Iw3ml3MJM0/fsfd97gcbrvFdL9yk4sZq7dUFvM6+9kXqjws/3ZpmX21fJfaYThlJ19tsMyngrbWnJB7RyfvZESOxmoWWgEemTYCYxANntcMr8C9ukXkAA41tMyZYHkPxERRhhCjYSOicJFHyaARjKNSQYGNa40eoiuweCv7/9abYrBYbT/zVb4u8vkrEmIRDeje23W8f5w8RuQZtC/quVzKCpQc5FfQrcKeAaFlE4nH4x/TRHdf"
    >Setup Project</a>
    
  • Pre-filling any form data via URL parameters when loading a new form via intent.

    Example

    <a
      href="odkcollect://form/8cbeca17-8f3c-4616-8756-f146cbbe3d49?task_id=92&comment=Yahoo!! i was preselected by @mit&model=extended&category=medical&name=Name By @mit"
    >Open Form To Test Preselect</a>
    

Detailed changes:

  • Added intent filter on FirstLaunchActivity for project setup.
    • Now project can be setup simply through the browsable link with this format odkcollect://project/configuration?data=<base64-encoded-settings-data>
  • Added helper class ProjectSetupHelper to handle project setup by browsable link.
  • Shift form opening intent filter from FormFillingActivity to FormUriActivity (more efficient error handling).
    • FormUriActivity acts as a gatekeeper to validate the URI format. Once validated, it navigates the user to the FormFillingActivity to begin data entry. However, if the URI format is not as expected, an informative error message is returned, guiding the user towards a correct format.
  • Added some functionality on the FormUriActivity and it's view model to handle deep link uri and open the form accordingly.
  • Removed URI handling related functionality from the FormLoaderTasks.
  • Minor enhancement in ContentUriHelper class methods.

Resources for testing

This zip contains the signed APK for testing, XLSForm, and test HTML with intent URLs:

The branch with the code:

Discussion

  • We couldn't make both loading the project and creating a form with prefilled data work in the same activity - the user will always have to load the project first, then create forms with pre-filled data.
  • The next step for loading data by intent is to test using your proposed URL structure from Enketo :+1:

Deep Linking

  • As for deep linking, in order to use https:// deep links from a website:
    • The domain must be registered on the Google Play store -> Deep Links section.
    • We must create a Digital Asset Links JSON file assetslinks.json at the location https://yourdomain.name/.well-known/assetlinks.json to declare the association between the website and intent filters.

With this in mind, it seems to me that deep linking with a https:// protocol to ODK Collect may not be possible, as each user requiring this feature will have a different website / domain!

I guess the deep linking capability makes sense when a company wishes to link their website with their app, but for our specific use case I'm not sure anything other than a custom protocol like odkcollect:// will be possible!

We are doing further research to confirm!

Error Handling

Scenario 1: ODK Collect is installed, but no projects are configured

This error message seems pretty good!

Scenario 2: ODK Collect is installed, and projects have been loaded, but not the project or form required for the intent to work:

This is the existing error - we are looking into updating this to be more like: No project is configured with a form named "My Cool Form". Please ensure the project and form are loaded correctly first.

Scenario 3: ODK Collect is installed, the correct project and form are loaded, but there was an error parsing the URL content:

...Screenshot to come...

Scenario 4: ODK Collect is installed, the correct project and form are loaded, but the URL params did not match form fields:

...Screenshot to come...

4 Likes

Thanks for looking into it!

I'm not totally sure about this. I believe we could have URLs like https://getodk.org/app/collect/configure that open a specific activity with query parameters. The server you want to configure would be part of the base64-encoded settings, not part of the domain.

Presumably we could layer this on, right? We can have multiple URLs match to the same activity?

1 Like

Of course, you are right!

We have done some research and have promising results on this front - will update tomorrow!

1 Like

Test

As a test we developed a basic app and published to the Play Store for deep link https testing.

By configuring the Deep Link in the Play Store settings and updating assetslinks.json with the deep links, loading by intent from a https:// url works for both the officially installed and sideloaded (signed with a different key than the official version) apps without issue.

The only difference being:

Summary

To allow for loading by intent via https Deep Links, we can do the following:

  • Settle on a URL structure such as the one proposed: https://getodk.org/app/collect/configure
  • Add this URL to the Deep Links section on the Play Store
  • Create the file https://getodk.org/.well-known/assetlinks.json including <data android:host="https://getodk.org"> (more details)

The user should be able to load data via intent using URLs like:

Configure project: https://getodk.org/app/collect/configure?data=<base64-encoded-settings-data>

Load form data: https://getodk.org/app/collect/load?d[/path/to/node]=value&d[/path/to/node]=value

2 Likes