4

I have an app with PopupWindow containing WebView that opens Facebook's page, any context menu like: the Autocomplete on text fields in the WebView or even the Long Press that should display options for the user to copy/paste/cut crashes the app right away with the follow error:

FATAL EXCEPTION: main
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@418cdab0 is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:700)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:345)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
at android.view.Window$LocalWindowManager.addView(Window.java:554)
at android.widget.PopupWindow.invokePopup(PopupWindow.java:1013)
at android.widget.PopupWindow.showAtLocation(PopupWindow.java:856)
at android.widget.PopupWindow.showAtLocation(PopupWindow.java:820)
at android.webkit.WebViewClassic$PastePopupWindow.show(WebViewClassic.java:971)
at android.webkit.WebViewClassic.showPasteWindow(WebViewClassic.java:7037)
at android.webkit.WebViewClassic.access$10300(WebViewClassic.java:235)
at android.webkit.WebViewClassic$PrivateHandler.handleMessage(WebViewClassic.java:11376)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)

Notice: my understanding of the problem is that the context menu/window are shown actually in the scope of the app itself not inside the WebView using another PopWindow (Auto generated by the Core WebView Class) this class references the context incorrectly.

My Code is as follows:

Main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/openpopup"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Open Popup Window" />

</LinearLayout>

Popup.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/background_light"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_margin="1dp"
        android:background="@android:color/darker_gray"
        android:orientation="vertical" >
     >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <WebView
                xmlns:ptr="http://schemas.android.com/apk/res-auto"
                android:id="@+id/webviewActionView"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:minHeight="200dp"
                android:minWidth="200dp"
                android:scrollbars="none" >
            </WebView>

            <Button
                android:id="@+id/dismiss"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="Dismiss" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

MainActivity.java

import android.os.Bundle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.PopupWindow;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
        btnOpenPopup.setOnClickListener(new Button.OnClickListener() {

            @SuppressLint("NewApi")
            @Override
            public void onClick(View arg0) {

                LayoutInflater layoutInflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
                View popupView = layoutInflater.inflate(R.layout.popup, null);
                final PopupWindow popupWindow = new PopupWindow(popupView,
                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);

                popupWindow.setTouchable(true);
                popupWindow.setFocusable(true);

                WebView popupWebview = (WebView) popupView.findViewById(R.id.webviewActionView);
                popupWebview.loadUrl("https://m.facebook.com");
                Button btnDismiss = (Button) popupView
                        .findViewById(R.id.dismiss);
                btnDismiss.setOnClickListener(new Button.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        // TODO Auto-generated method stub
                        popupWindow.dismiss();
                    }
                });
                popupWindow.showAsDropDown(btnOpenPopup, 50, -30);
            }
        });
    }
}
Shehabic
  • 6,787
  • 9
  • 52
  • 93

3 Answers3

6

As commented above in my answer you can use Custom dialog for your requirement.. i have created a sample PopupDialog.. please check and let me know..if it solve your purpose.. Autocomplete and ContextMenus will work as expected..

MyActivity.java

package com.example.testapp;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

public class MyActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
    btnOpenPopup.setOnClickListener(new Button.OnClickListener() {

        @SuppressLint("NewApi")
        @Override
        public void onClick(View arg0) {

            final PopupDialog popupDialog = new PopupDialog(MyActivity.this);
            popupDialog.setContentView(R.layout.popup);

            WebView popupWebview = (WebView) popupDialog.findViewById(R.id.webviewActionView);
            popupWebview.setWebChromeClient(new WebChromeClient());
            popupWebview.setWebViewClient(new WebViewClient());

            popupWebview.loadUrl("http://m.facebook.com");
            Button btnDismiss = (Button) popupDialog
                    .findViewById(R.id.dismiss);
            btnDismiss.setOnClickListener(new Button.OnClickListener() {
                @Override
                public void onClick(View v) {

                    // TODO Auto-generated method stub
                    popupDialog.dismiss();
                }
            });
//                popupDialog.showAtLocation(50,50);
            popupDialog.showAsDropDown(findViewById(R.id.openpopup));
        }
    });
}
}

PopupDialog.java

package com.example.testapp;

import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class PopupDialog extends Dialog{
private final Context mContext;

public PopupDialog(Context context) {
    super(context);
    mContext=context;
    requestWindowFeature(Window.FEATURE_NO_TITLE);
}

public void showAtLocation(int x,int y)
{
    WindowManager.LayoutParams wmlp = getWindow().getAttributes();
    wmlp.gravity = Gravity.TOP | Gravity.LEFT;
    wmlp.x = x;
    wmlp.y = y;
    show();
}
public void showAsDropDown(View view)
{
    float density = mContext.getResources().getDisplayMetrics().density;
    WindowManager.LayoutParams wmlp = getWindow().getAttributes();
    int[] location = new int[2];
    view.getLocationInWindow(location);
    wmlp.gravity = Gravity.TOP | Gravity.LEFT;
    wmlp.x = location[0]+(int)(view.getWidth()/density);
    wmlp.y = location[1]+(int)(view.getHeight()/density);
    show();
}
}

edit: added popup.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:minHeight="400dp"
          android:minWidth="200dp"
          android:background="@android:color/background_light"
          android:orientation="vertical" >

<LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_margin="1dp"
        android:background="@android:color/darker_gray"
        android:orientation="vertical" >
    >

    <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_margin="20dp"
            android:orientation="vertical" >

        <WebView
            xmlns:ptr="http://schemas.android.com/apk/res-auto"
                android:id="@+id/webviewActionView"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:minHeight="200dp"
                android:minWidth="200dp"
                android:scrollbars="none" >
        </WebView>

        <Button
                android:id="@+id/dismiss"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="Dismiss" />
    </LinearLayout>
</LinearLayout>

Akhil
  • 6,667
  • 4
  • 31
  • 61
2

yes i think you should use your activity name in place of "getBaseContext()". i am just created one demo with your code. its working fine in my pc. and when you are getting this exception?

Ajay
  • 1,189
  • 1
  • 12
  • 28
  • It crashes when u long press on the user field in fb login, and changing getBaseContext doesn't help either; try this on JB 4.1+ – Shehabic Mar 30 '13 at 06:53
  • ok now please download my code from below link and check what happen and inform me.. http://www.mediafire.com/?ypp5cq22p7n66pq – Ajay Mar 30 '13 at 07:21
  • Well it crashes, you didn't change anything in the code already. – Shehabic Mar 30 '13 at 11:29
  • ok friend, you give me -1, but its working fine in my so i have given you this link.. but its ok :) – Ajay Apr 01 '13 at 05:33
  • I didn't, and generally you didn't change anything in the code,just for you information (posting an answer meaning that you got a solution if you want to say that it's working on your device then just add a comment) – Shehabic Apr 01 '13 at 05:36
1

Nothing wrong in your code..seems to be Android 4.1+ specific issue.. Webview is also using PopupWindow for showing Paste context menu in input box..Nested popupwindows through webview is causing WindowManager crash, while adding new view to it.

I have tried Custom Dialog(in place of Popupwindow) with embedded Webview, Paste context menu is coming fine in Android 4.1+..

If you are not so specific about this paste context menu you can disable it for particular android version using below code..i have tried it.. it will stop app from being crash..

  popupWebView.setOnLongClickListener(new OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            if (Build.VERSION.SDK_INT >= 16)
                  return true;
              return false;
        }
    });

Also you can look at Webview HitTestResult class for displaying some custom made Context menus, according to your needs..

Gomino
  • 12,127
  • 4
  • 40
  • 49
Akhil
  • 6,667
  • 4
  • 31
  • 61
  • It's not just on click/longClick, it happens also on autocomplete in forms, I don't wanna disable it; instead I wanna fix it even if I'll have to extend the default popupWindow class and override some methods. – Shehabic Apr 02 '13 at 08:11
  • Extending PopupWindow will not solve problem.. As for drawing PopupWindow Window is needed from parent activity.. In case of nested Popupwindow parent is popupwindow for child popupwindow.. I will recommend creating custom Dialog with x,y coridinates of your choice.. that will solve the purpose. e.g. [See here](http://stackoverflow.com/questions/5469005/show-alertdialog-in-any-position-of-the-screen).. – Akhil Apr 02 '13 at 09:22