오랫만에 포스팅이다

최근에 앱 업데이트를 진행하다가

안하던 짓을 했다.

기존에 manifest 파일에 minSdkVersion="8" 만 세팅해서 쓰다가 아래의 이클립스 warning 경고가 눈에 거슬려 

<uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion="?"

뭐 별일 있겠냐 싶어서 targetSdkVersion="14"로 설정했다. 단순히 나는 target을 "이 앱은 해당 버전에 최적화 되었습니다." 라고 이해하고서는 말이다.

배포를 하고나니 플레이어 재생시 가로세로 전환마다 동영상 재생이 재시작 되는 버그가 나타났다.


오. 마. 이. 갓.;


소스에 문제로 우선 판단하고 디버깅했으나 별다는 이상 여부 없었다. 혹시나 하는 맘에 해당 targetSdkVersion을 빼보니 정상 동작;;; 뭐지 이거 단순한 알림 개념이 아니었나;

레퍼런스를 찾아봤다.

http://developer.android.com/guide/topics/manifest/uses-sdk-element.html 


android:targetSdkVersion
An integer designating the API Level that the application targets. If not set, the default value equals that given to minSdkVersion.


앱이 타킷으로 하는 API 레벨을 지정된 상수코드란다. 지정하지 않으면 minSdkVersion과 동일시 한단다. 그렇구나. 기존에는 API레벨이 8로 지정되어 쓰이다가 14로 바뀌면서 MediaPlayer 작동방식이 바뀐게 있나보다;

여튼 다시 해당 속성은 빼고 정상화 시키도록 했고, 미디어 재생 프로세스는 바뀐게 뭐가 있는지 또 찾아봐야겠다.


결론은...  targetSdkVersion 제대로 알고 확인하고 쓰자!!!

Posted by 빈솔B
,
이제는 유지보수에만 전념하던 그래서 약간은 지루했던(잘 알지도 못하면서;) 안드로이드 개발에 또 하나의 도전 과제가 생겼다. 나태에 젖어 흐느적 거리고 있던 내게 팀장님이 재미있는 미끼를 던져주었다. 이름 하여 PhoneGap 하고픈 말은 나중에 보중하고 일단 개발자라면 늘 친숙한 Hello world 부터 찍어보자

개발자는 두 가지 부류가 있다.
설계를 머리에 이고 키보드 부터 두드리는자,
그리고 UML부터 그려가며 말과 글과 그림그리며 오감으로 개발을 시작하는 자.
다들 안다. 후자가 고수의 길이며 정도라고..
하지만 난 늘 전자의 유혹에 무릎을 꿇는다..
후자 스타일이라면 먼저 PhoneGap 이 무언지 부터 살펴보자
http://phonegap.com/

시작하기
http://phonegap.com/start#android

1.요구사항
- 이클립스 3.4 이상 필요하단다 찾아보니 Ganymede 이상이면 될듯하다. 아직도 난 정식명칭이 궁금 가니메데? 개니메드? 가나이메드? ㅋ
- 이클립스를 사용하지 않는 이 듀토리얼의 터미널 버전 - 이건 무슨말인지 모르겠다;

2. SDK 설치 + PhoneGap
- 이클립스 받아 설치
- 안드로이드 SDK 받아 설치
- ADT 까지 받아 설치, 여기까지는 기존에 안드로이드 개발을 했던 분이라면 이미 준비되어 있을거라 생각된다.
- PhoneGap 최신 버전을 받아서 압축을 풉니다. 이 압축 푼 내용을 안드로이드 디렉토리와 함께 작업할 껍니다. 나는 다른 라이브러리 처럼 플러인이나 라이브러리 path를 잡아줘야 되는 줄 알았다.
하지만 내용을 보면 알겠지만 jar, js, htm 이 들어 있어 안드로이드 소스에 직접 복사해서 넣는 방법을 쓰며 배포시 패키지 만들때 포함되어 릴리즈 되는 것 같다.

3. 새 프로젝트 만들기
- New>Android Project 로 실제 Helloworld 안드로이드 프로젝트를 만든다.
- 버전선택을 맘대로 정한다. 테스트로 2.2
- 앱네임과 패키지 명, 메인 액티비티 명을 이름 짓는다.
- 최소 sdk 버전은 써도 안써도 좋다. 2.2로 정했다면 2.2의 api 레벨인 8로 테스트로 세팅한다.
여기까지는 기존에 안드로이드 개발해보신 분이라면 무난히 수월하게 따라 왔으리라고 본다.
이 후에 설정 작업이 재미있다.
- 여러분이 만든 Helloworld 프로젝트 루트 디렉토리에 다음과 같이 /libs  디렉토리를 만들고 기존에 있는 /assets 디렉토리 밑에 /www 디렉토리를 새로 만든다. 그러면 /libs , /assets/www  두 개의 디렉터리가 만들어진다.
- 아까 다운로드 받아 압축 풀어놨던 파일들 중에 android 디렉토리 안에 phonegap.js 파일을 /assets/www 에 복사해 붙여 넣는다.
- phonegap.jar 파일을 /libs 디렉토리 안에 복사하기 해서 붙여 넣는다.
- 마지막으로 이번엔 xml 디렉토리 전체를 복사하여 /res 디렉토리에 붙여넣는다.
- 이제 만들었던 메인 액티비티 소스를 고쳐보자. 
- 고치기에 앞서서 추가했던 jar를 프로젝트 path에 설정해주자. 프로젝트명 오른쪽 마우스 클릭해 Properties를 선택하여 java build path를 살펴보면 Libraries 탭을 설정해서 Add JARs를 선택해 현재 프로젝트에 속해있는 phonegap.jar 파일을 선택해 포함시킨후 확인 저장한다.
- 다음으로 소스를 고친다 이미 기본으로 생성된 소스를 다음과 같이 수정한다.
- Activity 로 extends된 클래스를 DroidGap으로 바꾼다.
- 뷰를 그려주는 역할을 하는 setContentView() 대신 super.loadUrl("file://android_asset/www/index.htm"); 으로 설정한다. 해당 index.htm 은 조금 있다 만들겠다. file:///android_asset 은 루트의 기본 디렉토리인 /assets 를 말합니다.
- ctrl + o 를 눌러서 import com.phonegarp.*; 이 추가되고 import android.app.Activity;는 제거됩니다.
- 그리고 환경설정파일인 AndroidManifest.xml 파일에 버전정보 다음으로 아래 내용을 추가합니다. 아래는 제 소스 샘플입니다.

<?xml version="1.0" encoding="utf-8"?>
 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.binsolb.phonegap.helloworld"
      android:versionCode="1"
 
      android:versionName="1.0">
 
  <supports-screens
android:largeScreens="true"
 
android:normalScreens="true"
 
android:smallScreens="true"
 
android:resizeable="true"
 
android:anyDensity="true"
 
/>
<uses-permission android:name="android.permission.CAMERA" />
 
<uses-permission android:name="android.permission.VIBRATE" />
 
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
 
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
 
<uses-permission android:name="android.permission.INTERNET" />
 
<uses-permission android:name="android.permission.RECEIVE_SMS" />
 
<uses-permission android:name="android.permission.RECORD_AUDIO" />
 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
<uses-permission android:name="android.permission.READ_CONTACTS" />
 
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
<uses-permission android:name="android.permission.GET_ACCOUNTS"
/>
 
<uses-sdk android:minSdkVersion="8" />
 
    <application android:icon="@drawable/icon" android:label="@string/app_name">
 
        <activity android:name=".HelloActivity"
                  android:label="@string/app_name"
 
                  android:configChanges="orientation|keyboardHidden">
 
            <intent-filter>
 
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
 
            </intent-filter>
 
        </activity>
 
    </application>
 
</manifest>


- 그리고 다음의 회전설정도 추가해줍니다.

4. Hellow World htm 만들기
-앞서 말씀드렸던 htm파일을 만듭니다. 위치는 /assets/www  아래에 index.htm  파일을 아래와 같이 만듭니다.

<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap</title>
<script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>

- 스크립트 파일 명은 복사 해 넣은 버전의 파일명 그대로 쓰시면됩니다. 위는 제 샘플입니다.
5 배포합니다.
- 시뮬레이터로 배포 또는 디바이스로 배포를 합니다.
- Run as > Android Application 선택으로 실행시킵니다.

6. 결과 확인


잘나오는 군요 뿌듯합니다 이제 간단히 tv팟을 만들어봐야겠어요!


*혹시 잘 모르시는 분은 좀더 자세한 설명의 아래 페이지를 참고하세요~
http://wiki.phonegap.com/w/page/30862722/phonegap-android-eclipse-quickstart
 
Posted by 빈솔B
,
디버그 키 만기가 1년이랍니다.. ㅎㅎ
안드로이드 개발 3년차지만 그걸 몰랐네요; 그 동안 포멧 몇번 해서 그런가?
여튼 찾아보니 많은 개발자분이 경험하셨네요.
이클립스 > Window > Preferences > Android > Build > Default debug keystore 
에 나타난 경로를 참고하셔서 직접 debug.keystore 을 찾아서 삭제하시고 다시 빌드하시면 새롭게 key가 생성됩니다.

아래 블로그 참고했습니다~
http://tactlee.egloos.com/2661977
Posted by 빈솔B
,
앱 개발하다보면 만든 앱의 manifest.xml 파일에 설정해 놓은 versionCode, versionName 값이 필요할 경우가 있습니다. 아래와 같이 간단합니다.

PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);

int versionNumber = pinfo.versionCode;

String versionName = pinfo.versionName;

 
reference:
 http://envyandroid.com/archives/94/get-android-versioncode-and-versionname
Posted by 빈솔B
,
라이브러리에서 제공하는 기본 체크박스 이미지 대신 다른 이미지를 쓰고 싶다면
아래와 같이 setBackgroundResource 아닌 setButtonDrawable 값으로 사용하면 된다.

CheckBox deleteCheckBox = ((CheckBox)view.findViewById(android.R.id.checkbox));
deleteCheckBox.setButtonDrawable(R.drawable.btn_check_off); (o)
deleteCheckBox.setBackgroundResource(R.drawable.btn_check_off); (x)

XML로는
android:button="@drawable/button_img"

Posted by 빈솔B
,

 - 법륜스님 '스님의 주례사' 中 다 이룬다고 좋은 것은 아니다. 에서 발췌.

 * 남자 스님이라 간혹 보수적인(남성위주-_-) 문장이 있지만 감안하고 읽을만 합니다. :)

 

 

 

 우리 인생에도 예기치 못한 사건들이 생기곤 합니다.

 비가 오면 오는 대로, 바람이 불면 부는 대로, 더우면 더운 대로, 추우면 추운 대로 상황에 구애받지 않는 게 중요해요.

 날씨에 관계없이 우리가 대응할 수 있는 준비를 갖추는 게 훨씬 더 현명한 방법이에요.

 

 애는 공부 잘하고, 남편은 일찍 들어오고, 아내는 순종하고, 세상 사람들은 나를 칭찬하기를 바랍니다.

 하지만 늘 좋은 일만 일어나기를 비는 방식으로 우리의 인생 문제가 해결될까요?

 원하는 것이 이뤄지지 않을 때 짜증 나고 괴롭죠?

 그런데 사람이 원하는 걸 다 이룰 수 있읍니까, 없습니까? 없어요. 원하는 게 다 이루어질 수는 없어요.

 

 여러분이 아무리 목매달고 기도해도 안 이루어지는 게 있어요. 이루어지지 않으면 문제인가요?

 이루어지지 않아도 아무런 문제가 없어요.

 다 이루어질 수도 없고, 다 이루어진다고 좋은 것도 아닌데 다 이루어져야 좋다고 생각하는 데서 인생의 고통이 생기는 거에요.

 원하는 것을 해보고, 되면 좋고 안 되어도 좋다고 생각하세요. 지금 안 되는 게 다음에 더 좋은 일일지도 몰라요.

 

 몇 십 년 전에 한 학생이 데모를 하다가 감옥에 갔어요. 이 어머니가 매일매일 '제발 감옥에서 빨리 나오게 해달라' 고 기도를 했어요.

 그리고 실제로 1심에서 집행유예로 석방이 된 거에요. 어머니가 너무너무 좋아했어요.

 그런데 나와서 3개월 만에 교통사고가 나서 아들이 세상을 떠났어요. 이때 어머니가 저를 붙잡고 하시는 말씀이 뭔 줄 알아요?

 "그냥 감옥에 있어으면 죽지는 않았을걸. 내가 기도해서 꺼냈으니 내가 죽인거야." 이러면서 통곡을 했어요.

 

 우리는 한치 앞을 못 봐요. 무언가를 간절히 원할 때 그렇게 되면 좋은지, 안 좋은지 잘 모르면서 무조건 매달려요.

 소원하던 것이 이루어지면 정말로 좋을까요? 알 수 없어요. 그냥 최선을 다할 뿐이에요. 되고 안 되고는 중요한 게 아니에요.

 

 우리의 인생에는 이런 일도 일어나고 저런 일도 일어나요. 정말 예상하지 못했던 일들이 일어납니다.

 갑자기 가족 중에 누가 죽기도 하고, 갑자기 사업이 부도가 나기도 하고, 몸에 병이 나기도 합니다.

 스스로 예기치 못한 일이라고 하지만 인생 전체로 보면 늘 일어나고 있는 일 이에요.

 

 순간적으로 슬프지만 슬픔에 빠지지 않고, 실패하면 그 실패를 딛고 다시 일어나는 거에요.

 상황과 조건에 구애받지 않는 삶이 아무런 일도 일어나지 않기를 바라는 것보다 훨씬 더 자유롭고 행복합니다.

 

 살다 보면 원하는 것이 있는데 이것이 이루어질 때도 있고, 안 이루어질 때도 있어요.

 안 이루어지면 어때요 다시 시도하면 되요. 또 안 되면 또 다시 시도하면 되요. 그래도 안 되면 어떻게 할까요?

 그냥 안 하면 됩니다. 그래도 하고 싶으면 어떻게 할까요? 또 하면 됩니다. 그냥 하면 돼요.

 어차피 한 번에 성공하면 다른 일을 또 해야 하지 않습니까. 결국 그 시간에 열 가지를 하든, 한 가지를 열 번 하든 인생은 똑같아요.

 

 되고 안 되고는 별로 중요한 게 아니에요. 무조건 되어야 한다고 생각하기 때문에 인생이 괴로운 겁니다.

 세상 일은 다 될 수도 없고, 된다고 좋은 것도 아니에요.

 이 진리를 제대로 알면 인생사에 대해서 그렇게 매달리지 않을 수 있어요.

 

 

Posted by 빈솔B
,
인텐트의 일부 기능입니다. 
1. 어플을 처음부터 실행시키고 싶을때

부르는 쪽
private OnClickListener mGetListener = new OnClickListener() 
public void onClick(View v) {
     Intent intent = new Intent(Intent.ACTION_MAIN);
     intent.addCategory(Intent.CATEGORY_LAUNCHER);
     intent.setData(Uri.parse("[scheme_name]://"));
     startActivity(intent);
     }
 };

받는쪽 manifest.xml 내용
<activity android:name=".MainActivity"
         android:label="@string/app_name"
         android:screenOrientation="portrait"
         android:configChanges="keyboardHidden|orientation" 
     android:theme="@android:style/Theme.NoTitleBar">
 <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.DEFAULT"/> 
               <category android:name="android.intent.category.LAUNCHER" />
               <data android:scheme="[scheme_name]" />  
              </intent-filter>
        </activity>

요렇게 구성하면 해당 앱을 다른 앱에서 호출 할 수 있습니다.
-------------------------------------------------------------------------------2010.12.02 수정 시작
죄송합니다. 테스트할 때는 위의 구성에 문제가 없었는데 signed된 apk로 만들어 설치를 해보니 계속 설치하 안되는 겁니다. 설정에서 설치된 프로그램 보니까 분명 존재하는데.. 메뉴에서 프로그램 아이콘이 생기지 않는 겁니다.. 뭔가 문제가 있음을 알고 좀 더 찾아보니 
위의 분 말씀대로 런처에서는 하면 안되는 것이었습니다!!! 에효.. 그럼 테스트일때는 어떻게 된거죠;;; 
여튼 간에 스킴을 받아들이는 다른 액티비티를 통해서 런처 Activity를 실행시키는 수 밖에 없는 것 같았습니다.( 혹, 더 좋은 방법 있으면 꼭 공유 부탁드려요~^^) 그래서 아래와 같이 수정했습니다. 

<activity android:name=".MainActivity" 
             android:label="@string/app_name"
             android:screenOrientation="portrait" 
 android:configChanges="keyboardHidden|orientation"
             android:theme="@android:style/Theme.NoTitleBar">
            <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".UriActivity" android:theme="@android:style/Theme.NoTitleBar" android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="[scheme_name]" android:host="[path]"/>
        </intent-filter>
</activity> 
위와 같이 Activity를 하나 더 만들어서 UriActivity에서 Intent로 런처Activity를 호출하면 됩니다.
----------------------------------------------------------------------------------2010.12.02 수정 끝

2. 안드로이드 브라우저 폰에서 어플내에 특정 Activity를 실행시키고 싶을때 (단, 그 특정 앱의 Acivity의 scheme name을 알고 있어야 합니다.)

브라우저 웹페이지 
<a href="[sheme_name]://">특정 어플 부르기</a>

해당 앱의 manifest.xml 내용 중
<activity android:name=".[activity_name]" 
                android:screenOrientation="landscape"
                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
        android:configChanges="keyboardHidden|orientation">
        <intent-filter>  
               <action android:name="android.intent.action.VIEW"/>  
               <category android:name="android.intent.category.DEFAULT"/>  
               <category android:name="android.intent.category.BROWSABLE"/>  
               <data android:scheme="[scheme_name]" android:host="[path]"/>  
             </intent-filter>
</activity>

이렇게 하면 브라우저 페이지에서 특정 앱 내의 Activity 호출이 가능합니다. ( 제 경우에는 특별히 host 나 data  및 type 을 설정하지 않았습니다.) 더 많은 정보는 아래 주소의 레퍼런스를 참고하세요~


Posted by 빈솔B
,
ERROR/WindowManager(21862): Activity xxx.xxx.xxx.xxx.List has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@47fbf900 that was originally added here

ERROR/WindowManager(21862): android.view.WindowLeaked: Activity xxx.xxx.xxx.xxx.List has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@47fbf900 that was originally added here

일반 자바 프로그래밍에서도 마찬가지겠지만 컴파일 오류는 잡기 쉬운 반면, 런타임 오류는 좀처럼 잡기 어려운것 같습니다.. 안드로이드 프로그래밍하면서도 위와 같은 런타임 오류를 어렵지 않게 만날 수 있습니다. 안드로이드 디버거는 프로그래머가 당황하지 않게 대체로 힌트로 잘 설명되어 있습니다. 내용을 읽어보면 "어떤 Activity 상에서 여기에 원래 추가되었던 윈도우가 새고 있다" 뭐 대충 이런말입니다. 제 코드를 살펴보니 Dialog를 만들어 놓고 Activity가 finish 될 때 해당 Dialog 를 제대로 Dismiss를 해주지 않아서 생기는 문제 였습니다. 그래서 아래와 같이  

@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
if(isDebugMode) Log.i(TAG, "Release Dialog Resource");
if ( dialog != null)
dialog.dismiss();
}

했더니 문제가 생기지 않았습니다. onStop 상태에서 할지 onDestroy 상태에서 할지는 여러분의 프로그램 상태에따라 달라지지 싶습니다. 즐거운 안드로이드 코딩하세요~
Posted by 빈솔B
,
방법1
방법2
방법3
방법4
방법5
방법6

구글링 결과 여러가지 방법이 있었으나 조금 비슷한것도 있고 다른 방법도 있었지만.. 결국 안먹혔습니다 OTL.. 역시 경험의 위대함을 다시 한번 느꼈습니다. 사내 분이 알려주신 방법입니다.. 감사드립니다.
일단 에뮬레이터를 만들어 둡니다. (필자 경우에는 2.0.1 버전으로 anroid_1 이란 이름으로 만들어 두었습니다.해당 에뮬레이터를 미리 실행시켜 두지 않습니다.)
./emulator -http-proxy http://[Proxy sever id]:[port] -avd android_1
이렇게 실행하면 끝이네요~
Posted by 빈솔B
,
하.. 
험한 코딩 산을 지나 야근열차행 QA(보다 완변한 어플을 위해 고생해주신 QA분들께 감사^^) 터널을 지나 드뎌 안드로이드 tv팟 마켓용이 오픈 임박입니다.
일부 폰에 프리로드 되어있던 tv팟은 검색도 안되고 여러모로 불편하게 사용하신 점을 많이 개선했고 검색 기능도 넣었답니다. 많이들 기다리셨죠; 죄송합니다;
 그 동안 개발하면서 짬짬히 노하우를 써왔었는데요.. 오픈에만 집중하다보니 블로깅을 차일로 자꾸 미루어 왔었네요 ^^
이제 하나 둘 정리하면서 글 좀 올려볼께요. 변덕쟁이 날씨에 건강들 조심하세요~ 
 
Posted by 빈솔B
,