Java – Converting java to kotlin breaks the context menu

Converting java to kotlin breaks the context menu… here is a solution to the problem.

Converting java to kotlin breaks the context menu

I was working on converting the project to Kotlin and found a problem. The context menu of the java code is broken in the generated kotlin.
This is a simplified test of the project’s source code. It contains only one primary activity with a single layout and context menu. The java version works, but the kotlin version crashes. The only unusual thing I could think of was that the View I was registering was an imageView in RelativeLayout.

 java.lang.NullPointerException:
        Parameter specified as non-null is null: 
        method kotlin.jvm.internal.Intrinsics.checkNotNullParameter
        , parameter menuInfo
    at com... MainActivity.onCreateContextMenu(MainActivity.kt)
    at android.view.View.createContextMenu(View.java:8392)
    at com.android.internal.view.menu.ContextMenuBuilder
        .show(ContextMenuBuilder.java:81)
    at com.android.internal.policy.impl
        . PhoneWindow$DecorView
        .showContextMenuForChild(PhoneWindow.java:2517)
    at android.view.ViewGroup.showContextMenuForChild(ViewGroup.java:658)

MainActivity.java is:

public class MainActivity extends AppCompatActivity {
    private static int animationSpeed = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

registerForContextMenu(findViewById(R.id.imageView));
    }
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.speed_select, menu);
        menu.getItem(animationSpeed).setChecked(true);
    }
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        int itemId = item.getItemId();
        boolean rv = true;
        switch(itemId) {
            case R.id.animate_slow: animationSpeed = 0; break;
            case R.id.animate_normal: animationSpeed = 1; break;
            case R.id.animate_fast: animationSpeed = 2; break;
            default: Log.d("onContextItemSelected", String.format(
                    "menu item unhandled:0x%08x", itemId)
            );
                rv = false;
        }
        return rv;
    }
}

MainActivity.kt is:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        registerForContextMenu(findViewById(R.id.imageView))
    }
    override fun onCreateContextMenu(menu: ContextMenu, v: View,
                                     menuInfo: ContextMenuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo)
        val inflater = menuInflater
        inflater.inflate(R.menu.speed_select, menu)
        menu.getItem(animationSpeed).isChecked = true
    }
    override fun onContextItemSelected(item: MenuItem): Boolean {
        val itemId = item.itemId
        var rv = true
        when (itemId) {
            R.id.animate_slow -> animationSpeed = 0
            R.id.animate_normal -> animationSpeed = 1
            R.id.animate_fast -> animationSpeed = 2
            else -> {
                Log.d("onContextItemSelected", String.format(
                        "menu item unhandled:0x%08x", itemId))
                rv = false
            }
        }
        return rv
    }
    companion object {
        private var animationSpeed = 0
    }
}

My menu file is:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <group 
        android:checkableBehavior="single"
        android:id="@+id/animate_speed" >
       <item android:id="@+id/animate_slow"
             android:title="@string/slow" />
       <item android:id="@+id/animate_normal"
             android:title="@string/normal" />
       <item android:id="@+id/animate_fast"
             android:title="@string/fast" />
       </group>
</menu>

The activity layout is:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/relative_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_centerInParent="true"
        android:background="@drawable/andy"
        />
</RelativeLayout>

I tried opening onCreateContextMenu but never got there.
I’m using Kotlin 1.40, AndroidStudio 4.01, SDK 30, and gradle 4.01. I’ve been looking at the documentation and code these days, and to me, the generated kotlin looks correct.
Thanks!

Thanks to John Healy below, this issue has been resolved.
John said he thought it might be in Kotlin’s empty security treatment. I was skeptical, so I added a log statement in the Java code at work and menuInfo came out as a null value. I added a @Nullable comment to the Java declaration and it gave me:

public void onCreateContextMenu(
    ContextMenu menu, View v,
    @Nullable ContextMenu.ContextMenuInfo menuInfo)

Testing of the Java code shows that the compiler and lint are working fine, and the code still runs. I ran jave through the conversion process again and got the kotlin signature of the function was:

override fun onCreateContextMenu(
    menu: ContextMenu, v: View,
    menuInfo: ContextMenuInfo?)

I tested Kotlin and it works now!

Note: For your inspiration and entertainment, I post the source code here
git hub .

Solution

I just made this reply to my question so that people would notice that since the commenters found a solution. This fix only applies if you are using a menu list instead of creating a separate menu item, and is only required for Kotlin because of the way Kotlin handles null security. Please see the end of the question and my comments on the issue,

Steve S. S。

Related Problems and Solutions