오늘은 푸시서버를 구현해보도록 할께요

카카오톡이나 많은 SNS서비스를 제공하는 앱들이 해당 기능을 활용하여 통신을 하고 있죠?

 

간단할 것 같지만, 막상 해보면 어디서 시작해서 어떤 방식으로 구현을 해야하는지 막막할 수 있어요

저 또한 그랬고 그래서 많은 서핑들을 하게 되는데, 

 

항상 뭔가 아쉽더라구요.

너무 쉬워서 생략해놓은 곳도 있고 기본이라고 생각하고 그냥 언급하지 않은 내용들 때문에

하나 성공하고 나면 결국 다른걸로 막혀서 또 검색하고,,,  그걸 반복하면. 반나절이 지나가더라구요

 

저 처럼 헤매실 분들을 위하여 정리해보아요~

 

 

 

"렘군와 함께하는 푸시서버 만들기"

 

자~ 한 시간만 따라하면 아이폰으로 "Hello World" 메세지가 뜰 수 있어요~

대신 엄청 집중하셔야 되요~

 

깔끔하게 Action위주로 정리해서 알려드릴께요

(참고로 APNS는 시뮬레이터에서는 테스트 불가능,개발자라이센스 보유하여야 아이폰장비에서 테스트가능)

 

 

 


  가정1 : Provisioning Portal 에서 Certificates, Devices  등록되어 있다는 가정

            만약 안되어 있다면 ->  http://artyst.egloos.com/2650529 참고 (보노님 블로그)

 

  <가정1 요약>

   1. App IDs 등록

   일단, APNS 테스트를 위해서 App등록부터 해야되요. 아직 프로젝트 진행은 아니고 테스트단계니까

   일단 편하게 이름짓자구요. 저는 APNS로 일단 등록했어요

   어디서 하는지 알죠?

   developer.apple.com 사이트 > member center > provision Portal 접속 > App IDs

 

   2. Provisioning 생성

   프로비저닝 명 정해주고, 인증서 선택하고, 1번에서 만든 App ID 선택하고 Devices 선택해주면되죠

 

 

 

 

 

  가정2 : APNS SSL Certificate 발급 받고 xCode 프로젝트속성에 설정 잡았다는 가정

          만약 안되어 있다면 -> http://artyst.egloos.com/2652130 참고 (보노님 블로그)

 

  <가정2 요약>

   1. 푸시 가능 SSL Certificate 를 받아야되요 <- 이게 제일 중요~!! 어플 여러개 개발해봤지만, 여기 숨어있으리라곤 생각못했어요

      App IDs 로 가셔서 APNS 앱 오른쪽에  Configure 클릭 > Enable for Apple Push Notification Service 선택 > Done 버튼 클릭

      -> 이때, 패스워드 설정하실 꺼에요 나중에 서버구현시 필요하니까. 꼭 적어놓으세요

 

   2. Download 해서 SSL Certificate 를 내려받기 (푸시기능이 되는 프로비져닝이죠)

 

   3. xCode 새 프로젝트 생성 > 프로젝트속성에서 code sign 프로비져닝 선택하기

 

   4. 운영서버 구현

       1) APNS 보안통신할 때 사용할 인증서 추출해서 웹서버 특정 폴더에 밑에 복사하기  <- 아주 중요

           (복사방법은 위에 보노님블로그에 기설명됨)

       2) 톰캣 서버 구성 (프로젝트 생성 및 코딩)

 

   5. 클라이언트 구현

 

 

 

1,2,3번까지는 대부분 순조롭게 진행되실꺼구요

4 번 5번은 좀더 구체적으로 들어가볼게요~

 

 

 

4. 운영서버 구현

 

 

<사전준비물>

 

1) 자바 jdk 1.6 설치 : http://www.oracle.com/technetwork/java/javase/downloads/index.html

2) 이클립스 설치 : http://eclipse.org/downloads/

3) 톰캣 설치 : http://tomcat.apache.org/ (6버전)

4) 이클립스 톰캣 플러그인 설치(option) : http://www.eclipsetotale.com/tomcatPlugin.html

 

이클립스 톰캣 설정 방법은 검색을 통해 진행하세요

http://blog.naver.com/ssari93/130086724100 (소토로님 블로그)

http://blog.naver.com/ssari93/130086768842 (소토로님 블로그)

 

 

 

 

 

<프로젝트 생성>

 

1) APNS 라는 이름으로 톰캣 프로젝트 생성하구요, 경로는 이클립스 workspace 밑에 둡니다.

  2) 톰캣 실행시 해당 경로를 홈으로 잡혀야겠죠?

      톰캣이 설치되어 있는 폴더 주소(기본)를 아래와 같이 따라가보시면 아무것도 없을 수 있는데요

      C:\Tomcat 6.0\conf\Catalina\localhost

      여기에 아래의 내용을 담아 프로젝트명.xml 파일을 만들어 주세요 (ex apns.xml) (톰캣프로젝트 생성하면 자동으로 생성되는 경우도 있음)

      <Context path="/apns" reloadable="true" docBase="E:\eclipse\workspace\apns"     

       workDir="E:\eclipse\workspace\apns\work" />

      이게 없으면 http://localhost:8080/apns/index.jsp 이렇게 호출 했을 때 해당 프로젝트를 정확히 인식할 수 없어요

 

  3) 톰캣 프로젝트 바로 아래에 index.jsp 파일 하나 생성해주시구요 Hello world 적고 출력해보세요

      http://localhost:8080/apns/index.jsp  (포트는 톰캣 server.xml 설정에 따라 달라 질 수 있어요)

      이게 제대로 나온다면 이제 코딩들어가죠

 

  4) 첨부파일 다운로드 받아서 압축을 풀면 lib 폴더가 나오는데, 해당 폴더 아래의 내용들을

     eclipse\workspace\apns\WEB-INF\lib 폴더에 copy 해주세요

     APNS서버 구성을 위해 필요한 라이브러리들입니다. (개별로 서핑해서 찾으실려면 고생하실까봐 첨부했어요)

        javapns
      log4j
      bcprov-ext
      commons-io
      commons-lang

 

 

 

 

 

<index.jsp 코딩>

 

<%@ page language="java" contentType="text/html; charset=EUC-KR"  pageEncoding="EUC-KR"%>
 
<%@ page import="javapns.back.PushNotificationManager" %>   
<%@ page import="javapns.back.SSLConnectionHelper" %>   
<%@ page import="javapns.data.Device" %>   
<%@ page import="javapns.data.PayLoad" %>   
<%@ page import="java.lang.Object" %>   
<%@ page import="org.apache.commons.lang.StringUtils" %>   

 

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
</head>
<body>

<%
 
System.out.println( "start~~!!!" );

 

 

String deviceToken = "4a525b59 273042ce ba95afae eb09db8f 2782ed06 cb5370b9 a57c02d1 fd6cf11d";   

//토큰 번호는 아이폰 클라이언트  구현 후 디버그 모드로 출력하면 알 수 있어요. 아래 5번 클라이언트구현 글 참고

 

PayLoad payLoad = new PayLoad();

payLoad.addAlert("hello kim, 반가워~!!");    // 아이폰에 통지 보낼 메세지 내용입니다.
payLoad.addBadge(1);
payLoad.addSound("default");                  
  
PushNotificationManager pushManager = PushNotificationManager.getInstance();


pushManager.addDevice("iPhone", deviceToken);
  
//Connect to APNs
String host = "gateway.sandbox.push.apple.com";
int port = 2195;
String certificatePath = "E:/eclipse/workspace/apns/ApnsCert.p12";     // 위에 가정2 4번에 설명한 부분이에요, 복사해놓은 경로명+파일명

String certificatePassword = "APNS인증서 패스워드";                                           // 위에 가정2 1번 부분에 설명한 패스워드에요

pushManager.initializeConnection(host, port, certificatePath, certificatePassword, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);

 
//Send Push
Device client = pushManager.getDevice("iPhone");

pushManager.sendNotification(client, payLoad);

pushManager.stopConnection();

pushManager.removeDevice("iPhone");


%>

please wait ...!


</body>
</html>

 

 

 

 

(빨간 색 부분만 변경하시면되요)

 

 

5. 클라이언트 구현

 

 

아이군님의 블로그에 설명이 너무나 잘 되어 있어서 링크 걸어요

http://theeye.pe.kr/entry/how-to-programming-push-notification-on-my-iphone-with-javapns

 

<내용 요약>

APNS 관련 함수들 몇가지면 수정해주시면되요

- (void)application:(UIApplication *)application  
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

 

- (void)application:(UIApplication *)application 
    didReceiveRemoteNotification:(NSDictionary *)userInfo 

 

- (BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  

 

 

 

일단, 구현은 다 되었네요. 테스트를 해봐야 하는데요

 

 

 

<테스트방법>

1. 맥북에 아이폰 연결 후 실행모드(Device) 변경 해서 실행 (폰에 설치되고 실행됩니다. 화면에는 회색 화면만 뜨겠죠)

2. http://localhost:8080/apns/index.jsp  pc 브라우저에 해당 페이지 호출

3. 몇 초 후 아이폰에 푸시메시지 들어옴 확인

   어플이 실행중이지 않을 경우 닫기/보기 버튼 중 보기버튼을 누르면 해당 어플이 뜹니다.

 

 

메세지가 잘 들어왔나요?

한번만에 되신 분은 정말... 존경~!!

 

저는 톰캣 설정하는 것부터 오랫만에 해보는 거라 많이 헤맸거든요 ^^;

처음부터 다시 꼼꼼히 살펴보시면~~~  되실거에요

 

일단, 무작정 따라하기로 통보는 정상적으로 들어왔네요~!

 

이제 앞으로 몇가지 응용을 더 해봐야겠죠?

 

특정인에게만 통보 하기~

통보 받은 걸 화면에 대화창에 보여주기~

메신저 비스무리해보이는 어플 만들어보기~

 

 

완성되는 대로 포스팅할께요~~!!

긴글 읽어주셔서 감사합니다.

출처 : http://artyst.egloos.com/2652130

(2) 서버 소스코드


개발환경 : 자바(JAVA)

@ PHP 나 Objective C 는 다루는 곳이 많으므로 자바(JAVA) 로만 작성하도록 하겠다.


APNS 와 통신하려면 먼저 java에서 편리하게 이용할 수 있는 라이브러리를 다운로드 받자. http://code.google.com/p/javapns/ 에서 jar 파일을 다운로드 받을 수 있다.



그런데 이 API를 사용하기 위해서 필요한 라이브러리가 있다.


commons-lang-x.x.jar 가 없다면 org.apache.commons.lang.StringUtils 를 찾을 수 없다며 에러가 발생한다.

bcprov-ext-jdk16-xxx.jar 가 없다면 암호화관련 에러가 발생한다.


http://commons.apache.org/lang/ => apache

http://www.bouncycastle.org/ => 암호화 모듈


위의 사이트에서 각각 다운로드 받을 수 있다.


이제 서버에서 APNS로 메시지를 발송하는 JAVA 서버 프로바이저를 작성해보자.

deviceToken 에는 아이폰 클라이언트 어플에서 APNS 에 등록하고 나서 받은 토큰값을 넣어주자.

certificatePath 에는 인증서 경로와 파일명을 넣어주고, password에는 인증서를 추출할때 입력한 암호를 넣자.


import javapns.back.PushNotificationManager;

import javapns.back.SSLConnectionHelper;

import javapns.data.Device;

import javapns.data.PayLoad;


public class ApnsManager {

  public void provider() throws Exception {

    try {

      String deviceToken = "6a4aa1981062d0b8bxxbbaa3b6b0fdc275exyyx0f5dcac1ce101d314acca1a35";


      PayLoad payLoad = new PayLoad();

      payLoad.addAlert("알림 메시지 테스트");

      payLoad.addBadge(1);

      payLoad.addSound("default");

      PushNotificationManager pushManager = PushNotificationManager.getInstance();

      pushManager.addDevice("iPhone", deviceToken);

      //Connect to APNs

      String host = "gateway.sandbox.push.apple.com";

      int port = 2195;

      String certificatePath = "/work/project/apple-apns-key.p12";

      String certificatePassword = "인증서암호";

      pushManager.initializeConnection(host, port, certificatePath, certificatePassword, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);

      //Send Push

      Device client = pushManager.getDevice("iPhone");

      pushManager.sendNotification(client, payLoad);

      pushManager.stopConnection();


      pushManager.removeDevice("iPhone");

    }

    catch (Exception e) {

      e.printStackTrace(); 

    }

}



혹시 푸시알림을 제대로 받지 못했다면 아이폰의 설정을 살펴보자.



~ 끝 ~

기본적인 tab(상단)
http://blog.naver.com/lomi525?Redirect=Log&logNo=140113900088


tab 하단에 붙이기
http://mainia.tistory.com/554


tabWidget 높이 조절
http://neodreamer.tistory.com/420


tab - activitygroup, intent activity, 하단
http://rosaria1113.blog.me/110885107

[펌] Custom Android Button Style and Theme  안드로이드 / Programing...

2011/04/26 14:11

복사http://blog.naver.com/khch2489/30107398483

In this tutorial, we’ll see how it’s possible to create a custom button style for an Android application by using the Android styling API. First of all we need to define the new look for our custom button style. We would use three different NinePatch drawables.

A custom black nine patch drawable background for enabled, not pressed buttons :

black button background

A custom orange nine patch drawable background for enabled, pressed buttons :

Orange button background

A custom red nine patch drawable background for disabled buttons :

Red button background

All of these three custom drawable must be placed in the /res/drawables directory of our Android application. Now we must declare wich background should be used for each possible state of our custom button (pressed, disabled, focused, …). This is done by declaring a selector in a custom XML file :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
 android:state_enabled="false"
        android:drawable="@drawable/btn_red" />
    <item 
     android:state_pressed="true" 
     android:state_enabled="true"
        android:drawable="@drawable/btn_orange" />
    <item 
     android:state_focused="true" 
     android:state_enabled="true"
        android:drawable="@drawable/btn_orange" />
    <item 
     android:state_enabled="true"
        android:drawable="@drawable/btn_black" />
</selector>

Each item in the selector associates a drawable to a button state. Items must be declared in a specific order. The android selector draws the first selector item that match the current button state, no matter if another matching item is defined in the selector declaration. For exemple, the selector bellow will never draw the orange background for a disabled and focused button as the first item always applies to disabled button :

<item 
    android:state_enabled="false"
    android:drawable="@drawable/btn_red" />
<item 
    android:state_pressed="true" 
    android:state_enabled="false"
    android:drawable="@drawable/btn_orange" />

In order to draw orange background for disabled and focused buttons, and a red background for disabled buttons that are not focused we should have invert the items declaration order :

<item 
    android:state_pressed="true" 
    android:state_enabled="false"
    android:drawable="@drawable/btn_orange" />
<item 
    android:state_enabled="false"
    android:drawable="@drawable/btn_red" />

Let’s put our selector declaration in a file called btn_custom.xml in the /res/drawables directory of our Android application. Now, it’s time to define a custom button style that will apply to every buttons in the application. This can be achieve with both the /res/values/styles.xml file and the /res/values/themes.xml file. In the first one we’ll customize our button appearence (selector, text size, text color, shadow, …).

<resources>
    <style name="Button" parent="@android:style/Widget.Button">
        <item name="android:gravity">center_vertical|center_horizontal</item>
        <item name="android:textColor">#FFFFFFFF</item>
        <item name="android:shadowColor">#FF000000</item>
        <item name="android:shadowDx">0</item>
        <item name="android:shadowDy">-1</item>
        <item name="android:shadowRadius">0.2</item>
        <item name="android:textSize">16dip</item>
        <item name="android:textStyle">bold</item>
        <item name="android:background">@drawable/btn_custom</item>
        <item name="android:focusable">true</item>
        <item name="android:clickable">true</item>
    </style>
</resources>

Then, we must set this style to be used by the Android button Widget. In the /res/values/themes.xml file :

<resources>
    <style name="CustomButton" parent="android:style/Theme.NoTitleBar">
        <item name="android:buttonStyle">@style/Button</item>
    </style>
</resources>

The theme can be applied to the whole application by setting the android:theme attribute in the<application> tag of the AndroidManifest.xml file.

<application android:icon="@drawable/icon" 
    android:label="Custom button"
    android:theme="@style/CustomButton">

Now, that’s what your buttons looks like :

Android custom button

Android custom button

You can browse the full source code of the Eclipse project here :
SampleCustomButton

 

 

출처 : http://blog.androgames.net/category/android-tutorials/page/7/

setContentView앞에서 적용해야하는 것!!
View
titleView = getWindow().findViewById(android.R.id.title);
if (titleView != null) {
 
ViewParent parent = titleView.getParent();
 
if (parent != null && (parent instanceof View)) {
   
View parentView = (View)parent;
    parentView
.setBackgroundColor(Color.rgb(0x88, 0x33, 0x33));
 
}
}

출처 : http://catnest.tistory.com/54

http://mudchobo.tistory.com/479

  - HttpClient() 를 이용해서 tworld 로그인하기


http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple-restful-client-at-android/
- How-to: Android as a RESTful Client

http://www.wiseant.net/95
 - org.apache.http.client.ClientProtocolException on Android

http://www.mail-archive.com/android-beginners@googlegroups.com/msg12690.html
- [android-beginners] Authentication and Handling Cookies in Android

http://stackoverflow.com/questions/3039137/maintain-cookie-session-in-android
- Maintain cookie session in Android

http://stackoverflow.com/questions/2511145/defaulthttpclient-get-and-post-commands-java-android
- DefaultHttpClient GET and POST commands Java Android

http://inkyungil.com/tc/entry/HttpClient의-HttpPost-메서드를-사용하여-로그인을-처리한-예제
- HttpClient의 HttpPost 메서드를 사용하여 로그인 처리 예제

http://www.anddev.org/doing_http_post_with_android-t492.html
- Doing HTTP Post with Android

http://stbaeya.com/tc/199
-안드로이드 htmlParser 이용한 로젠택배 조회 서비스 만들기.

http://www.wiseant.net/92
[링크]Android API를 이용한 Apache http client 예제

http://hc.apache.org/httpcomponents-client-dev/tutorial/html/index.html
- HttpClient Tutorial, HttpClient 레퍼런스정도.

http://www.androidpub.com/718115
쿠키를 사용한 로그인에 대한 질문, 안드로이드 펍

출처 : http://blog.naver.com/leespop/140121910438
<xml...>

<EditText

        android:imeOptions="actionDone" // actionSearch , actionGo..

/>

<Class>

editText01 = (EditText) findViewById(R.id.EditText01);


editText01.setOnEditorActionListener(new OnEditorActionListener() {
            
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                
                if(actionId == EditorInfo.IME_ACTION_DONE){ // IME_ACTION_SEARCH , IME_ACTION_GO
                   // Toast.makeText(MainActivity.this, "123", Toast.LENGTH_SHORT).show();

                     처리 할 일들..
                }
                return false;
            }
        });








android:imeOptions="...."에 따라 키보드에 원하는 값을 넣을수가 있다.

actionSearch 는 바로 위의 그림처럼 돋보기 그림이 나오고 , actionGo 는 "이동"이라는 글자가 나타나며

actionDone 은 완료라는 글자가 나타난다.


키패드를 클릭했을때 이벤트처리는 setOnEditorActionListener를 달아 처리해주면 된다.


처음그림처럼 제조사에서 만든거같은 키패드에서는 이 속성이 먹히지 않는거 같다.

입력방법을 키보드입력으로 바꾸고 실험해보니 잘된다

'Android > Tip&Tech' 카테고리의 다른 글

android titlebar 색깔 바꾸기  (0) 2011.05.03
안드로이드 로그인 관련 예제  (0) 2011.05.03
android image drag(드래그 앤 드랍)  (0) 2011.05.02
예제소스모음  (4) 2011.05.02
Custom Tab Tip  (1) 2011.04.29

리소스에서 bitmap을 읽어오고 화면에 출력한 후 touch를 이용해서 drag하는 예제

추천 참고예제 :
http://stackoverflow.com/questions/4255859/touch-and-drag-image-in-android
 

http://www.droidnova.com/playing-with-graphics-in-android-part-iv,182.html 
 

출처 : http://blackzaket.blog.me/80101582245

package com.jjihun.bitmaptest;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class BitmapView extends View {
 Bitmap bitmap;
 int width;
 int height;
 int dx;
 int dy;
 public BitmapView(Context context, AttributeSet attrs) {
  super(context, attrs);

  bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.yuandi);
  // 얘 뒤져보면 byte[] 에서 bitmap생성하는 것도 있심

  width = bitmap.getWidth();
  height = bitmap.getHeight();
 }

 @Override
 protected void onDraw(Canvas canvas) {
  canvas.drawBitmap(
     bitmap, // 출력할 bitmap
     new Rect(0,0,width,height),   // 출력할 bitmap의 지정된 영역을 (sub bitmap)
     new Rect(dx,dy,dx+width,dy+height),  // 이 영역에 출력한다. (화면을 벗어나면 clipping됨)
     null);

  super.onDraw(canvas);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  
  switch(event.getAction()) {
  case MotionEvent.ACTION_DOWN:
   break;
  case MotionEvent.ACTION_UP:
   break;
  case MotionEvent.ACTION_MOVE:
// 주루룩 drag했을 경우 히스토리가 모두 기록되어서 전달됨
   int length=event.getHistorySize();
   float sx, sy, ex, ey;
   
   if (length != 0) {
    sx = event.getHistoricalX(0);
    sy = event.getHistoricalY(0);
    ex = event.getHistoricalX(length-1);
    ey = event.getHistoricalY(length-1);

    dx += (int)(ex-sx);
    dy += (int)(ey-sy);
   }
   invalidate();
   break;
  }
  
  return true;
 }
}

'Android > Tip&Tech' 카테고리의 다른 글

안드로이드 로그인 관련 예제  (0) 2011.05.03
<안드로이드 팁]edittext에 원하는 키보드옵션넣기  (0) 2011.05.02
예제소스모음  (4) 2011.05.02
Custom Tab Tip  (1) 2011.04.29
Android selector 관련 팁  (0) 2011.04.29
‘전체’ 목록(116)
목록열기 

1.app - API Demos / 실행사진, 소스 추가  (2010/06/11)

Activity 액티비티

Hello World
Demonstrates a basic screen activity.
Code:
HelloWorld.java
Layout:
hello_world.xml
Save & Restore State
Demonstrates how an activity should save state when it is paused.
Persistent State
Demonstrates how you can save and restore preferences, which are stored even after the user closes the application.
Receive Result
Demonstrates how an activity screen can return a result to the activity that opened it.
Forwarding
Demonstrates opening a new activity and removing the current activity from the history stack, so that when the user later presses BACK they will not see the intermediate activity.
Redirection
Demonstrates how to save data to preferences and use it to determine which activity to open next.

Translucent  / 실행시 밑에 내용이 안보이게

Demonstrates how to make an activity with a transparent background.
TranslucentBlur  // 실행시 밑에 내용이 약간 보이게..
Demonstrates how to make an activity with a transparent background with a special effect (blur).

Service 서비스

Local Service Controller
Starts and stops the service class LocalService that runs in the same process as the activity, to demonstrate a service's lifecycle when using {@link android.content.Context#startService Context.startService} and {@link android.content.Context#stopService Context.stopService}.
Local Service Binding
Demonstrates binding to a service class LocalService that runs in the same process as the activity, to demonstrate using the {@link android.content.Context#bindService Context.bindService} and {@link android.content.Context#unbindService Context.unindService} methods with a service. This also shows how you can simplify working with a service when you know it will only run in your own process.
Remote Service Controller
Demonstrates starting a service in a separate process, by assigning android:process=":remote" to the service in the AndroidManifest.xml file.
Remote Service Binding
Demonstrates binding to a remote service, similar to the Local Service Binding sample, but illustrating the additional work (defining aidl interfaces) needed to interact with a service in another process. Also shows how a service can publish multiple interfaces and implement callbacks to its clients.
Service Start Arguments Controller
Demonstrates how you can use a Service as a job queue, where you submit jobs to it with {@link android.content.Context#startService Context.startService} instead of binding to the service. Such a service automatically stops itself once all jobs have been processed. This can be a very convenient way to interact with a service when you do not need a result back from it.
Code:
ServiceStartArgumentsController.java
ServiceStartArguments.java
Layout:
service_start_arguments_controller.xml
Foreground Service Controller
Controls the service class ForegroundService, which shows how you can write a Service that runs in the foreground and works on both pre-2.0 and post-2.0 versions of the platform. This example will selectively use the new foreground APIs that were introduced in Android 2.0 if they are available.

Alarm 알람 - sleep상태에서도 작동하게 해주는....

Alarm Controller
Demonstrates two ways you can schedule alarms: a one-shot alarm that will happen once at a given time, and a repeating alarm that will happen first at a given time and then continually trigger at regular intervals after that.
Code:
AlarmController.java
OneShotAlarm.java
RepeatingAlarm.java
Layout:
alarm_controller.xml
Alarm Service
Demonstrates how you can schedule an alarm that causes a service to be started. This is useful when you want to schedule alarms that initiate long-running operations, such as retrieving recent e-mails.
Code:
AlarmService.java
AlarmService_Service.java
Layout:
alarm_service.xml

Notification 공지 노티

NotifyWithText
Demonstrates popup notifications of varying length.
IncomingMessage
Demonstrates sending persistent and transient notifications, with a View object in the notification. It also demonstrated inflating a View object from an XML layout resource.

Search 검색

SearchInvoke
Demonstrates various ways in which activities can launch the Search UI.
SearchQueryResults
Demonstrates an activity that receives Search intents and handles them.
SearchSuggestionSampleProvider
Demonstrates how to configure and use the built-in "recent queries" suggestion provider.

2. appwidget - API Demos

Files 위젯

3.content - API Demos 컨텐츠

Resources

Styled Text  스타일 텍스트
Demonstrates loading styled text (bold, italic) defined in a resource file.
Resources  리소스
Demonstrates loading styled strings from a resource file, and extracting the raw text.

4.Graphics - API Demos

Drawable 이미지

ShapeDrawable
Demonstrates creating Drawables in XML.

OpenGL 오픈지엘

CameraPreview
Demonstrates capturing the image stream from the camera, drawing to a surface (extending SurfaceView) on a separate thread (extending Thread).
 
GL SurfaceView
Demonstrates how to perform OpenGL rendering in to a SurfaceView.
Code:
GLSurfaceViewActivity.java
Layout:
hello_world.xml
PolyToPoly
Demonstrates calling the {@link android.graphics.Matrix.html#setPolyToPoly(float[],int,float[],int,int)} method to translate coordinates on a canvas to a new perspective (used to simulate perspective).
DrawPoints
Demonstrates using the {@link android.graphics.Paint} and {@link android.graphics.Canvas} objects to draw random points on the screen, with different colors and strokes.
PathEffects
Demonstrates the use of {@link android.graphics.Path} and various {@link android.graphics.PathEffect} subclasses.
SurfaceView Overlay
Shows how you can place overlays on top of a SurfaceView.
Code:
SurfaceViewOverlay.java
Layout:
surface_view_overlay.xml
TouchPaint
Demonstrates the handling of touch screen events to implement a simple painting app.

5. media - API Demos

Files 미디어 플레이어

6.os - API Demos

Files 파일

7.text - API Demos

Linkify  텍스트
Demonstrates the {@link android.text.util.Linkify Linkify} class, which converts URLs in a block of text into hyperlinks.

Files 파일

8. view - API Demos

 

RelativeLayout 상대레이아웃

1. Vertical
Demonstrates a simple relative layout.
2. Simple Form
Demonstrates a more complex relative layout to create a form.

 

LinearLayout 직선레이아웃

1. Vertical
Demonstrates a simple LinearLayout, with child width set to WRAP_CONTENT.
2. Vertical (Fill Screen)
Demonstrates a simple LinearLayout, with child width set to FILL_PARENT.
3. Vertical (Padded)
Demonstrates a LinearLayout where one of the elements can expand to fill any remaining screen space (weight=1).
4. Horizontal
Demonstrates a horizontal LinearLayout, plus an expanding column.
5. Simple Form
Demonstrates nested layouts to create a user form.
6. Uniform Size
LinearLayout which uses a combination of wrap_content on itself and fill_parent on its children to get every item to be the same width.
7. Fill Parent
Demonstrates a horizontal linear layout with equally sized columns. Some columns force their height to match the parent.
8. Gravity
Demonstrates a simple linear layout with menu options demonstrating horizontal and vertical gravity options.
9. Layout Weight
Demonstrates how the layout_weight attribute can shrink an element too big to fit on screen.

 

ScrollView 스크롤뷰

1. Short
Demonstrates scrolling screen with buttons altermating with a text view.
2. Long
Demonstrates a longer scrolling screen similar to ScrollView1.

TableLayout 테이블 레이아웃

1. Basic
Demonstrates a basic TableLayout with identical children.
2. Empty Cells
Demonstrates a TableLayout with column-spanning rows and different child objects.
3. Long Content
Rows have different number of columns and content doesn't fit on screen: column 4 of row 2 shrinks all of the other columns
4. Stretchable
Demonstrates a TableLayout with a stretchable column.
5. Spanning and Stretchable
Demonstrates a complex TableLayout with spanning columns and stretchable columns to create a menu-like layout.
6. More Spanning and Stretchable
Similar to example 5, but with an additional "checked" column.
7. Column Collapse
Similar to example 6, but now with buttons on the bottom of the screen that enable you dynamically hide or show columns.
8. Toggle Stretch
Demonstrates toggling the "stretch" value on a column to fill the screen width.
9. Toggle Shrink
Demonstrates toggling the "shrink" value on a column to make an over-wide table shrink to fit the screen size.
10. Simple Form
Demonstrates using a table to design a user form.
11. Gravity
Demonstrates the use of advanced gravity attributes, such as center_horizontal and right|bottom to align cell contents in a table.
12. Various Widths
Demonstrates the use of elements of various widths in a table.

 

Baseline 기본선

Demonstrates the use of the android:layout_alignBaseline XML attribute in various page layouts.

1. Top
Demonstrates the default baseline alignment in a simple LinearLayout with items at the top of the screen.
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:text="@string/baseline_1_label" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:text="@string/baseline_1_button" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="@string/baseline_1_bigger" />
</LinearLayout>
2. Bottom
Demonstrates the default baseline alignment in a simple LinearLayout with items at the bottom of the screen.
3. Center
Demonstrates the default baseline alignment in a simple LinearLayout with items in the center of the screen.
4. Everywhere
Demonstrates the default baseline alignment in a complex LinearLayout.
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:text="@string/baseline_4_label" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:layout_gravity="center_vertical"
        android:text="@string/baseline_4_button" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:textSize="20sp"
        android:text="@string/baseline_4_bigger" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:text="@string/baseline_4_label_2" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="3dip"
        android:layout_gravity="bottom"
        android:text="@string/baseline_4_label_3" />
</LinearLayout>
6. Multi-line
Demonstrates a baseline alignment with a multiline field.
7. Relative
Demonstrates baseline alignment in a RelativeLayout.
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/anchor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:textStyle="bold"
        android:text="@string/baseline_7_fat" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignBaseline="@id/anchor"
        android:layout_height="wrap_content"
        android:text="@string/baseline_7_lean" />
</RelativeLayout>
BaselineNested1
Demonstrates baseline aligning specific elements in three parallel vertical LinearLayout objects.
BaselineNested2
Demonstrates baseline aligning specific elements in three mixed vertical and horizontal LinearLayout objects.
BaselineNested3
Demonstrates baseline alignment within nested LinearLayout objects.

Radio Group 라디오그룹

Radio Group
Demonstrates using radio buttons and capturing the selected item.

ScrollBars 스크롤바

1. Basic
Demonstrates a scrollable LinearLayout object.
2. Fancy
Demonstrates a scrollable LinearLayout object with a custom thumb slider image.

Visibility 보이기/안보이기/사라지기

Visibility
Demonstrates toggling the visibility of a View object between visible, invisible, and gone.

Lists 리스트

1. Array
Demonstrates binding a ListAdapter to a string array as a data source, and displaying the elements on the screen.
2. Cursor (People)
Demonstrates binding results from a database query to a field in a template.
3. Cursor (Phones)
Demonstrates binding multiple columns from a database query to fields in a template.
4. ListAdapter
Demonstrates implementing a custom ListAdapter to return View objects laid out in a custom manner.
5. Separators
Demonstrates implementing a custom ListAdapter that includes separators between some items.
6. ListAdapter Collapsed
Demonstrates another custom list adapter with that returns expandible items.
 
7. Cursor (Phones)
Demonstrates a list adapter where data comes from a Cursor object.
8. Photos
Demonstrates a list activity that uses a custom ListAdapter, setting the view for an empty item, and also how to customize the layout of a ListActivity.
 

Custom 일반

CustomView
Demonstrates implementing a custom view subclass.

ImageButton 이미지버튼

ImageButton
Demonstrates an ImageButton: a button with an arbitrary graphic on it.

Date Widgets 데이타 위젯

1. Dialog
Demonstrates the DatePickerDialog and TimePickerDialog picker dialogs.
2. Inline
Demonstrates using a TimePicker directly in a layout without using a confirmation button or dialog.

Gallery 갤러리 이미지

1. Icons
Demonstrates implementing a Gallery widget and extending GalleryAdapter to create a custom class to serve out source images to the widget.
2. People
Demonstrates populating a Gallery with images from the contacts photos.

Spinner

Spinner
Demonstrates populating two Spinner widgets with values.

Grid 그리드

1. Icon Grid
Demonstrates populating a GridView widget with a list of applications using a custom ListAdapter object.
 
2. Photo Grid
Demonstrates populating a GridView widget with images using a custom ListAdapter object.
 

ImageSwitcher

ImageSwitcher
Demonstrates using the ImageSwitcher widget with a custom Adapter.

TextSwitcher

TextSwitcher
Demonstrates using the TextSwitcher widget.

Animation 애니메이션

1. Shake
Demonstrates a simple tweened animation (android.view.animation.Animation).
2. Push
Demonstrates a variety of transformations (android.view.animation.Animation), including fading, motion, and rotation.

Controls

1. Theme White
Demonstrates a variety of common form type widgets, such as check boxes and radio buttons using the white theme.
 
2. Theme Dark
Demonstrates a variety of common form type widgets, such as check boxes and radio buttons using the dark theme.

Auto Complete 자동 완성

1. Screen Top
Demonstrates the use of AutoCompleteTextView, an autocomplete dropdown box below a text box, with data taken from an array.
2. Screen Bottom
Demonstrates an autocomplete box above a text box.
3. Scroll
Demonstrates an autocomplete text box in the midst of a vertical list.
4. Contacts
Demonstrates an autocomplete text box that gets its content from a database query.
5. Contacts with Hint
Demonstates an autocomplete text box that understands the * wildcard.

Progress Bar 프로그레스 바

1. Incremental
Demonstrates large and small rotating progress indicators that can be incremented or decremented in units.
2. Smooth
Demonstrates large and small continuously rotating progress indicators used to indicate a generic "busy" message.
3. Dialogs
Demonstrates a ProgressDialog, a popup dialog that hosts a progress bar. This example demonstrates both determinate and indeterminate progress indicators.
4. In Title Bar
Demonstrates an Activity screen with a progress indicator loaded by setting the WindowPolicy's progress indicator feature.

Focus 초점

1. Vertical
Demonstrates how to block selection of a specific screen element.
2. Horizontal
Demonstrates how to change the order of which screen element is selected when the user presses arrow keys.
3. Circular
Another version of Focus2.

 

 

 


 
posted by 불꽃남자

'Android > Tip&Tech' 카테고리의 다른 글

<안드로이드 팁]edittext에 원하는 키보드옵션넣기  (0) 2011.05.02
android image drag(드래그 앤 드랍)  (0) 2011.05.02
Custom Tab Tip  (1) 2011.04.29
Android selector 관련 팁  (0) 2011.04.29
ndroid Fake Iphone UI 2 Source  (0) 2011.04.29


출처 : http://android.attemptone.com/layouts/custom-tabs/ 


Tabs can be a difficult component when you first start building Android applications.  To do something as simple as change the height of the tabs you need to build a view and pass it to setIndicator in the TabSpec.  It might sound confusing but hopefully this code sample helps.  It uses custom tab backgrounds, tab height, and programmatic tab creation.


Image Files:

Custom Tab Images

MyActivity.java

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TabHost.TabContentFactory;
import android.widget.TabHost.TabSpec;

//Custom Tabs
public class MyActivity extends Activity {

int tabHeight = 40;


@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
LinearLayout main = new LinearLayout(this);
main.setOrientation(LinearLayout.VERTICAL);
setContentView(main);

TabHost tabs = new TabHost(this);
tabs.setId(android.R.id.tabhost);
main.addView(tabs);

TabWidget tabWidget = new TabWidget(this);
tabWidget.setId(android.R.id.tabs);
tabs.addView(tabWidget);

FrameLayout tabContent = new FrameLayout(this);
tabContent.setId(android.R.id.tabcontent);
tabContent.setPadding(0, tabHeight, 0, 0);
tabs.addView(tabContent);

TextView content = new TextView(this);
content.setText("This is the Frame Content");
content.setId(100);
tabs.setup();

TabSpec tspec1 = tabs.newTabSpec("Tab1");
tspec1.setIndicator(makeTabIndicator("One"));
tspec1.setContent(new PreExistingViewFactory(content));
tabs.addTab(tspec1);

TabSpec tspec2 = tabs.newTabSpec("Tab2");
tspec2.setIndicator(makeTabIndicator("Two"));
tspec2.setContent(new PreExistingViewFactory(content));
tabs.addTab(tspec2);

TabSpec tspec3 = tabs.newTabSpec("Tab3");
tspec3.setIndicator(makeTabIndicator("Three"));
tspec3.setContent(new PreExistingViewFactory(content));
tabs.addTab(tspec3);

}

private TextView makeTabIndicator(String text){

TextView tabView = new TextView(this);
LayoutParams lp3 = new LayoutParams(LayoutParams.WRAP_CONTENT, tabHeight, 1);
lp3.setMargins(1, 0, 1, 0);
tabView.setLayoutParams(lp3);
tabView.setText(text);
tabView.setTextColor(Color.WHITE);
tabView.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.CENTER_VERTICAL);
tabView.setBackgroundDrawable( getResources().getDrawable(R.drawable.tab_indicator));
tabView.setPadding(13, 0, 13, 0);
return tabView;

}

class PreExistingViewFactory implements TabContentFactory{

private final View preExisting;
protected PreExistingViewFactory(View view){
preExisting = view;
}

public View createTabContent(String tag) {
return preExisting;
}

}

}

res/drawable/tab_indicator.xml


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non focused states -->
<item android:state_focused="false"
android:state_selected="false"
android:state_pressed="false"
android:drawable="@drawable/tab_deselect" />
<item android:state_focused="false"
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/tab_select" />

<!-- Focused states -->
<item android:state_focused="true"
android:state_selected="false"
android:state_pressed="false"
android:drawable="@drawable/tab_deselect" />
<item android:state_focused="true"
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/tab_select" />


<!-- Pressed -->
<item android:state_pressed="true"
android:drawable="@drawable/tab_focus" />
</selector>

 

Android selector  ├뷰 

2011/01/05 11:40

복사http://blog.naver.com/jolangma/150100210916

출처:

step 1. View의 각 상태에 맞는 이미지를 준비합니다.

 res/drawable 폴더내에 두어야하는건 아시죠?!


(image를 nine patch해서 사용하시면 좋겠죠...^~^)

 normal

 활성화 상태, View.setEnabled(true)상태와 같다.

 빠져서는 안되는 필수요소입니다.

 disable

 비활성화 상태, View.setEnabled(false)상태와 같다.

 필요할 때 선언할 수 있는 선택요소입니다.

 focused

 EditText에서 입력 대기 상태, 즉 커서가 깜박이는 경우에 해당합니다.

 또는 비활성화 상태에서 selected 된 상태입니다.

 필요할 때 선언할 수 있는 선택요소입니다.

 pressed

 Button 종류의 View가 눌려진 상태, View 종류의 View가 터치된 상태입니다.

 빠져서는 안되는 필수요소입니다.

 selected

 device의 트랙볼이나 dpad방향키로 View가 선택된 상태. 

 이 때, state_window_focused 상태는 true가 됩니다.

 요즘 기기들 중 쿼티 자판이 있는 경우를 제외한 기기들은 이 상태를 쓸 경우가 별로 없습니다. 왜냐하면 기본 입력 방식이 터치니깐요...=_=

 필요할 때 선언할 수 있는 선택요소입니다.



step 2. res/drawable 폴더내에 아래와 같은 형식의 .xml 파일을 생성합니다.

 res/drawable-hdpi/btn_white_stone.xml


<selector xmlns:android="http://schemas.android.com/apk/res/android">

    /!-- 아래 두 개의 선언은, 그래도 혹시 모르니 selected 상황이 해제되었을 

          때를 대비해 추가해 두는게 좋습니다. --/

    <item android:state_window_focused="false" android:state_enabled="true"

        android:drawable="@drawable/btn_normal" />

    <item android:state_window_focused="false" android:state_enabled="false"

        android:drawable="@drawable/btn_disable" />


    <item android:state_pressed="true" 

        android:drawable="@drawable/btn_square_pressed" />


    <item android:state_focused="true" android:state_enabled="true"

        android:drawable="@drawable/btn_square_selected" />


     /!-- 아래의 선언이 없을 경우 selected 상황에서는 버튼이 보이지 않습니다. --/

    <item android:state_enabled="true"

        android:drawable="@drawable/btn_square_normal" />


    <item android:state_focused="true"

        android:drawable="@drawable/btn_square_focused" />    

    

    /!-- 아래의 선언이 없을 경우 selected 상황에서는 버튼이 보이지 않습니다. --/

    <item

         android:drawable="@drawable/btn_square_disable" />

</selector>

 그래서 필자는 보통은 아래와 같이,

 보통상태와 버튼이나 뷰가 눌러졌을 때의 상태, 두 경우만 선언해서 사용하고 있습니다.


<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_window_focused="false" android:state_enabled="true"

        android:drawable="@drawable/btn_help_normal" />


    <item android:state_pressed="true" 

        android:drawable="@drawable/btn_help_pressed" />


    <item android:state_enabled="true"

        android:drawable="@drawable/btn_help_normal" />

  

</selector>


step 3. Button의 background로 위에서 생성한 drawable을 지정합니다.

 res/layout/layout.xml


<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Test Button"

android:background="@drawable/btn_white_stone"

/>

[출처] Android selector|작성자 jolangma


출처 : http://www.androidside.com/bbs/board.php?bo_table=B46&wr_id=13466

TITLE : Android Fake Iphone UI 2 Source

Date : 2010/08/06

Description :

This is Source that make Iphone style UI by android slector button.

Download Link : iphoneUI2

Reference Site :

안드로이드 Selector 버튼을 하단에 4개 배치하여  아이폰 하단의 네비게이션 UI를 배낀 소스

안드로이드의 다양한 에니메이션 기능과 확장성은 아이폰 스타일의 UI도 꾸밀 수 있다.

한국에서 안드로이드폰이 아이폰보다 많이 팔렸다는 뉴스가 나옴에도 불구하고

아직까지 모바일UI에 대세는 아이폰UI라는 점이 참 아이러니 하다 .

안드로이드 기본UI도 충분히 강력하고 효율적이라고 생각하지만 (비록 투박하지만),

주문하는 입장에서 아이폰UI를 부탁하니 어쩔 수 없는 일이다. 

1. 하단에 이미지버튼 4개를 배치하기 위해서 selector xml과 이미지파일을 Drawble 에 등록한다.

셀렉터는 defult와 pressed로 구별해서 정의한다.

 <selector xmlns:android=”http://schemas.android.com/apk/res/android“>
     <item android:state_pressed=”true”
           android:drawable=”@drawable/home_1″ /> <!– pressed –>
    
     <item android:drawable=”@drawable/home_0″ /> <!– default –>
 </selector>

2. 상단의 TextView와 하단의 LinearLayout (버튼4개 묶음)은 RelativeLayout 으로 구성하여서

해상도가 변경되어도 버튼들이 하단에 위치하게 한다.

3. 해상도가 변경되어도 레이아웃이 유지된다.

하단의 이미지버튼을 9Patch로 작업한다면 아래처럼 깨지지도 않을 것이다.

(본 예제는 초상권때문에 버튼 만드는 것도 엄청 고생했기때문에 나인패치까지는고려안햇슴;)

4. 버튼을 눌렀을 때 클릭리스너는 각각의 Listner는 Activity에서 정의했고,

XML에서 android:onClick=”ClickHandler” 속성으로 달아주었다.

4. 이 과정은 본 예제의 핵심으로 이미지 버튼으로 구성시 Activity가 쌓이게 되는데,

android:LunchMode=”singleTask”로 설정해 줌으로써 Activity가 쌓이지 않고 Singleton Pattern으로 1개씩만 생성된다.

<P.S> 구현된 화면을 보니, 아무래도 화면전환 시 하단 네비게이션이 같이 움직여서 어색하다..

하단에 버튼을 배치하고 viewFlipper나 TabWidget을 활용하면 네비게이션 움직임 없이 구성될 듯 싶으나,

예제의 특성 상 Activity 이동으로 간단하게 구현해 보았다.

Creative Commons License
이 저작물은 크리에이티브 커먼즈 저작자표시 3.0 Unported 라이선스에 따라 이용할 수 있습니다.

http://test.androday.com:7788/blog/?p=406

태스크란? (Task, Activity Stack)어피니티란? (Android Affinity)플래그란? (Android Flag)  Android APP / 컴퓨터활용 

2011/03/16 11:48



http://blog.naver.com/oklmg/70105002506



태스크란? (Task, Activity Stack)

어피니티란? (Android Affinity)
플래그란? (Android Flag)





안드로이드 태스크란? (Android Task, Activity Stack)


-  Task는 어플리케이션에서 실행되는 액티비티를 보관하고 관리하며 Stack형태의 연속된 Activity로 이루어진다


- 선입후출(First In Last Out)형태로 나중에 적재된 액티비티일 수록 가장 먼저 사용된다

  만약 1페이지>2페이지>3페이지 순으로 액티비티를 이동했을때 실행순서대로 Task에 push해 놓았다가

  back버튼을 누르면 3페이지>2페이지>1페이지순으로 Task에서 pop시켜 되돌아 간다고 생각하면 된다


- 서로 다른 어플리케이션간의 이동에도 Task를 이용해 사용자 경험(UX)를 유지시켜 준다


- 최초적재 액티비티는 Root Activity 라고 하며 어플리케이션 런처로부터 시작된다


- 마지막으로 적재되는 액티비티는 Top Activity 라고 하며 현재 화면에 활성화 되어있는 액티비티를 말한다


- Task내에는 서로 다른 어플리케이션의 액티비티들이 포함될 수 있어 어플리케이션에 경계없이 

  하나의 어플리케이션인것 처럼 보이게 해준다


- Task의 Stack내에 존재하는 액티비티들은 모두 묶여서 background와 foreground로 함께 이동한다

  홈버튼 클릭(task interrupt => background 이동), 홈버튼 롱클릭(recent task => foreground 이동)


- Flag를 사용하여 Task내 액티비티의 흐름을 제어할 수 있다



어피니티란? (Android Affinity)


- 어플리케이션 내의 액티비티들은 하나의 어피니티를(affinity:친화력) 가지고 있다


- AndroidManifest 에서 <activity> 요소의 taskAffinity 속성을 사용해 개별 affinity가 지정 가능하다


- FLAG_ACTIVITY_NEW_TASK 플래그를 가진 인텐트 객체로 부터 호출된 allowTaskReparenting 속성을

  True로 가지고 있는 액티비티에 한해 affinity가 동작한다


- 위 조건이 만족한 상황에서 시작된 액티비티는 자신과 동일한 어피니티를 갖는 태스크가 있을경우

  해당 태스크로 이동한다


- 즉, [b]어피니티를 가진 A액티비티가 호출되어 해당 태스크에 속해있을때 [b]어피니티를 가진 태스크가

  호출되면 A액티비티는 [b]어피니티를 가진 태스크로 이동한다


- 어피니티에 의해 태스크가 이동된 후에 back버튼으로 반환시 원래 해당하던 태스크로 돌아간다


- 하나의 어플리케이션내에서 하나 이상의 기능을 갖는 어플리케이션이 존재할경우 각 액티비티별로 다른

  어피니티를 지정해 관리할 수 있다



플래그란? (Android Flag)


- AndroidManifest 에서 플래그를 사용할때에는 <activity> 요소의 launchMode 속성을 사용하며

  launchMode에서 사용가능한 속성은 다음과 같이 4가지만 가능하다


  standard: 

  스택중 어느곳에나 위치 가능하며 여러개의 인스턴스가 생성가능하다


  singleTop: 

  스택중 어느곳에나 위치 가능하며 여러개의 인스턴스가 생성가능하고 호출한 activity와 현재

  최상위 activity가(top activity) 동일한 경우 최상위 activity가 재사용 된다(기존 최상위 activity는 pop)


  singleTask: 

  루트 액티비티로만 존재하며 하나의 인스턴스만 생성가능하다(타 task에서 동일 activity 사용불가)

  다른 액티비티 실행시 동일 Task내에서 실행이 가능하다

 

 singleInstance: 루트 액티비티로만 존재하며 하나의 인스턴스만 생성가능하고 태스크내에 해당

  액티비티 하나만 속할 수 있어 다른 액티비티를 실행시키면 새로운 Task가 생성되어

  (FLAG_ACTIVITY_NEW_TASK와 동일) 그 Task내에 포함된다



- 소스코드에서 플래그를 사용하고 싶을때에는 Intent에 addFlags() 또는 setFlags() 메소드를 사용한다


  FLAG_ACTIVITY_NEW_TASK: 

  동일 affinity의 task가 있으면 그곳에 실행되고 아니면 새로운 task를 실행


  FLAG_ACTIVITY_SINGLE_TOP: 

  상단 singleTop과 같으며, 실행시 재사용 액티비티의 실행은 onPause(), onNewIntent(), onResume()

  순으로 호출된다

  ☞ [B]를 single top설정: [A][B] 상태에서 [B] 호출시 => [A][재사용된B]

  ☞ [B]를 single top설정: [B][A] 상태에서 [B] 호출시 => [B][A][B]


  FLAG_ACTIVITY_NO_HISTORY:

  해당 액티비티는 재활성화시(back키를 눌러 다시 활성화될때) pop 된다

  ☞ [B]를 no history설정: [A][B][A] 상태에서 back키 사용시 [A]가 pop 되고 [B] 역시 

  no history에 의해 pop => [A]


  FLAG_ACTIVITY_REORDER_TO_FRONT:

  activity 호출시 이미 같은 activity가 task내에 있으면 같은 activity는 pop 시키고 해당 activity가 push 된다

  ☞ [A]를 reorder to front설정: [A][B] 상태에서 [A] 호출시 같은 activity인 [A]가 pop되고 => [B][A]


  FLAG_ACTIVITY_CLEAR_TOP:

  해당 task에 있는 모든 activity를 pop 시키고 해당 activity가 root activity로 task에 push된다

  ☞ [A]를 clear top설정: [A][B] 상태에서 [A] 호출시 모두 pop되고 => [A]

  단, 해당 플래그는 액티비티를 모두 onDestroy() 시킨 후 새롭게 onCreate() 시키기 때문에 [A]를

  유지하려면 FLAG_ACTIVITY_SINGLE_TOP 플래그와 함께 사용하면 된다


http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_BROUGHT_TO_FRONT



Clear Task


- Task를 오랫동안 사용하지 않고 방치해 두면 시스템은 Root Activity를 제외한 모든 액티비티를 Clear 시킨다

- 이러한 동작은 Activity의 속성을 수정하여 제어할 수 있다


  alwaysRetainTaskState:

  Task의 Root Activity에 true로 설정되어 있다면 상단에 언급되었던 동작은 발생하지 않으며 Task는

  오랜 시간 이후에도 Stack에 있는 모든 Activity를 유지한다


  clearTaskOnLaunch:

  이 속성이 true로 설정되어 있으면 alwaysRetainTaskState 와 정반대로 사용자가 Task를 떠났다가

  다시 돌아올 때마다 항상 Stack은 Root Activity로 정리된다


  finishOnTaskLaunch:

  이 속성은 clearTaskOnLaunch와 유사하지만 전체 Task가 아닌 단일 Activity에서 동작한다

  그리고 그것은 Root Activity를 포함한 어떤 Activity가 사라지는 원인이 될 수도 있다

  true로 설정되어 있을 때, Activity는 현재 Session 동안 Task의 일부만 유지한다

  만일 사용자가 해당 Task를 벗어났다가 다시 돌아오면 더이상 존재하지 않는다

'Android > Tip&Tech' 카테고리의 다른 글

Android selector 관련 팁  (0) 2011.04.29
ndroid Fake Iphone UI 2 Source  (0) 2011.04.29
c2dm 에 관한 또다른 참고내용  (0) 2011.04.26
android c2dm sample source  (1) 2011.04.26
c2dm simple example  (0) 2011.04.25

아이폰4를 사용하며 안드로이드를 공부하는 사람으로서 지내다보니 아이폰과 안드로이드OS 폰에는 각각 장단점이 많이 있다 느껴진다.
 
 그 중에 하나가 바로 push 방식.
 서버-클라이언트 관계에서 지금까지는 대부분 클라이언트에서 서버에 요청하는 방식(Pull) 이 많이 사용되었다. 하지만 최근 스마트폰 같이 테블렛 기기가 많이 공급되면서 클라이언트가 서버에게 요청하고 다시 자료를 받는 식의 방법은 패킷을 많이 소모하게 되여 서버에서 새로운 소식이 있을 때 클라이언트로 알려주는 Push방식이 많이 사용된다.
 
 아이폰에서는 이미 서비스가 되고있어 많이 사용되었지만 안드로이드에서는 2.2 에서 부터 서비스를 시작하였다. 정확히는 모르겠지만 아직 구글에서도 테스트 중이여서 정확도 100프로같이 잘 된다고는 보장하지 못하는거 같다.

 무튼 이렇게 안드로이드에서 서비스를 시작한 것이 C2DM(Cloud To Device Messaging Framework) 이다.
 자세한 정보는 http://code.google.com/intl/ko-KR/android/c2dm/index.html 에 있지만 난 뭔소린지 잘 모르겠다.

 그렇다면 이제 순서대로 C2DM을 이용해서 Push Notification을 만들어보자.
 
 만들기 전에 필요한 준비물이 있다!? 
  1. Android OS 2.2 (Proyo) 이상의 단말 (에뮬레이터는 안되는 걸로 알고있지만 될 수도 있음)
  2. 준비된 단말은 마켓에 로그인이 가능한(한마디로 Google 계정이) 있어야 하고 로그인이 되어있어야 한다.
  3. 그리고 Third Party Server 뭐라고 하는데 잘 모르겠고 단순히 Google에 Push 기능을 사용하고 싶고 그래서 당신들 서버를      사용하고 싶다는 신청서를 제출하면 된다. 신청은 http://code.google.com/intl/ko-KR/android/c2dm/signup.html <-- 여기서
    -. 신청을 할 때 Name of Your Android App 이 항목에는 C2DM 기능을 사용할 어플리케이션의 페키지 명을 적으면 된다.
      만약에 내가 com.tistory.j2enty.c2dm 이라는 패키지명을 사용했다면 그래도 적어서 내면 되더라...
       그리고 모 나머지는 읽어보고 적어내거나 선택하면 된다고 생각한다. (몇번을 테스트하면서 안되길래 저 패키지명이 잘못되       었나를 여러번 생각해봐서.. 다른 사람도 그럴꺼같아서....;;)
    -. 신청을 완료하고 나면 신청할 때 기입했던 메일계정에 영문의 메일이 하나 온다. (예전에는 오래걸렸는데 요즘은 바로바로         회신을 보내주는 듯..) 무튼 이 메일 (보낸이 android-c2dm)이 오면 사용이 가능해진 것이다.

 준비가 완료 되었다면 이제 부터...
 
-. Android App
 1. 일단 다 만들어놓고 안됐었던 원인이 있는 manifest 파일정보에 대한 설명  
 

AndroidManifest.xml 접기

<?xml version="1.0" encoding="utf-8"?>

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

 

package="com.tistory.j2enty.c2dm"

 

android:versionCode="1"

 

android:versionName="1.0">

 

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

 

<activity android:label="@string/app_name" android:name="C2dmActivity">

 

<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="com.tistory.j2enty.c2dm" />

 

</intent-filter>

 

<intent-filter>

 

<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>

 

<category android:name="com.tistory.j2enty.c2dm" />

 

</intent-filter>

 

</receiver>

 

<activity android:name="ShowMsgActivity"></activity>

 

</application>

 

 

 

<permission android:name="com.tistory.j2enty.c2dm.permission.C2D_MESSAGE" android:protectionLevel="signature"/>

 

<uses-permission android:name="com.tistory.j2enty.c2dm.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" />

 

 

 

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

</manifest> 

AndroidManifest.xml 접기

 
  가장 먼저 퍼미션에서 C2D_MESSAGE 에 대한 퍼미션을 줄 때에는 앞에 개발중인 어플리케이션의 패키지 명을 먼저 적는다.
   ex)패키지명.permission.C2D_MESSAGE
  그 아래 있는 uses-permission 에서도 똑같이

  그리고 com.google.android.c2dm.permission.RECEIVE 라는 퍼미션은 c2dm메시지를 받을 수 있게 해주는 퍼미션이고 그 밑에  는 인터넷사용, 휴대폰의 슬립상태 접근, 계정접근, 핸드폰의 현재 상태 읽기 등의 퍼미션을 추가한다.

  또한 중요한 것이 receiver 인데 서버에서 오는 메시지에 대한 처리를 모두 receiever에서 해주기 때문이다. 여기서 추가하는    Action 은 메시지를 받았을 경우와 c2dm의 사용을 등록했을 때에 대한 action을 등록하고 카테고리는 현재 작업중인 패키지명을  추가해주면된다.

 2. Activity에서 필요한 코드
 

C2dmActivity 닫기

switch(v.getId())

 

{

 

case R.id.c2dm_btnRegist:

 

//Android C2DM에Push메시지를 받겠다는메시지를보내는Intent

 

//정상적으로등록이되면Android C2DMServer쪽에서 인증키를보내준다.

 

//이인증키는해당어플리케이션과해당기기를대표하는인증키로서버에서메시지를보낼때사용되며

 

//서버에등록을할때마다인증키는달라진다.

 

Intent registrationIntent =new Intent("com.google.android.c2dm.intent.REGISTER");

 

 

 

//인텐트에함께보내는내용은"app"과"sender"가있는데이두Key는 모두google에서 제시한것이기때문에

 

//임의로변경이불가능하다.

 

//app에는 해당어플리케이션정보를담아서보내고

 

//sender에는 개발자의주소를담아서보낸다.

 

registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0,new Intent(), 0));

 

registrationIntent.putExtra("sender",mailAddress);

 

 

 

startService(registrationIntent);

 

break

 

 

 

case R.id.c2dm_btnUnregist:

 

//Android C2DM에Push메시지를 그만받겠다는메시지를보내는Intent

 

Intent unregIntent =new Intent("com.google.android.c2dm.intent.UNREGISTER");

 

unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0,new Intent(), 0));

 

startService(unregIntent);

 

break

 

default:

 

break

}

C2dmActivity 닫기


  이 코드에 대한 설명은 위에 코드에 주석을 추가하였다.

main.xml 닫기

<?xmlversion="1.0"encoding="utf-8"

 

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

 

android:orientation="vertical"

 

android:layout_width="fill_parent"

 

android:layout_height="fill_parent"

 

>

 

<Buttonandroid:layout_width="fill_parent"

 

android:layout_height="wrap_content"

 

android:text="가입"

 

android:id="@+id/c2dm_btnRegist"

 

/>

 

 

 

<Buttonandroid:layout_width="fill_parent"

 

android:layout_height="wrap_content"

 

android:text="해지"

 

android:id="@+id/c2dm_btnUnregist"

 

/>

</LinearLayout>

main.xml 닫기



 3. 리시버에 대한 코드

C2dmReceiver 닫기

/**

 

*<pre>

 

* C2DM의사용신청후서버에서오는메시지를처리하는메소드

 

*</pre>

 

*

 

*@param context

 

*@param intent

 

*/

 

privatevoid handleRegistration(Context context, Intent intent)

 

{

 

Log.e("C2DM","handleRegistration");

 

 

 

//서버에서넘어오는메시지의내용에key이름 "registration_id"에는이기기에만사용하는인증키값이담겨서넘어온다.

 

String registration = intent.getStringExtra("registration_id");

 

 

 

if (intent.getStringExtra("error") !=null)

 

{

 

Log.e("C2DM","error");

 

}

 

elseif (intent.getStringExtra("unregistered") !=null)

 

{

 

Log.e("C2DM","unregistered");

 

}

 

elseif (registration !=null)

 

{

 

Log.e("C2DM", registration);

 

}

 

}

 

 

 

/**

 

*<pre>

 

* C2DM서버에서오는메시지(개발자측이보내는메시지)를처리하는메소드

 

*</pre>

 

*

 

*@param context

 

*@param intent

 

*/

 

privatevoid handleMessage(Context context, Intent intent)

 

{

 

Log.e("C2DM","handleMessage");

 

 

 

String title = intent.getStringExtra("title");

 

String msg = intent.getStringExtra("msg");

 

 

 

// 화면깨우기

 

PushWakeLock.acquireCpuWakeLock(context);

 

 

 

Intent i =new Intent(context, ShowMsgActivity.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);

}

C2dmReceiver 닫기


  리시버에 대한 소개도 주석과 함께 추가하였다.
  다만 여기서 각각의 메소드를 호출 할 때 handleRegeistration 의 경우에는 Reciever에서 받은 인텐트 액션이    com.google.android.c2dm.intent.REGISTRATION 일 경우 호출하게 되며
  handleMessage 메소드의 경우에는 com.google.android.c2dm.intent.RECEIVE 라는 인텐트 액션일 경우 호출하게 한다.

  이 예제에서는 화면깨우기라는 부분을 추가하였다. 이 부분은 지금 현재 안드로이드 폰이 슬립상태일 경우 이 상태를 깨어난 상  태로 바꿔주게 되며 서버에서 받은 메시지를 보여준다. 이 에 대한 처리는 c2dm메시지를 받았을 경우 해주는 부분이기 때문에  Android notification을 사용해서 nitify 해주거나 진동을 사용해서 알려주거나 하는 등의 방법으로 알려주며 각 어플리케이션의 특  징과 개발자의 입맛에 따라 구현이 가능할 것이다.

 이렇게 만들어진 어플리케이션을 실행하고 가입버튼을 누르게되면 C2DM에 등록을 하고 난 후 넘어오는 값이 이 기기에 대한 인증키이며 이 인증키값을 이용해서 서버에서 메시지를 보내게 된다.

 여기서 얻게 되는 키값을 임의로 arrId 라고 하겠다.
 
-. 내 어플리케이션에 대한 인증키 받기.
 

Auth_android 닫기

publicclass Auth_android

 

{

 

privatestatic StringHOST ="https://www.google.com/accounts/ClientLogin"

 

privatestatic StringEMAIL ="abcdedfe@gmail.com"      //사용자 아이디

 

privatestatic StringPASS ="password"                 //비밀번호

 

privatestatic StringSOURCE ="androidpush-test. nexusone-2.2"   //어플리케이션에 대한 간단한 설명

 

 

 

publicstaticvoid 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();

 

}

 

}

}

Auth_android 닫기

 
 위의 클래스를 실행하게 되면 처음 C2DM에 신청한 ID를 통해 인증을 거치고 사용가능한 키값이 넘어온다. 이 키값역시 위에 기기에 대한 인증키값과 함께 사용하여 메시지를 전달하는 서버에서 사용된다.

여기서 넘어오는 키 값을 편의에 따라 AUTH 라고 하겠다.





-. 서버의 구현

 

접기

publicclass Push

 

{

 

privatestatic StringHOST ="http://android.apis.google.com/c2dm/send"

 

 

 

//해당어플리케이션의인증키

 

privatestatic StringAUTH ="DQAAAKkAAAA...................................";

 

 

 

//C2DM을사용하겠다고신청한디바이스의인증키

 

privatestatic String[]arrId =

 

{

 

"APA91bF6WBifiEJ0VtG2bxmKBc_EPWVhmB......................................"

 

};

 

 

 

publicstaticvoid main( String[] args )throws Exception

 

{

 

for (int i=0; i<arrId.length i++)

 

{

 

//보낼메시지

 

androidPush(arrId[i],"바보","꺼져");

 

}

 

}

 

 

 

publicstaticvoid 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();

 

}

 

}

}

접기


  이 소스에 대한 필요한 설명은 코드에 함께 추가하였다.
 여기서 AUTH에는 위에서 인증받은 어플리케이션에 대한 인증키값을
 arrId에는 그 위에서 인증받은 기기에 대한 인증키값을 넣어준다.
 
 
 -. 실행순서
  가장 먼저 실행해야 할 것은 어플리케이션의 인증키값을 받아오는 Auth_android 의 실행이다. 이 값으로 얻어진 키값을 서버(Push.java)에 추가한다. 
  그리고 나서 안드로이드 어플리케이션을 실행하여 가입버튼을 누르고 좀 기다리면 서버에서 보내주는 기기 인증키값이 나온다. 이 키값을 서버(Push.java)에 추가한다.
  여기서 주의해야 할 것은 가입버튼을 누를 때마다 다른 키값이 넘어오게 되므로 이 점은 개발할 때 유의해야할 것이다. 
 마지막으로 서버를 실행하게 되면 입력한 메시지가 휴대폰에 뜬다!!!ㅋㅋ 그럼 밑에 스샷을 추가하겠다!!

 -. 스크린샷
  1. 어플리케이션 인증! 


 실행하게 되면 맨 아래 나오게되는 Auth값만 필요하다. 이 키값이 어플리케이션의 키값이 된다.

 2. 디바이스 인증
 


 가입 버튼을 누르게 되면 아래 로그캣창에 가장 긴 값이 바로 디바이스 인증키이다. 

  3. C2DM 서버에서 클라이언트로 보내기!
 

 가운데 쯤에 있는 //보낼 메시지에 받을 기기의 키값과 타이틀, 내용을 실어서 보낸다~
 그럼 한...2~3초 후에 기기에서 그 메시지를 받아서 보여준다!!
 

 이번에 테스트에서는 다이얼로그를 통해서 표현했지만 여러가지 방법으로 사용이 가능할 것이다.


-. 정리
 이번 테스트에서 얻은 결과로는 아이폰과 똑같은 기능을 하는 Push기능을 안드로이드에서 구현이 가능하다는 것을 알게되었다.
 지금 진행하고 있는 프로젝트에서  제대로 사용하기 위해서는 현재 이 C2DM을 통해서 Push메시지를 받기로 한 디바이스 인증 키값을 서버에서 가지고 있어야 한다는 점이다.  이 부분은 좀더 알아 봐야겠지만 왠지 C2DM 서버를 통해서 얻을 수 있을 것 같다.. 아니면 뭐..;;

 그리고 한번에 하나의 디바이스로 밖에 보낼 수 없기 때문에 반복문을 사용해서 메시지를 전달해야 하며.
 현재는 구글에서 정해진 일정 길이의 텍스트만 전송이 가능하다. 왜냐하면 C2DM 서비스는 대량의 컨텐츠를 푸쉬하게 설계되지 않았고 특정 앱에세 새로운 데이터가 있음을 알려주는 역할만 하도록 설계되었기 때문이다.
 
 또한 푸시메시지를 받기로 신청한 어플리케이션은 현재 작동중이지 않아도 된다 (BroadCastReceiver를 사용했기 때문에)
 그리고 따로 푸시메시지를 해지 하지 않고 해당 어플리케이션을 삭제하여도 푸시메시지는 더 이상 오지않는다.

 

출처 http://j2enty.tistory.com/entry/Android-Push-NotificationC2DM-Service

출처 : http://stackoverflow.com/posts/5219182/edit

Hi use this code........

public class C2dmEx extends Activity
{
  

static TextView mytext = null;
    Context context = null;
    Intent intent = null;
    
    
   
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mytext = (TextView) findViewById(R.id.mytext);  
        mytext.setText("app started");

        
        Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
        registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); 
        registrationIntent.putExtra("sender","your Mail Id");
        startService(registrationIntent);
        
        mytext.setText("Grabbing your device registration ID.....");
      
        Log.i("Recieve","1");
        
    }
}



public class C2DMReceiver extends  BroadcastReceiver
{
//private CustomerBean customer = null;
private RegisterManager reg = null;
private C2dmEx ex = null;
int UserId = 0;
private boolean auth = false;
private CustLogin cl = null;
public C2DMReceiver()
{
cl = new CustLogin();
}
@Override
public void onReceive(Context context, Intent intent)
   {
       Log.i("Recieve","2");
    C2dmEx.mytext.setText("Intent Received!");
       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)
   {
       
       String registration = intent.getStringExtra("registration_id"); 
       if (intent.getStringExtra("error") != null)
       {
          C2dmEx.mytext.setText("There was an error with your device registration!");
           // Registration failed, should try again later.
       } 
       else if (intent.getStringExtra("unregistered") != null)
       {
           // unregistration done, new messages from the authorized sender will be rejected
          C2dmEx.mytext.setText("You have been 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.
                         
       // UserId = customer.getId();
       // Log.i("id",String.valueOf(UserId));
       
        String RegId = registration; 
        Log.i("reg",String.valueOf(RegId) );
        C2dmEx.mytext.setText("Your registration code is: " + RegId);
       
       
       
       
       
       }
   }
   
   private void handleMessage(Context context, Intent intent)
   {
       C2dmEx.mytext.setText("You have been alerted!");
   }

}

and your manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.mmp.geopon"
      android:versionCode="1" 
      android:versionName="0.1">
      
    <application android:icon="@drawable/icon" android:label="@string/app_name">
    
        <activity android:name=".C2dmEx"
                  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="com.mmp.geopon" />
        </intent-filter>
        
         <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="com.mmp.geopon" />
       </intent-filter>
        
   </receiver>
        

    </application>
    
    <uses-sdk android:minSdkVersion="8" />
    <permission android:name="com.mmp.geopon.permission.C2D_MESSAGE" android:protectionLevel="signature" />
     <uses-permission android:name="com.mmp.geopon.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.READ_PHONE_STATE"/>
    
   
    
</manifest>


출처 : http://www.androidside.com/bbs/board.php?bo_table=B46&wr_id=14705

안녕하세요^^ 리칼입니다.


저도 안드로이드를 하면서 제가 이런글을 올리게 될지 몰랐네요 ㅎㅎ


솔직히 자기의 소스를 공유하면서 강좌 하시는분들을 보면서 정말 대단하다고 생각하였습니다.


자기 시간내기도 힘든데 저렇게 강좌하시는분들이 있기에 안드로이드의 미래가 밝다고 생각합니다.


그래서 조금이나마 저도 보템이 되려고 이렇게 c2dm에 관한 예제를 올리게 되었습니다^^


자! 그럼 시작할께요. 미숙하지만 잘봐주셨으면 감사하겠습니다~


일단 c2dm이 Cloud to Device Messaging 의 약자로 알고 있습니다. 안드로이드 2.2 프로요부터 생겨난


이 API는 개발자들이 서버와 모바일 어플리케이션간에 데이타를 쉽게 싱크할 수 있도록, 서버쪽 에서에서


안드로이드 폰 상의 어플리케이션으로 데이타를 손쉽게 전달할 수 있도록 도와줍니다.


쉽게 생각하시면 아이폰의 푸쉬 기능이라고 보시면 됩니다. 현재 c2dm이 나오기전까진 polling기법을


토하여 실시간 메시징을 했다고 알고있는데 이번 이 api가 추가되면서 일반 서드파티 개발자도 손쉽게


프로그래밍을 개발할수 있으리라 봅니다. 자! 서론이 길었네요 바로 본론으로!


먼저 푸쉬를 사용하기 위해선 구글에 신청을 하여야 합니다. 이게좀 귀찮죠. 신청을 해도 바로 사용할수


있는게 아니라 승인 메일이 와야합니다. 전 일주정도 걸린듯 하네요..미리미리 신청하세요~~


http://code.google.com/intl/ko-KR/android/c2dm/signup.html 이쪽에서 신청하시면 됩니다.


그러면 메일이 invite어쩌고 올겁니다. 그러면 그담부터 사용해도 된다는거죠


제가 만들어 보려는 예제는 메시지를 보내서 실시간으로 다른 폰으로 가게 하는겁니다. 하지만


그럴려면 폰이 2대가 있어야 하는데 대부분 1대밖에 없다는 가정하에 제가 제 자신폰으로 보내게 하도록


구성하였습니다.


간단히 기본 구성원리를 설명해드리겠습니다.


c2dm을 사용하기 위해선 크게 5가지가 필요합니다.


개발자ID(구글계정), 애플리케이션ID(패키지이름정도), 등록ID(특정장치를식별하는 ID),
구글사용자계정(보통핸드폰로그인구글계정),인증토큰값이 필요합니다.


처음에 어플을 실행하여 개발자ID와 어플ID를 c2dm서버에 전송하여 등록ID를 발급 받습니다.
그러면 그 ID가 자기 식별고유ID가 됩니다. 이걸로 상대방 핸드폰을 식별하게 되는거죠.


그담에 메시지를 쓰고 전송을 누르면 먼저 c2dm에 인증을 요청해야 합니다. 아무나 보내면


보안에 문제가 있겠죠? 이때 개발자ID가 필요합니다. 개발자가 c2dm을 신청하였기 때문에


쓸수가 있는거죠~ 그리고 나서 인증을 되면 등록ID,인증토큰값,보낼 메시지등등을 푸쉬에 담아


상대방 핸드폰으로 보내게 됩니다... 여기서 중요한건 저는 지금 제 자신에게 푸쉬를 보내게


만들었습니다. 그러므로 다중 메시지보내기 어플을 만들게 된다면 상대방의 등록ID를 알아야겠죠?


그럴라면 서드파티서버가 필요합니다. 그 서버를 구현하여 디비를 만들고 상대방의 ID를 등록


시켜서 푸쉬를 보내야 할것입니다. 이건 사용자가 직접 구현해야 하는 사항입니다.


대충이렇습니다.

 


////////////////////////////////////////////////////////////////////////////////////

 


자 이제 c2dm 예제를 시작하겠습니다. 먼저 프로젝트 생성후 대충 저처럼 UI구성하세요.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:gravity="center_vertical"
    >
<TextView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="메세지 보내기"
    />
    <EditText
    android:id="@+id/msg_text"
    android:layout_width="180dip"
    android:layout_height="wrap_content"/>
    <Button
    android:id="@+id/msg_send"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="보내기"/>
</LinearLayout>

 

 

 


그리고 메인 activity 에서 아까 말한것 같이 등록ID를 발급 받아야 합니다.
이렇게 작성하시면 됩니다.


C2dm.java


public class C2dmTest extends Activity {
    /** Called when the activity is first created. */
EditText msg_text;
Button msg_send;
private OutputStream out = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
//C2DM 등록ID 발급
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // 어플리케이션ID
registrationIntent.putExtra("sender", "xxxxxxxx@gmail.com"); //개발자ID
startService(registrationIntent); //서비스 시작(등록ID발급받기)
                 // 위에서 지정한 "app"와 "sender"은 맘대로 지정하시는게 아니라 구글에서 필요한 변수명들입니다.
       
       
        msg_text = (EditText)findViewById(R.id.msg_text);
        msg_send = (Button)findViewById(R.id.msg_send);
       
        msg_send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
// 메시지를 보낼때 sender(발급받은 ID, 토큰인증값, 메시지)
sender(C2dm_BroadcastReceiver.registration_id,getAuthToken(),msg_text.getText().toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
    }
   
    public void sender(String regId,String authToken,String msg) throws Exception{
     StringBuffer postDataBuilder = new StringBuffer();
       
     postDataBuilder.append("registration_id="+regId); //등록ID
     postDataBuilder.append("&collapse_key=1");
     postDataBuilder.append("&delay_while_idle=1");
     postDataBuilder.append("&data.msg="+URLEncoder.encode(msg, "UTF-8")); //태울 메시지


        byte[] postData = postDataBuilder.toString().getBytes("UTF8");


        URL url = new URL("https://android.apis.google.com/c2dm/send");
       
        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=" + authToken);


        OutputStream out = conn.getOutputStream();
        out.write(postData);
        out.close();


        conn.getInputStream();

 


    }
   
    public String getAuthToken() throws Exception{
     String authtoken = "";
       
     StringBuffer postDataBuilder = new StringBuffer();
        postDataBuilder.append("accountType=HOSTED_OR_GOOGLE"); //똑같이 써주셔야 합니다.
        postDataBuilder.append("&Email=xxxxxx@gmail.com");  //개발자 구글 id
        postDataBuilder.append("&Passwd=xxxxxx");           //개발자 구글 비빌번호
        postDataBuilder.append("&service=ac2dm");
        postDataBuilder.append("&source=test-1.0");


        byte[] postData = postDataBuilder.toString().getBytes("UTF8");


        URL url = new URL("https://www.google.com/accounts/ClientLogin");
       
        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 br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
       
        String sidLine = br.readLine();
        String lsidLine = br.readLine();
        String authLine = br.readLine();
       
        System.out.println("sidLine----------->>>"+sidLine);
        System.out.println("lsidLine----------->>>"+lsidLine);
        System.out.println("authLine----------->>>"+authLine);
        System.out.println("AuthKey----------->>>"+authLine.substring(5, authLine.length()));
       
        authtoken = authLine.substring(5, authLine.length());
       
     return authtoken;
    }


}

 


소스를 보시면 대충 아실겁니다. 인증ID와 토큰값을 구해서 메시지만 푸쉬에 태워서 보내는겁니다.


자 여기서 중요한건 저 메시지를 이용하기 위해선 AndroidManifest.xml 등록을 해줘야 하는 부분이 있습니다.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="test.android.test"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".C2dmTest"
                  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=".C2dm_BroadcastReceiver"
       android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="test.android.test"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="test.android.test"/>
</intent-filter>
    </receiver>
    </application>
   
    <permission android:name="test.android.test.permission.C2D_MESSAGE"
     android:protectionLevel="signature"/>
    
    <uses-permission android:name="test.android.test.permission.C2D_MESSAGE"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
   
    <uses-sdk android:minSdkVersion="8" />


</manifest>


빨간색으로 표시한 부분이 추가한 부분입니다. 퍼피션 부분을 추가하고 자기 패키지에 맞게 설정을


하셔야 보내고 받기를 할수가 있습니다. 그리고 저위에 리시버 부분을 보시면 저부분이 발급ID를 만들기


위해 보내고 메시지를 받고 하는 부분입니다. C2dm_BroadcastReceiver 클래스 파일이 중요한 역할을


하게 되죠.


C2dm_BroadcastReceiver.java


public class C2dm_BroadcastReceiver extends BroadcastReceiver{
    /** Called when the activity is first created. */
static String registration_id = null;
static String c2dm_msg = "";
    @Override
    public void onReceive(Context context, Intent intent) {
     if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
    
     handleRegistration(context, intent);
    
     } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
    
     c2dm_msg = intent.getExtras().getString("msg");
    
     System.out.println("c2dm_msg======>"+c2dm_msg);
     Toast toast = Toast.makeText(context, "메시지 도착!\n"+c2dm_msg, Toast.LENGTH_SHORT );
  toast.setGravity( Gravity.TOP | Gravity.CENTER, 0, 150 );
  toast.show();
    
     }
    }
   
    private void handleRegistration(Context context, Intent intent) {
    
     registration_id = intent.getStringExtra("registration_id");
    
     System.out.println("registration_id====>"+registration_id);
    
     if (intent.getStringExtra("error") != null) {
    
     Log.v("C2DM_REGISTRATION",">>>>>" + "Registration failed, should try again later." + "<<<<<");
    
     } else if (intent.getStringExtra("unregistered") != null) {


     Log.v("C2DM_REGISTRATION",">>>>>" + "unregistration done, new messages from the authorized sender will be rejected" + "<<<<<");
    
     } else if (registration_id != null) {
    
     System.out.println("registration_id complete!!");
     }
    }
   
   
}


바로 이 클래스가 BroadcastReceiver를 상속받아서 통보를 하는 역할을 하게 되죠.


BroadcastReceiver 의 역할은 제 설명보다는 직접 찾아보시는게 좋을듯해요 ㅠㅠ 제가 설명을 잘못해서~


무튼 간단하게 통보 역할을 하신다고 보면 되겠네요~


저위에 보면 BroadcastReceiver를 상속받아  onReceive를 오버라이드 한걸 보실수 있습니다.


C2dmTest activity에서 처음에 등록을 하게되면 이 onReceive를 통해 아이디를 받을수 있습니다.


적절한 if문을 통해 받을수가 있죠


if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
이부분이 바로 등록ID를 받는 부분입니다. 등록ID를 받게되면 handleRegistration함수에서 id를 제대로 받았는지


검사하게 됩니다. 이제 등록ID를 받으면 준비가 된겁니다. 그리고 이제 메시지를 쓰셔서 보내기를


클릭하시면sender(C2dm_BroadcastReceiver.registration_id,getAuthToken(),msg_text.getText().toString());이 메소드를


통해 인증을 하고 등록값과 메시지를 상대방에게 보내지게 됩니다.


그러면 메시지를 수신하게 되면 또 BroadcastReceiver를 사용하게 됩니다. 아까 메니페스트에서 등록한


리시브가 그역할을 하게 됩니다.


이번엔 else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
이부분이 받게 되겠죠? 여기서 메시지를 이제 받아야겠죠? 아까 위에서 센드해서 보낼때


postDataBuilder.append("&data.msg="+URLEncoder.encode(msg, "UTF-8")); 이부분을 볼수 있습니다.


메시지 말고 다른 변수도 태워 보내실거면 저밑에 data.xxx로 추가하셔서 보내시면 됩니다.


postDataBuilder.append("&data.phone="+URLEncoder.encode(phone, "UTF-8")); 이렇게 보내시면 됩니다.


그렇게 보낸걸 받을때는 c2dm_msg = intent.getExtras().getString("msg");이렇게만 받으시면 됩니다.


data.xxx 에서 xxx만 꺼내시면 됩니다. 이렇게 하시면 푸쉬를 받게 되실 겁니다.


그리고 아직 c2dm이 정식으로 나온게 아니라 그런지 푸쉬가 잘안올때가 있습니다.


안오게 된다고 걱정마시고 계속 하시다 보면 언젠가 옵니다 ㅋㅋ


저부분에서 응용하시면 될듯하네요 노티를 사용하시거나 진동및 사운드 등등은


여러분의 몫!

 

 


휴 쓰다보니 정신없이 쓰게 됐네요 -_-;; 제대로 썻는지도 모르겠습니다..

 


무튼 저처럼 고생하시는 분없기를 기대하면서.. 저는 이만 


Push Notification on iOS & Android

 출처 : http://ruknabid.blogspot.com/2011/02/push-notification-on-mobile.html

  Apple은 Push Notification을 Apple 내부에 별도의 APNS (Apple Push Notification Service) 서버를 이용하는 방식으로 Push App. 개발을 지원함.

  이를 이용한 개발방법은 아래 블로그를 참조

  애플 아이폰 푸시알림 서비스 (APNS, Apple Push Notification Service)

 

  Android는 2.2 (Proyo) 버전부터는 APNS와 유사한 C2DM (Android Cloud to Device Messaging Framework) 서버를 제공함.

이에 대한 자세한 내용은 아래 링크를 참조

  Google Projects for Android: C2DM

  하지만, Android 2.1 이하 버전에는 Push Server가 존재하지 않는데, 아래 통계에서 보듯이 2.2 이하 버전의 점유율이 여전히 42%에 육박함.

           [Current Distribution of Android Platform Versions]

        Data collected during two weeks ending on February 2, 2011

 

  따라서, 2.1이하의 하위 버전에서의 Push Service 구현에 대한 고려가 필요하며, 이를 위한 방안은 아래 링크를 참조함.

  How to Implement Push Notifications for Android

  이를 간략히 요약하면, Polling, SMS Notification, Persistent TCP/IP 방식이 있으며,

  1. Polling은 real-time도 아니고 resource가 많이 필요하다는 문제점이 있고,

  2. SMS Notification은 SMS 발송 비용 문제가 크며,

  3. Persistent TCP/IP는 그나마 나으나, 제대로 구현하기 어렵다는 문제가 있음

  그래서, 위 블로그에서 추천하는 방법은 3번을 제대로 구현한 MQTT를 이용하는 방식임.

  MQTT에 관련된 내용은 아래 링크 참조

  IA92: WBI Brokers - Java implementation of WebSphere MQ Telemetry transport

'Android > Tip&Tech' 카테고리의 다른 글

android c2dm sample source  (1) 2011.04.26
c2dm simple example  (0) 2011.04.25
터치 이벤트 관련 글  (2) 2011.04.22
키패드 올려줄때 입력창 같이 올려주는 팁  (0) 2011.04.22
android 내장메모리에 쓰기  (0) 2011.04.22

 
[출처] http://blog.vizpei.kr/94697746 

어플리케이션 개발을 하다보면 반드시 해야하는 것이 모션 이벤트 처리 입니다.

터치 이벤트 같은 것들은 DOWN - MOVE - UP의 단계를 거치면서

사용자가 어떤 동작을 입력 하는지 감지 할 수 있습니다.

이 입력의 어떤 조합으로 사용자가 어떤 동작을 했는지 감지 할 수 있겠죠.

하지만 직접 이런 제스쳐들을 구현하기란 쉬운 일만은 아닙니다. (무엇보다 귀찮죠~)

 

그래서 Android에서는 GestureDetector라는 클래스를 아얘 제공합니다.

 

 

[About GestureListener]

 

GestureDetector는 두 가지 Listener를 가지고 있습니다.

interface GestureDetector.OnDoubleTapListener
interface GestureDetector.OnGestureListener

http://developer.android.com/reference/android/view/GestureDetector.html

 

자세한 설명은 Reference를 보시면 됩니다.

OnDoubleTapListener는 이름 그대로 두번 터치 했을 때,

OnGestureListener는 일반적인 제스쳐들, 한번 터치나 스크롤 관련 Listner입니다.

그리고 저 두 가지 interface를 모두 가진 녀석이 있습니다.

class GestureDetector.SimpleOnGestureListener

보통 SimpleOnGestureListener만 extends 하면 모든 제스쳐를 다 사용 할 수 있습니다.

 

 

[Usage of GestureDetector]

 

사용법도 매우 간단합니다.

GestureDetector를 만들기만 하면 땡이죠.

mGestureDetector = new GestureDetector(this, new SimpleGestureListener());
mGestureDetector.onTouchEvent(event);

음... 너무 뜬금 없는 코드인가요? 일단 아주 간단하게 적어 봤습니다.

1. GestureDetector를 만들 때 GestureListener를 등록 하고

2. 감시할 MotionEvent를 onTouchEvent에 넣어 주면 GetstureListener가 호출이 되는 구조 입니다.

 

좀 더 자세하게 살펴 보면,

  private final class SimpleGestureListener 
  extends GestureDetector.SimpleOnGestureListener {
      // Implementation
  }

  private GestureDetector mGestureDetector;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      mGestureDetector = new GestureDetector(thisnewSimpleGestureListener());
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
      return mGestureDetector.onTouchEvent(event);
  }

위와 같습니다.

 

 

[Analyze Gestures]

 

사용법은 간단하지만 제스쳐는 그리 간단하지 않습니다.

그래서 각 제스쳐에 대해서 분석을 해봤습니다.

public boolean onDoubleTap(MotionEvent e)
public boolean onDoubleTapEvent(MotionEvent e)
public boolean onDown(MotionEvent e)
public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY)
public void onLongPress(MotionEvent e)
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY)
public void onShowPress(MotionEvent e)
public boolean onSingleTapConfirmed(MotionEvent e)
public boolean onSingleTapUp(MotionEvent e)

일단 제스쳐 이벤트에 대해서 간단히 살펴 봅시다.

 

onDoubleTap은 두 번 터치입니다.

onDoubleTap은 두 번 터치하면 이벤트가 더 이상 발생 되지 않지만,

onDoubleTapEvent는 DOWN, MOVE, UP 이벤트를 모두 포함 합니다. (나중에 살펴 보겠습니다.)

 

onDown은 터치하려고 손을 대기만 해도 발생되는 이벤트며,

모든 제스쳐의 시작입니다.

 

onFling은 onScroll에서 끝을 살짝 튕기는 동작에서 발생하는 이벤트며,

onScroll은 말 그대로 스크롤 시에 발생하는 이벤트 입니다.

 

onLongPress는 길게 눌렀을 때 발생 하는 이벤트며,

onShowPress는 onLongPress보다는 좀 더 짧은 시간동안 누르고 있으면 발생 하는 이벤트 입니다.

 

onSingleTap은 한 번 터치 했을 때 발생하는 이벤트며,

onSingleTabConfirmed는 한 번 터치 하고 다음에 다시 터치 이벤트가 들어오지 않았을 때,

한 번 터치가 확실 하다고 확인 시켜주는 이벤트 입니다.

 

 

자... 이제 시작입니다! 이건 간단히 살펴 본것 밖에 안되는...

본격적으로 실제 제스쳐에 따라서 어떤 이벤트가 호출 되는지 살펴 보겠습니다.

시간은 로그를 통해 한번만 계산된 시간이며,

대략 그 시간 범위에서 이벤트가 발생 한다고 보시면 됩니다.

 

 

1. 아주 살짝 터치

000ms    onDown : ACTION_DOWN
060ms    onSingleTapUp : ACTION_UP
306ms    onSingleTapConfirmed : ACTION_DOWN

일단 손을 대면 무조건 onDown 이벤트 발생입니다.

살짝 터치를 하게 되면 보통 30~60ms 정도 후에 손이 떨어지게 됩니다.

손이 떨어 지면 onSingleTapUp 이벤트가 발생하며,

onDown이벤트 발생 후 약 300ms 안에 다시 onDown 이벤트가 발생 하지 않는다면

onSingleTapConfirmed 이벤트가 발생하여 확실히 한 번 터치 되었다는 이벤트를 발생 시킵니다.

 

 

2. 살짝 터치

000ms    onDown : ACTION_DOWN
097ms    onShowPress : ACTION_DOWN
172ms    onSingleTapUp : ACTION_UP
303ms    onSingleTapConfirmed : ACTION_DOWN

역시나 onDown 이벤트 부터 시작입니다.

1번 보다는 살짝 길게, 약 90~100ms 정도 터치되면 onShowPress 이벤트가 발생합니다.

172ms 이후에 손을 떼었으며,

역시나 300ms 정도 지나면 onSingleTapConfirmed 이벤트가 발생 됩니다.

 

 

3. 약간 길게 터치

000ms    onDown : ACTION_DOWN
103ms    onShowPress : ACTION_DOWN
460ms    onSingleTapUp : ACTION_UP

2번보다 좀 더 길지만, LongPress는 아닌 상황입니다.

역시 이 때도 약 100ms 정도에 onShowPress 이벤트가 발생하긴 하지만

300ms 이후에 손을 떼었기 때문에 onSingleTapConfirmed 이벤트가 먹히는 현상이 일어납니다.

 

 

4. 아주 길게 터치

000ms    onDown : ACTION_DOWN
096ms    onShowPress : ACTION_DOWN
590ms    onLongPress : ACTION_DOWN

LongPress가 발생 하는 상황입니다.

100ms 정도에 onShowPress 이벤트가 발생 하며,

약 590~600ms 정도에 onLongPress 이벤트가 발생 합니다.

이때 onSingleTapUp 이벤트는 발생 하지 않습니다.

 

 

두 번 터치 하는 경우에는 이벤트가 두 가지이기 때문에,

두 가지 조합의 경우 모두 살펴 보겠습니다.

 

 

5. 두 번 터치 (onDoubleTap)

000ms    onDown : ACTION_DOWN
060ms    onSingleTapUp : ACTION_UP
140ms    onDoubleTap : ACTION_DOWN
140ms    onDown : ACTION_DOWN (지연 될 수 있음)

먼저 onSingleTapUp 이벤트가 발생 합니다.

그리고 onSingleTapConfirmed 가 발생 하기 전에

다시 onDown 이벤트가 들어오게 되면 onDoubleTap 이벤트가 발생 합니다.

참고로 두번째 들어오는 onDown 이벤트는 onDoubleTap 이벤트보다 늦게 들어 올 수 있습니다.

(항상 같이 들어 오는게 아니라 onDoubleTap이 먼저 발생 합니다.)

 

 

6. 두 번 터치 (onDoubleTapEvent)

000ms    onDown : ACTION_DOWN
041ms    onSingleTapUp : ACTION_UP
130ms    onDoubleTapEvent : ACTION_DOWN
130ms    onDown : ACTION_DOWN (지연 될 수 있음)
190ms    onDoubleTapEvent : ACTION_UP

onDoubleTap 이벤트와의 차이는 DOWN, MOVE, UP 이벤트까지 모두 캐치된다는 점이며,

마지막에 onDoubleTapEvent에 UP 액션이 들어오는 것을 확인 할 수 있습니다.

(위의 경우, 190ms 이후에 두번째 터치에서 손이 떨어졌다는 것을 확인 할 수 있습니다.)

 

 

7. 두 번 터치 (onDoubleTap + onDoubleTapEvent)

000ms    onDown : ACTION_DOWN
080ms    onSingleTapUp : ACTION_UP
200ms    onDoubleTap : ACTION_DOWN
200ms    onDoubleTapEvent : ACTION_DOWN (지연 될 수 있음)
200ms    onDown : ACTION_DOWN (지연 될 수 있음)
260ms    onDoubleTapEvent : ACTION_UP

같이 사용 하게 되면 onDoubleTap, onDoubleTapEvent 이벤트 둘 다 발생하며,

항상 onDoubleTap 이벤트가 먼저 발생 하게 됩니다.

(결과적으로 onDoubleTap - onDoubleTapEvent - onDown 순서로 발생 합니다.)

 

 

8. 두 번 터치, 두 번째 터치시 스크롤 (onDoubleTapEvent)

000ms    onDown : ACTION_DOWN
080ms    onSingleTapUp : ACTION_UP
179ms    onDoubleTapEvent : ACTION_DOWN
179ms    onDown : ACTION_DOWN (지연 될 수 있음)
280ms    onDoubleTapEvent : ACTION_MOVE
289ms    onShowPress : ACTION_DOWN

290ms    onDoubleTapEvent : ACTION_MOVE
...
779ms    onLongPress : ACTION_DOWN
800ms    onDoubleTapEvent : ACTION_UP

평소에 절대 나올법한 제스쳐지만 onDoubleTapEvent의 특성을 살펴보기 위한 제스쳐 입니다.

두 번째 onDown 이벤트 이후에 MOVE 이벤트가 들어 오는 것을 확인 할 수 있으며,

한가지 특이한 점은 계속 스크롤 되지 않고 onLongPress 이벤트가 발생하면 끝난다는 점입니다.

손을 뗄 수 밖에 없는 상황이 오게 되죠.

 

위에서 보시다 시피,

조금 길게 눌러짐에 따라 onShowPress와 onLongPress가 도중에 발생 할 수도 있습니다.

 

 

9. 스크롤

000ms    onDown : ACTION_DOWN
030ms    onScroll : ACTION_DOWN, ACTION_MOVE
...

스크롤 이벤트는 간단 합니다.

최소 30ms 이후 부터는 onScroll 이벤트가 발생 할 수 있으며,

플링시키지 않고 살며시 손을 떼면 끝까지 onScroll 이벤트만 연속으로 발생 합니다.

 

 

10. 플링

000ms    onDown : ACTION_DOWN
030ms    onScroll : ACTION_DOWN, ACTION_MOVE
...
900ms    onFling : ACTION_DOWN, ACTION_UP

마지막에 손가락을 슬며시 튕기는 플링 동작입니다.

스크롤 이벤트와 비슷하지만, 마지막에 UP 액션과 함께 onFling 이벤트가 동작합니다.

 

스크롤과 플링 제스쳐 모두 시간에 따라 onShowPress 이벤트가 발생 할 수 있습니다.

 

 

[Outro]

 

Android에 기본으로(API Level 1) 들어 있는 GestureDetector에 대해서

조금은 자세하게 알아 봤습니다.

그냥 막연하게 이벤트 이름만 보고서 프로그래밍을 하기 보다는,

정확하게 어떻게 동작이 되는지 확인 하고 프로그래밍을 하면

좀 더 자신이 원하는 제스쳐를 캐치하여 좀 더 나이스한 어플리케이션을 만들 수 있을 것입니다.

 

엄청나게 강력하게 보이는 GestureDetector지만,

여기에도 한가지 단점이 있답니다.

 

다음에는 이 단점을 극복 할 수 있는 클래스를 만들어 보도록 하겠습니다.

'Android > Tip&Tech' 카테고리의 다른 글

c2dm simple example  (0) 2011.04.25
Push Notification on iOS & Android  (0) 2011.04.25
키패드 올려줄때 입력창 같이 올려주는 팁  (0) 2011.04.22
android 내장메모리에 쓰기  (0) 2011.04.22
마켓 등록 정보  (0) 2011.04.20


출처 : http://devbible.tistory.com/17
EditText 를 클릭 할 때 키패드는 자동으로 올라오게된다.
이때 키패드가 화면을 가려 입력시 불편을 줄 수있다.

*XML

1   <scrollview android:layout_width="fill_parent" android:layout_height="fill_parent">
2  
3 </scrollview>


//위에처럼 스크롤뷰로 감싸주면 키패드가 올라올때 스크롤이생겨서 사용자가 가려있는 뷰들을
//볼 수는 있지만..  사용자가 스크롤을 해주어야 해서 불편하다.

*Source
myEditText : 사용자가 입력하려는 EditText
myScrollView : 스크롤뷰
100 : 딜레이
0, 800 : 스크롤을 부드럽게 롤업하는 위치

01 myEditText.setOnFocusChangeListener(new OnFocusChangeListener(){
02  @Override
03  public void onFocusChange(View v, boolean hasFocus) {
04   if( hasFocus == true ){
05     
06    myScrollView.postDelayed( new Runnable(){
07  
08     @Override
09     public void run() {
10      myScrollView.smoothScrollBy(0800);
11     }
12      
13    }, 100);    
14  
15   }
16  }
17  });



// 위에처럼 EditText에 포커스가 갈때 스크롤이 되게 이벤트를 주면
// 사용자가 스크롤 하지않아도 시원하게 화면이 보이게된다

아래 방법은 SD카드 가 아닌 device 의 메모리에 이미지를 저장, 로드 , 삭제 하는 방식이다.

SD카드에 하려면 경로명을 정확히 넣어주고 메니페스트 파일에

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

이내용을 추가함을 잊지말자

 

 

1. Bitmap 저장

 

     imgview = (ImageView)findViewById(R.id.imageView1);       
     Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.comp1_16);     
     imgview.setImageBitmap(bm);     
     

try{
     
     File file = new File("test.png");          
     FileOutputStream fos = openFileOutput("test.png" , 0);          
     bm.compress(CompressFormat.PNG, 100 , fos);          
     fos.flush();     
     fos.close();
     
     Toast.makeText(this, "file ok", Toast.LENGTH_SHORT).show();     
     }catch(Exception e) { Toast.makeText(this, "file error", Toast.LENGTH_SHORT).show();}
     

 

2. 저장된 Bitmap 불러오기

 특이한것은 저장할때는 파일 이름만 있어도 알아서 app 의 data 폴더에 저장되지만 불러올때는

 전체 패스를 다 적어줘야한다.

try{    
    imgview = (ImageView)findViewById(R.id.imageView1);       
       String imgpath = "data/data/com.test.SDCard_Ani/files/test.png";
       Bitmap bm = BitmapFactory.decodeFile(imgpath);       
       imgview.setImageBitmap(bm);
       Toast.makeText(getApplicationContext(), "load ok", Toast.LENGTH_SHORT).show();
    }catch(Exception e){Toast.makeText(getApplicationContext(), "load error", Toast.LENGTH_SHORT).show();}

    

 

3. 저장된 Bitmap 파일 삭제하기

try{
    File file = new File("data/data/com.test.SDCard_Ani/files/");
    File[] flist = file.listFiles();
    Toast.makeText(getApplicationContext(), "imgcnt = " + flist.length, Toast.LENGTH_SHORT).show();
    for(int i = 0 ; i < flist.length ; i++)
    {
     String fname = flist[i].getName();
     if(fname.equals("test.png"))
     {
      flist[i].delete();
     }
    }
    }catch(Exception e){Toast.makeText(getApplicationContext(), "파일 삭제 실패 ", Toast.LENGTH_SHORT).show();}
    

구글마켓

http://market.android.com/publish/

개발자 등록비  25$ 필요

한국쪽은 유료결제 불가

해외쪽으로 가능 고로 해외 계좌 필요

수익분배 7:3

 

티스토어

http://dev.tstore.co.kr/

범용공인인증서 필요

수익분배 7:3

 

 

쇼스토어

http://appstoreseller.show.co.kr

서류 몇개 요청 개인일때는 신분증 , 통장사본 요구

수익분배 7:3

 

오즈스토어

개발중

수익분배  100%

 

 

 

애플앱스토어

개발자 등록비  년99$ 필요

수익분배 7:3

출처 : http://www.androidpub.com/748389
질답에 세번 질문을 하면서..동시에 구글링과 야후 검색을 며칠동안 한 결과..
Dialog 를 상속 받아서 customizing 해야 한다는 결론을 얻었습니다.
외국의 어느 커뮤니티에 누군가 남긴 상속 받아 재정의한 코드를 얻었구요.
가져올 때 링크를 기록하지 않았더니...다시 찾아보려고 노력해도...
찾을 수가 없네요. 

여기에 감사의 글과 함께..링크를 올리고 싶었지만...다시 못찾은 관계로...패스~ 
감사의 마음만...가득합니다.

우선.. 상속 받은 코드는 질답게시판에 댓글로도 올렸지만.
정리하는 차원에서 여기 다시 올립니다.

아래는 Dialog 를 상속 받은 클래스입니다.
01.class MyProgressDialog extends Dialog {
02. 
03. 
04.public static MyProgressDialog show(Context context, CharSequence title,
05.CharSequence message) {
06.return show(context, title, message, false);
07.}
08. 
09.public static MyProgressDialog show(Context context, CharSequence title,
10.CharSequence message, boolean indeterminate) {
11.return show(context, title, message, indeterminate, falsenull);
12.}
13. 
14.public static MyProgressDialog show(Context context, CharSequence title,
15.CharSequence message, boolean indeterminate, boolean cancelable) {
16.return show(context, title, message, indeterminate, cancelable, null);
17.}
18. 
19. 
20.public static MyProgressDialog show(Context context, CharSequence title,
21.CharSequence message, boolean indeterminate,
22.boolean cancelable, OnCancelListener cancelListener) {
23.MyProgressDialog dialog = new MyProgressDialog(context);
24.dialog.setTitle(title);
25.dialog.setCancelable(cancelable);
26.dialog.setOnCancelListener(cancelListener);
27./* The next line will add the ProgressBar to the dialog. */
28.dialog.addContentView(new ProgressBar(context), new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
29.dialog.show();
30. 
31.return dialog;
32.}
33. 
34.public MyProgressDialog(Context context) {
35.super(context, R.style.NewDialog);
36.}
37.}

위의 코드에서 참조하는 NewDialog 의 스타일은 다음과 같습니다. res/values/styles.xml 을 생성 시키고 
아래의 코드를 넣습니다.


01.<resources>
02.<style name="NewDialog">
03.<item name="android:windowFrame">@null</item>
04.<item name="android:windowBackground">@android:color/transparent</item>
05.<item name="android:windowIsFloating">true</item>
06.<item name="android:windowContentOverlay">@null</item>
07.<item name="android:windowTitleStyle">@null</item>
08.<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
09.<item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
10.<item name="android:backgroundDimEnabled">false</item>
11.<item name="android:background">@android:color/transparent</item>
12.</style>
13. 
14.</resources>

Dialog 를 이용하기 때문에.. 그리고.. WebView 에서 PageFinished 이벤트가 있기 때문에,
쓰레드를 사용할 필요가 없다는 것을 알게 되었습니다.
1.package pkg.WebViewTest;
01.import android.app.Activity;
02.import android.app.Dialog;
03.import android.content.Context;
04.import android.os.Bundle;
05.import android.os.Handler;
06.import android.os.Message;
07.import android.util.Log;
08.import android.view.MotionEvent;
09.import android.view.View;
10.import android.view.ViewGroup.LayoutParams;
11.import android.webkit.WebView;
12.import android.webkit.WebViewClient;
13.import android.widget.Button;
14.import android.widget.ProgressBar;
15.import android.widget.TextView;
1.public class WebViewTest extends Activity {
2./** Called when the activity is first created. */
3. 
4.public MyProgressDialog progressDialog;
1.@Override
2.public void onCreate(Bundle savedInstanceState) {
3.super.onCreate(savedInstanceState);
4. 
5.setContentView(R.layout.main);
6.wvc = (WebView)findViewById(R.id.WebView01);
7.wvc.getSettings().setJavaScriptEnabled(true);
01.wvc.loadUrl("http://m.naver.com");        
02.progressDialog = MyProgressDialog.show(this,"","",true,true,null);
03. 
04.wvc.setWebViewClient(new WebViewClient()
05.{
06.@Override
07.public void onPageFinished(WebView view, String url)
08.{
09.wvc.setVisibility(View.VISIBLE);
10.if (progressDialog!=null)
11.progressDialog.dismiss();
12.}
13.});
14.}
1.}

출처 : http://blog.naver.com/lowmans?Redirect=Log&logNo=100115258620

참고 : 
http://www.inter-fuser.com/2010/01/android-coverflow-widget.html
 
Android Gallery를 이뿌게~? 작업하기 위해서 구글 할부지께 물어본 결과 위에 링크한 CoverFlow를 알게 되었다
Gallery를 커스터 마이징한 소스인데 여간 잔망스러운게 아니여서.. 나름 깔끔하게 다듬어 본 소스를 공개한다.
 
불필요한 작업을 피하기 위해 바로 Gallery를 상속 받고
protected boolean getChildStaticTransformation(View child, Transformation t)
를 override 하여 작업하면 된다
 
getChildStaticTransformation 는 List에 연결된(gallery view 안에서 스크롤 할때 ) child가 어디에 위치 했는지 알고 싶을때 사용하는 method이다
 
metrix 는 child 의 bitmap 정보를  camera는  원근 효과를 주는 클래스들인데 이를  사용하여 gallery가 스크롤시 원근 효과를 주어 마치 스페이스 공간에서 움직이는 듯한 effect를 주었다 ..
 
view 를 click 할경우 animation 효과를 주어 나름 신경좀 써 봤지만.. 오히려 더 지저분한 느낌도 든다(순수한 나의 생각이지만  --;)
 
 
===========================================================================================================================
 
package lowmans.test;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Animation.AnimationListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
public class GalleryViewTest extends Activity implements AnimationListener{
 private MyGallery mGallery;
 Animation a;
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  mGallery = (MyGallery)findViewById(R.id.Gallery01);
  mGallery.setAdapter(new ImageAdapter(this));
  mGallery.setOnItemClickListener(new OnItemClickListener(){
   public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
     Animation animation = AnimationUtils.loadAnimation(GalleryViewTest.this, R.anim.anim);
     animation.setAnimationListener(GalleryViewTest.this);
     view.startAnimation(animation);
   }
  });
 }
 @Override
 public void onAnimationEnd(Animation animation) {
  Log.i("GalleryViewTest" , "onAnimationEnd");
 }
 @Override
 public void onAnimationRepeat(Animation animation) {}
 @Override
 public void onAnimationStart(Animation animation) {}
}
 
===========================================================================================================================
 
package lowmans.test;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;

public class MyGallery extends Gallery{
 private final static String TAG = "MyGallery";
 private Context mContext;
 private static Camera mCamera; 
 public MyGallery(Context context) {
   this(context, null); 
 }
 public MyGallery(Context context, AttributeSet attrs) {
   this(context, attrs, 0); 
 }
 public MyGallery(Context context, AttributeSet attrs, int defStyle) {
   super(context, attrs, defStyle);
   mContext = context;
   mCamera = new Camera();
   setSpacing(-30);  // child view 의 간격을 줄여 겹치는 듯한 효과를 준다
    }
  protected boolean getChildStaticTransformation(View child, Transformation t) {
  
  final int mCenter =(getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
  final int childCenter = child.getLeft() + child.getWidth() / 2;
  final int childWidth = child.getWidth();
  
  t.clear();
  t.setTransformationType(Transformation.TYPE_MATRIX);
  float rate = Math.abs((float)(mCenter - childCenter)/ childWidth);
  
  mCamera.save();
  final Matrix matrix = t.getMatrix();
  float zoomAmount = (float) (rate * 200.0);
  mCamera.translate(0.0f, 0.0f, zoomAmount);        
  mCamera.getMatrix(matrix);    
  matrix.preTranslate(-(childWidth/2), -(childWidth/2));   
  matrix.postTranslate((childWidth/2), (childWidth/2));
  mCamera.restore();
  return true;
    }

}
 
===========================================================================================================================
package lowmans.test;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.Gallery.LayoutParams;
 
public class ImageAdapter extends BaseAdapter {
    int mGalleryItemBackground;
    private Context mContext;
    private ImageView[] iv;
 
    private Integer[] mImageIds = {
         R.drawable.back_1 ,
         R.drawable.back_2 ,
         R.drawable.back_3 ,
         R.drawable.image ,
         R.drawable.back_1 ,
         R.drawable.back_2 ,
         R.drawable.back_3 ,
         R.drawable.image ,
        };
        
    private int cnt;
    public ImageAdapter(Context c) {
        mContext = c;
        cnt = mImageIds.length;
        iv = new ImageView[cnt];
       
       
        for(int i=0; i<cnt; i++){
          iv[i] = new ImageView(mContext);
          iv[i].setImageResource(mImageIds[i]);
          iv[i].setScaleType(ImageView.ScaleType.FIT_XY);
          iv[i].setLayoutParams(new Gallery.LayoutParams(200, 150));
        }
    }
    public int getCount() {
        return cnt;
    }
    public Object getItem(int position) {
        return position;
    }
    public long getItemId(int position) {
        return position;
    }
    public View getView(int position, View convertView, ViewGroup parent) {
         return iv[position];
    }
}
=========================================================================================================================== 
출처 : http://karukaru22.blog.me/140123913153

 style.xml

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NewDialog">
     <item name="android:windowFrame">@null</item>
     <item name="android:windowBackground">@android:color/transparent</item>
     <item name="android:windowIsFloating">true</item>
     <item name="android:windowContentOverlay">@null</item>
     <item name="android:windowTitleStyle">@null</item>
     <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
     <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
     <item name="android:backgroundDimEnabled">false</item>
     <item name="android:background">@android:color/transparent</item>
 </style>
 
<style name="Theme.CustomDialog" parent="android:style/Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
    </style> 
 
</resources>

 public class ProgressActivity extends Activity {
    Context mContext = this;
    Dialog dilog ;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      
       Circle c = new Circle();
        new Thread(c).start();
        dilog = new Dialog(this,R.style.NewDialog);
        //dilog =ProgressDialog.show(mContext, "", "잠시만 기달려주세요");
        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
        View v = inflater.inflate(R.layout.progress_circle, null);
        dilog.setContentView(v);
        dilog.show();
    }
   
   
    private class Circle implements Runnable {
     
  @Override
  public void run() {

  try {
   Thread.sleep(10000);
   handler.sendEmptyMessage(0);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   }
  }
     
    }
 
    private Handler handler = new Handler(){
     public void handleMessage(Message msg){
      if (msg.what == 0){
       dilog.dismiss();
       Toast.makeText(ProgressActivity.this, "완료~", 0).show();
      }
     }
    };
   
}

 

다이얼로그에 창을 투명하게 하고 거기다가 프레그시바를 붙인겁니다~

 

주의깊게 볼것은 다이얼로그에 스타일을 적용햇으며 스타일.xml에 보시면 transparent는 투명하게 해준다는 뜻입니다..

 

이걸 응용해서 액티비티 자체도 투명하게 할수 있습니다..

 


 

AndroidMainfest.xml에서

 

투명하게 원하는 액티비티에

 

  android:theme="@style/Theme.CustomDialog" 이렇게 주면된다.. 그리고 위에 style.xml도 마지막라인줄에 것을 적용시켜야함

 

 

출처 : http://blog.naver.com/lowmans/100121671992
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at


     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

    <item android:state_window_focused="false" android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_window_focused="false" android:state_enabled="false"
        android:drawable="@drawable/btn_default_normal_disable" />
    <item android:state_pressed="true" 
        android:drawable="@drawable/btn_default_pressed" />
    <item android:state_focused="true" android:state_enabled="true"
        android:drawable="@drawable/btn_default_selected" />
    <item android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_focused="true"
        android:drawable="@drawable/btn_default_normal_disable_focused" />
    <item
         android:drawable="@drawable/btn_default_normal_disable" />
</selector>


[펌] http://monodream77.blog.me/130088948287

안드로이드 플랫폼에서의 일관성 있는 아이콘 제작을 위해 
Android developers site에서  제공하는 가이드라인입니다. 
안드로이드 어플리케이션에 사용되는 아이콘을 디자인을 할 때 가이드라인에서 명시하는 기본적인 요소들을 고려해야 안드로이드 플랫폼 안에서 일관성 있는 어플리케이션의 디자인을 만들 수 있을 것 같습니다. 가이드라인에서는 그림자의 거리나 사이즈까지 디테일하게 수치를 명시하고 있지만 어플리케이션 디자인에 따라 디테일한 부분은 충분히 디자이너의 의도에 따라 달라져도 될 것 같습니다. 실제로 요즘 나오는 안드로이드 어플리케이션들을 보면 어플리케이션의 컨셉에 따라 안드로이드의 가이드라인을 따르지 않은 경우도 많이 있습니다. 하지만 제품의 독특한 컨셉이나 사용성의 목적상 필요한 부분이 아니라면 안드로이드의 가이드라인을 따르는 것이 안드로이드 폰 사용자에게 일관성 있는 느낌과 사용하기 쉬운 UI를 제공할 수 있을 것 같습니다. 어느 부분까지 가이드를 따르고 어느 부분에서 차별화 되게 디자인을 할 지는 디자이너들이 판단할 몫인 것 같습니다.

 

※ 원문에서 중요한 부분만 번역하였으며 필자의 의견은 다른 컬러로 표기되었습니다.

 

원문 출처 :  Icon Design Guidelines, Android 2.0

 

 

 

 

Icon Design Guidelines, Android 2.0

 

 

Providing Density-Specific Icon Sets

 

안드로이드는 여러가지 해상도를 가진 단말에서 동작하게 되어 있기 때문에 어플리케이션이 어느 단말에서건 돌아갈 수 있다는 것을 염두해 두어야 한다. 그래서 단말의 모든 스크린 사이즈(물리적 크기)나 해상도(가로/세로 픽셀수)에 적당한 아이콘을 제공해야 한다.

 

안드로이드를 지원하는  스크린의 밀도는 아래와 같이 Low density(ldpi), Medium density(mdpi), High density(hdpi)의 세 가지로 나뉘고 아이콘의 크기도 세 가지로 제작 되어야 합니다. 여기서 주목할 점은 VGA급의 스크린이 스크린의 사이즈(대각선 길이)에 따라 mdpi에 속할 수도 있고 hdpi에 속할 수도 있다는 것입니다.

같은 VGA급의 해상도라 하더라도 4.8"~5.8"에 적용되면 mdpi 크기의 중간 크기 아이콘을 적용하고, 3.3"~4.0"(갤럭시S WVGA, 4")에 적용되면 hdpi의 큰 아이콘을 적용해야 한다는 것이죠. 그렇게 해야 스크린에 나타나는 버튼이나 아이콘의 물리적 크기는 동일하게 보일 것입니다. 같은 mdpi사이즈의 아이콘(예: 48x48크기의 런쳐 아이콘)을 320x480, 3.0" 스크린에서 보이는 것과, 480x800, 4.8"스크린에서 보이는 것은 동일한 물리적인 크기(스크린에서 아이콘을 자로 재었을 때 동일한 크기) 가진다는 것입니다.

물리적인 아이콘의 크기는 해상도에 반비례하고 스크린 사이즈에 비례한다고 보시면 됩니다. 물리적인 아이콘의 크기가 중요한 이유는 가시성은 물론이고 충분한 터치영역을 확보하기 위해서 입니다.

아직 안드로이드를 탑재한 S-pad(480x800, 7"~10" 예상)와 같은 태블릿 PC에는 어느 기준을 적용해야 할지 명시가 되어있지는 않습니다.

 

안드로이드에서 제공하는 해상도에 따른 정책은 Supporting Multiple Screens부분을 참고하시기 바랍니다.

Low density (120), ldpi

Medium density (160), mdpi

High density (240), hdpi

Small screen

  • QVGA (240x320), 2.6"-3.0" diagonal

Normal screen

  • WQVGA (240x400), 3.2"-3.5" diagonal

  • FWQVGA (240x432), 3.5"-3.8" diagonal

  • HVGA (320x480), 3.0"-3.5" diagonal

  • WVGA (480x800), 3.3"-4.0" diagonal

  • FWVGA (480x854), 3.5"-4.0" diagonal

Large screen

  • WVGA (480x800), 4.8"-5.5" diagonal

  • FWVGA (480x854), 5.0"-5.8" diagonal

   Examples of device screens supported by Android. 

 

 

 

안드로이드의 기본 해상도는 Medium density(mdpi)이며 아래의 기준에 따라 아이콘이 제작되어야 한다.

 

1. 먼저 기본 density(mdpi)에 따라 아이콘을 디자인

 

2. 기본 drawable resources폴더에 아이콘을 위치하고 안드로이드 가상머신이나 T-Mobile G1과 같은 HVGA단말에서 테스트

 

3. 테스트를 하며 기본 density아이콘 수정

 

4. 테스트 및 수정/보완이 완료되면 다른 사이즈의 아이콘 제작

  - high density 아이콘은 150%로 확대

  - low density 아이콘은 75%로 축소

 

 5. 아이콘을 각각의 density에 맞는 폴더에 저장

  - Medium density 아이콘은 res/drawable-mdpi/ 폴더 또는 res/drawable/ 폴더에 저장

  - High density 아이콘은 res/drawable-hdpi 폴더

  - Low density 아이콘은 res/drawable-ldpi 폴더

 6. high/low density 단말에서 아이콘이 잘 보이는지 각각 테스트

 

mdpi 한 가지의 아이콘만 있어도 ldpi, hdpi의 아이콘을 코드상에서 늘이고 줄여서 적용되게 하는 방법도 있다고 합니다. 하지만 강제 확대/축소가 되기 때문에 이미지가 조금 거칠어 보이고 각각의 아이콘이 제공되는 것보다 연산이 들어간다고 하네요. 주의할 점은 두 가지 방법의 코딩 방식이 다르기 때문에 개발 초기에 세 가지 사이즈 아이콘을 모두 제공할지, mdpi한가지만 제공하고 나머지는 확대/축소할 지 개발자와 협의를 하고 진행을 해야 한다는 것입니다. 이상적인 방법은 hdpi의 가장 큰 아이콘을 제작하고, mdpi, ldpi의 아이콘을 축소하여 제작하는 것입니다. 비트맵으로 제작되었을 경우 큰 아이콘을 작은 아이콘으로 축소하는 것이 덜 깨지고 다듬기도 더 용이하기 때문입니다.

 

 

Table 1. 아이콘 종류에 따른 세가지 density에서의 아이콘 크기.

Icon Type

Standard Asset Sizes (in Pixels), for Generalized Screen Densities

Low density screen (ldpi)

Medium density screen (mdpi)

High density screen (hdpi)

Launcher

36 x 36 px

48 x 48 px

72 x 72 px

Menu

36 x 36 px

48 x 48 px

72 x 72 px

Status Bar

24 x 24 px

32 x 32 px

48 x 48 px

Tab

24 x 24 px

32 x 32 px

48 x 48 px

Dialog

24 x 24 px

32 x 32 px

48 x 48 px

List View

24 x 24 px

32 x 32 px

48 x 48 px


 

 

 

Launcher Icon

 

런쳐 아이콘은 단말의 홈스크린이나 런쳐 윈도우에서 어플리케이션을 실행시키는 아이콘을 말합니다.

 

 

Style

런쳐 아이콘은 아래와 같은 스타일을 따라야 한다. 이 가이드라인은 아이콘 디자인을 제한한다기 보다는 다른 아이콘과의 통일성을 위한 것이다. 

 

Clean and contemporary - 깔끔하고, 현대적으로

일반적으로 통용되는 컨셉으로 할 수도 있고 독특하게 할 수도 있다. 그러나 구식이거나 낡아보여서는 안된다.

 

Simple and iconic - 단순하고 상징적으로

- 자연스러운 캐리커쳐 스타일; 형태는 단순하지만 특징을 부각시켜서 작은 크기에서도 잘 보여야 한다. 복잡해서는 안됨

- 어플리케이션의 일부를 표현함으로서 전체를 상징하도록 (예를들면 뮤직 아이콘은 스피커로 표현하고 있음.)

- 자연스러운 기하학적이고 유기적인 선과 형태를 이용여 현실적으로 표현(사진처럼 현실적이여서는 안됨.)

- 큰 이미지의 잘린 일부분으로 표현해서는 안됨.

 

Tactile and textured - 촉감이 느껴지는 질감

광택이 나지 않는, 질감을 살린 소재로 표현.

 

Forward-facing and top-lit - 정면뷰와 상단 라이팅

안드로이드2.0 이상에서는 약간의 투시가 있는 정면 뷰와 상단 라이팅을 사용해야 함

 

 

 

 

 

Figure 1. 런쳐 아이콘 스타일

 

 

 

 

Do's and Don'ts

 

- 안드로이드 아이콘은 Modern, minimal, matte, tactile, and textured 해야 함.

- 정면 뷰와 상단 라이팅을 사용해야 함

- 앤틱(Antique)하거나 복잡하거나 광택이 나거나 단순도형(flat vector)이면 안된다.

- 회전이 되었거나, 잘렸거나, 채도가 과하게 높으면 안된다.

 

 

 

Figure 2. 런쳐 아이콘의 잘된 예와 잘못된 예

 

 

 

Materials and colors - 질감과 색

 

런쳐 아이콘은 촉감이 느껴지고 상단 라이팅을 사용하고 질감이 느껴지는 소재를 이용하여 만들어야 한다. 단순한 형태의 아이콘이라도, 실제 존재하는 소재로부터 만들어진 것처럼 보이도록 해야 한다. 안드로이드 플랫폼의 기본 아이콘은 Figure 3의 소재들을 사용하고 있다. 아이콘을 만들 때 이 소재를 사용해도 되고, 직접 만들어도 된다.

 

안드로이드 런처 아이콘은 보통 큰 배경 형태 위에 작은 형태가 올라와있는 형태와 강조색과 중간색이 결합된 컬러로 구성된다. 아이콘은 중간색의 결합된 형태를 사용할 수도 있지만, 강한 대비는 유지되어야 한다. 가능하면 아이콘에서 강조색은 한가지만 사용되어야 한다.

 

런쳐아이콘은 중간색과 강조색의 범위를 포함하는 제한된 컬러팔레트를 사용해야 한다. 아이콘은 채도가 과하게 표현되어서는 안된다.

 

런쳐 아이콘에서 추천하는 컬러 팔레트는 figure 4에 있는 것들이다. 기본색과 강조색의 팔레트를 모두 사용할 수 있다. 컬러 팔레트에  white-black의 수직 그라데이션을 오버레이시켜 사용할 수도 있다.

 

 

 

 

Figure 3. 아이콘을 만드는데 사용할 수 있는 소재들

 

 

 

 

 

Figure 4. 추천하는 기본색과 강조색으로 이루어진 소재들

 

 

 

 

추천하는 팔레트의 강조색을 사용하여 위와 같은 소재를 만들 때, Figure 5에 있는 컬러를 조합하여 만들 수 있다. icons pack에 기본 소재, 컬러, 그라데이션을 제공하는 포토샵 템플릿 파일(Launcher-icon-template.psd)이 들어 있다.

 

 

 

 

 

 

Figure 5. 아이콘을 위한 추천 컬러 팔레트

 

 

 

 

 

Size and positioning - 크기와 위치

 

런쳐 아이콘은 다양한 모양과 형태로 제작되지만, 크기와 위치를 조정하여 일정한 크기로 보여야 한다.

Figure 6 은 아이콘 파일 안에 아이콘을 위치하는 여러가지 방법을 보여준다. 아래에 명시된 것 처럼, 일정한 크기로 보이고 그림자들 포함하기 위해 아이콘 파일의 실제 테두리보다 아이콘은 작게 들어가야 한다. 아이콘이 사각형이거나 사각형에 가깝다면, 아이콘은 더 작게 조절되어야 한다.

 

 

- 아이콘 파일 전체의 테두리는 붉은색으로 표시

- 실제 아이콘이 들어갈 공간은 파란색으로 표시. 아이콘이 들어갈 공간은 그림자나 다른 효과를 포함시키기 위해 실제 아이콘 파일의 크기보다 작게 되어있다.

- 사각 형태의 아이콘이 들어갈 공간은 오랜지컬러로 표시. 사각형태의 아이콘이 들어갈 공간은 다른 아이콘들과 크기가 비슷하게 보이기 위해 다른 아이콘들의 공간보다 작게 되어 있다.

 

 

 

                                                                                     Figure 6. 아이콘 파일 안에서의 아이콘 크기와 위치

 

 

 

 

Using the Launcher Icon Template - 런쳐 아이콘 템플릿 사용

 

 

Android Icon Templates Pack 2.0에는 기본 아이콘의 소재와 컬러가 들어있다. 템플릿은 포토샵이나 다른 이미지 에디터에서 사용할 수 있도록 .psd파일로 제공된다. Launcher-icon-template.psd에 있는 질감과 컬러를 런쳐 아이콘을 만드는데 사용할 수 있다.

 

아이콘을 만든 다음에는, 아래에 아이콘 크기별로 명시된 대로 그림자를 만들 수 있다.

 

 

 

 

아이콘은 투명도가 있는 PNG파일로 저장하라. hdpi 스크린에서는 72x72px, mdpi 스크린에서는 48x48px의 크기로 저장되어야 한다

출처 : http://spitfire.tistory.com/78
안드로이드 기본 SDK에서 제공하는 widget... MSDN수준의 문서화를 기대하는 건 아니지만 아무래도 API설명이 좀 부실하다 보니 있는 기능을 몰라서 안 쓰거나 없는 기능인데 찾느라 한참 헤메는 경우가 있는 것 같더군요.

뭐, 기본제공하는 ImageView에 없는 기능인 Touch Scroll/ Pinch to zoom 되게 하느라 좀 헤메서 결과물은 아무도 안 오는 블로그에라도 좀 올려놓으면 편할 듯 하네요.

아무런 설명 없이 달랑 클래스만 올려놓기는 그러니 조금 설명하자면...

일단 android.widget.ImageView 클래스를 상속받아 만든 클래스입니다. 당연히 ImageView의 속성들은 다 가지고 있고, 터치 이벤트 처리를 위해서 onTouchEvent 메서드가 오버라이드 되어 있습니다.

(사실 안드로이드 UI에서 터치이벤트를 가장 처음 받는 것은 그 view이기 때문에, view내에서 자체적으로 이벤트를 처리한다면 굳이 onTouchListener를 통해서 이벤트를 넘겨줄 필요가 없습니다)

대부분의 코드는

http://flysky.thoth.kr/?document_srl=4547416

이 주소에서 가져왔고, 디버그용 dumpEvent 메서드, onTouch 대신 onTouchEvent사용, 이미지 매트릭스 초기화 메서드 약간 수정(개선인지 개악인지는 모르겠지만...) 정도가 변경사항입니다.

사용법은 그냥 안드로이드 이미지 뷰 쓰듯이 쓰면 됩니다. 

사실 지금 쓰고 있는 건 안드로이드 프로젝트의 갤러리 부분 소스코드를 가져와서 약간 개량한 걸 사용중인데, 그것도 독자적으로 쓸만한 수준이 되면 공개해 보려고 합니다.아무래도 motionevent같은 걸 쓰는 게 좀 더 편할 듯 해서 말이지요. 2.2에서는 멀티터치 이벤트 처리도 gesturedetector를 쓸 수 있으니 지금 구현하기 좀 귀찮은 멀티터치 이벤트들도 쉽게 처리할 수 있겠지요.
출처 : http://taehoonkoo.tistory.com/143

오늘 제가 해볼 것은 Preference Category를 Customizing 해보는것입니다. 
우선 결과 부터 보여드리면, 


이런식으로 Category Title을 변경해보는것이죠.

위와 같은 Layout의 Preference XML은 아래와 같습니다. 

<?xml version="1.0" encoding="utf-8"?>


<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"

        android:title="@string/app_name">

    <PreferenceCategory

     android:title="@string/category_name">

    <Preference android:key="app_name"

        android:persistent="false"

        android:title="@string/app_name"

    />

    </PreferenceCategory>  

</PreferenceScreen>


PreferenceScreen에 PreferenceCategory를 추가하고, 
PreferenceCategory에 Preference Item이 하나 있는 형태 이지요, 

여기서, PreferenceCategory의 Layout을 Customizing하는게 목적입니다.

Preference Item의 Layout을 변경할때 사용하는것은,  android:layout 속성을 정의 하는것입니다. 


android:layout="@layout/my_preference_category" 뭐 이런식으로 추가 해주는것지요 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

  xmlns:android="http://schemas.android.com/apk/res/android"

  android:orientation="horizontal"

  android:layout_width="fill_parent"

  android:layout_height="25dip"

  android:gravity="center_vertical"

  android:paddingLeft="5dip"

  android:background="#323331">

  

  <ImageView 

  android:layout_width="wrap_content"

  android:layout_height="fill_parent"

  android:src="@android:drawable/ic_menu_info_details"/>

  <TextView

  android:textStyle="bold"

  android:id="@android:id/title"

  android:layout_width="wrap_content"

  android:layout_height="fill_parent" 

  android:text="test"

  android:gravity="center"

  android:textColor="#FF0000"

  />

  

</LinearLayout>


여기서 부터, 안드로이드 개발의 짜증나는 점이 나타나는데, 
PreferenceCategory에서 지정한 title 값과, 내가 만든 Layout의 TextView가 연결되어야 하는데, 
id값을 어떻게 지정해주어야 한다는 정보가, developer.android.com을 아무리 뒤져 봐도 없습니다.

결국 Android Source코드를 보고 나서야 
@android:id/title로 줘야 한다는걸 알게 되었고,  이렇게 해주고 나니 정상적으로  동작합니다.

android:layout은 PreferenceCategory의 XML Attribute가 아니라, Preference 아이템 클래스들의 최상위 클래스인 Preference Class의 
XML Attribute입니다. 

즉, PreferenceCategory뿐 아니라, CheckBoxPreference 등등 모든 Preference들을 이와 같은 원리로 Customizing할 수 있습니다. 

출처 : http://blog.naver.com/man8408?Redirect=Log&logNo=110104491800

안드로이드의 스크린 가로, 세로 모드를 고정시키는 방법은 2가지입니다.

 

1. source에서 수정

- Activity를 상속받은 클래스에서 onCreate() 안에 super.onCreate()전에 다음 함수를 실행시켜주면 간단하게 고정이 됩니다.

 setRequestedOrientation(Activity.SCREEN_ORIENTATION_LANDSCAPE);


2. AndroidManifest.xml에서 수정

- 가로 : landscape, 세로 : portrait 둘중 하나로 설정하면 된다.

<activity android:screenOrientation="landscape"

android:configChanges="keyboardHidden|orientation" />

 

3. 결과화면

   

출처 : http://mylovely1004.tistory.com/11

+ Recent posts