요구사항 및 구현 방법
앱 밖에 표시되는 즉 안드로이드 디바이스 화면 에 표시되는 view를 만들고싶다.
- 화면위에 그리기 원한을 사용한다.
- 서비스에서 view를 호출한다.
구현
1. 권한
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
화면위에 그리기 권한과
안드로이드 오레오 이상에서 서비스를 호출하기위한 포그라운드 서비스 등록
2. 권한획득 및 서비스 호출
class MainActivity : AppCompatActivity() {
private val serviceIntent by lazy { Intent(this, ImmortalService::class.java) }
companion object {
private const val ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 1004
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkPermission()
}
@RequiresApi(Build.VERSION_CODES.M)
private fun checkPermission() {
when{
Settings.canDrawOverlays(this) -> {
callService()
}
else->{
permissionPopup()
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun permissionPopup(){
AlertDialog.Builder(this@MainActivity)
.setTitle("권한이 필요합니다.")
.setMessage("화면위의 그리기 권한이 필요")
.setPositiveButton("확인") { _, _ ->
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"))
startActivityForResult(intent,
ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE)
}
.setNegativeButton("취소") { _, _ -> }
.create().show()
}
private fun callService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
callService()
}
}
}
MainActivity에서는 화면위에 그리기 권한과 서비스 호출을 하고있다.
3. 서비스 구현
class ImmortalService : Service() {
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelID = "채널id"
val strTitle = "타이틀"
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
var channel = notificationManager.getNotificationChannel(channelID)
var builder: NotificationCompat.Builder? = null
var notification: Notification? = null
if (channel == null) {
channel = NotificationChannel(
channelID,
strTitle,
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
builder = NotificationCompat.Builder(this, channelID)
builder.setSmallIcon(R.mipmap.ic_launcher)
builder.setContentText("노티 텍스트")
notification = builder.build()
notificationManager.notify(1, notification)
startForeground(1, notification)
}
initView()
}
fun initView(){
// inflater 를 사용하여 layout 을 가져오자
val inflate = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
// 윈도우매니저 설정
val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, // Android O 이상인 경우 TYPE_APPLICATION_OVERLAY 로 설정
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT
)
// 위치 지정
params.gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
// activity_overlay.xml 불러오기
val mView = inflate.inflate(R.layout.activity_overlay,null)
val overlayButton :Button = mView.findViewById(R.id.btn)
overlayButton.setOnClickListener{
Log.d("오버레이", "initView: 버튼 클릭")
}
windowManager.addView(mView,params)
}
}
해당 서비스의 역활은 죽지않는 서비스를 위한 Notification channel 생성과
xml 파일을 WindowManager에게 적용시켜
앱 밖에 표시되도록 한다.
4. 앱 밖에 표시될 화면 구현
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="click"
android:textColor="#ffffff"
android:layout_marginTop="12dp" />
</LinearLayout>
결과


'Android' 카테고리의 다른 글
Android ViewBinding으로 FindViewByid 랑 작별하기 (0) | 2021.09.25 |
---|---|
Android Coroutine 사용하기 (0) | 2021.09.25 |
Android Retrofit 사용하여 API 호출하기 (0) | 2021.09.25 |
Android Retrofit 인터셉터 사용하기 (0) | 2021.09.25 |
Android 다크모드(Theme 사용) 적용하기 (0) | 2021.09.25 |