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

Prevent most common restore crashes #5366

Merged
merged 4 commits into from
Dec 8, 2022
Merged

Conversation

seadowg
Copy link
Member

@seadowg seadowg commented Nov 25, 2022

Work towards #5240

This fixes crashes that occur when restoring to the quit form dialog or the form hierarchy which are currently the most "popular" crashes for v2022.4. Both cases result in slightly strange behaviour:

  • Restoring to the quit form dialog will show the hierarchy and then the dialog when you exit the hierarchy. Additionally, the dialog title will just be "no form loaded".
  • Restoring to the hierarchy will show normal form entry instead of the hierarchy

Both these quirks are definitely preferable to a crash and shouldn't be a huge problem for users (and I believe both would have been existing problems in earlier versions). I think we should merge this with them, and then update the issue so that we can fix them when we deal with the other related crashes and problems.

I ended up changing the structure of QuitFormDialogFragment to accommodate the restore scenario (where the dialog is recreated by the Activity before the form has loaded) and it led me down a path I've considered before (passing dependencies in Fragment constructors instead of using Dagger), but never really had the push to get there. That's discussed in https://github.com/getodk/collect/pull/5366/files#r1032477176, and I'd be especially interested in thoughts from @grzesiek2010.

What has been done to verify that this works as intended?

New tests where possible and verified manually.

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

Discussed inline.

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?

Should just fix restoring (the process of reproducing that is in the issue) for the quit form dialog and form hierarchy cases. It'd be good to test these flows as well as the dialog and hierarchy in more normal contexts.

Before submitting this PR, please make sure you have:

  • run ./gradlew checkAll and confirmed all checks still pass OR confirm CircleCI build passes and run ./gradlew connectedDebugAndroidTest locally.
  • 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

@@ -60,6 +57,12 @@ public class QuitFormDialogFragment extends DialogFragment {
private FormEntryViewModel formEntryViewModel;
private Listener listener;

private final FormSaveViewModel.FactoryFactory formSaveViewModelFactoryFactory;

public QuitFormDialogFragment(FormSaveViewModel.FactoryFactory formSaveViewModelFactoryFactory) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I've been going back and forth on if we should be thinking about replacing Dagger in Fragments with Fragment constructors (and FragmentFactory) and the direction here is a big push there. I'm thinking we should look at restructuring how we generally do ViewModel factories and create one per Activity that can then be passed to Fragment constructors. That would mean that here the constuctor could just be:

class QuitFormDialogFragment(private val viewModelFactory: ViewModelProvider.Factory) {

...and then we'd have a single factory for FormEntryActivity:

class Factory(...dependencues...) : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            when (modelClass) {
                FormEntryViewModel::class.java -> { ... }
                FormSaveViewModel::class.java -> { ... }
                ...
            }
        }
}

As an aside, this approach would also make moving to Hilt (#4940) easier, as we'd have less of a problem with the incompatibility with Fragment.

I'd like to have finished the though in this PR, but reorganizing the view model construction for FormEntryActivity is probably not something we'd want to release as part of a hotfix, so I think it makes sense to discuss here briefly, and then create an issue (or I just follow up immediately with a different PR).

Copy link
Member

Choose a reason for hiding this comment

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

I agree if we can use factories and constructors to pass dependencies to fragments we should do that instead of suing any DI tool.

@@ -145,6 +145,11 @@ public void onCreate(Bundle savedInstanceState) {
formEntryViewModel = new ViewModelProvider(this, formEntryViewModelFactory).get(FormEntryViewModel.class);

FormController formController = formEntryViewModel.getFormController();
if (formController == null) {
finish();
Copy link
Member Author

Choose a reason for hiding this comment

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

This is how the previous version of Collect behaved, but there was no test, and we ended up missing the behaviour change in code review.

@seadowg seadowg marked this pull request as ready for review November 25, 2022 14:32
@seadowg seadowg added the high priority Should be looked at before other PRs/issues label Nov 25, 2022
@seadowg seadowg added this to the v2022.4.1 milestone Nov 25, 2022
@grzesiek2010
Copy link
Member

grzesiek2010 commented Nov 29, 2022

Wouldn't it be easier in case of QuitFormDialogFragment not to recreate it at all? It's not a crucial dialog so to me it could disappear after rotating a device or reopening the app if it makes the implementation simpler. I think in many places we tried to much to implement some dialogs using DialogFragment what was not necessary and only made some stuff more complex. This might be a good example of it.

@seadowg
Copy link
Member Author

seadowg commented Nov 29, 2022

Wouldn't it be easier in case of QuitFormDialogFragment not to recreate it at all? It's not a crucial dialog so to me it could disappear after rotating a device or reopening the app if it makes the implementation simpler. I think in many places we tried to much to implement some dialogs using DialogFragment what was not necessary and only made some stuff more complex. This might be a good example of it.

So you mean just replace it with a standard AlertDialog (or probably a MaterialAlertDialog)? I'm definitely not opposed to that. You're definitely right that there are places where we've probably overvalued consistency on rotation and are having to pay a heavy cost in maintenance and that this is probably one of them (unless there's a good reason I can't think of right now).

How about we leave this as-is for a hot fix, and then I convert QuitFormDialogFragment to a simple dialog as part of the refactor I mention in #5366? I'm mostly done with that anyway.

@grzesiek2010
Copy link
Member

So you mean just replace it with a standard AlertDialog (or probably a MaterialAlertDialog)? I'm definitely not opposed to that. You're definitely right that there are places where we've probably overvalued consistency on rotation and are having to pay a heavy cost in maintenance and that this is probably one of them (unless there's a good reason I can't think of right now).

Yes simple MaterialAlertDialog should work here unless I'm missing something...

How about we leave this as-is for a hot fix, and then I convert QuitFormDialogFragment to a simple dialog as part of the refactor I mention in #5366? I'm mostly done with that anyway.

Whatever is more convenient for you, if you prefer to do that in this way I'm ok with it.

@seadowg
Copy link
Member Author

seadowg commented Nov 29, 2022

Yes simple MaterialAlertDialog should work here unless I'm missing something...

Yeah I think so? But also wondering if I've missed something here.

Whatever is more convenient for you, if you prefer to do that in this way I'm ok with it.

Ace. I think it'll be easier to follow-up.

@grzesiek2010
Copy link
Member

But also wondering if I've missed something here.

You will be implementing that change so if there is anything we might be missing now you should catch it later. We can also ask @lognaturel if she agrees that this dialog is not important enough to be a DialogFragment.

@srujner
Copy link

srujner commented Dec 8, 2022

Tested with Success!

Verified on Android 13

Verified cases:

  • Switching to a different app during:
    • Save/Ignore dialog
    • Permission dialogs
    • Hierarchy view
  • Background processing set to "No"
  • Minimize the app, rotate the screen
  • Select one map widgets
  • Restoring to the quit form dialog shows the hierarchy
  • Restoring to the hierarchy shows normal form entry instead of the hierarchy
  • Restoring in Edit Saved Form, View Sent Form

@dbemke
Copy link

dbemke commented Dec 8, 2022

Tested with Success!

Verified on Androids: 10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
behavior verified high priority Should be looked at before other PRs/issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants