hmk run dev
안드로이드 잠금화면 점유 본문
캐시워크같은 잠금화면위에 어플화면을 띄울 수 있는 기능을 개발을 해보자!
안드로이드 같은 경우 잠금화면 액티비티를 개발자가 개발 할 수 있다(IOS는 아직까지 안돼는 걸로...)
잠금화면을 점유하기 위해서 BroadcastReceiver에서
ACTION_SCREEN_OFF, ACTION_SCREEN_ON 액션을 받아서
화면에 액티비티를 띄워야 하는데 Manifest파일에 receiver를 등록해 정적 동작은 실행되지 않는다.
그래서 Service를 만들어 백그라운드에서 동작하게 만들어야 하는데 안드로이드 O 버전부터 백그라운드 동작을
죽이기 때문에 일정 시간이 지나면 더 이상 동작하지 않는다.
이것을 해결하기 위해 AlarmManager로 알람을 등록해 서비스를 계속 실행시키는 방법도 있는데 이것은
알람 주기 자체를 안드로이드에서 제한을 하는 건지 30초 이상은 지나야 동작하기 때문에 그전에
서비스가 죽어버리면 그 사이에는 동작하지 않게된다.
다른 해결법은 서비스 시작을 startForegroundService로 시작하고
서비스의 onCreate나 onStartCommand 부분에 Notification을 등록하고 startForeground 함수를 호출하는
방법이다. 여기서 또 문제가 발생하게 되는데 foreground로 동작하게 해도 OS가 이 서비스의
Context를 Sleep상태로 만들어 버리는지 일정시간이 지나면 동작하지 않는 문제가 있다.
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkPermission()
}
fun checkPermission() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(!Settings.canDrawOverlays(this)) {
val uri = Uri.fromParts("package", packageName, null)
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, uri)
startActivityForResult(intent, 0)
} else {
val intent = Intent(applicationContext, LockScreenService::class.java)
startForegroundService(intent)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == 0) {
if(!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "해라", Toast.LENGTH_LONG).show()
} else {
val intent = Intent(applicationContext, LockScreenService::class.java)
startForegroundService(intent)
}
}
}
}
LockScreenService
class LockScreenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
//setTurnScreenOn(true)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
} else {
this.window.addFlags(
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
}
setContentView(R.layout.activity_lock_screen)
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
finish()
}
}
}
LockScreenActivity
class LockScreenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
//setTurnScreenOn(true)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
} else {
this.window.addFlags(
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
}
setContentView(R.layout.activity_lock_screen)
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
finish()
}
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bluewhale.lockscreentest">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".LockScreenActivity"
android:label="@string/title_activity_lock_screen"
android:theme="@style/AppTheme.NoActionBar"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:taskAffinity="com.bluewhale.lockscreentest.lockscreen"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".service.LockScreenService"
android:enabled="true"
android:permission="android.permission.SYSTEM_ALERT_WINDOW"
android:exported="false"/>
</application>
</manifest>
매니페스트 파일을 살펴보자
excludeFromRecents - 최근화면에 기록되지 않는 것
taskAffinity - 다른 액티비티와 같은 Task에서 생성되기 때문, 잠금화면 종료 시 최근화면에서 이 앱이 사라지게 되는 것을 방지
SYSTEM_ALERT_WINDOW 권한을 획득하고 부여 > 다른 앱 위에 그리기 권한 승인
LockscreenService에서 receiver의 코드중에 addFlag로 ACTIVITY_NEW_TASK가 있는데 이 Flag를 주지 않으면
Exception이 발생하게 된다.
출처
https://whale-order.tistory.com/10
[Android] 잠금화면 액티비티 설정
Android에는 잠금화면 액티비티를 개발자가 개발 할 수있다. 그런데 스택오버플로우나 여러 블로그에는 똑같은 글만 돌고 도는듯 하고, 기타 오류에 대해서는 해결법을 찾기 어려웠다. 잠금화면
whale-order.tistory.com