[Android] 간단하게 만드는 주기적으로 업데이트하는 위젯 만들기

안드로이드의 위젯은 주기적으로 onUpdate를 호출 할 수 있습니다. 다음과 같이 설정할 경우 30분마다 한번씩 위젯을 갱신할 수 있습니다.

android:updatePeriodMillis=”1800000″

하지만 위의 방법은 구글측에서 제한을 두어 최소 30분 이하의 갱신을 수행할 수 없습니다. 이하의 값을 설정할 경우 자동으로 30분 갱신으로 등록이 됩니다. 0을 등록할 경우 슬립모드에 들어갔다가 화면이 켜질경우에 업데이트를 수행하게 됩니다.

이때문에 더 짧은 주기의 업데이트를 수행하기 위해서는 AlarmManager를 활용하여 수동으로 위젯을 업데이트 하는 방법밖에 없어 보입니다. 다행이도 안드로이드에는 다음과 같은 위젯을 업데이트 하기 위한 인텐트 필터가 존재합니다.

android.appwidget.action.APPWIDGET_UPDATE

저 인텐트를 주기적으로 호출해 주기만 하면 문제가 해결될 수 있을것 같군요. 위젯을 사용하기 위한 방법까지 포함해서 하나하나 정리해 보도록 하겠습니다.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
  xmlns:android="http://schemas.android.com/apk/res/android"
  package="kr.pe.theeye.widget"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-sdk android:minSdkVersion="7" />

  <application
  android:icon="@drawable/icon"
  android:label="@string/app_name">

  <receiver android:name="HelloWidgetProvider" android:label="위젯테스트">
    <intent-filter>
      <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
      android:name="android.appwidget.provider"
      android:resource="@xml/hellowidgetproviderinfo" />
  </receiver>

  </application>
</manifest>

위에서 중요한 부분은 android:name에 AppWidgetProvider를 상속받은 클래스의 클래스명을 적어줘야 한다는 것입니다. android:label에는 위젯을 추가할때에 표시될 위젯의 이름이 표시됩니다. 메타데이터에는 위젯의 기본적인 정보가 적혀있는 XML의 위치를 정의 합니다.

hellowidgetproviderinfo.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/hellowidget_layout"
android:minWidth="220dp"
android:minHeight="72dp"/>

여기서는 위젯의 크기라던가 레이아웃XML파일의 위치를 정의할 수 있습니다. 레이아웃의 크기는 하나의 셀에 74픽셀을 사용하게 됩니다. 하지만 [여기]를 참고해 보면 위젯의 사이즈는 총 크기에서 2픽셀을 빼라고((number of cells * 74) – 2) 되어있군요.

hellowidget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:background="#fff">
<TextView 
            android:id="@+id/widgettext"
   android:layout_width="fill_parent" 
   android:layout_height="wrap_content"
   android:gravity="center"
   android:text="@string/hello"/>
</LinearLayout>

위의 디자인이 제가 테스트로 사용하기 위한 위젯 디자인입니다. 단순하게 글짜만 바꿀 수 있도록 TextView하나를 사용하였습니다.

HelloWidgetProvider.java

/**
 * 위젯의 상태를 주기적으로 갱신하는 예제
 * 
 * @author Eye
 * @since 2011.04.01
 */
public class HelloWidgetProvider extends AppWidgetProvider
{
  private static final String TAG = "HelloWidgetProvider";
  private static final int WIDGET_UPDATE_INTERVAL = 5000;
  private static PendingIntent mSender;
  private static AlarmManager mManager;

  /* (non-Javadoc)
   * @see android.appwidget.AppWidgetProvider#onReceive(android.content.Context, android.content.Intent)
   */
  @Override
  public void onReceive(Context context, Intent intent)
  {
    super.onReceive(context, intent);

    String action = intent.getAction();
    // 위젯 업데이트 인텐트를 수신했을 때
    if(action.equals("android.appwidget.action.APPWIDGET_UPDATE"))
    {
      Log.w(TAG, "android.appwidget.action.APPWIDGET_UPDATE");
      removePreviousAlarm();

      long firstTime = System.currentTimeMillis() + WIDGET_UPDATE_INTERVAL;
      mSender = PendingIntent.getBroadcast(context, 0, intent, 0);
      mManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
      mManager.set(AlarmManager.RTC, firstTime, mSender);
    }
    // 위젯 제거 인텐트를 수신했을 때
    else if(action.equals("android.appwidget.action.APPWIDGET_DISABLED"))
    {
      Log.w(TAG, "android.appwidget.action.APPWIDGET_DISABLED");
      removePreviousAlarm();
    }
  }

  /* (non-Javadoc)
   * @see android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[])
   */
  @Override
  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
  {
    // 현재 클래스로 등록된 모든 위젯의 리스트를 가져옴
    appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, getClass()));
    super.onUpdate(context, appWidgetManager, appWidgetIds);

    final int N = appWidgetIds.length;
    for(int i = 0 ; i < N ; i++)
    {
      int appWidgetId = appWidgetIds[i];
      updateAppWidget(context, appWidgetManager, appWidgetId);

      Toast.makeText(context, "onUpdate(): [" + String.valueOf(i) + "] " + String.valueOf(appWidgetId), Toast.LENGTH_SHORT).show();
    }
  }

  /**
   * 위젯의 형태를 업데이트합니다.
   * 
   * @param context 컨텍스트
   * @param appWidgetManager 위젯 메니저
   * @param appWidgetId 업데이트할 위젯 아이디
   */
  public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
  {
    Date now = new Date();
    RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.hellowidget_layout);
    updateViews.setTextViewText(R.id.widgettext, "[" + String.valueOf(appWidgetId) + "]" + now.toLocaleString());
    appWidgetManager.updateAppWidget(appWidgetId, updateViews);
  }

  /**
   * 예약되어있는 알람을 취소합니다.
   */
  public void removePreviousAlarm()
  {
    if(mManager != null && mSender != null)
    {
      mSender.cancel();
      mManager.cancel(mSender);
    }
  }
}

여기서 참고해볼점은 위젯이 등록될 때 알람을 등록하고 위젯을 삭제할때 알람을 취소시킨다는것입니다. 또한 다음의 코드를 이용하여 위젯의 숫자와 상관없이 동일하게 갱신할 수 있도록 할 수 있습니다.

int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, getClass()));

위의 코드를 사용하면 현재의 WidgetProvider를 사용한 모든 홈스크린에 등록된 위젯들의 ID를 얻어올 수 있습니다.
사용자 삽입 이미지
[샘플코드 다운로드]

[iPhone] URL스키마를 이용한 다른 어플리케이션 실행하기

아이폰 어플리케이션은 다른 외부의 어플리케이션을 URL을 사용하여 실행 할 수 있습니다. 예를 들어 다음의 어플리케이션들을 이와같은 URL을 이용하여 실행할 수 있습니다.

1. 일반 웹브라우저 호출

내가 원하는 임의의 페이지로 이동하는 방법은 간단합니다. 앞으로 소개해 드릴 방법 역시 다음과 같은 방법으로 URL을 호출하시면 됩니다.
[code]NSURL *url = [NSURL URLWithString:@”http://theeye.pe.kr”];
[[UIApplication sharedApplication] openURL:url];[/code]

2. 구글맵 실행

다음과 같이 구글맵은 검색을 위한 문자열을 사용하여 원하는 위치로 이동을 할 수 있습니다.

http://maps.google.com/maps?q=${QUERY_STRING}

여기서 중요한것은 ${QUERY_STRING}을 사용하기 위해서는 URL 인코딩을 하여야 한다는 것입니다. 간단한 예제는 다음과 같습니다.
[code]// 쿼리문 생성
NSString* searchQuery = @”1 Infinite Loop, Cupertino, CA 95014″;
 
// 특수 문자 처리를 위해 인코딩을 합니다.
searchQuery = [addressText stringByAddingPercentEscapesUsingEncoding:
                      NSUTF8StringEncoding];
 
// URL 문자열을 생성합니다.
NSString* urlString = [NSString stringWithFormat:
                              @”http://maps.google.com/maps?q=%@”,
                              searchQuery];
 
// 실행
[[UIApplication sharedApplication]
  openURL:[NSURL URLWithString:urlText]];[/code]

3. 메일 에이전트 실행

위와 마찬가지로 매우 쓸만하고 간단하고 빠르게 메일을 발송할 수 있는 메일 에이전트를 호출합니다. 기본적으로 들어있는 것을 호출하는 것뿐이지만 받는 사람 주소가 미리 입력되어있습니다. 메일을 발송하기 위한 기본적인 형태는 HTML의 그것과 매우 똑같습니다.

mailto://${EMAIL_ADDRESS}

예를 들어 test@abcd.com으로 발송하기 위해서는 다음과 같이 사용하면 됩니다.
[code][[UIApplication sharedApplication] openURL:[NSURL URLWithString:
  @”mailto://test@abcd.com”]];[/code]

4. 전화걸기 (아이폰에서만 사용 가능)

마찬가지로 openURL을 사용하여 전화도 걸 수 있습니다. 한가지 장점은 전화를 종료하였을 때 어플리케이션으로 복귀를 할 수 있다는 점입니다. J2ME나 WML에서 사용하는 UML 스키마의 형태와 비슷합니다. 예를 들어 02-1234-1234로 전화를 걸 경우 다음과 같이 하면 됩니다.
[code][[UIApplication sharedApplication] openURL:[NSURL URLWithString:@”tel://0212341234″]];[/code]

5. SMS 보내기

전화걸기와 마찬가지로 아이팟 터치등에서는 사용할 수 없습니다. 매우 빠르게 SMS 클라이언트를 호출 할 수 있습니다. SMS의 URL은 전화와 달리 //를 사용하지 않습니다.

sms:${PHONENUMBER_OR_SHORTCODE}

[code][[UIApplication sharedApplication] openURL:[NSURL URLWithString:@”sms:55555″]];[/code]

6. 앱스토어 실행하기

앱스토어에서 나의 어플리케이션의 구매 버튼이 있는 페이지로 바로 이동시키는것은 꽤 중요한 일입니다. 앱스토어 링크의 경우엔 따로 URL스키마를 사용할 필요가 없습니다. 단지 아이튠즈의 URL링크를 그대로 사용하면 됩니다. URL이 다음과 같이 보인다고 가정하겠습니다.

[code]NSURL *appStoreUrl = [NSURL URLWithString:@”http://itunes.apple.com/us/app/id396402081?mt=8″];
[[UIApplication sharedApplication] openURL:appStoreUrl];[/code]

잘 되는군요^^

출처: http://iphonedevelopertips.com/cocoa/launching-other-apps-within-an-iphone-application.html