Compared to iOS, Android didn’t have native support for eye protection before Q, so we had to implement one ourselves.

In fact, online code is also quite a lot, and even some companies’ products are this function.

Let’s talk about the idea of implementation first. If you don’t want to listen to the idea first, you can jump directly to the code. The code is not difficult.

1. Implementation idea

  • inWindowCover with aViewTo achieve overall coverage across App, status bar and navigation bar
  • The background of the View is set to a transparent color that filters blue light (filter layer)
  • Pay attention to compatibility issues with the Android system

Code 2.

Create an EyeCareService to add a filter layer to the Window

class EyeCareService : Service() {
    private lateinit var windowManager: WindowManager
    private lateinit var coverLayout: FrameLayout
    override fun onBind(intent: Intent?).: IBinder? {
        return null
    }

    override fun onCreate(a) {
        super.onCreate()
        windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val params = WindowManager.LayoutParams().apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                this.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY or
                        WindowManager.LayoutParams.TYPE_STATUS_BAR
            } else {
                this.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
            }
            this.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            this.format = PixelFormat.TRANSLUCENT
        }
        windowManager.defaultDisplay.apply {
            params.gravity = Gravity.START or Gravity.TOP
            val point = Point()
            this.getRealSize(point)
            //params.width = point.x
            // Only half of the layer is covered, so increase the mask layer size and set the width to y as well
            params.width = point.y
            params.height = point.y
        }

        coverLayout = FrameLayout(this)
        coverLayout.setBackgroundColor(Utils.getColor(30))
        windowManager.addView(coverLayout, params)
    }

    override fun onDestroy(a) {
        windowManager.removeViewImmediate(coverLayout)
        super.onDestroy()
    }


}
Copy the code

Filter layer to set the method of obtaining color, generally pass in 30

/** * filter blue light **@paramBlueFilterPercent Blue light filtering percentage [10-80] */
public static @ColorInt
int getColor(int blueFilterPercent) {
	int realFilter = blueFilterPercent;
	if (realFilter < 10) {
		realFilter = 10;
	} else if (realFilter > 80) {
		realFilter = 80;
	}
	int a = (int) (realFilter / 80f * 180);
	int r = (int) (200 - (realFilter / 80f) * 190);
	int g = (int) (180 - (realFilter / 80f) * 170);
	int b = (int) (60 - realFilter / 80f * 60);
	return Color.argb(a, r, g, b);
}
Copy the code

The start method is invoked in the activity

private fun openEyeCareMode(a) {

    if (Build.VERSION.SDK_INT >= 23) {
        if (Settings.canDrawOverlays(this)) { // Have hover window permission enable service binding binding permission
            val intent = Intent(this, EyeCareService::class.java)
            startService(intent)
        } else { // There is no hover window permission, go to the hover window permission
            try {
                val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    } else { // there is a hover window permission by default, but huawei, xiaomi,oppo and other mobile phones will have their own set of Android6.0 will have their own set of hover window permission management also need to be adapted
        val intent = Intent(this, EyeCareService::class.java)
        startService(intent)
    }

}

override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
        if (Build.VERSION.SDK_INT >= 23) {
            if(! Settings.canDrawOverlays(this)) {
                // Permission grant failed, suspension window cannot be opened
                return
            } else {
                // Permission granted successfully
            }// Have hover window permission enable service binding binding permission
        }
        val intent = Intent(this, EyeCareService::class.java)
        startService(intent)
    }
}
Copy the code

3. Practical effect

4. The latter

So there’s a couple of things to think about, and I’m not going to post the code here, but I’m going to talk about it a little bit

  • StartService is an intent that delivers the closing parameter
  • The foreground Service is considered to prevent the Service from being killed by the system when the system goes back to the background for a long time, resulting in function failure