Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add question type that shows preview and prints to Android-connected printer #5854

Merged
merged 31 commits into from
Jan 12, 2024

Conversation

grzesiek2010
Copy link
Member

@grzesiek2010 grzesiek2010 commented Nov 28, 2023

Closes #5831

Why is this the best possible solution? Were any other approaches considered?

We have decided to use the Android printing API because it's versatile and should work with different printers. The API offers three different ways of printing and the one we have chosen is with HTML because in our case we can relatively easily build such HTML docs using the calculation column and the concat function. The sample from the form attached below looks like:

concat("
<qrcode width=’150’ height=’150’>First name: ", ${first_name}, "Last name: ", ${last_name}, "Age: ", ${age}, "Email: ", ${email}, "Phone number: ", ${phone_number}, "</qrcode>
</br>
<img width=’150’ height=’150’ src=’”, ${photo}, “’>
<table>
   <tr>
      <td>First name</td>
      <td>", ${first_name}, "</td>
   </tr>
   <tr>
      <td>Last name</td>
      <td>", ${last_name}, "</td>
   </tr>
   <tr>
      <td>Age</td>
      <td>", ${age}, "</td>
   </tr>
   <tr>
      <td>Email</td>
      <td>", ${email}, "</td>
   </tr>
   <tr>
      <td>Phone number</td>
      <td>", ${phone_number}, "</td>
   </tr>
</table>
"
)

I said it's relatively easy because the more complex the expected output is the more args the function will have. The problem with this function is that every time we want to add an expression that should be evaluated like ${first_name}, ${last_name} etc. above we need to add them in as separate args because that's how this function works what makes creating bigger documents difficult and error-prone.
It would be good to have a new function like:

fill_template(“<qrcode>length: ${length} height: ${height}</qrcode>”)

That would allow us to have one string, like in kotlin for example. However, this is something we can add separately later.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

This introduces a new question type, so the changes seem to be isolated and should not affect other functionalities. Given that, we can (almost) fully focus on testing what's new. The only thing that I would verify to avoid regression is the old printer question type. The old one should be created if the appearance starts with printer and the new one if it is just printer:

  • printer then start the new printer displayed
  • printer:printer:org.opendatakit.sensors.ZebraPrinter the old printer widget should be displayed

Do we need any specific form for testing your changes? If so, please attach one.

I've created one sample form:
Printing.xlsx

Does this change require updates to documentation? If so, please file an issue here and include the link below.

Yes getodk/docs#1720

Before submitting this PR, please make sure you have:

  • added or modified tests for any new or changed behavior
  • run ./gradlew checkAll and confirmed all checks still pass OR confirm CircleCI build passes and run ./gradlew connectedDebugAndroidTest locally.
  • added a comment above any new strings describing it for translators
  • verified that any code or assets from external sources are properly credited in comments and/or in the about file.
  • verified that any new UI elements use theme colors. UI Components Style guidelines

@grzesiek2010 grzesiek2010 force-pushed the COLLECT-5831 branch 4 times, most recently from 0697060 to c471c67 Compare November 29, 2023 22:02
@lognaturel
Copy link
Member

lognaturel commented Nov 30, 2023

It's magical, @grzesiek2010! 🎉

I notice it's taking a very long time to load the preview screen after tapping the "Initiate printing" button (I think it should just be "Print"). I also keep getting "ODK Collect isn't responding" after tapping the button including when the preview screen is up and after I've exited out of printing.

I think it's related to the image manipulation. It seems less bad when I don't take a picture (but I do still get not responding bottom sheets every few seconds after I've printed, even when on the main menu screen).

@grzesiek2010
Copy link
Member Author

I notice it's taking a very long time to load the preview screen after tapping the "Initiate printing" button

I've added a progress dialog so that it's clear that something is happening (the HTML is being parsed).

I think it should just be "Print"

Ok, done.

I think it's related to the image manipulation. It seems less bad when I don't take a picture (but I do still get not responding bottom sheets every few seconds after I've printed, even when on the main menu screen).

I've updated the way images are added to the HTML. Now I don't convert them to bitmaps and include data URLs but passing a path so maybe it will be faster.

@lognaturel please test if now it works faster and does not cause any ANRs.

@lognaturel
Copy link
Member

Can't reproduce any of the issues I'd highlighted. 👍

@grzesiek2010 grzesiek2010 marked this pull request as ready for review December 12, 2023 22:20
@grzesiek2010 grzesiek2010 changed the title Printing Add question type that shows preview and prints to Android-connected printer Dec 12, 2023
@lognaturel lognaturel requested review from seadowg and removed request for lognaturel December 14, 2023 17:42
Copy link
Member

@seadowg seadowg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so cool! I've only had a chance to run through the general structure, and that's mostly looking good to me! A couple of things though:

  1. I think it'd be nicer to have PrinterWidgetViewModel passed into the widget via the constructor (probably as an interface defined in widgets that the ViewModel implements) so that the Activity is in control of constructing it and making observations on its data. This limits the amount of dependencies going into the purely view layer and also means the widget doesn't need to understand anything about the lifecycle it's part of.
  2. I'd be interested in if it's possible to do the HTML parsing/manipulation without adding a new dependency (and not increasing the APK size as much) using some of the XML parsing libraries we already use (like KXML2 for instance).

@grzesiek2010
Copy link
Member Author

grzesiek2010 commented Dec 20, 2023

I think it'd be nicer to have PrinterWidgetViewModel passed into the widget via the constructor (probably as an interface defined in widgets that the ViewModel implements) so that the Activity is in control of constructing it and making observations on its data. This limits the amount of dependencies going into the purely view layer and also means the widget doesn't need to understand anything about the lifecycle it's part of.

Do you mean building the ViewModel in FormFillingActivity then passing to ODKView and then from ODKView to WidgetFactory and from WidgetFactory to the widget? Seems like a long path.

I'd be interested in if it's possible to do the HTML parsing/manipulation without adding a new dependency (and not increasing the APK size as much) using some of the XML parsing libraries we already use (like KXML2 for instance).

I tried kxml2 but it is primarily designed for parsing XML and it didn't work well with HTML. I think we should use a reliable library like Jsoup to make sure even complex HTML constructions work well. We don't want to test such cases manually.

@seadowg
Copy link
Member

seadowg commented Jan 3, 2024

Do you mean building the ViewModel in FormFillingActivity then passing to ODKView and then from ODKView to WidgetFactory and from WidgetFactory to the widget? Seems like a long path.

Yes. We already use this approach for a few ViewModels - we pass the FormEntryViewModel and the FormSaveViewModel (as a QuestionMediaManager which I prefer to the way FormEntryViewModel is passed). It's probably yet another sign that ODKView should be Fragment (it even has its own lifecycle), but that's probably too big a conversion to do here.

Copy link
Member

@seadowg seadowg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grzesiek2010 grzesiek2010 force-pushed the COLLECT-5831 branch 3 times, most recently from 1aafbd5 to bc817ff Compare January 5, 2024 09:32
.circleci/config.yml Outdated Show resolved Hide resolved
@grzesiek2010 grzesiek2010 force-pushed the COLLECT-5831 branch 3 times, most recently from b5e9a71 to 2fac81b Compare January 9, 2024 00:24
@@ -52,6 +52,10 @@ public static String getAnswerText(FormEntryPrompt fep, Context context, FormCon
IAnswerData data = fep.getAnswerValue();
final String appearance = fep.getQuestion().getAppearanceAttr();

if (appearance != null && appearance.equals(Appearances.PRINTER)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no test for this change? Maybe we should rename this method to something like getHierarchySummary and write a unit test for it that makes sure the printer questions always return "". It'd be nice for the helper to live in the new hierarchy module, but I realise that was actually created in a different PR!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge #5885 first then and I can add a new test in the same package.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to merge this first so we can get it out as a beta ASAP. Are you able to add a test in #5885?

@seadowg seadowg merged commit fb370b3 into getodk:master Jan 12, 2024
6 checks passed
@dbemke
Copy link

dbemke commented Jan 15, 2024

I'm not sure what is the expected when it comes to how the data from questions is represented in the printed version. Here's an example of an answer with new lines and a photo (that changes shape).
printing

What information does the generated QR contain?

@grzesiek2010
Copy link
Member Author

I'm not sure what is the expected when it comes to how the data from questions is represented in the printed version. Here's an example of an answer with new lines and a photo (that changes shape).

Everything is up to you. If you used the form I have attached the code that generates the document contains:
<img width=’150’ height=’150’ src=’”, ${photo}, “’>
That means the size of the photo should be 150x150. You can change it.

What information does the generated QR contain?

again if you used my form it's this part:
<qrcode width=’150’ height=’150’>First name: ", ${first_name}, "Last name: ", ${last_name}, "Age: ", ${age}, "Email: ", ${email}, "Phone number: ", ${phone_number}, "</qrcode>

you can add/remove something you can change the size of the code as well.

@srujner
Copy link

srujner commented Jan 18, 2024

I Noticed two strange behaviors:

  1. When I set Background Processing to NO, then after generating PDF file and saving it, the PDF file has 0B and I'm not able to open it in any app.

  2. When I change the color of the generated PDF file to Black and White and save it, then when opened with Printer+ app the photo is black and white, but when I use the default google app the photo is in color. Is this correct?

@grzesiek2010
Copy link
Member Author

Both those issues seem like something not directly related to Collect. If the preview is generated correctly that's where our job ends. Could you try to do the same with other apps that print documents and compare the behavior?

@srujner
Copy link

srujner commented Jan 19, 2024

@grzesiek2010 Checked on different apps and it works in similar way.

Tested with Success!

Verified on device with Android 12,13

Verified cases:

@dbemke
Copy link

dbemke commented Jan 19, 2024

Tested with Success!

Verified on device with Android 10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add question type that shows preview and prints to Android-connected printer
5 participants