Android - WebView default selected toolbar's copy button doesn't work

ErdoganErdogan Member ✭✭
edited December 2 in Xamarin.Forms

As you know, when selecting a text, it appears a popup menu.

When I click the "copy" on the menu, I can't get selected/copied text but the event is fired. (in MyMenuItemOnMenuItemClickListener class > OnMenuItemClick method)

--

If I copy it using "Share > Copy to Clipboard" menu, I can get it. The event isn't fired. (This is not our goal. Just for comparison.)

And if I copy any text except the WebView and then click the copy button (not 'copy to clipboard') I could get last copied text. What's wrong with Webview's copy button? Why I couldn't get selected text?

There is no problem with iOS.

--

Xaml;

<local:CustomWebView x:Name="webView" Uri="https://docs.microsoft.com/en-us/xamarin/ios/" HeightRequest="300"/>

CustomWebView Class;

public class CustomWebView : WebView
        {
            public static readonly BindableProperty UriProperty = BindableProperty.Create(
                propertyName: "Uri",
                returnType: typeof(string),
                declaringType: typeof(CustomWebView),
                defaultValue: default(string));

            public string Uri
            {
                get { return (string)GetValue(UriProperty); }
                set { SetValue(UriProperty, value); }
    }
}

Custom Renderer;

 [assembly: ExportRenderer(typeof(CustomWebView), typeof(WebViewRendererDroid))]
    namespace TApp.Droid
    {
        public class WebViewRendererDroid : ViewRenderer<CustomWebView, Android.Webkit.WebView>, IOnPrimaryClipChangedListener /* ViewRenderer<CustomWebView, Android.Webkit.WebView>*/
        {
            Context _context;
            public WebViewRendererDroid(Context context) : base(context)
            {
                _context = context;
            }

            protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
            {
                base.OnElementChanged(e);

                if (Control == null)
                {
                    var webView = new Android.Webkit.WebView(_context);
                    webView.Settings.JavaScriptEnabled = true;
                    webView.Settings.AllowContentAccess = true;
                    webView.LoadUrl("https://docs.microsoft.com/en-us/xamarin/ios/");

                    SetNativeControl(webView);
                }
                if (e.NewElement != null)
                {
                    Control.LoadUrl("https://docs.microsoft.com/en-us/xamarin/ios/");
                }
            }

        ClipboardManager myClipBoard;
        public void OnPrimaryClipChanged()       
        {
            ClipData clipData = myClipBoard.PrimaryClip;
            ClipData.Item item = clipData.GetItemAt(0);
            MessagingCenter.Send<object, string>(this, "Hi", item.Text);
        }
        }
    }

MainActivity.cs;

  namespace TApp.Droid
    {
        [Activity(Label = "TApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
        public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                TabLayoutResource = Resource.Layout.Tabbar;
                ToolbarResource = Resource.Layout.Toolbar;

                base.OnCreate(savedInstanceState);

                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                LoadApplication(new App());
            }

            public override void OnActionModeStarted(ActionMode mode)
            {
                IMenu menu = mode.Menu;
                menu.GetItem(0).SetOnMenuItemClickListener(new MyMenuItemOnMenuItemClickListener(this));
                base.OnActionModeStarted(mode);
            } 
        }

        public class MyMenuItemOnMenuItemClickListener : Java.Lang.Object, IMenuItemOnMenuItemClickListener
        {
            private MainActivity mContext;

            public MyMenuItemOnMenuItemClickListener(MainActivity activity)
            {
                this.mContext = activity;
            }

            public bool OnMenuItemClick(IMenuItem item)
            {
                var clipboard = (ClipboardManager)mContext.GetSystemService(Context.ClipboardService);

                var clipboard2 = (Android.Text.ClipboardManager)Android.App.Application.Context.GetSystemService(Context.ClipboardService);

                var pasteData = "";

                string aaa = clipboard.Text;

                if (!(clipboard.HasPrimaryClip))
                {
                    // If it does contain data, decide if you can handle the data.
                }
                else if (!(clipboard.PrimaryClipDescription.HasMimeType(ClipDescription.MimetypeTextPlain)))
                {
                    // since the clipboard has data but it is not plain text
                }
                else
                {
                    //since the clipboard contains plain text
                    var copiedText = clipboard.PrimaryClip.GetItemAt(0);
                    // Gets the clipboard as text
                    pasteData = copiedText.Text;
                }

                Toast.MakeText(mContext, pasteData, ToastLength.Short).Show();
                return true;
            }
        }
    }

Best Answer

Answers

  • LeonLuLeonLu Member, Xamarin Team Xamurai

    I test your code, I found if you run your code in the android emulator, this value is come from the PC's Clipboard not the android emlator. If you run your code in the android device, your code cannot get the empty value,

    There is a clipboard plugin for Xamarin (supported platforms are iOS, Mac, Android, UWP and Gtk#). It supports saving text to and getting it from the clipboard in a cross-platform way. The NuGet package is:
    https://www.nuget.org/packages/Xamarin.Plugins.Clipboard/

    To set text

    CrossClipboard.Current.SetText("my clipboard text");

    To get text

    string clipboardText = await CrossClipboard.Current.GetTextAsync();

  • JohnHardmanJohnHardman GBUniversity mod

    @Erdogan said:
    As you know, when selecting a text, it appears a popup menu.

    When I click the "copy" on the menu, I can't get selected/copied text but the event is fired. (in MyMenuItemOnMenuItemClickListener class > OnMenuItemClick method)

    @Erdogan said:
    If I copy it using "Share > Copy to Clipboard" menu, I can get it. The event isn't fired. (This is not our goal. Just for comparison.)

    @Erdogan said:
    What's wrong with Webview's copy button?

    I think it would be helpful if you explained what you actually want to do. The WebView's Copy toolbar item does work (I've just tested it in a build using XF 4.3.0.991211, with an Android target framework of Android 9.0), copying text from the WebView's content and then pasting it into other apps). You seem to be adding complexity, but without knowing what you actually want to do it's impossible to know what to do with that complexity.

  • ErdoganErdogan Member ✭✭
    edited December 2

    Thanks for answers! @LeonLu Actually, I've tried both (Xamarin.Plugins.Clipboard and Xamarin.Essentials) plugins. All of them, If I copy a text from an external browser and after that back to my app for copying, I can get external copied value, not my app. I couldn't find any way to get the copied value on my app.

    For example for Xamarin.Plugins.Clipboard;
    OnMenuItemClick fired in the mainActivity class;

            public bool OnMenuItemClick(IMenuItem item)
            {
                var clipboardText = GetClipboardText();           // << no luck
    
                ClipboardImplementation imp = new ClipboardImplementation();
                string aaaaa = imp.GetText();                            // << no luck
                var bbbbb = imp.GetTextAsync();                     // << no luck
    
                return true;
            }
    
            private async Task<object> GetClipboardText()
            {
                return await CrossClipboard.Current.GetTextAsync();
            }
    

    The ClipboardImplementation class is inside my Android project.

    And I am trying it my Android device, not in the emulator.
    Android 9.0 / API 28

  • ErdoganErdogan Member ✭✭

    Hello @JohnHardman,

    I think I explained my goal, It is very clear. The WebView's Copy toolbar item doesn't work in my project and I'm searching for the solution. If I copy a text from my webview and paste to another app, the value is the previous value (the copied value is not coming from my app). Yes, I'm adding complexity because trying to be a success in this process. Clearly, I want to get copied text from this action;

  • JohnHardmanJohnHardman GBUniversity mod
    edited December 2

    @Erdogan

    In my experience, and in testing this again today, using a WebView with no additional copy/paste-related code, the Copy toolbar item is working. Selecting some text in the WebView, hitting Copy, then going to another app (e.g. Gmail) and selecting Paste results in the expected text being pasted into the other app. If you remove all of your additional code, is this what you see happening?

  • ErdoganErdogan Member ✭✭

    Yes @JohnHardman, you right. When I remove the OnActionModeStarted method and MyMenuItemOnMenuItemClickListener class from the MainActivity class, it could be saved the copied value but my goal is getting the value when clicking the copy link. Simply, how can I do that according to your experience?

  • ErdoganErdogan Member ✭✭
    Accepted Answer

    The answer came from @LeoZhu on StackOverflow. I tagged him here that I think the same person is:)

    https://stackoverflow.com/questions/59139654/android-webview-default-selected-toolbars-copy-button-doesnt-work/59151692#59151692

    Thank you Leo.

Sign In or Register to comment.