Preferred Architecture of Hard Challenge?


#1

Bill,

I’m wondering what you’re preferred architecture would be for the hard challenge. There are two rather straight forward options:

  1. CrimeFragment shows and passes mDate to an “Options Dialog,” which in turns shows and passes mDate to DatePickerFragment or TimePickerFragment. In other words, mDate (among other items) gets passed down the line.
  2. CrimeFragment shows and “Options Dialog,” and upon onActivityResult() shows and passes date to DatePickerFragment or TimePickerFragment. In other words, the Options Dialog drives the flow.

I attempted both, but found that in order for Option 1 to work you need to “boomerang” the result of DatePickerFragment or TimePickerFragment back to CrimeFragment (instead of passing back to Option Dialog, and then having Option Dialog pass back to CrimeFragment); the Option Dialog fragment gets an onDestroy() call when it’s view is destroyed. This could have been an elegant solution that reduced coupling (CrimeFragment only knows about Option Dialog, freeing up Option Dialog to know about Date Pickers and Time Pickers and possibly other pickers in the future). Maybe there still is a way…?

Option 2 couples CrimeFragment to all the dialog fragments, but maybe that’s unavoidable. I get a funny code smell that I need to if-else / switch in CrimeFragment’s onActivityResult() to determine which DialogFragment is calling back; the behavior we desire is to get a callback about a modification to “date,” and DatePicker and TimePicker do this (Dialog Picker is the odd man out, which is what lead me to try implementing Option 1). I think we could handle that without any conditional code.

How would you manage these object interactions?


#2

Good question. I agree with your feeling that the first option is better. I’d like to explain the issue in a bit more detail for other folks reading the thread, because it’s an interesting one.

Here’s what we’re referring to by the idea of “boomerang”. Let’s say we’ve got DatePickerFragment, TimePickerFragment, and then a third fragment OptionsFragment that chooses between the other two. To achieve this with the “boomerang” method, we’d start by starting OptionsFragment:

FragmentManager fm = getActivity()
        .getSupportFragmentManager();
OptionsFragment dialog = OptionsFragment
    .newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
dialog.show(fm, DIALOG_OPTION);              	

Then when the user selected one or the other option in OptionsFragment, you’d display the next dialog - but you’d pass along the original target fragment and target request code:

FragmentManager fm = getActivity()
        .getSupportFragmentManager();
DialogFragment dialog;

if (isSelectingTime) {
    dialog = new TimePickerDialog();
} else {
    dialog = new DatePickerDialog();
}

dialog.setTargetFragment(getTargetFragment(), getTargetRequestCode());
dialog.show(fm, DIALOG_TIME_OR_DATE);              	

Note the next to last line - that’s the “boomerang” Evan refers to.

Of course, the reason that this is a problem is that while it works in our case here, it doesn’t work in general. That’s because CrimeFragment has no way to tell which fragment actually serviced its request in onActivityResult(). Here, that’s no problem – TimePickerDialog and DatePickerFragment both return a date. What if they returned different kinds of objects? You could hack around it, but there’s no clean solution.

The best way to fix this is to keep OptionsFragment from disappearing. This is not possible with the AlertDialog method we used in the book, but you can accomplish this by changing your implementation up a bit. Instead of overriding onCreateDialog, override onCreateView and construct a view as you normally would in Android. By doing that, you can prevent OptionsFragment from being destroyed when Date or Time are selected, which will allow you to set OptionsFragment as the target fragment.

If you really need to use AlertDialog, there are hacks to get around the auto dismissal, too - check out this answer on SO for more info: http://stackoverflow.com/a/15619098/432927