-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Dialogs - general approach #1226
Conversation
@grzesiek2010 I think we can move ahead with this approach 👍 |
How are different actions accounted for with this approach? For example if you wanted to redo the admin password reset dialog, would you extend |
@srsudar this approach assumes that if you want to add new dialog you have to add new action here: https://github.com/opendatakit/collect/pull/1226/files#diff-172d9a1f2eb7a4e8c8032f12eea2fb07R29 and new case here https://github.com/opendatakit/collect/pull/1226/files#diff-172d9a1f2eb7a4e8c8032f12eea2fb07R80 |
I would prefer that the actions be defined where the dialogs are created, perhaps with something like this. That said, if you're both happy with the switch/case approach, it works for me! I'm much happier with where |
Interesting. That seems little odd to me, but I do think it would be better than it currently exists. The things that I find odd about it:
I am understanding the design, @grzesiek2010 ? What do you think? |
Very well described, @srsudar, thanks so much for the really precise description of concerns. Are you happy with introducing a new |
I'm not opposed to The code in |
Thanks so much @srsudar for taking some time out of your refactoring to help with ours! 😊 It's always nice to have a couple of opinions. @grzesiek2010, @srsudar just finished his PhD at University of Washington 👏 and has been involved in ODK in various ways. I find he has exceptional code taste so that's why I like to pull him into conversations like this one. How about trying out a callback system for actions and not introducing |
Thanks for the kind words, @lognaturel ! Makes me think I should go fix all the problems in that repo I linked. Thanks for letting me chime in. I'm also happy to butt out and shut up since I'm writing my thesis and I won't be the one implementing it. =) |
Hah, I certainly appreciate any advice I can get. Like you said, there are a lot of dialogs so any thoughts on refining the approach are valuable. Sending you good thesis vibes! |
sure nice to met you @srsudar and please accept my congratulations! I've just improved my previous solution so please review again. I kept the base class so it might be the only thing you don't like but generally it looks better now I hope. |
Can callbacks go into Take this, for example: private void buildResetSettingsFinalDialog(String message) {
// final object to use it in the callback closure
final MyObject myObject = createMyObjSomehow(this);
CollectDialogBundle.Builder dialogBuilder = new CollectDialogBundle.Builder();
dialogBuilder
.setIcon(android.R.drawable.ic_dialog_info)
.setDialogTitle(getContext().getString(R.string.reset_app_state_result))
.setDialogMessage(message)
.setPositiveButtonText(getContext().getString(R.string.ok))
.setOnPositiveButtonClickCallback(new CollectDialogBundle.SingleButtonCallback() {
@Override
public void onClick(Dialog dialog) {
// This would be a problem on rotation
myObject.doSomethingFancy();
dialog.dismiss();
((AdminPreferencesActivity) context).recreate();
}
})
.setCancelable(false);
((CollectAbstractActivity) context).buildDialog(dialogBuilder.build());
} I think that the call to Am I wrong about this? I haven't run it and I'd believe I'm missing something. How does the material-dialogs project handle callbacks? Do they use |
It looks like the Android documentation recommends making use of That approach could lead to a lot of subclasses, although they could still make use of the consolidated Top level subclasses would be my preferred approach, I think, as it would lend itself to tests. To be fair, though, I haven't used If people didn't want to rely on subclasses, In the model I'm imagining for a subclass approach, each public class ChangePasswordDialogFragment extends CollectDialogFragment {
public interface ChangePasswordCallbacks {
void onDialogPositiveClick(String pwd);
}
ChangePasswordCallbacks callbacks = null;
// snip
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Android docs wrap this in a try catch
callbacks = (ChangePasswordCallbacks) activity;
}
@Override
public void onCreateDialog(Bundle savedInstanceState) {
// snip
builder.setPositiveButton(resourceId, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
if (callbacks == null) { return; }
callbacks.onDialogPositiveClick(getPassword());
}
});
// snip
} public class AdminPreferencesActivity implements ChangePasswordCallbacks {
PasswordManager passwordMgr; // see note below
// snip
void onDialogPositiveClick(String pwd) {
// The Activity itself isn't responsible for saving the password in my ideal world.
// This way no code lives in the Activity other than creating objects and
// hooking up callbacks. This makes it easier to write tests without needing to
// spin up a UI, which would have to happen if the Activity itself saves the code.
passwordMgr.setPassword(pwd);
}
} Does that look reasonable or bizarre? I've only looked at a tiny subset of the dialogs that exist in the app. Maybe this is inappropriate/overkill for most of them. This could also be a bigger change than is being aimed at with this PR. Edit: added call to |
Looking at this again, it looks like the example I've been using of resetting the admin password might not fit with this more general Dialog refresh. The preferences use Nevertheless, here is a an example of what my subclass-heavy approach would look like. I opted to not provide callbacks to the hosting activity and instead do everything inside the There are several things that I like about doing it this way.
Note that to get the Robolectric tests to run I had to use storix's |
You are right and it may be a problem, that's why the previous solution was better (worked in a proper way in that case). My aim was to prepare a solution to avoid creating separate class for each dialog (currently we have over 40 dialogs and it will continue to rise as we actively work on the app adding new features etc.). That's why I don't like the solution with I really like my current proposal but I agree with you that it may be faulty when an activity is recreated. I was looking at implementation of https://github.com/afollestad/material-dialogs and it looks similar to mine but they just dissmis a dialog when an activity is recreated so they don't have this problem :) We can of course do the same but I really want to keep our dialogs. Maybe a perfect solution doesn't exist... |
If the dialog is just displaying a message, I agree that a separate class might be overkill. If it has to do something, like changing a password, I don't think a separate class is a big deal. It could still employ a centralized builder approach to consolidate code and present a consistent style. I agree that it could end up creating a lot of callback interfaces, but that is only an issue for when the container In the cases where material-dialogs needs to communicate with the container All this being said, I don't have nearly as good a sense of the Collect codebase as the others here. I don't think it's a good idea to put callbacks in the |
@srsudar ok thank you very much for this discussion. I'll rethink everything again. @lognaturel @shobhitagarwal1612 and the others your opinion is welcome as well. |
Ok the third approach is ready. What do you think @srsudar @lognaturel |
Glancing through it (not building, running, rotating, etc), I think this structure looks great. There are a few things lurking in sections of the code that aren't the subject of this PR that I'm not sure about (saving a reference to the I could see a case being made for not actually using callbacks in this reset example, since the dialog could dismiss itself and then do Does the way you're getting |
Do you mean this?:
You are right that dialog is not a good example so I added another one. |
When I made my example of how I like dialogs to look I ran into trouble with:
If your code builds and runs, then you got around that problem, which is great. Do you still want opinions on #1259 ? If a dialog just shows text and doesn't do anything, I don't think it needs its own class. I don't know what will happen when some of the standard dialogs are pulled out into Will that behavior stay the same when moved to I don't think fixing all these problems is necessary for this PR. If you want to keep a straight forward, limited number of separate classes, approach to this PR like you did in #1259 in order to update the look of dialogs, I think that is reasonable. Longer term I would like to do things like pull out the dialog classes to less tightly coupled, more modular and more testable units. Is that at all helpful? I worry I've really derailed this discussion. Dialogs are tricky. You have looked in depth at all the dialogs being used in Collect--I have not. I would love if we get to a place like my example, where all the dialog machinery is mostly self-contained and tested. That should maybe happen in individual PRs, however, not on this PR. If the purpose of this PR is to solve the dialog problems, I think it will get too big. If the purpose of this PR is to make dialogs use material design, using only your general approach, defining callbacks in-line as they are currently in the codebase, seems good to me. The only thing to be careful of here is that the new dialogs handles rotation. This will depend on what each dialog is doing and how it is currently set up. |
Thank you @grzesiek2010 and @srsudar for your patience and willingness to experiment to get a solution that meets long-term needs. Forgive me if this should be obvious but why is I have to admit that I'm not loving any of these general-purpose solutions yet and I'm wondering whether we may have better luck looking at individual categories of dialogs one at a time rather than trying to solve every problem at once ("boiling the ocean!"). For example, I really only have one big complaint with the look and feel of dialogs and I documented it in #1265. That kind of dialog with save/ignore/cancel shows up in 5 different places and I think perhaps handling those first might be a nice, focused way to start with immediate benefit. It shows up every single time a user exits a form through the back button. The other glitches like disappearing on rotation and inconsistent coloring are annoying but are not dramatically affecting usability, I think. Please correct me if I'm wrong but I believe that neither this proposal nor the one in #1259 will make it easier to create that kind of dialog. I understand that @grzesiek2010's goal with this is more to clean up the code and I do think that's important but ideally it could be approached incrementally with other user benefits such as increased testing or better look and feel as @srsudar was getting at in his last message. @srsudar's admin password example has a lot of properties I like:
@grzesiek2010, I think you want to minimize the number of classes but I think that a larger number of classes with more reduced scope and responsibility might actually help most with readability and maintainability (and yes, testability). |
No thank you. That approach was not good as there was one general class and now I know that it's not possible to do that in a proper way using only one class for all dialogs.
I didn't investigate it carefully but I only made a quick investigation and I replaced this dialog using:
CollectDialogBundle.Builder dialogBuilder = new CollectDialogBundle.Builder();
and it works well so I think it's not a problem.
It's not necessary in this approach but I think it's nice to have this class especially for SimpleDialog class (in other cases we can set most of elements in dialog class of course). Generally I like this approach there is one class we can use for all simple dialogs that don't need any callbacks and for others we can create a separate class for each one. I think this is what we need. |
5d88a28
to
9da599e
Compare
Replaced by #1269 for now. Some of the ideas explored here may come back as more complex dialog types are added in. @grzesiek2010 can you please keep the branch around so we can refer back to it? |
@lognaturel this is my general proposal for managing dialogs. It contains one example - I replaced one dialog from admin settings to show how it works.
@shobhitagarwal1612 you are interested in this as well so please look at it and let me know what you think.