hmk run dev

안드로이드 잠금화면 점유 본문

카테고리 없음

안드로이드 잠금화면 점유

hmk run dev 2022. 2. 13. 15:52

캐시워크같은 잠금화면위에 어플화면을 띄울 수 있는 기능을 개발을 해보자!

 

안드로이드 같은 경우 잠금화면 액티비티를 개발자가 개발 할 수 있다(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

 

Comments