onPrepareActionMode not called when creating ActionMode

AndroidAndroid Support-LibraryAndroid ActionmodeAndroid Appcompat

Android Problem Overview


I just finished adjusting one of my apps to the new v22.1.1 support & appcompat libraries, see here and here for more details. When I did some testing, something was off with the ActionModes I'm using.

When you start a ActionMode using a startSupportActionMode() call - it does not matter if you use the now deprecated ActionBarActivity base class or the new AppCompatActivity base class - onPrepareActionMode() is not being called.

In previous versions, including v21.0.3 & v22.0.0, onPrepareActionMode() was called automatically, when the ActionMode was initially created using startSupportActionMode().

I tested it on a 2.2, 4.4.2 and 5.0 device, so it seems not to be version dependent.

Does anyone know, if this is intended behavior, that's been introduced in v22.1.1, or a bug?

I found this issue, but there's not a lot of feedback here...

Edit May 11 2015:

As mentioned in the Android issue tracker 159527, this issue not only affects the v22.1.x of appcompat and the support library, but also the 5.1 Android implementation.

Two possible temporary solutions at the moment, a general one:

@Override
public ActionMode startSupportActionMode(final ActionMode.Callback callback) {
  // Fix for bug https://code.google.com/p/android/issues/detail?id=159527
  final ActionMode mode = super.startSupportActionMode(callback);
  if (mode != null) {
    mode.invalidate();
  }
  return mode;
}

and a 'quick and dirty' one (when you instantiate your ActionMode):

final ActionMode actionMode = startSupportActionMode(new MyActionMode());
if(actionMode != null) {
	actionMode.invalidate();
}

If you don't use appcompat (ActionBarActivity/AppCompatActivity) you need to replace startSupportActionMode() with startActionMode().

Unfortunately it's still not clear if this is intended new behavior or a bug. According to the API doc it's a bug/regression, but who knows...

Android Solutions


Solution 1 - Android

I have created a demo and its working fine,onPrepareActionMode gets called each time the action mode is shown. Always called after onCreateActionMode, but may be called multiple times if the mode is invalidated. [ I request any one to make a small edit. I need status bar color same as that of toolbar but dynamically, you can see unnecessary Drawyer Layout is used to achieve this effect,But if i remove drawyer layout the status bar color not changes according to toolbar color. In utility you can see by default default theme color is red, toolbar intially gets red color but status bar does not, only and only if i remove drawyer layout. I need to do this using style. ] Create a resource layout and name it = > action_mode_activity

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:orientation="vertical"
            app:insetForeground="#4000">

            <include
                android:id="@+id/toolbar"
                layout="@layout/toolbar" />

            <EditText
                android:id="@+id/editTextCopy"
                android:layout_width="fill_parent"
                android:layout_height="40dp"
                android:layout_marginTop="19dp"
                android:ems="10"
                android:inputType="textMultiLine"
                android:text="Long click to share!">

                <requestFocus />
            </EditText>
        </LinearLayout>
  
</android.support.v4.widget.DrawerLayout>

Create a Activity name it ActionModeActivity

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.example.deepakpawar.demolearning.R;
import com.example.deepakpawar.demolearning.demo.load.recycler.Utils;

/**
 * Created by Deepak Pawar on 9/24/2015.
 */
public class ActionModeActivity extends AppCompatActivity implements View.OnLongClickListener, ActionMode.Callback {

    EditText editTextCopy;
    android.view.ActionMode mActionMode;
    private Toolbar toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Utils.onActivityCreateSetTheme(this);
        setContentView(R.layout.action_mode_activity);

        // 1. Get the editText
        editTextCopy = (EditText) findViewById(R.id.editTextCopy);

        // 2. add long-click listener
        editTextCopy.setOnLongClickListener(this);

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        if (toolbar != null) {
            setSupportActionBar(toolbar);
            ActionBar actionBar = getSupportActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeButtonEnabled(true);
            actionBar.setTitle("Android Students");
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        } 
    }

    @Override
    public boolean onLongClick(View view) {

        // if actionmode is null "not started"
        if (mActionMode != null) {
            return false;
        }
        // Start the CAB
        mActionMode = this.startActionMode(this);
        view.setSelected(true);
        return true;

    }

    // 4. Called when the action mode is created; startActionMode() was called
    @Override
    public boolean onCreateActionMode(android.view.ActionMode mode, Menu menu) {

        // Inflate a menu resource providing context menu items
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.action_menu, menu);
        return true;
    }

    // 5. Called when the user click share item
    @Override
    public boolean onActionItemClicked(android.view.ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_share:
                Toast.makeText(this, "Shared!", Toast.LENGTH_SHORT).show();

                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
        }
    }

    // 6. Called each time the action mode is shown. Always called after onCreateActionMode, but
    // may be called multiple times if the mode is invalidated.
    @Override
    public boolean onPrepareActionMode(android.view.ActionMode mode, Menu menu) {

        Toast.makeText(ActionModeActivity.this,"onPrepareActionMode Called ",Toast.LENGTH_SHORT).show();
        return false; // Return false if nothing is done
    }

    // 7. Called when the user exits the action mode
    @Override
    public void onDestroyActionMode(android.view.ActionMode mode) {
        mActionMode = null;
    }
}

//Utils class having method to change theme // i created it because need to change app theme dynamically import android.app.Activity;

public class Utils {
    private static int sTheme;
    public final static int THEME_DEFAULT = 0;
    public final static int THEME_WHITE = 1;
    public final static int THEME_BLUE = 2;

    /**
     * Set the theme of the Activity, and restart it by creating a new Activity of the same type.
     */

    public static int getsTheme() {
        return sTheme;
    }

    public static void changeToTheme(Activity activity, int theme) {
        sTheme = theme;
        activity.recreate();
//        activity.finish();
//        activity.startActivity(new Intent(activity, activity.getClass()));
    }

    /**
     * Set the theme of the activity, according to the configuration.
     */
    public static void onActivityCreateSetTheme(Activity activity) {
        switch (sTheme) {
            default:
            case THEME_DEFAULT:
                activity.setTheme(R.style.FirstTheme);
                break;
            case THEME_WHITE:
                activity.setTheme(R.style.SecondTheme);
                break;
            case THEME_BLUE:
                activity.setTheme(R.style.Thirdheme);
                break;
        }
    }
}

v21-themes.xml

<resources>

    <style name="AppTheme" parent="AppTheme.Base">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>
        <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
        <item name="android:windowSharedElementExitTransition">@android:transition/move</item>

        <item name="android:actionOverflowButtonStyle">@style/Widget.ActionButton.Overflow</item>
       <!-- <item name="android:navigationBarColor">@color/PrimaryColor</item>-->

        <item name="windowActionBar">false</item>
        <item name="windowActionModeOverlay">true</item>

        <!-- To Make Navigation Drawer Fill Status Bar and become Transparent Too -->
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>


<!--//if darker status bar needed-->
       <!-- <item name="android:windowTranslucentStatus">true</item>-->
    </style>


    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/PrimaryColor</item>
        <item name="colorPrimaryDark">@color/PrimaryDarkColor</item>
        <item name="colorAccent">@color/AccentColor</item>
        <item name="android:textColorPrimary">@color/TextPrimaryColor</item>
        <item name="android:windowBackground">@color/WindowBackground</item>
    </style>

    <style name="Widget.ActionButton.Overflow" parent="@android:style/Widget.Holo.ActionButton.Overflow">
        <item name="android:contentDescription">@string/accessibility_overflow</item>
    </style>


    <!-- style for the tool bar backgrounds -->
    <style name="ToolBarStyle" parent="ToolBarStyle.Base" />

    <style name="ToolBarStyle.Base" parent="">
        <item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
        <item name="theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
    </style>

    <style name="ToolBarStyle.Event" parent="ToolBarStyle">
        <item name="titleTextAppearance">@style/TextAppearance.Widget.Event.Toolbar.Title</item>
    </style>

    <style name="TextAppearance.Widget.Event.Toolbar.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
        <!--Any text styling can be done here-->
        <item name="android:textStyle">normal</item>
        <item name="android:textSize">18sp</item>
        <item name="android:textColor">#000000</item>
    </style>

    <!-- Customize your theme example here. -->

    <style name="FirstTheme">
        <item name="android:textColor">#FF0000</item>
        <item name="colorPrimary">#FF0000</item>
        <item name="colorPrimaryDark">#ff0000</item>
        <item name="colorAccent">#ff0087</item>
        <item name="android:shadowColor">#00ccff</item>
        <item name="android:shadowRadius">1.5</item>
        <item name="android:shadowDy">1</item>
    </style>

    <style name="SecondTheme">
        <item name="android:textColor">#00FF00</item>
        <item name="colorPrimary">#00FF00</item>
        <item name="colorPrimaryDark">#00FF00</item>
        <item name="colorAccent">#00FF90</item>
        <item name="android:shadowColor">#00ccff</item>
        <item name="android:shadowRadius">1.5</item>
        <item name="android:shadowDy">1</item>
    </style>

    <style name="Thirdheme">
        <item name="android:textColor">#0000F0</item>
        <item name="colorPrimary">#0000F0</item>
        <item name="colorPrimaryDark">#0000F0</item>
        <item name="colorAccent">#0090F0</item>
        <item name="android:shadowColor">#00ccff</item>
        <item name="android:shadowRadius">1.5</item>
        <item name="android:shadowDy">1</item>
    </style>


    <style name="AppCompatAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
        <item name="colorAccent">#FFCC00</item>
        <item name="android:textColorPrimary">#FFFFFF</item>
        <item name="android:background">#5fa3d0</item>
    </style>

</resources>

Solution 2 - Android

I had a similar problem.

After I heightened the value of "compileSdkVersion" and "buildToolsVersion" of build.gradle, I found out that onPrepareActionMode was not called.

  • compileSdkVersion : 21 to 26
  • buildToolsVersion : 21.1.2 to 26.0.2

So I moved my code from (A) to (B). (please see below)

I'm not sure if it's the right solution, but it works.

Here is the excerpt of my code.

list1 = findViewById(R.id.listView1);

list1.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        //(A)
	    //MenuItem menuItem1 = menu.findItem(R.id.menu_item1);
		//menuItem1.setVisible(false);
        return false;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_action_mode, menu);

        //(B)
	    MenuItem menuItem1 = menu.findItem(R.id.menu_item1);
		menuItem1.setVisible(false);

        return true;
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondarksagaView Question on Stackoverflow
Solution 1 - AndroidDeepakPanwarView Answer on Stackoverflow
Solution 2 - AndroidSankameView Answer on Stackoverflow