Question

Let's see this situation: you've got a CAB open with DONE & DISCARD options on a screen with a form to be filled and you won't accept an error value for a field. Hence, the user should either fill in a valid value (and press Back/Done for it to be accepted) or press Discard on the CAB.

Pressing DONE initiates onDestroyActionMode() immediately, after which the CAB is closed. So, this is the place (beside the onBackPressed()) where the check for validity is initiated.

The problem is, if creating a new ActionMode there (if the form validity check fails), the recursive loop will be started (as the new CAB will first start closing the old one and so on) --> StackOverflowError.

I tried creating a state variable to prevent the StackOverflowError, but in that case it works only every second time (and only onBackPressed()) :-/

So, the question: how do I keep the CAB open (or how to re-open a new one) immediately after onDestroyActionMode() was called on it?

Was it helpful?

Solution

The first commenter is indeed correct. Action modes are transient and can be finished by the user at any time by design. They're for representing something like a batch selection that you can add to or remove from before taking an action on the full set, mail triage and text editing being two examples. Finishing an action mode should be non-destructive, just like canceling a dialog. It should not be treated as a confirmation step.

Speaking of text editing, you're heavily implying that your form involved here contains text fields. What happens when a user highlights some text and the TextView starts its own editing action mode? Starting an action mode will implicitly finish any current action mode.

You should use another affordance to express an edit in progress.

OTHER TIPS

Answering the core question: how can you recreate an ActionMode after a back press:

You can postDelayed a Runnable to a Handler to recreate an ActionMode. The delay needed can vary a little between devices, I've found 200ms to work with everything I've tried. In an Activity or FragmentActivity try these snippets:

RepeatActionModeRunnable mRepeatActionModeRunnable;
Handler mHandler = new Handler();

@Override
protected void onPause() {
    mHandler.removeCallbacks(mRepeatActionModeRunnable);
    super.onPause();
}

private class RepeatActionModeRunnable implements Runnable {
    ActionMode.Callback mRepeatActionMode;
    public RepeatActionModeRunnable(ActionMode.Callback actionMode) {
        mRepeatActionMode = actionMode;
    }
    @Override
    public void run() {
        mActionMode = startActionMode(mRepeatActionMode);
    }
}

Then in onDestroyActionMode you can use this when you need to (i.e. you'll no doubt want some logic wrapping this to detect if it should or shouldn't be recreated):

mHandler.removeCallbacks(mRepeatActionModeRunnable);
mRepeatActionModeRunnable = new RepeatActionModeRunnable(new SomeActionMode());
mHandler.postDelayed(mRepeatActionModeRunnable, 200);

As for whether CAB is or isn't to be used for forms/data input, such situations are well suited to a "call for action" and that is also the reason d'etre for the ActionMode pattern. The existence of hurdles/nuances/bugs such as recreating after back button press and if a user selects text to edit should not constitute rationale for it to not be OK to use, rather for it to be seen as perhaps not the path of least resistance in terms of a solution (it's a challenge!). The existence of the 'Done & Discard' pattern alone also shouldn't constitute rationale for not using ActionMode for situations involving data input. These are all possibilities that may suit your situation better or worse rather than a right or wrong way.

PS: regarding how to deal with TextSelectionCAB, here's a solution: Detect ActionMode nesting

Actually, You can do something like this, then there won't be StackOverflowException anymore.:

 @Override
        public void onDestroyActionMode(ActionMode mode) {
            //your code
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    startSupportActionMode(mActionCallBack);
                }
            });
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top