'개발/모바일OS(안드로이드,아이폰,윈도모바일등)'에 해당되는 글 59건

  1. 2013.11.15 Background gradient
  2. 2013.07.19 [안드로이드] 디컴파일 되지 않는 시스템어플 디컴파일 하기
  3. 2013.05.25 Android Native Heap Size
  4. 2011.07.13 Cocoa 1일차 2
  5. 2011.03.21 [android] C2DM을 이용한 push notification 12
  6. 2011.03.21 android blowser history 정보 조회 4
  7. 2011.03.21 webview plugin 설정 6
  8. 2011.02.16 안드로이드에서의 dp, dpi, px 8
  9. 2011.01.05 안드로이드 수신/발신 정보 가져오기 android call log 9
  10. 2011.01.03 다이얼로그 생성시 BadTokenException 이슈 3
2013. 11. 15. 10:50

Background gradient




Create a Layout with a Drawable Gradient background and inside that your real layout.

For example, create mygrandientbg_xml in drawables dir like:

<shape android:shape="rectangle" xmlns...">
 <gradient
     android:startColor="#255779"
     android:centerColor="#3e7492"
     android:endColor="#a6c0cd"
     android:angle="90" />
</shape>

Then create a FrameLayout like that

<FrameLayout
  ....
  android:background="@drawable/mygrandientbg"
  android:padding=20dip
 >
 put you layout here and set it to "centered"


2013. 7. 19. 14:53

[안드로이드] 디컴파일 되지 않는 시스템어플 디컴파일 하기




※ 이강좌는 혼자 연구하면서 알게 되는 과정을 차근차근 재구성한겁니다..



(아무도 안알려주고...우씽...)



이번 강좌는 순정 시스템 apk파일을 디컴파일 할때 에러가 나면서 디컴파일 되지 않는

파일을 디컴파일 하는 방법을 알려드리겠습니다.

먼저 준비물이 필요합니다.

* Java SE Development Kit 6u21

* apk manager

* framework-res.apk 파일

* twframework-res.apk 파일

* phone.apk 파일 (강좌를 보며 따라할수있는 디컴파일 되지 않는 apk파일)

- Java SE Development Kit와 apk manager가 없으신분은 http://toforyou.tistory.com/55 이곳에서 받으시기 바랍니다.

테마를 제작하시거나 개발자분이시라면 시스템어플들을 한번씩은 디컴파일 시도해보신 경험이 있으실겁니다.

하지만 일부 apk파일은 오류가 나면서 디컴파일이 되지 않습니다.

대표적인 예로 phone.apk파일이나 Contacts.apk, Settings.apk 등이 있는데요

이 강좌를 보시면 디컴파일이 되지 않아 압축풀고 이미지만 바꿔야 했던거와는 달리

세부적인 수정도 가능해질겁니다.

예제로 보기 위해서 일단 phone.apk파일을 디컴파일 시도해봅시다.

먼저 apk manager 파일을 압축푸시고 준비물인 phone.apk 파일을

apk manager 안에 place-apk-here-for-modding폴더에 넣고

Script.bat 실행합니다.



먼저 22번을 눌러서 phone.apk파일을 선택해야합니다.


phone.apk파일이 선택되셨으면

9번을 눌러서 디컴파일 해줍니다.





그럼 디컴파일이 되다가 다음과 같이 에러가 납니다.

Can't find framework resources for package of id: 2. You must install proper framework files, see project website for more info.
"An Error Occured, Please Check The Log (option 21)"

대충 해석해보면  프레임워크 리소스 파일에서 2번대의 id를 찾을수 없다는 말을 합니다.

아까 apk manager를 압축풀었던 폴더에 가보시면 log.txt 파일이 생성되어있습니다.

이유를 알기 위해 log.txt 파일을 분석해봐야합니다.

모든 안되는 이유는 이 log.txt파일에 나와있거든요




#############################################################################################################

-------------------------------------------------------------------------- 
|2011-02-17 --  0:24:42.38| 
-------------------------------------------------------------------------- 
java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) Client VM (build 19.0-b09, mixed mode, sharing)
'adb'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는
배치 파일이 아닙니다.
C:\apk\place-apk-here-for-modding\../place-apk-here-for-modding/signedPhone.apk을(를) 찾을 수 없습니다.
C:\apk\place-apk-here-for-modding\../place-apk-here-for-modding/unsignedPhone.apk을(를) 찾을 수 없습니다.
1. I: Loading resource table...
W: Skipping "android" package group
 I: Decoding resources...
2. I: Loading resource table from file: C:\Users\Blue\apktool\framework\1.apk
3. W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textColor, value=0x0205009a
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000f
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textColor, value=0x0205009a
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000e
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000f


#############################################################################################################



다음과 같이 나와있습니다.

1. 번에서는 resource 테이블을 로딩한다고 나와있습니다.

resource 파일 뭔지 기억하시나요??

제가 http://toforyou.tistory.com/55 이강좌에서 설명드렸습니다. resources.arsc이파일은 모든 ID의 집합체라구요

2. 번을 보시면 C:\Users\Blue\apktool\framework\1.apk 파일에서 resources.arsc를 불러 온다고 합니다.
(이 폴더는 컴퓨터마다 다릅니다. 꼭 log.txt에서 자신의 폴더를 확인하세요)


3 번을 보시면 그 리소스에서 0x0207000d id를 찾을수 없다는 말을 합니다.

여기서

평소에 framework파일과 twframework파일에서 public.xml파일을 유심히 보셨다면 알수있는게 있습니다.

    <public type="attr" name="theme" id="0x01010000" />
    <public type="attr" name="label" id="0x01010001" />
    <public type="attr" name="icon" id="0x01010002" />
    <public type="attr" name="name" id="0x01010003" />
    <public type="attr" name="manageSpaceActivity" id="0x01010004" />
    <public type="attr" name="allowClearUserData" id="0x01010005" />
    <public type="attr" name="permission" id="0x01010006" />
    <public type="attr" name="readPermission" id="0x01010007" />
    <public type="attr" name="writePermission" id="0x01010008" />
    <public type="attr" name="protectionLevel" id="0x01010009" />
    <public type="attr" name="permissionGroup" id="0x0101000a" />
    <public type="attr" name="sharedUserId" id="0x0101000b" />

이 위에 나와있는것이 framework파일의 public.xml의 일부분 입니다.

여기서 공통점을 찾으셨나요?

모두 id가 0x01******로 시작한다는겁니다.

그럼 twframework 파일의 public.xml파일을 살펴보겠습니다.

    <public type="attr" name="twTabWidgetStyle" id="0x02010000" />
    <public type="attr" name="twRadioButtonStyle" id="0x02010001" />
    <public type="attr" name="twAlertDialogStyle" id="0x02010002" />
    <public type="attr" name="twSoftkeyItemStyle" id="0x02010003" />
    <public type="attr" name="twColorPickerStyle" id="0x02010004" />
    <public type="attr" name="twProgressBarStyle" id="0x02010005" />
    <public type="attr" name="twSeekBarStyle" id="0x02010006" />
    <public type="attr" name="fullDark" id="0x02010007" />
    <public type="attr" name="topDark" id="0x02010008" />
    <public type="attr" name="centerDark" id="0x02010009" />
    <public type="attr" name="bottomDark" id="0x0201000a" />
    <public type="attr" name="fullBright" id="0x0201000b" />

이런식으로 id가 모두 0x02******으로 시작합니다.

여기서 알수있듯이 

W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textColor, value=0x0205009a
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000f
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textColor, value=0x0205009a
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000d
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000e
W: Could not decode attr value, using undecoded value instead: ns=android, name=textAppearance, value=0x0207000f

이 에러들은 모두 twframwork-res에 사용되는 id값을 필요로 하고있었습니다.

왜 phone.apk파일을 디컴파일하는데 twframwork파일의 id값들을 요구를 할까요?

그건 phone.apk파일을 만들때 저 id들이 사용되었는데 디컴파일하면서 저 아이디 값이 무엇인지 알려고 하는거고 그 과정에서 

id값들을 찾기 못하기에 내는 에러입니다.

C:\Users\Blue\apktool\framework\1.apk

그럼 아까 2번 에러에서 나왔던 폴더를 들어가보면



1.apk 파일이 덩그러니 놓여있습니다.

열어보면 리소스 파일이 들어있습니다.

이 1.apk파일은 우리가 이런 어플들을 디컴파일 시도할때 생깁니다. 

지워도 자동으로 다시 생기구요

그리고 몇가지를 시도한끝에 몇가지를 알수있었습니다.

저 1.apk파일안에 들어있는 리소스 파일은

framework-res에 쓰이는 리소스 파일이라는겁니다. 즉 0x01******번대의 id정보가 담겨있다는겁니다.

그럼 삭제할때마다 나타나는 1.apk파일은 어디서 튀어나오는걸까요?

그건 apk manager폴더 안에 other폴더에 있는 apktool.jar에 답이 있습니다.



다시 android-framework.jar 을 열어보면


1.apk파일에 쓰였던 리소스 파일이 들어있습니다.

즉 여기에 framework-res파일의 리소스가 있었기에

phone.apk를 디컴파일할때 0x01*****번대의 id를 못찾는 다는 에러는 나오지 않는것입니다.

하지만 이 1.apk파일에 들어있는 리소스는 우리가(제가) 사용하는 삼성 프레임워크와는 좀 다릅니다.

저 리소스 파일이 어떤 핸드폰의 리소스 파일인지는 모르지만

id값이 약간 다르기때문에 0x01******번대의 에러도 가끔 나타납니다.

여기서 한가지 중요한사실은

framework와 twframework로 나누어져있는 삼성 갤럭시의 시스템입니다.

다른 제조회사의 경우 나눠져있는경우도 있고 다 framework에 들어있는 경우도 있거든요

그렇다면 phone.apk파일을 디컴파일 해주기 위해서는 2번대의 id를 인식해줄수 있게 twframework-res.apk의

리소스를 인식할수 있게 해줘야 합니다.

먼저 C:\Users\Blue\apktool\framework\ (사용자마다 이폴더는 다릅니다)

폴더에 있는 1.apk파일을 삭제해주시고

그 폴더에 framework-res.apk파일과 twframework-res.apk파일을 복사해주세요
(자신의 사용하는 기종의 펌웨어 버전 것을 복사하세요. 다른 기종걸 넣고 징징거리지 마세요)
(이걸 응용한다면 다른 회사핸드폰의 시스템어플도 디컴파일하는게 가능합니다. 요구하는게 몇번 id대의 파일인지만 조사하면요)



그리고 1번대의 id를 가지는 framework-res.apk파일은 1.apk파일로 변환해주시고

2번대의 id를 가지는 twframework-res.apk파일은 2.apk파일로 변환을 해줍니다.

그리고 다시 apk manager로 돌아와 phone.apk파일을 디컴파일 해봅니다.



에러없이 디컴파일이 완료되었습니다.

사실 1.apk파일과 2.apk파일 안에는 리소스 파일만 남기고 전부 싹다 지워도 상관없습니다.

디컴파일할때 필요한건 리소스 파일뿐이거든요

하지만 귀찮아서 그냥 원본 갔다가 붙여놓는겁니다.

평소에 디컴파일 안되던 파일들도 디컴파일이 다 잘되실겁니다.

이로써 강좌 끝.

출처 : http://toforyou.tistory.com/66

2013. 5. 25. 11:23

Android Native Heap Size




Androi


2011. 7. 13. 10:53

Cocoa 1일차




nib  파일 --> xib  파일
xib 파일 클릭

프로그램 짤때
xcode  사용 --> 로직
 xib 클릭시  interface Builder  호출됨
  interface Builder : 버튼 화면을 화면을 꾸민다. 화면에 보이는 객체, Resource 의 그래픽적 표현
   [alloc]init 됨.

delegate : callback 함수라고 생각함

inspector :
  1.객체 내부 속성의 초기화
  2. 객체와 객체의 연결, 객체와 이벤트의 연결
  3. 객체의 출력 위치
  4. 해당 객체의 클래스 타입
  


command+shift+L : 라이브러리 :
command+shiift+I : :Inspector ( 속성 , 초기화)
IBOulet : inteface builder에서 보이기
 
2011. 3. 21. 18:10

[android] C2DM을 이용한 push notification




C2DM 소개

http://code.google.com/intl/ko-KR/android/c2dm/ 



등록

http://code.google.com/intl/ko-KR/android/c2dm/signup.html 에서 가입을 하고나면 해당 메일 주소로 메일이 하나 온다.



AUTH 정보 받기

Auth_android.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
 
 
public class Auth_android
{
    private static String HOST = "https://www.google.com/accounts/ClientLogin";
    private static String EMAIL = "메일주소";
    private static String PASS = "메일주소 비밀번호";
    private static String SOURCE = "알아보기 쉽게 적힌 프로그램 정보(예 : company-project-version. apple-iphone-4.2.1)";
     
 
    public  static void main( String[] args ) throws Exception
    {
        try {
            StringBuffer postDataBuilder = new StringBuffer();
            postDataBuilder.append("Email=" + EMAIL);
            postDataBuilder.append("&Passwd=" + PASS);
            postDataBuilder.append("&accountType=GOOGLE");
            postDataBuilder.append("&source=" + SOURCE);
            postDataBuilder.append("&service=ac2dm");
             
            byte[] postData = postDataBuilder.toString().getBytes("UTF8");
             
            URL url = new URL(HOST);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length",Integer.toString(postData.length));
            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();
            BufferedReader in = new BufferedReader(   new InputStreamReader(conn.getInputStream()));
 
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
             System.out.println(inputLine);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }   
}
 
 
 
 
// 결과
SID=DQAAAL0AAADVvGAVXO.......
LSID=DQAAAL8AAAAp5iSaE8h.........
Auth=DQAAAL8AAADrYkFbucd............// 요것만 사용함



registration_id 받기

AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="utf-8"?>
      package="mint.TestC2dm"
      android:versionCode="1"
      android:versionName="1.0"
      android:minSdkVersion="8">
       
    <permission android:name="mint.TestC2dm.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="mint.TestC2dm.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     
     
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".TestC2dm"
                android:theme="@android:style/Theme.Black.NoTitleBar"
                android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <receiver android:name=".C2DMReceiver"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="mint.TestC2dm" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="mint.TestC2dm" />
            </intent-filter>
        </receiver>
         
        <activity android:name=".showMsg"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.AlertDialogs" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>



TestC2dm.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package mint.TestC2dm;
 
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
 
public class TestC2dm extends Activity {
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
     
    // 등록
    public void onRegist(View v)
    {
        Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
        registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
        registrationIntent.putExtra("sender", "메일주소");
        startService(registrationIntent);
    }
  
    // 해지  
    public void onUnregist(View v)
    {
        Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
        unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
        startService(unregIntent);
    }
}



main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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">
    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="가입"
        android:onClick="onRegist"/>
    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="해지"
        android:onClick="onUnregist"/>
</LinearLayout>l



C2DMReceiver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package mint.TestC2dm;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
 
public class C2DMReceiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("###############", "onReceive");
        if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
            handleRegistration(context, intent);
        } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
            handleMessage(context, intent);
         }
     }
 
    private void handleRegistration(Context context, Intent intent) {
        Log.e("###############", "handleRegistration");
        String registration = intent.getStringExtra("registration_id");
        if (intent.getStringExtra("error") != null) {
            // Registration failed, should try again later.
        } else if (intent.getStringExtra("unregistered") != null) {
            Log.e("@@@@@@@@unregistered", "unregistered");
        } else if (registration != null) {
           // Send the registration ID to the 3rd party site that is sending the messages.
           // This should be done in a separate thread.
           // When done, remember that all registration is done.
            Log.e("@@@@@@@@registration_id", registration);
        }
    }
     
    private void handleMessage(Context context, Intent intent) {
        Log.e("###############", "handleMessage");
        String title = intent.getStringExtra("title");
        String msg = intent.getStringExtra("msg");
         
    // 화면 깨우기
        PushWakeLock.acquireCpuWakeLock(context);
 
        Intent i = new Intent(context, showMsg.class);
        Bundle b = new Bundle();
        b.putString("title", title);
        b.putString("msg", msg);
        i.putExtras(b);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
    }
}



PushWakeLock.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package mint.TestC2dm;
 
import android.app.KeyguardManager;
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
 
class PushWakeLock {
 
    private static PowerManager.WakeLock sCpuWakeLock;
    private static KeyguardManager.KeyguardLock mKeyguardLock;
    private static boolean isScreenLock;
 
    static void acquireCpuWakeLock(Context context) {
        Log.e("PushWakeLock", "Acquiring cpu wake lock");
        if (sCpuWakeLock != null) {
            return;
        }
 
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
 
        sCpuWakeLock = pm.newWakeLock(
                PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
                PowerManager.ACQUIRE_CAUSES_WAKEUP |
                PowerManager.ON_AFTER_RELEASE, "I'm your father");
        sCpuWakeLock.acquire();
         
//        KeyguardManager km = (KeyguardManager)context.getSystemService(context.KEYGUARD_SERVICE);
//        mKeyguardLock = km.newKeyguardLock("key guard");
//        if (km.inKeyguardRestrictedInputMode()) {
//        mKeyguardLock.disableKeyguard();
//          isScreenLock = true;
//        } else {
//          isScreenLock = false;
//        }
 
    }
 
    static void releaseCpuLock() {
        Log.e("PushWakeLock", "Releasing cpu wake lock");
//        if (isScreenLock) {
//          mKeyguardLock.reenableKeyguard();
//          isScreenLock = false;
//      }
 
        if (sCpuWakeLock != null) {
            sCpuWakeLock.release();
            sCpuWakeLock = null;
        }
    }
}

showMsg.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package mint.TestC2dm;
 
 
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
 
public class showMsg extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
         
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         
        String title, msg;
        Bundle bun = getIntent().getExtras();
        title = bun.getString("title");
        msg = bun.getString("msg");
         
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(showMsg.this);
         
        alertDialog.setPositiveButton("닫기", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                PushWakeLock.releaseCpuLock();
                showMsg.this.finish();
            }
        });
         
        alertDialog.setNegativeButton("보기", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startActivity(new Intent().setClassName(getPackageName(), getPackageName()+".TestC2dm"));
                PushWakeLock.releaseCpuLock();
                showMsg.this.finish();
            }
        });
         
        alertDialog.setTitle(title);
        alertDialog.setMessage(msg);
        alertDialog.show();
 
        // 폰 설정의 조명시간을 가져와서 해당 시간만큼만 화면을 켠다.
        int defTimeOut = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, 15000);
        TimerTask task = new TimerTask() {
                 @Override
                public void run() {
                        PushWakeLock.releaseCpuLock();
                }
        };
             
        Timer timer = new Timer();
        timer.schedule(task, defTimeOut);
    }
}

등록 결과
1
2
3
4
5
###############: onReceive
###############: handleRegistration
registration_id: APA91bGxex5sJi5hbeQkGUaURZo8.......





메세지 보내기

Push.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
 
 
public class Push
{
    private static String HOST = "http://android.apis.google.com/c2dm/send";
    private static String AUTH = "DQAAAL8AAADrYkFbucd............";
     
    // DB에 저장된 디바이스 토큰 목록
    private static String[] arrId =
    {
        "APA91bGxex5sJi5hbeQkGUaURZo8......."
//      "APA91bHCRg6NhgMYv8Rbb2LVCoj4al......."
    };
     
 
    public  static void main( String[] args ) throws Exception
    {
        for (int i=0; i<arrId.length; i++)
        {
            androidPush(arrId[i], "제목", "내용");
        }
    }
     
    public static void androidPush(String  regId, String title, String msg) throws Exception {
        try {
            StringBuffer postDataBuilder = new StringBuffer();
            postDataBuilder.append("registration_id=" + regId); // 등록ID
            postDataBuilder.append("&collapse_key=1");
            postDataBuilder.append("&delay_while_idle=1");
            postDataBuilder.append("&data.title=" + URLEncoder.encode(title, "UTF-8")); // 제목
            postDataBuilder.append("&data.msg=" + URLEncoder.encode(msg, "UTF-8")); // 내용
             
            byte[] postData = postDataBuilder.toString().getBytes("UTF8");
             
            URL url = new URL(HOST);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length",Integer.toString(postData.length));
            conn.setRequestProperty("Authorization", "GoogleLogin auth="+AUTH);
            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();
            conn.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }   
}



결과




문제점

- 메세지는 한번에 하나의 폰에만 전송 가능(해당 registration_id를 가진 폰에만 전송)=>그래서 loop를 돌아야한다
- 가끔씩 엄청 느리게 오는 메세지가 있다.(잘 되다가도 10분후에 메세지가 오는 경우도 있다)
- 폰에 구글계정을 등록해놔야한다


장점

- 한번 구독을 하면 앱이 실행되고있지 않아도 구독이 가능(재부팅 후 앱 실행없이도 정상 작동)
저작자 표시 비영리 동일 조건 변경 허락

2011. 3. 21. 15:51

android blowser history 정보 조회




  Cursor mCur = this.managedQuery(Browser.BOOKMARKS_URI, Browser.HISTORY_PROJECTION, null, null, null);
  mCur.moveToFirst();
  if (mCur.moveToFirst() && mCur.getCount() > 0) {
   while (mCur.isAfterLast() == false) {
    Log.v("titleIdx", mCur.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
    Log.v("urlIdx", mCur.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
    mCur.moveToNext();
   }
  }

아카이브 파일

2011. 3. 21. 15:50

webview plugin 설정




           Log.d("","type = " + type);

          webview = (WebView) findViewById(R.id.webview);      //webView 설정

                     if(type == 0 ){

                                webview.getSettings().setPluginState(PluginState.ON); //플러그인 사용

          }else if(type == 1 ){

                    webview.getSettings().setPluginState(PluginState.OFF); //플러그인 미사용

          }else if(type == 2 ){

                    webview.getSettings().setPluginState(PluginState.ON_DEMAND); //플러그인 사용자 요청시

          }else {

                   

          }

아카이브 파일

2011. 2. 16. 09:39

안드로이드에서의 dp, dpi, px





dp를 평소에 즐겨사용했지만, 확실히 무슨 개념인지를 몰라 조사하던중에 

잘 정리되어있는 블로그를 발견 !


수정(101130)

단순히 px을 dp로 변환하고자 한다면 dp = px * 0.66625로 계산한다.(해상도480*800 기준)

480*800 px => 320*533 dp이다.




출처 : http://blog.naver.com/dythmall?Redirect=Log&logNo=30096162077


dp (dip, density independent pixel) 은 안드로이드에서 여러 화면 크기를 서포트 해주기 위해서 만든 유닛이다.

만약 많은 화면에서 내가 만든 레이아웃이 제대로 보이길 원한다면 dp를 써서 화면을 만드는 것이 좋다.

우선 주의할 것은 안드로이드는 160dpi를 기본으로 생각한다.
이것은 (320 x 480) 스크린의 density를 나다내는 것이다.

그러므로 480 x 800  (240dpi)의 스크린을 dp로 나타낸다면 320 x 533 (480 / 1.5, 800 / 1.5) 이 된다.
dp를 이용해서 레이아웃을 잡을때 이 수치를 꼭 기억하고 잡자!
dp로 레이아웃을 잡을때 버튼을 4개 만들고 LinearLayout (horizontal)에 넣은뒤 
width를 80dp (80*4 = 320)로 잡으면 4개가 같은 넓이의 버튼이 된다.
하지만 480을 생각하고 120dp 라고 넓이를 잡으면 오른쪽 1개 버튼은 화면을 지나서 보이는걸 볼것 이다.

그렇다면 코드상에서 dp를 pixel로 바꾸려면 어떻게 해야할까?
식은 pixel = dp * (density / 160)

dp는 원하는 값이고 density 는 
DisplayMetrics outMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
outMetrics.densityDpi 
로 가지고 올 수 있다.

여기서 outMetrics.density 라는 변수가 있는데 이것은 (density / 160) 의 결과 값이 들어 있다.
그러므로 식은 
pixel = dp * outMetrics.density 
가 된다.

여기서 코드상에 dp를 pixel로 변한하는 식이 필요한 이유는,
안드로이드 레이아웃을 코드상에서 고치면 dp를 쓸 수 없기 때문이다.
모든 setWidth나 setHeight 등등은 인자를 pixel로 받는다.
2011. 1. 5. 14:59

안드로이드 수신/발신 정보 가져오기 android call log





폰 사용량을 알기 위해 안드로이드 수신/발신 정보를 가져오려고 한다.

 

안드로이드 수신/발신 정보 가져오기 CallLog 이용

  1. private void getHistory() {
  2. String[] projection = { CallLog.Calls.CONTENT_TYPE, CallLog.Calls.NUMBER, CallLog.Calls.DURATION, CallLog.Calls.DATE };
               
    Cursor cur = managedQuery(CallLog.Calls.CONTENT_URI, null, CallLog.Calls.TYPE + "= ?",
                    new String[]{ String.valueOf(CallLog.Calls.OUTGOING_TYPE) },                CallLog.Calls.DEFAULT_SORT_ORDER);
           
    Log.d("db count=", String.valueOf(cur.getCount()));
    Log.d("db count=", CallLog.Calls.CONTENT_ITEM_TYPE);
    Log.d("db count=", CallLog.Calls.CONTENT_TYPE);

    if(cur.moveToFirst() && cur.getCount() > 0) {
       while(cur.isAfterLast() == false) {
          StringBuffer sb = new StringBuffer();

          sb.append("call type=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.TYPE)));
          sb.append(", cashed name=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.CACHED_NAME)));
          sb.append(", content number=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.NUMBER)));
          sb.append(", duration=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.DURATION)));
          sb.append(", new=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.NEW)));
          sb.append(", date=").append(timeToString(cur.getLong(cur.getColumnIndex(CallLog.Calls.DATE)))).append("]");
          cur.moveToNext();
          Log.d("call history[", sb.toString());
         
          }
       }
  3. }
  4. androidManifest.xml에 추가
  5. <uses-permission android:name="android.permission.READ_CONTACTS" />

 

아주 편리하게도 api가 있다. CallLog를 이용해서 DB 에 저장된 내용을 가져올 수 있다.

주석 없이도 볼 수 있을 거란 생각에 주석은 생략한다.

 

CallLog의 경우 사용자가 전화통화내역 삭제를 누를 경우 볼 수 없다.

그리하야 별도로 call log 를 쌓으려고 한다.

방법은 직접 action을 감지하고, 통화 내역을 계산하여 별도의 DB에 쌓는 것이다.

안드로이드 수신/발신 정보 모니터링 하기

  1.     private String LOG_TAG = "CheckCall";
        private static int pState = TelephonyManager.CALL_STATE_IDLE;
  2.  
  3. public void onReceive(Context context, final Intent intent) {
            TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

            telManager.listen(new PhoneStateListener(){
                public void onCallStateChanged(int state, String incomingNumber){
                        if(state != pState){
                            if(state == TelephonyManager.CALL_STATE_IDLE){
                               Toast.makeText(mContext, "IDLE" + String.valueOf(System.currentTimeMillis()), Toast.LENGTH_SHORT).show();
                            }
                            else if(state == TelephonyManager.CALL_STATE_RINGING){
                                Toast.makeText(mContext, "RINGING" + String.valueOf(System.currentTimeMillis() +incomingNumber), Toast.LENGTH_SHORT).show();
                            }
                            else if(state == TelephonyManager.CALL_STATE_OFFHOOK){
                               Toast.makeText(mContext, "OFFHOOK" + String.valueOf(System.currentTimeMillis()), Toast.LENGTH_SHORT).show();
                            }
                            pState = state;
                        }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE);
           
            if(Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())){
                Log.i(LOG_TAG, "out=" + intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
            }
        }
  4.  
  5. AndroidManifest.xml

  6. <receiver android:name=class name>
            <intent-filter>
                  <action android:name="android.intent.action.PHONE_STATE" />
                  <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
    </receiver>

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

  8.  
  9. 참고자료

 

이정도 코드 역시 주석이 없이도 쉬이 할 수 있을 것 같아서 그냥 붙여 넣었다. 매니페스트에 권한과 인텐트 필터 넣는 것을 몰라 한참을 리서치 했다.

 

  1. state

  2. 발신시
  3. Intent.ACTION_NEW_OUTGOING_CALL -> offhook -> idle

  4. 수신시
  5. Ring -> offhook -> idle

 

 원문 :http://whdnfl21.springnote.com/pages/6833219

2011. 1. 3. 11:47

다이얼로그 생성시 BadTokenException 이슈





요즘 어플리케이션 개발에 한창입니다. 점점 안드로이드 플랫폼에 익숙해져 가는것 같습니다. 그러나 때로는 명확해 보이는 코드가 오류를 던져 대면 당혹스러워 지는군요. 이번 경우가 그렀습니다.

오류 발생 코드

  1. public void onCreate( Bundle $bundle ) {   
  2.         super.onCreate( $bundle ) ;   
  3.         setContentView( R.layout.ui_setting ) ;   
  4.            
  5.         Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;   
  6.         dialogShowBtn.setOnClickListener( this ) ;   
  7.     }   
  8.   
  9.     public void onClick(View v) {   
  10.         // TODO Auto-generated method stub   
  11.         switch( v.getId() ) {   
  12.         case R.id.dialogShowBtn :   
  13.             CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ;   
  14.             customDialog.show() ;   
  15.             break ;   
  16.         }   
  17.     }  

오류 메세지

Uncaught handler: thread main exiting due to uncaught exception android.view.WindowManager$BadTokenException: Unable to add window — token null is not for an application

해결책

  1. public void onCreate( Bundle $bundle ) {   
  2.         super.onCreate( $bundle ) ;   
  3.         setContentView( R.layout.ui_setting ) ;   
  4.            
  5.         Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;   
  6.         dialogShowBtn.setOnClickListener( this ) ;   
  7.     }   
  8.   
  9.     public void onClick(View v) {   
  10.         // TODO Auto-generated method stub   
  11.         switch( v.getId() ) {   
  12.         case R.id.dialogShowBtn :   
  13.             CustomDialog customDialog = new CustomDialog( this ) ;   
  14.             customDialog.show() ;   
  15.             break ;   
  16.         }   
  17.     }  

토의

일단 다이얼로그의 생성자에 인자로 보내는 Context를 getApplicationContext()로 얻어서 넘기는 대신에, Activity 자신을 직접 넘김으로써 오류를 잡는데는 성공했지만 아직까지는 명확하게 왜 이렇게 해야 하는지 알아가는 중입니다. 다만, 레퍼런스를 읽어보면 다이얼로그는 Owner Activity에 종속적으로 실행이 된다고 하는데 위에 오류 발생하는 코드를 보면 Activity를 알 길이 없습니다.

하지만 그렇다고 아래와 같은 코드도 BadTokenException 오류를 발생하기는 마찬가지였습니다.

  1. public void onCreate( Bundle $bundle ) {   
  2.         super.onCreate( $bundle ) ;   
  3.         setContentView( R.layout.ui_setting ) ;   
  4.            
  5.         Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;   
  6.         dialogShowBtn.setOnClickListener( this ) ;   
  7.     }   
  8.   
  9.     public void onClick(View v) {   
  10.         // TODO Auto-generated method stub   
  11.         switch( v.getId() ) {   
  12.         case R.id.dialogShowBtn :   
  13.             CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ;   
  14.             customDialog.setOwnerActivity( this ) ;   
  15.             customDialog.show() ;   
  16.             break ;   
  17.         }   
  18.     }  

관련예제