
오늘은 안드로이드 위젯에 대해 알아보려고 한다.
위젯은 뭘까?

위에 사진을 보면 공룡과 선인장이 있는 작은 화면이 보일 것이다.
위 내용물이 바로 "위젯"이다.
위젯은 안드로이드에서 사용자의 편의를 위해 앱을 실행하지 않아도 특정 기능을 수행할 수 있도록 도와주는 구성 요소이다.
위젯을 만들기 위해서는 크게 아래의 Step을 진행하면 된다.
Step 1. Appwidget-provider XML 작성
Step 2. Widget layout 작성
Step 3. AppWidgetProvider class 작성
Step 4. AndroidManifest에 반영
위의 흐름을 따라가며, XML을 작성해보자.
제일 먼저, res 폴더 위에 마우스를 올려

Android Resource Diectory를 클릭하자.

XML로 설정해주고 OK를 누른다.
이후, xml 폴더 안에 원하는 이름으로 위젯 생성 정보를 담을 xml 파일을 생성한다.

자 이제 작성할 내용을 확인해보자.
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="40dp"
android:resizeMode="none"
android:previewImage="@mipmap/ic_launcher"
android:initialLayout="@layout/widget_activity"
android:description="@string/app_name"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen" />
하나씩 구성 요소를 살펴보자.
android:minWidget : 위젯을 생성할 때 기준이 되는 최소 가로 길이
android:minHeight : 위젯을 생성할 때 기준이 되는 최소 세로 길이
android:resizeMode : 위젯의 사이즈를 사용자가 변경할 수 있는지를 선언하는 부분
android:initialLayout : 위젯 생성시 기준이 되는 레이아웃
android:description : 표시할 위젯 선택 도구에 대한 설명 ( 안드로이드 12부터 사용됨 )
android:updatePeriodMillis : 위젯의 업데이트 주기 ( 최소 30분 )
android:widgetCateogory : 위젯이 어디에 표시되는지를 선언하는 부분 ( ex. 잠금화면, 홈 화면 )

안드로이드는 기본적으로 위젯을 생성하면 사이즈가 주워진다. 이런 사이즈의 기준은
위의 표와 같이 우리가 최소 가로/세로를 얼마로 지정 했냐에 따라 달라진다.
또한, Android 12부터는 아래와 같이 targetCell~~~ 를 사용하여 보다 편리하게 작성할 수 있다.
<appwidget-provider
android:targetCellWidth="3"
android:targetCellHeight="2"
android:minWidth="180dp"
android:minHeight="110dp">
</appwidget-provider>
위젯을 작성하기 전 우리는 RemoteViews에 대해 알아야 한다.
앱 위젯을 만들면 앱과 위젯은 서로 다른 프로세스에서 동작한다.

위 사진처럼 위젯에서 이벤트를 발생했을 때 TID와 앱에서 특정 이벤트를 발생했을 때 TID가 서로 다른 점을 확인할 수 있다.
그럼 우리는 어떻게 위젯을 조작할까?
이럴 때 사용되는 게 RemoteViews이다.
RemoteViews는 일종의 View의 작동 로직을 저장하는 설계도이다.
우리는 RemoteViews를 활용해 버튼 클릭 이벤트 등을 정의해 AppWidgetManager로 넘겨주어 위젯의 기능을 설계하는 것이다.
하지만, 이런 RemoteViews에서 사용할 수 있는 컴포넌트는 제한적이다.

우리가 생각한 것 보다 이용할 수 있는 게 적다.
고로 위 지원 요소를 참고해 레이아웃을 구성해야한다.
하지만, 우리는 간단한 예제를 작성할 것이기 때문에 버튼 하나만 추가하도록 하자.

위와 같이 버튼을 누르면 토스트를 생성할 버튼을 추가해줬다.
이제 우리는 기본적인 UI 작업은 끝났다.
이제는 생성한 UI에 생명을 불어넣어 줘야 한다.

Class 이름은 각자 사용하고 싶은 걸로 편하게 작성하자.
하지만 extends 부분에 "AppWidgetProvider"로 상속해주는 걸 잊지 말자.

위와 같이 Class 파일 생성이 완료되면 우리는 2가지의 메서드에 대해 유심히 파악해야 한다.
제일 먼저, onUpdate() 메서드이다.
onUpdate() 메서드는 우리가 최초에 만들었던 Appwidget-provider XML안에 있는 updatePeriodMillis의 주기에 맞게 실행되는 함수이다.
또한, 위 메서드는 사용자가 위젯을 최초에 추가하면 호출된다.
onReceive() 메서드는 모든 브로드캐스트와 이전의 각 콜백 전에 호출된다. 사용 예시로, 위 이미지의 intent action을 통해 분기를 사용해 특정 동작을 유도할 수 있다.
분명, 위에서 Layout을 작성할 때 우리는 RemoteViews를 사용해 View가 작동하는 로직을 전달한다 했다.
하지만, 위 이미지에서는 RemoteViews에 대한 내용을 확인할 수 없다.
그 이유는, updateWidgetView()라는 함수로 따로 관리하고 있기 때문이다.
updateWidgetView() 함수를 살펴보자.

위 코드는 개인적으로 따로 관리하고 싶어 작성한 코드이니, 바로 onUpdate()에 적용해도 문제없다.
제일 먼저 살펴봐야 하는 부분은 RemoteViews를 생성하는 부분이다. context를 사용해 패키지 이름과 Widget에 사용될 Layout을 지정해준다.
이후 우리는 setOnClickPendingIntent를 사용해 View가 눌렸을 때 팬딩 인텐트를 사용할 것이기 때문에 팬딩 인텐트를 생성해준다.
그 다음 생성한 Remoteviews를 통해 클릭 이벤트가 적용될 view id와 팬딩 인텐트를 전달하고, AppWidgetManger를 통해
위젯을 최종 업데이트한다.
여기서 주의할 점은 팬딩 인텐트 안에 넣을 인텐트를 생성할 때 context와 작동할 Class를 "명시적"으로 작성해줘야 한다.
new Intent(TOAST_ACTION) 이런식으로 생성하면, 차후 팬딩 인텐트가 onReceive에 전달되지 않을 수 있다.

이외에도 Widget 관련 몇 가지 메서드를 알아보자
onAppWidgetOptionsChanged() : 위젯이 처음 배치 될 때와, 크기가 변경될 때 마다 호출된다.
onDeleted() : 위젯 호스트에서 위젯이 삭제될 때마다 호출된다.
onEnabled() : 위젯의 인스턴스가 처음 생성될 때 호출된다.
onDisabled() : 위젯의 마지막 인스턴스가 위젯 호스트에 있을 때 호출된다.
자 이제 마지막으로 AndroidManifest에 우리가 작성한 내용을 반영해보자.

<manifest>
<application>
<receiver>
...
</receiver>
</application>
</manifest>
위와 같이 AndroidManifest에 내용을 작성해서 앱이 설치되면, 위젯의 정보가 어디에 저장되어 있고, 내가 전달받을 인텐트의
정보는 어떤 부분인지 필터를 설정해준다.
여기서 주의 할 점은 android.appwidget.~~~ 과같이 입력하다 보면 자동완성으로 끝내려 했지만, 자동완성이 안 되는 경우가 있다. 이 경우 당황하지 말고 위 내용을 잘 입력해주자.
