androidxamarinoverlayandroid-picture-in-picture

Creating Xamarin App with overlay over other Applications


I'm trying to create a xamarin android app which should have a small overlay window which is displayed above other apps.

I have not found sample code or documentation whatsoever unfortunately.

What I am trying to achieve is something similar to the Apple Control Dot, the Facebook chat-dot or the google maps navigation overlay.

I also have a simple makro recorder app installed on my phone which can do this given the accessibility permission "Draw on top of other Apps" is enabled. sample overlay window

In the above screenshot you can see the small recording control on the right. The control overlay can be dragged around the screen, settings opened etc.

Anyone an Idea on how to do that?

Best regards,

Julian


Solution

  • Here is a simple sample you could refer to.

    Create a FloatingService :

    [Service]
    class FloatingService : Service,Android.Views.View.IOnTouchListener
    {
        WindowManagerLayoutParams layoutParams;
        IWindowManager windowManager;
        View floatView;
        public override void OnCreate()
        {
            base.OnCreate();
        }
    
        [return: GeneratedEnum]
        public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
        {
            showFloatingWindow();
            return StartCommandResult.NotSticky;
        }
        public override IBinder OnBind(Intent intent)
        {
            return null;
        }
    
        private void showFloatingWindow()
        {
            windowManager = GetSystemService(WindowService).JavaCast<IWindowManager>();
            LayoutInflater mLayoutInflater = LayoutInflater.From(ApplicationContext);
            floatView = mLayoutInflater.Inflate(Resource.Layout.floatview, null);
            floatView.SetBackgroundColor(Android.Graphics.Color.Transparent);
            floatView.SetOnTouchListener(this);
            ImageView iv1 = floatView.FindViewById<ImageView>(Resource.Id.iv1);
            ImageView iv2 = floatView.FindViewById<ImageView>(Resource.Id.iv2);
            ImageView iv3 = floatView.FindViewById<ImageView>(Resource.Id.iv3);
            iv1.Click += delegate { Toast.MakeText(ApplicationContext, "The first Image Click", ToastLength.Short).Show(); };
            iv2.Click += delegate { Toast.MakeText(ApplicationContext, "The second Image Click", ToastLength.Short).Show(); };
            iv3.Click += delegate { Toast.MakeText(ApplicationContext, "The third Image Click", ToastLength.Short).Show(); };
            
            // set LayoutParam
            layoutParams = new WindowManagerLayoutParams();
                if (Build.VERSION.SdkInt >= Build.VERSION_CODES.O)
                {
                    layoutParams.Type = WindowManagerTypes.ApplicationOverlay;
                }
                else
                {
                    layoutParams.Type = WindowManagerTypes.Phone;
                }
                layoutParams.Flags = WindowManagerFlags.NotTouchModal;
                layoutParams.Flags = WindowManagerFlags.NotFocusable;
    
                layoutParams.Width = 400;
                layoutParams.Height = 100;
                layoutParams.X = 300;
                layoutParams.Y = 300;
                windowManager.AddView(floatView, layoutParams);
                
            }
        private int x;
        private int y;
        public bool OnTouch(View v, MotionEvent e)
        {
            switch (e.Action)
            {
            
                case MotionEventActions.Down:
                    x = (int)e.RawX;
                    y = (int)e.RawY;
                    break;
    
                case MotionEventActions.Move:
                    int nowX = (int) e.RawX;
                    int nowY = (int) e.RawY;
                    int movedX = nowX - x;
                    int movedY = nowY - y;
                    x = nowX;
                    y = nowY;
                    layoutParams.X = layoutParams.X+ movedX;
                    layoutParams.Y = layoutParams.Y + movedY;
    
            
                windowManager.UpdateViewLayout(floatView, layoutParams);
                    break;
    
                default:
                    break;
            }
            return false;
        }
    }
    

    the floatview.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="horizontal"
       android:layout_width="wrap_content"
       android:layout_height="match_parent">
    
      <ImageView
        android:id="@+id/iv1"
        android:src="@android:drawable/ic_media_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    
      <ImageView
        android:id="@+id/iv2"
        android:src="@android:drawable/star_on"
        android:layout_marginLeft="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
      <ImageView
        android:id="@+id/iv3"
        android:src="@android:drawable/ic_menu_more"
        android:layout_marginLeft="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    </LinearLayout>
    

    then in your Activity start FloatingService :

     button.Click += async delegate
          {
             if (!Settings.CanDrawOverlays(this))
              {
                StartActivityForResult(new Intent(Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:" + PackageName)), 0);
              }
             else
              {
                StartService(new Intent(this, typeof(FloatingService)));
    
              }
          }
    
     protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
        {
            if (requestCode == 0)
            {
                if (!Settings.CanDrawOverlays(this))
                {
    
                }
                else
                {
                    StartService(new Intent(this, typeof(FloatingService)));
                }
            }
        }
    

    and add <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> in your Manifest.xml.

    the effect like below:

    enter image description here