Knowledge points Reference link
About creating suspended windows
- First, you need to get WindowManager
WindowManager manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
- Add parameters to the window (size, location, type...)
WindowManager.LayoutParams viewParam = new WindowManager.LayoutParams();
Then you can set the Params of the window, such as viewparams type = WindowManager. LayoutParams. Type_ APPLICATION_ Overlay et al
- Create a View as the layout and UI of the Window
View view = LayoutInflater.from(this).inflate(R.layout.view2, null);
- Add the previously created View and Params into the Window
manager.addView(view, viewParam);
Through the above steps, you can create a floating window.
Note: the window type cannot be set randomly. There are relevant requirements. For details, please refer to Official documents
Record a pit: the requirement is to create a floating Window with a translucent picture to cover the whole mobile phone screen. Click and slide events should pass through the floating Window - z this requirement is very clear, so if you want to gesture through the floating Window, you need to set the flags of the Window to viewparam when setting Params flags = WindowManager. LayoutParams. FLAG_ NOT_ TOUCHABLE | WindowManager. LayoutParams. FLAG_ NOT_ FOCUSABLE | WindowManager. LayoutParams. FLAG_ NOT_ TOUCH_ MODAL; The problem here is that if the Window type is TYPE_APPLICATION_OVERLAY can take effect on android R, but the gesture can not be transmitted to the floating Window on android S. the solution is as follows:
Window flag: this window can never receive touch events.
The intention of this flag is to leave the touch to be handled by some window below this window (in Z order).
Starting from Android Build.VERSION_CODES#S, for security reasons, touch events that pass through windows containing this flag (ie. are within the bounds of the window) will only be delivered to the touch-consuming window if one (or more) of the items below are true:
Same UID: This window belongs to the same UID that owns the touch-consuming window.
Trusted windows: This window is trusted. Trusted windows include (but are not limited to) accessibility windows (TYPE_ACCESSIBILITY_OVERLAY), the IME (TYPE_INPUT_METHOD) and assistant windows (TYPE_VOICE_INTERACTION). Windows of type TYPE_APPLICATION_OVERLAY are not trusted, see below.
Invisible windows: This window is View#GONE or View#INVISIBLE.
Fully transparent windows: This window has LayoutParams#alpha equal to 0.
One SAW window with enough transparency: This window is of type TYPE_APPLICATION_OVERLAY, has LayoutParams#alpha below or equal to the maximum obscuring opacity (see below) and it's the only window of type TYPE_APPLICATION_OVERLAY from this UID in the touch path.
Multiple SAW windows with enough transparency: The multiple overlapping TYPE_APPLICATION_OVERLAY windows in the touch path from this UID have a combined obscuring opacity below or equal to the maximum obscuring opacity. See section Combined obscuring opacity below on how to compute this value.
If none of these cases hold, the touch will not be delivered and a message will be logged to logcat.
Here I choose to reduce the transparency to 0.8
Start the suspension window
The suspended window should exist independently of the activity. You should choose to use Service to start the window. The following is the code about Service in the whole project:
package com.example.picturedemo; public class NotifyService extends Service { private WindowManager manager = null; private View view = null; private WindowManager.LayoutParams viewParam; private RemoteViews remoteViews; public ImageView picture; private static String model; private boolean flag = true; private boolean isRightPicture; private Bitmap bitmap; private int height; private int width; private int x; private NotificationManager notificationManager; private Notification notification; public NotifyService() { } @SuppressLint("ServiceCast") @RequiresApi(api = 31) @Override public void onCreate() { super.onCreate(); NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver(); model = Build.MODEL; Log.i("Version", model); IntentFilter filter = new IntentFilter(); filter.addAction("PictureNotify"); this.registerReceiver(receiver,filter); manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); viewParam = new WindowManager.LayoutParams(); viewParam.x = WindowManager.LayoutParams.WRAP_CONTENT; viewParam.y = WindowManager.LayoutParams.WRAP_CONTENT; //windowInsetsController.hide(WindowInsets.Type.statusBars()); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { viewParam.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { viewParam.type = WindowManager.LayoutParams.TYPE_PHONE; viewParam.type = WindowManager.LayoutParams.TYPE_STATUS_BAR; } viewParam.alpha = (float) 0.5; viewParam.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| //WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS| //WindowManager.LayoutParams.FLAG_FULLSCREEN| //WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN| WindowManager.LayoutParams.TYPE_STATUS_BAR; // Display display = manager.getDefaultDisplay(); // Point p = new Point(); // display.getRealSize(p); // viewParam.width = p.x; // viewParam.height = p.y; //viewParam.flags = WindowManager.LayoutParams.TYPE_STATUS_BAR; viewParam.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; viewParam.format = PixelFormat.TRANSLUCENT; remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification); } @SuppressLint("WrongConstant") @Override public int onStartCommand(Intent intent, int flags, int startId) { if(startId == 1) { notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE); // Service and broadcasting of tong bulletin board if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT); notificationChannel.enableLights(true); notificationChannel.enableVibration(false); if (notificationManager != null) { notificationManager.createNotificationChannel(notificationChannel); } } Intent intent1 = new Intent("PictureNotify"); intent1.putExtra("notDisplay", 1); PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // Do not display Intent intent2 = new Intent("PictureNotify"); intent2.putExtra("leftDisplay", 2); PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, 1, intent2, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent2); // Left display Intent intent3 = new Intent("PictureNotify"); intent3.putExtra("rightDisplay", 3); PendingIntent pendingIntent3 = PendingIntent.getBroadcast(this, 2, intent3, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button3, pendingIntent3); // Right display notification = new NotificationCompat.Builder(this, "1") .setStyle(new NotificationCompat.DecoratedCustomViewStyle()) .setContent(remoteViews) .setSmallIcon(R.mipmap.ic_launcher2) .build(); startForeground(1, notification); // Add floating window view = LayoutInflater.from(this).inflate(R.layout.view2, null); picture = view.findViewById(R.id.picture); if (manager != null) { manager.addView(view, viewParam); } if (picture != null) { if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); isRightPicture = true; picture.setImageResource(R.drawable.l18right); } else { ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright); picture.setLayoutParams(params); isRightPicture = true; picture.setImageResource(R.drawable.phoneright); } //picture.setAlpha((float)0.5); } } return super.onStartCommand(intent, flags, startId); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if(picture != null) { if (isL18()) { if(isRightPicture) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18left); isRightPicture = false; } } else { if(isRightPicture) { ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright); picture.setLayoutParams(params); picture.setImageResource(R.drawable.phoneright); } else { ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneleft); picture.setLayoutParams(params); picture.setImageResource(R.drawable.phoneleft); isRightPicture = false; } } //picture.setAlpha((float)0.5); } } @Override public IBinder onBind(Intent intent) { return null; } private ViewGroup.LayoutParams measurePicture(int drawable) { bitmap = BitmapFactory.decodeResource(getResources(), drawable); height= bitmap.getHeight(); width= bitmap.getWidth(); x = NotifyService.this.getResources().getDisplayMetrics().widthPixels; float ratio=(float) height/(float) width; ViewGroup.LayoutParams params = picture.getLayoutParams(); params.width = x; params.height = (int) (ratio * x); return params; } private boolean isL18() { DisplayMetrics dm = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels;// Screen width (pixels) //int height = dm.heightPixels; float density = dm.density;//Screen density (0.75 / 1.0 / 1.5) //int densityDpi = dm.densityDpi;// Screen density dpi (120 / 160 / 240) //Screen width algorithm: screen width (pixels) / screen density int screenWidth = (int) (width/density);//Screen width (dp) Log.i("ScreenWidth" , String.valueOf(width)); if(width > 1440) { return true; } return false; } class NotifyBroadCastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if("PictureNotify".equals(action)) { int code1 = intent.getIntExtra("notDisplay" ,4); int code2 = intent.getIntExtra("leftDisplay",4); int code3 = intent.getIntExtra("rightDisplay" , 4); if(code1 == 1) { //picture.setVisibility(View.GONE); if(flag) { manager.removeView(view); remoteViews.setTextViewText(R.id.button2 , "display"); notificationManager.notify(1,notification); flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE1" , String.valueOf(code1)); } else if(code2 == 2) { if(flag) { // manager.removeView(view); // flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE2" , String.valueOf(code2)); if(isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18left); } else { picture.setImageResource(R.drawable.phoneleft); } isRightPicture = false; } else if(code3 == 3) { //picture.setVisibility(View.VISIBLE); //picture.setAlpha((float) 0.5); if(flag) { //manager.removeView(view); //flag = false; } else { remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); manager.addView(view , viewParam); flag = true; } Log.i("CODE3" , String.valueOf(code3)); if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { picture.setImageResource(R.drawable.phoneright); } isRightPicture = true; } } } } }
The main work is three aspects:
- Create a window and add it to WindowManager
- Load different pictures according to different phone sizes
- Open the front desk service to control the display and style of pictures
About front desk service
The custom notification bar was mentioned in the previous blog, and the steps here are similar
- Customize the foreground service used to display in the notification bar. The steps are the same as creating the notification bar. RemoteViews is required
// Create notification channel notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE); // Service and broadcasting of tong bulletin board if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT); notificationChannel.enableLights(true); notificationChannel.enableVibration(false); if (notificationManager != null) { notificationManager.createNotificationChannel(notificationChannel); } } remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification); notification = new NotificationCompat.Builder(this, "1") .setStyle(new NotificationCompat.DecoratedCustomViewStyle()) .setContent(remoteViews) .setSmallIcon(R.mipmap.ic_launcher2) .build();
- Start front desk service
startForeground(1, notification);
- If the foreground service (such as the button in the notification bar) is required to get a response, it needs to broadcast. In the notification, the pendingent sends a broadcast (not, the specific sending code is at the bottom). Here is the interface that the upper layer can call to access
Intent intent1 = new Intent("PictureNotify"); intent1.putExtra("notDisplay", 1); PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // Do not display
- Create a Receiver to accept broadcast events
class NotifyBroadCastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if("PictureNotify".equals(action)) { int code1 = intent.getIntExtra("notDisplay" ,4); int code2 = intent.getIntExtra("leftDisplay",4); int code3 = intent.getIntExtra("rightDisplay" , 4); if(code1 == 1) { //picture.setVisibility(View.GONE); if(flag) { manager.removeView(view); remoteViews.setTextViewText(R.id.button2 , "display"); notificationManager.notify(1,notification); flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE1" , String.valueOf(code1)); } else if(code2 == 2) { if(flag) { // manager.removeView(view); // flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE2" , String.valueOf(code2)); if(isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18left); } else { picture.setImageResource(R.drawable.phoneleft); } isRightPicture = false; } else if(code3 == 3) { //picture.setVisibility(View.VISIBLE); //picture.setAlpha((float) 0.5); if(flag) { //manager.removeView(view); //flag = false; } else { remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); manager.addView(view , viewParam); flag = true; } Log.i("CODE3" , String.valueOf(code3)); if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { picture.setImageResource(R.drawable.phoneright); } isRightPicture = true; } } } }
- Register broadcast recipients
NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("PictureNotify"); this.registerReceiver(receiver,filter);
So far, you can respond to the click of the front desk service. Here is an additional point to note:
To update the UI or style in the notification bar of the foreground service, in addition to directly using remoteviews Settextviewtext (r.id.button2, "display"); In addition, you need to call notificationmanager once notify(1,notification);
Start service
public class MainActivity extends AppCompatActivity { private static String model; private Button button; @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent2 = new Intent(MainActivity.this, NotifyService.class); if(Settings.canDrawOverlays(MainActivity.this)) { startService(intent2); } else { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivity(intent); } finish(); } }
Configure permissions required
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Barrier free window
Requirement background: the above suspended window cannot be overwritten in some interface settings and drop-down notification bar
After unremitting efforts, a solution was finally found
Use another type TYPE_ACCESSIBILITY_OVERLAY can solve this problem
But when you use it happily, you will find the following errors
After checking some data, it is found that this type must be used with accessibility service
The following is the code after using AccessibilityService:
public class NotifyService extends AccessibilityService implements LifecycleOwner { private WindowManager manager = null; private View view = null; private WindowManager.LayoutParams viewParam; private RemoteViews remoteViews; private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this); public ImageView picture; private static String model; private boolean flag = true; private Bitmap bitmap; private int height; private int width; private int x; private NotificationManager notificationManager; private Notification notification; public NotifyService() { } @Override public Lifecycle getLifecycle() { return lifecycleRegistry; } @SuppressLint("ServiceCast") @RequiresApi(api = 31) @Override public void onCreate() { super.onCreate(); lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); initObserve(); openNotify(); } private void initObserve() { ViewModleMain.INSTANCE.isShowWindow().observe(this, aBoolean -> { if(aBoolean) { showWindow(); } else { if(!isNull(view)) { if(view.getWindowToken() != null) { if(manager != null) { manager.removeView(view); } } } } }); } @Override protected void onServiceConnected() { super.onServiceConnected(); lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); } @Override public boolean onUnbind(Intent intent) { lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { } @Override public void onInterrupt() { } private void openNotify() { NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("PictureNotify"); this.registerReceiver(receiver,filter); remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification); // Service and broadcasting of tong bulletin board if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT); notificationChannel.enableLights(true); notificationChannel.enableVibration(false); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { notificationManager.createNotificationChannel(notificationChannel); } } Intent intent1 = new Intent("PictureNotify"); intent1.putExtra("notDisplay", 1); PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // Do not display Intent intent2 = new Intent("PictureNotify"); intent2.putExtra("leftDisplay", 2); PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, 1, intent2, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent2); // Left display Intent intent3 = new Intent("PictureNotify"); intent3.putExtra("rightDisplay", 3); PendingIntent pendingIntent3 = PendingIntent.getBroadcast(this, 2, intent3, PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.button3, pendingIntent3); // Right display notification = new NotificationCompat.Builder(this, "1") .setStyle(new NotificationCompat.DecoratedCustomViewStyle()) .setContent(remoteViews) .setSmallIcon(R.mipmap.ic_launcher2) .build(); notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE); startForeground(1,notification); } private void showWindow() { manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(displayMetrics); viewParam = new WindowManager.LayoutParams(); viewParam.x = WindowManager.LayoutParams.WRAP_CONTENT; viewParam.y = WindowManager.LayoutParams.WRAP_CONTENT; //windowInsetsController.hide(WindowInsets.Type.statusBars()); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { viewParam.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { viewParam.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } } else { viewParam.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } viewParam.alpha = (float) 0.5; viewParam.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| //WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS| //WindowManager.LayoutParams.FLAG_FULLSCREEN| //WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN| WindowManager.LayoutParams.TYPE_STATUS_BAR; // Display display = manager.getDefaultDisplay(); // Point p = new Point(); // display.getRealSize(p); // viewParam.width = p.x; // viewParam.height = p.y; //viewParam.flags = WindowManager.LayoutParams.TYPE_STATUS_BAR; viewParam.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; viewParam.format = PixelFormat.TRANSPARENT; // Add floating window view = LayoutInflater.from(this).inflate(R.layout.view2, null); picture = view.findViewById(R.id.picture); manager.addView(view, viewParam); if (picture != null) { if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright); picture.setLayoutParams(params); picture.setImageResource(R.drawable.phoneright); } //picture.setAlpha((float)0.5); } //startForeground(1, notification); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if(picture != null) { if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright); picture.setLayoutParams(params); picture.setImageResource(R.drawable.phoneright); } //picture.setAlpha((float)0.5); } } private ViewGroup.LayoutParams measurePicture(int drawable) { bitmap = BitmapFactory.decodeResource(getResources(), drawable); height= bitmap.getHeight(); width= bitmap.getWidth(); x = NotifyService.this.getResources().getDisplayMetrics().widthPixels; float ratio=(float) height/(float) width; ViewGroup.LayoutParams params = picture.getLayoutParams(); params.width = x; params.height = (int) (ratio * x); return params; } private boolean isL18() { DisplayMetrics dm = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels;// Screen width (pixels) //int height = dm.heightPixels; float density = dm.density;//Screen density (0.75 / 1.0 / 1.5) //int densityDpi = dm.densityDpi;// Screen density dpi (120 / 160 / 240) //Screen width algorithm: screen width (pixels) / screen density int screenWidth = (int) (width/density);//Screen width (dp) Log.i("ScreenWidth" , String.valueOf(width)); if(width > 1440) { return true; } return false; } private boolean isNull(View view) { return view == null; } class NotifyBroadCastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if("PictureNotify".equals(action)) { int code1 = intent.getIntExtra("notDisplay" ,4); int code2 = intent.getIntExtra("leftDisplay",4); int code3 = intent.getIntExtra("rightDisplay" , 4); if(code1 == 1) { //picture.setVisibility(View.GONE); if(flag) { manager.removeView(view); remoteViews.setTextViewText(R.id.button2 , "display"); notificationManager.notify(1,notification); flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE1" , String.valueOf(code1)); } else if(code2 == 2) { if(flag) { // manager.removeView(view); // flag = false; } else { manager.addView(view , viewParam); remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); flag = true; } Log.i("CODE2" , String.valueOf(code2)); if(isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18left); } else { picture.setImageResource(R.drawable.phoneleft); } } else if(code3 == 3) { //picture.setVisibility(View.VISIBLE); //picture.setAlpha((float) 0.5); if(flag) { //manager.removeView(view); //flag = false; } else { remoteViews.setTextViewText(R.id.button2 , "Do not display"); notificationManager.notify(1,notification); manager.addView(view , viewParam); flag = true; } Log.i("CODE3" , String.valueOf(code3)); if (isL18()) { ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right); picture.setLayoutParams(params); picture.setImageResource(R.drawable.l18right); } else { picture.setImageResource(R.drawable.phoneright); } } } } } }
The code here refers to someone else's code. Because the service life cycle cannot be controlled in the start-up monitoring, LiveCycle is used (not), so it refers to someone else's / xyx
Two tool classes:
ViewModleMain.kt
object ViewModleMain : ViewModel() { //Floating window creation and removal are based on accessibility services var isShowWindow = MutableLiveData<Boolean>() //Floating window creation and removal var isShowSuspendWindow = MutableLiveData<Boolean>() //Floating window display and hide var isVisible = MutableLiveData<Boolean>() }
Utils.kt
object Utils { const val REQUEST_FLOAT_CODE=1001 /** * Jump to the settings page to apply for accessibility assistance */ fun accessibilityToSettingPage(context: Context) { //Open the auxiliary function page try { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK context.startActivity(intent) } catch (e: Exception) { val intent = Intent(Settings.ACTION_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK context.startActivity(intent) e.printStackTrace() } } /** * Determine whether the Service is enabled * */ fun isServiceRunning(context: Context, ServiceName: String): Boolean { if (TextUtils.isEmpty(ServiceName)) { return false } val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val runningService = myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo> for (i in runningService.indices) { if (runningService[i].service.className == ServiceName) { return true } } return false } /** * Determine suspended window permission */ private fun commonROMPermissionCheck(context: Context?): Boolean { var result = true if (Build.VERSION.SDK_INT >= 23) { try { val clazz: Class<*> = Settings::class.java val canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context::class.java) result = canDrawOverlays.invoke(null, context) as Boolean } catch (e: Exception) { Log.e("ServiceUtils", Log.getStackTraceString(e)) } } return result } /** * Check whether the barrier free service permission is enabled */ fun checkAccessibilityPermission(context: Activity) : Boolean{ return isServiceRunning(context, NotifyService::class.java.canonicalName) } }
Next comes mainactivity java
public class MainActivity extends AppCompatActivity { @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent2 = new Intent(MainActivity.this, NotifyService.class); if(Settings.canDrawOverlays(MainActivity.this)) { // Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); // startActivity(intent); if(!Utils.INSTANCE.checkAccessibilityPermission(this)){ Utils.INSTANCE.accessibilityToSettingPage(this); } ViewModleMain.INSTANCE.isShowWindow().postValue(true); } else { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivity(intent); } finish(); } }
About how to turn on accessibility services on your phone:
Generally, it is in mobile phone settings - > more settings - > accessibility
Just open the corresponding service