getActionProvider: item does not implement SupportMenuItem
Asked Answered
F

4

2

I am trying to implement a ShareActionProvider using the support library in a contextual action bar in my fragment. I face no issues implementing it in a normal action bar( onCreateOptionsMenu() ), but when i try it in the CAB ( onCreateActionMode() in MultiModeListener interface), I get the error :

getActionProvider: item does not implement SupportMenuItem; returning null

Looking at the Android source at https://cells-source.cs.columbia.edu/plugins/gitiles/platform/frameworks/support/+/30837f1095c803f332f4a1c3f0917c8afdd50156/v4/java/android/support/v4/view/MenuItemCompat.java, the problem seems to be because my MenuItem is not an instance of SupportMenuItem :

 public static ActionProvider getActionProvider(MenuItem item) {
    if (item instanceof SupportMenuItem) {
        return ((SupportMenuItem) item).getSupportActionProvider();
    }

    // TODO Wrap the framework ActionProvider and return it
    Log.w(TAG, "getActionProvider: item does not implement SupportMenuItem; returning null");
    return null;
}

Any ideas on how i can go about resolving this ?

Manifest :

<activity
            android:name=".myactivity_ActionBarActivity"
            android:theme="@style/Theme.AppCompat.Light"
            android:windowSoftInputMode="stateUnchanged">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>

Activity :

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;

public class myactivity_ActionBarActivity extends ActionBarActivity{
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.actionbaractivity_layout); //loads a fragment

    }
}

fragment :

import android.support.v7.widget.ShareActionProvider;
import android.support.v4.view.MenuItemCompat;
import android.view.MenuItem;
import android.view.Menu;
import android.support.v4.app.Fragment;

...
...

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    ...
    ...
    //Handle Action mode events
    myListView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
        private ShareActionProvider mShareActonProvider;
        ....
        ....
        @Override
        public boolean onCreateActionMode(ActionMode mode,
        Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.chatsession_contextmenu, menu);

            //get the ShareActionProvider from the menu item
            MenuItem item = menu.findItem(R.id.share_menu);
            mShareActonProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);

            return true;
        }
    }

    ...
    ...
}

Menu layout file :

<?xml version="1.0" encoding="utf-8" ?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:myapp="http://schemas.android.com/apk/res-auto">
        <item
            android:id="@+id/delete_menu"
            android:title="Delete message"
            myapp:showAsAction="ifRoom|withText"
            android:icon="@drawable/ic_action_discard">
        </item>

        <item
            android:id="@+id/share_menu"
            android:title="Share message"
            myapp:showAsAction="ifRoom|withText"
            android:icon="@drawable/ic_action_share"
            myapp:actionProviderClass="android.support.v7.widget.ShareActionProvider">
            </item>

    </menu>

Edit 1 :

The root of the problem seems to be the difference in the Menu object that is being passed as argument to onCreateActionMode(ActionMode mode, Menu menu) and onCreateOptionsMenu(Menu menu, MenuInflater inflater). Only the one in onCreateOptionsMenu has the MenuWrapperICS. Here is a screenshot of both objects in debug mode :

onCreateActionMode(ActionMode mode, Menu menu) :

enter image description here

onCreateOptionsMenu(Menu menu, MenuInflater inflater) :

enter image description here

Flavor answered 14/6, 2014 at 12:17 Comment(0)
F
5

The problem is that the MultipleModeListener interface extends the android.view.ActionMode.Callback, as can be seen in the source code at http://androidxref.com/4.4.2_r2/xref/frameworks/base/core/java/android/widget/AbsListView.java#6301. If you are using ShareActionProvider from the support library, you need the android.support.v7.view.ActionMode.Callback instead.

The solution is to create your own ActionMode.CallBack implementation instead of using the framework's MultipleModeListener. This way you make sure that the support libraries are being used wherever required.

For example :

  1. Import the v7 version of ActionMode and ActionBarActivity in your fragment

    import android.support.v7.view.ActionMode;
    import android.support.v7.app.ActionBarActivity;
    
  2. Create an onClickListener for your list view and use startSupportActionMode to start your custom ActionMode.CallBack implementation

    myListView.setItemsCanFocus(false);
    myListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    actionMode = null;
    myListView.setOnItemClickListener(new OnItemClickListener(){
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id){
                    if(myListView.getCheckedItemCount() == 0){
                        actionMode.finish();
                        return;
                    }
    
                        if(actionMode == null){
                            actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(new ContextualActionBar());
                        }
    
                }
            });
    
  3. Create your custom ActionMode.Callback implementation

    private class ContextualActionBar implements ActionMode.Callback{
        private ShareActionProvider mShareActionProvider;
    
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch(item.getItemId()){
    
            case R.id.share_menu :
                mode.finish();
                return true;
    
            default :
                return false;
            }
        }
    
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.chatsession_contextmenu, menu);
    
            //Initialize the ShareActionProvider
            MenuItem shareMenuItem = menu.findItem(R.id.share_menu);
            mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareMenuItem);
            Intent shareIntent = new Intent(Intent.ACTION_SEND);
            shareIntent.setType("text/plain");
            shareIntent.putExtra(Intent.EXTRA_TEXT, "test message");
            mShareActionProvider.setShareIntent(shareIntent);
            return true;
        }
    
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            //Nullify the actionMode object 
            //so that the onClickListener can identify whether the ActionMode is ON  
            actionMode = null;
    
            //Uncheck all checked messages 
            SparseBooleanArray selectedItems = myListView.getCheckedItemPositions();
            for(int i=0;i<selectedItems.size();i++){
                myListView.setItemChecked(selectedItems.keyAt(i), false);
            }
        }
    
        @Override
        public boolean onPrepareActionMode(ActionMode arg0, Menu arg1) {
            // TODO Auto-generated method stub
            return false;
        }
    
    }
    
Flavor answered 16/6, 2014 at 10:47 Comment(0)
W
2

Be careful to use the right MenuInflater when populating the IMenu in onCreateActionMode. When I use the one from the mode object, as you do in your fragment class, it doesn't create the support version of ShareActionProvider. I switched to using the MenuInflater from the parent AppCompatActivity class and it worked fine.

Given that the mode object is from the support library, one would assume it would use the support inflater, but apparently not.

Wachter answered 9/10, 2015 at 13:24 Comment(1)
worked for me in combination with the accepted answer.Spruce
R
0

Are you sure that your activity extends ActionBarActivity?

Rena answered 15/6, 2014 at 5:47 Comment(5)
Yes i do use ActionBarActivity. I have edited the question with the activity code.Flavor
I suggest to create your ActionProvider class inside onCreateOptionsMenu first, let us know if that works.Rena
The ActionProvider works perfectly in onCreateOptionsMenu. I wonder how it differs from onCreateActionMode in the MultiChoiceModeListener interface.Flavor
The MultiChoiceModeListener does not implement SupportMenuItem. This, I believe, is done by Activity class. You can try work around that by declaring the menuItem as a member in your Acitivity class.Rena
I don't think that is possible. If i declare the menuItem anywhere outside the MultiChoiceModeListener() definition, I get the error - Cannot refer to a non-final variable menuItem inside an inner class defined in a different method. And if i declare it as final, i get the error The final local variable menuItem cannot be assigned, since it is defined in an enclosing type.Flavor
B
0

I had an almost identical setup. The problem in my case was that Proguard was optimizing away the constructor of the ShareActionProvider. There is a bug that they while they do keep the class and methods of ActionProvider classes detected in your XML, they don't keep the constructors or the class name.

If you have a "Cannot instantiate class" warning in your log, then this would apply to you, too.

Here is the Android bug report that helped me.

And the proguard config I added was:

-keepnames public class * extends android.support.v4.view.ActionProvider
-keepclassmembers public class * extends android.support.v4.view.ActionProvider {
    <init>(android.content.Context);
}
Butyrin answered 16/6, 2015 at 19:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.