AppWidget 구현 시 XXXConfigure, XXXProvider, Service 정말 많은 것을 구현해야 합니다.
그중에서도 Widget의 구성요소를 사용자로하여금 설정할 수 있게 해 주는 것이 XXXConfigure 인데요..
XXXConfigure를 만드는 순간 우리는 유령 Widget에 시달려야 합니다.
XXXConfigure는 Activity형태라는 것 다 아실테고..
User가 Activity를 빠져나가는 방법은 여러가지가 있을 수 있습니다.
1. home 버튼
2. back 버튼
3. 취소버튼 - 친절하게 XXXConfigure 화면에서 취소버튼을 만들은 경우
4. 그외 상상에 맞기겠습니다.
메뉴얼이나 developer site에 가서 보면
XXXConfigure내에서 Widget 만들기를 취소하는 것은 XXXConfigure에 setResult()에 RESULT_CANCELED를 setting하는 것으로 되어 있으나...아무리 이렇게 해봐도 유령은 계속 계속 생기게 됩니다.
현재 얼마나 많은 appWidget이 host에 등록 되어 있는지 대한 코드는 다음과 같습니다.
ComponentName compName = new ComponentName(this, XXXProvider.class);
int appWidgetIds[] = AppWidgetManager.getInstance(this).getAppWidgetIds(compName);
AppWidgetHost host = new AppWidgetHost(this, 0);
for(int i = 0; i < appWidgetIds.length; i++){
Log.d(TAG, "[" + i + "] " + appWidgetIds[i]);
}
이코드를 XXXConfigure onCreate()에 넣어 보시면 현재 얼마나 많은 위젯들이 있는지 알 수 있겠죠.
유령은 XXXConfigure를 실행하고 setResult(RESULT_OK, intent); 가 호출되지 않은 상태인데요, 화면에 위젯이 표시되지 않고 host에 id로만 존재하는 놈을 말합니다.
그럼 어떻게 지울 수 있을까요....?
항상, onPause() 아니면 onDestroy()에서 (onPause()가 나을 것 같아요..) 유령을 찾아 지워야 합니다.
지우는 코드는 위에서 언급한 widgetId를 보는 코드와 동일합니다.
ComponentName compName = new ComponentName(this, XXXProvider.class);
int appWidgetIds[] =AppWidgetManager.getInstance(this).getAppWidgetIds(compName);
AppWidgetHost host = new AppWidgetHost(this, 0);
for(int i = 0; i < appWidgetIds.length; i++){
Log.d(TAG, "[" + i + "] " + appWidgetIds[i]);
if(appWidgetIds[i]가 유령입니까?){
host.deleteAppWidgetId(appWidgetIds[i]);
}else{
// do nothing..
}
}
빨간색으로 칠한 부분을 생각해 보겠습니다.
화면에 widget이 생성되어 있는지 아닌지 알아내는 방법을 못찾았습니다.(좀 알려주세요.)
그걸 안다면 이런짓을 하지는 않을 겁니다. 그러나 방법을 모르기에 제대로 생성된(즉, User가 XXXConfigure화면에서 OK버튼을 눌렀을때) widget과 아닌것을 구분하는 구분자 정도는 widget 생성시 만들어서 어디에 저장해 놓아야 합니다. 가장 편리하고 쉬운 것은 SharedPreferences를 이용하는 것입니다.
즉, 제대로된 생성된 appWidget의 경우, SharedPreferences 에 제대로 된놈인가를 표시하는 것이죠....
appWidgetIds[i] 가 유령인지 아닌지 판단시 SharedPreferences에 등록이 되어 있는지를 체크 하는 것입니다.
유령체크코드는 application마다 다르기에 여기서는 언급은 하지 않지만요...
조금은 지저분하고 googling을 해봐도 딱히 유령을 가장 깔끔하게 지우는 방법을 찾지는 못했습니다.
암튼 이만.... 댓글 달아 주시면 답변 드리겠습니다.
2012년 2월 19일 일요일
2012년 1월 11일 수요일
AppWidget에서 사용할 수 있는것은?
AppWidget에서 사용할 수 있는 Layout
AppWidget에서 사용할 수 있는 Widget들..
FrameLayout,LinearLayout
RelativeLayout. AppWidget에서 사용할 수 있는 Widget들..
AnalogClock, Button, Chromometer, ImageButton, ImageView, ProgressBar TextView너무 없죠.. 수수께끼 하는 것도 아니고.. 그래도 만들어 볼만한게 AppWidget입니다.그러나 3.x, 4.x로 넘어가면서 사용할 수 있는게 더 많아 졌다니...
실행중인 Task별 Activity 보기..
1. AndroidManifest.xml에 Permission 처리
- <uses-permission android:name="android.permission.GET_TASKS"/>
2. 다음과 같이 코딩을 하세요..
// ActivityManager를 얻고..
ActivityManager actMgr = (ActivityManager)AppPinScreenLockActivity.this.getSystemService(Activity.ACTIVITY_SERVICE);
List<RunningTaskInfo> info = actMgr.getRunningTasks(7);
for (Iterator<RunningTaskInfo> iterator = info.iterator(); iterator.hasNext();) {
RunningTaskInfo runningTaskInfo = (RunningTaskInfo) iterator.next();
Log.d(TAG, "" + runningTaskInfo.topActivity.getClassName());
}
- <uses-permission android:name="android.permission.GET_TASKS"/>
2. 다음과 같이 코딩을 하세요..
// ActivityManager를 얻고..
ActivityManager actMgr = (ActivityManager)AppPinScreenLockActivity.this.getSystemService(Activity.ACTIVITY_SERVICE);
List<RunningTaskInfo> info = actMgr.getRunningTasks(7);
for (Iterator<RunningTaskInfo> iterator = info.iterator(); iterator.hasNext();) {
RunningTaskInfo runningTaskInfo = (RunningTaskInfo) iterator.next();
Log.d(TAG, "" + runningTaskInfo.topActivity.getClassName());
}
2012년 1월 7일 토요일
Implementing Dynamic Tab Widget
1. layout xml file (main.xml) 만들기
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="5dp">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@android:id/tabcontent" android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:padding="5dp"/>
</LinearLayout>
</TabHost>
- xml의 계층을 건들지 마시고 각 id의 name은 미리 android에 선언된 이름이므로 바꾸지 말아야 한다.
2. MainActivity 만들기
- Tab을 만들기 위해서는 우선 TabActivity를 상속받은 Activity를 만들고, TabHost.TabContentFactory 를 implement해야 한다.
public class TabTestActivity extends TabActivity implements TabHost.TabContentFactory {
.......
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 위에서 만든 main.xml setContentView(R.layout.main);
// main.xml 내에 있는 @android:id/tabhost
TabHost tabHost = getTabHost();
// 1. 새로운 tab생성. 물론 생성되는 모든 TabSpec들은 @android:id/tabs 내에 포함되게 됨
TabHost.TabSpec tabSpec = tabHost.newTabSpec(getString(R.string.str_appranker_tab1));
// 2. 이부분에서 단순히 String만을 주게 되면 Tab Widget에 String 만 표시된다.
// 만약, 더 다양한 효과를 주고 싶으면 이부분에 View를 넘기면 된다.
tabSpec.setIndicator(getString(R.string.str_appranker_tab1));
// 3. 이부분이 상단 Tab을 눌렀을때 하단에 표시되는 내용부분이다.
// 직접, View를 넘길수 없으며 Dynamic하게 생성되는 View들을 FrameLayout내에 넣어야 하기에 TabContentFactory를 이용하게 된다.
tabSpec.setContent(this);
// 4. TabHost에 생성한 Tab을 넣는다.
// Tab을 더 생성하고 싶으면 1~4 다시 수행. tabHost.addTab(tabSpec);
}
@Override
public View createTabContent(String tag) {
LayoutInflater inflater = getLayoutInflater();
// Tab을 구별할 수 있는 tag 값에 따라 다른 View를 R.layout.content inflate해서 넣으면 되며, 가장 중요한 것은 @android:id/tabcontent의 하위 view로 rootview를 정의해야한다는것..
// 그래야 나중에 framelayout에서 보이고 안보이고를 결정해 주죠......
if(tag.equals(getString(R.string.str_tab1))){
return inflater.inflate(R.layout.content, getTabHost().getTabContentView());
}
return null;
}
}
-- 근데요.. 하나의 activity내에서 FrameLayout에서 보여주게될 복잡한 view들을 control한다는것은 조금 힘들고 지저분해 보이네요...
-- tabSpec.setContent()에 Intent를 넣어 Activity가 실행되게하여 MVC모델에 충실하게 만드는 것은 어떨런지.....
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="5dp">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@android:id/tabcontent" android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:padding="5dp"/>
</LinearLayout>
</TabHost>
- xml의 계층을 건들지 마시고 각 id의 name은 미리 android에 선언된 이름이므로 바꾸지 말아야 한다.
2. MainActivity 만들기
- Tab을 만들기 위해서는 우선 TabActivity를 상속받은 Activity를 만들고, TabHost.TabContentFactory 를 implement해야 한다.
public class TabTestActivity extends TabActivity implements TabHost.TabContentFactory {
.......
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 위에서 만든 main.xml setContentView(R.layout.main);
// main.xml 내에 있는 @android:id/tabhost
TabHost tabHost = getTabHost();
// 1. 새로운 tab생성. 물론 생성되는 모든 TabSpec들은 @android:id/tabs 내에 포함되게 됨
TabHost.TabSpec tabSpec = tabHost.newTabSpec(getString(R.string.str_appranker_tab1));
// 2. 이부분에서 단순히 String만을 주게 되면 Tab Widget에 String 만 표시된다.
// 만약, 더 다양한 효과를 주고 싶으면 이부분에 View를 넘기면 된다.
tabSpec.setIndicator(getString(R.string.str_appranker_tab1));
// 3. 이부분이 상단 Tab을 눌렀을때 하단에 표시되는 내용부분이다.
// 직접, View를 넘길수 없으며 Dynamic하게 생성되는 View들을 FrameLayout내에 넣어야 하기에 TabContentFactory를 이용하게 된다.
tabSpec.setContent(this);
// 4. TabHost에 생성한 Tab을 넣는다.
// Tab을 더 생성하고 싶으면 1~4 다시 수행. tabHost.addTab(tabSpec);
}
@Override
public View createTabContent(String tag) {
LayoutInflater inflater = getLayoutInflater();
// Tab을 구별할 수 있는 tag 값에 따라 다른 View를 R.layout.content inflate해서 넣으면 되며, 가장 중요한 것은 @android:id/tabcontent의 하위 view로 rootview를 정의해야한다는것..
// 그래야 나중에 framelayout에서 보이고 안보이고를 결정해 주죠......
if(tag.equals(getString(R.string.str_tab1))){
return inflater.inflate(R.layout.content, getTabHost().getTabContentView());
}
return null;
}
}
-- 근데요.. 하나의 activity내에서 FrameLayout에서 보여주게될 복잡한 view들을 control한다는것은 조금 힘들고 지저분해 보이네요...
-- tabSpec.setContent()에 Intent를 넣어 Activity가 실행되게하여 MVC모델에 충실하게 만드는 것은 어떨런지.....
2012년 1월 2일 월요일
intent-filter의 Grouping..?
안드로이드가 배우기 힘든것은 될것 같은데 안되는 것들이 있고, 이를 극복하기 위해서 참고할 만한 글을 찾기에 너무 힘들고.... 내가 직접 bug-around 해야하고...
안드로이드 개발자에게 직접 묻기도 뭐하고...
암튼, 오늘 찾아 낸 거...
intent-filter도 grouping이 필요하다는 것.. (나만 몰랐나?, 원래 해야하나? 누구든 알면 알려줘여..)
PACKAGE_ 관련 action과 BOOT_COMPLETED, USER_PRESENT는 다른 group으로 묶어야 제대로 동작을 합니다.
말하자면 아래와 같이.... 참고 하세요..
<receiver android:name=".XXXBootLauncher">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
안드로이드 개발자에게 직접 묻기도 뭐하고...
암튼, 오늘 찾아 낸 거...
intent-filter도 grouping이 필요하다는 것.. (나만 몰랐나?, 원래 해야하나? 누구든 알면 알려줘여..)
PACKAGE_ 관련 action과 BOOT_COMPLETED, USER_PRESENT는 다른 group으로 묶어야 제대로 동작을 합니다.
말하자면 아래와 같이.... 참고 하세요..
<receiver android:name=".XXXBootLauncher">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
2012년 1월 1일 일요일
Package 삭제 추가에서 PackageName알아내기
등록된 BroadcastReceiver로 Package의 추가 삭제 intent가 전달되는 데요..
packagename은 Uri형태로 전달 됩니다.
즉, package:com.helloaxl.xxx 형태인데요...
이때 packageName을 정확히 얻어 올려면,
intent.getData().getSchemeSpecificPart() 를 호출해야 com.helloaxl.xxx를 얻어 올수 있답니다.
packagename은 Uri형태로 전달 됩니다.
즉, package:com.helloaxl.xxx 형태인데요...
이때 packageName을 정확히 얻어 올려면,
intent.getData().getSchemeSpecificPart() 를 호출해야 com.helloaxl.xxx를 얻어 올수 있답니다.
Package 추가 삭제 알아내기
Manifest 파일에 다음과 같이 receiver를 등록합니다.
<receiver android:name=".XXXReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
여기서 가장 중요한것은 <data android:scheme="package" />...
빼먹지 마시라는거...
피드 구독하기:
글 (Atom)