몰?.루();

안드로이드 화면 끄기(Screen off) 본문

프로그래밍/안드로이드, 코틀린

안드로이드 화면 끄기(Screen off)

toonraon 2016. 6. 23. 08:11

만들고 있는 어플에서 화면을 끄는 기능을 넣어보려고 했다. 당연히 메소드하나로 굉장히 쉽게 구현이 가능할 것이라 생각했고 실제로 구글에서 Android screen off 같은 검색어로 검색하면 굉장히 간단해보이는 코드가 검색된다.


PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "tag");
wl.acquire();
wl.release();


가장 유명한 Screen off 코드.

그런데 이게 어찌된 것인지 모르겠지만 내 휴대폰은 저 코드가 먹히지 않았다. 솔직히 아직까지도 이유를 알 수가 없다. 내가 상상한 것과 다른 코드인 것인지 구식 코드라서 요즘 API에서는 먹히지 않는 것인지는 모르겠으나 확실한 것은 내 휴대폰에서는 작동이 되지 않는다는 것.


참고로

<uses-permission android:name="android.permission.WAKE_LOCK" />

당연히 Manifest.xml에 이 퍼미션은 줬었다. 애초에 이 퍼미션을 안 적어서 코드가 작동이 안 되는 것이라면 오류를 뿜뿜!하고 앱이 죽었어야 정상인데 문제는 아무 로그도, 어떠한 오류창도 뜨지 않고 그냥 아무 일도 안 일어났다는 것.



그래서 또다른 코드를 찾아봤었다. 그랬더니

Window mywindow = getWindow();
WindowManager.LayoutParams lp = mywindow.getAttributes();
lp.screenBrightness = 0;
mywindow.setAttributes(lp);

이런 코드를 검색할 수 있었다.

그런데 이 코드의 문제는 휴대폰을 실제로 끄는 것이 아니라 그냥 화면 밝기를 엄청 어둡게하는 코드였다는 것. MX 플레이어 같은 동영상 재생 어플에서 흔히 쓰이는 강제 밝기 조절 코드인 것 같은데 유용한 코드이긴하지만 화면을 완전히 끄는 코드는 아니였다. 참고로 저 lp.screenBrightness = 1;로 수정하면 화면이 굉장히 밝아졌다. 뭐, 언젠가 쓸 일이 있을 것 같아서 메모도 할 겸 겸사겸사 쓴 것인데 여튼 확실한 건 내가 원하는 Screen off는 아니였다는 것.



그래서 구글 플레이스토어에서 대충 아무거나 Screen off라는 어플을 다운로드 받아봤다. 그런데 한 가지 발견했던 것은 처음 실행할 때 Admin 권한을 받는 창이 뜬다는 것. 위의 코드들이 적혀있는 사이트들에서는 어딜봐도 그런 이야기는 없었다. 여튼 권한을 주고 그 어플을 실행해보니 실제로 내 폰이 잘 꺼졌다. 딱 내가 원하는 모습으로. 그래서 Admin 관련해서 뒤져보다가 Google 공식 문서를 발견했다. (링크: https://developer.android.com/guide/topics/admin/device-admin.html#developing) 안드로이드 Device Admin에 관한 내용이 적혀있는데 Screen lock 뿐만 아니라 여러가지에 대해서 두루적혀있어서 여기서는 Screen off에 대해서만 요약하기로 한다.



일단 결론적으로 말하자면, 코드 1, 2 줄로 화면 끄기를 구현할 수 있을 것이라는 나의 처음의 기대는 완전히 박살났다. 굉장히 복잡하다.


사용되는 파일만 해도 Manifest.xml, strings.xml, device_admin_samlple.xml(직접 생성), Shutdown.class(직접 생성), ShutdownAdminReceive.class(직접생성) 총 5개의 파일을 건드린다.


당연히 직접 생성이라고 적힌 파일들의 이름은 마음대로 변경해도 된다. Shutdown.class가 실질적으로 '화면을 꺼라!'라고 코드를 실행하는 부분이다.



먼저 화면을 끄기 위해선 Admin 권한을 받아야만한다.


DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getApplicationContext().getSystemService(Context.DEVICE_POLICY_SERVICE);

ComponentName componentName = new ComponentName(getApplicationContext(), ShutdownAdminReceiver.class);

if(!devicePolicyManager.isAdminActive(componentName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
startActivityForResult(intent, 0);
}

devicePolicyManager.isAdminActive(componentName) 를 통해서 현재 어플이 Admin 권한을 받아왔는지 확인할 수 있다. admin 권한이 있을 경우 true 없으면 false를 반환한다. Admin 권한이 없을 경우엔 Admin을 받아와야만 앞으로 일을 진행할 수 있다.


componentName에 쓰인 ShutdownAdminReceiver.class는 Admin 권한이 주어졌을 때와 거부 당했을 때 이벤트를 처리할 수 있다.


Intent를 통해서 Admin을 받아오는 창을 띄울 수 있다. 당연하게도, 우리는 Admin 요청 창을 띄울 수만 있을 뿐 강제적으로 Admin을 받아올 수 없다. Intent를 통해서 admin 요청 화면을 띄워올 수 있다.



이와 같은 창이 뜬다. 물론, ShutdownAdminReceiver.class를 완성했을 때의 이야기다. 그럼 ShutdownAdminReceiver를 보자.


public class ShutdownConfigAdminReceiver extends DeviceAdminReceiver {
@Override
public void onDisabled(Context context, Intent intent) {
Toast.makeText(context, "관리자 권한을 받아오지 못했습니다.", Toast.LENGTH_SHORT).show();
}

@Override
public void onEnabled(Context context, Intent intent) {
Toast.makeText(context, "관리자 권한을 받았습니다.", Toast.LENGTH_SHORT).show();
}
}

DeviceAdminReceiver를 상속하고 있으며 onEnabled와 onDisabled로 아까 창에서 권한을 받아왔을떄와 권한을 해제했을 때 간단한 Toast를 띄우게 해놓았다.


굳이 Toast창을 띄우지 않고 아무것도 구현 안해도 되지만 최소한 DeviceAdminReceiver를 상속받은 클래스는 존재해야한다. 최소한 내가 알기론 그렇다.


알다시피 Receiver와 Activity는 class 파일을 생성하는 것만으론 어플에서 등록되지 않아서 인지하지 못한다.

Manifest.xml을 수정해야한다.


AndroidManifest.xml 파일을 다음 코드를 추가한다.

우선, 당연하게도

<uses-permission android:name="android.permission.WAKE_LOCK" />

WAKE_LOCK 퍼미션은 주어야한다.


그리고 receiver를 등록해야한다.

<receiver android:name=".shutdown.ShutdownAdminReceiver"
android:label="@string/sample_device_admin"
android:description="@string/sample_device_admin_description"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="@xml/device_admin_sample" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>

receiver 코드를 추가하는 곳은 뭐 다들 알겠지만 <application> 안에 다른 <activity>나 <receiver> 뒤에 적당히 붙여넣으면 된다. 여기서 android:label과 android:description의 값이 @string으로 되있는 것을 알 수 있는데 필자가 굳이 strings.xml 파일을 안 쓰고 단순히 문자열을 쓸 수 있지 않을까해서 해봤는데 실패했다. 무조건 strings.xml을 통해야만 하는 듯하다.



저기서 설정한 label과 description은 디바이스 관리자에서 볼 때 나타난다. 필자는 보다시피 android:label="@string/sample_device_admin" 는 SAMPLE DEVICE ADMIN으로 설정했었고 android:description="@string/sample_device_admin_description" 는 DESCRIPTION으로 적어놨었기에 저렇게 뜬다.


<resources>
<string name="app_name">My Application</string>
<string name="sample_device_admin">SAMPLE DEVICE ADMIN</string>
<string name="sample_device_admin_description">DESCRIPTION</string>
</resources>

strings.xml 파일 모습


그리고 아까 Manifest에서 적었던 것 중에 <meta-data android:name="android.app.device_admin"

android:resource="@xml/device_admin_sample" /> 에서 @xml/device_admin_sample 의 정체는 res/xml/device_admin_sample.xml 파일이다.



device_admin_sample.xml 파일 코드 내용

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<force-lock />
</uses-policies>
</device-admin>

여기서 실질적으로 아까 Admin 요청화면에서 화면 잠금이라고 적혀있던 부분이 <force-lock /> 이고 아까 링크한 Google Admin 문서에 가보면 이 외에도 수많은 권한을 요청할 수 있음을 알 수 있다. 예를 들어 카메라 해제 권한, 화면 잠금해제 비밀번호 변경 권한, 데이터 전체 삭제 권한(!!!!) 등... 보면 굉장히 무서운 짓을 할 수 있는 권한들이 많다. 그에 비하면 Screen off는 굉장히 착한 친구(?)인데 왜 굳이 이렇게 Admin까지 요청해야하는 것인지 이해할 수 없었다.



여기까지하면 아까 봤던 Admin 요청창이 정상적으로 떠야한다.


자, 이제 진짜 화면 끄기를 해보자. 준비단계만 지나면 굉장히 간단하다. 당연히 화면 끄기 코드는 Shutdown.class에 썼다.

devicePolicyManager.lockNow();

이거면 끝난다.

Comments