Search Results for '전체 분류'


2064 posts related to '전체 분류'

  1. 2011/12/06 App 위젯을 만들기
  2. 2011/12/06 부팅시 자동으로 내 서비스 올리기
  3. 2011/12/06 안드로이드 연락처 목록 가져오기/삭제하기 방법
  4. 2011/12/06 안드로이드에서 구글 계정 가져오는 방법
  5. 2011/12/06 안드로이드에서 SMS 목록 가져오기/삭제하기 방법 1
  6. 2011/12/06 안드로이드 SharedPreferences 기능 이용하기
  7. 2011/12/06 canvas 내용 파일저장하기
  8. 2011/12/06 Intent에서 앱 호출하는 방법 정리
  9. 2011/12/06 apk 파일 업데이트
  10. 2011/12/06 /system/app 권한 풀기 방법
  11. 2011/12/06 Intent 활용 예제 소스
  12. 2011/12/06 android pdf 또는 hwp 소스 실행하기
  13. 2011/12/06 삼성,LG 오픈소스
  14. 2011/12/06 Android Cookkie 와 Session 관련
  15. 2011/12/06 화면 계속 켜지게
  16. 2011/12/06 progress bar 모양 바꾸기
  17. 2011/12/06 VideoView - raw에 저장된 mp4 play하기 uri생성방법
  18. 2011/12/06 안드로이드 바코드 스캔 라이브러리( barcord scan library for android, ZXing)
  19. 2011/12/06 메모리 누수 관련 내용 모음 글
  20. 2011/12/06 안드로이드 색상코드 형식
  21. 2011/12/06 Intent 활용 예제
  22. 2011/12/06 파일 위치에 따른 미디어 재생 샘플 코드
  23. 2011/12/06 주소록의 전화번호 가져오기
  24. 2011/12/06 [UI 설계] Widget, View, Layout 개론
  25. 2011/12/06 ViewPage 예제
  26. 2011/12/06 android TabActivity 의 이해
  27. 2011/12/06 안드로이드 apk decompile 하기 완결판 - APK 디컴파일 하기 JAVA
  28. 2011/12/06 원격서버의 Virtualbox 의 웹관리툴 Phpvirtualbox 관련 삽질
  29. 2011/12/05 SharePoint 2010 설치
  30. 2011/11/23 ABCUpload Component 4.1 1

일단 위젯을 만들기 전에 필요한 파일들이 무엇인지 알아보겠습니다.

* ExrateWidgetProvider.java - 위젯 메인 파일
* UpdateService.java - 주기적인 실행을 위한 서비스 파일
* exratewidget_layout.xml - 위젯 레이아웃 파일
* exrate_widget.xml - 위젯 설정 파일

* 그럼 각각의 파일내부를 들쳐 보도록 하겠습니다. 간단하게 xml 파일부터 설명드립니다.

> AndroidManifest.xml
<!-- 서비스 파일 등록 -->
<service android:name=".UpdateService" android:label="UpdateService">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
< /service>

< !-- 리시버 등록 -->
<receiver android:name=".ExrateWidgetProvider" android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/exrate_widget" />
< /receiver>

> exrate_widget.xml - 위젯 설정 파일
<?xml version="1.0" encoding="utf-8"?>
< appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:updatePeriodMillis="1800000"
android:initialLayout="@layout/exratewidget_layout"
/>

> exratewidget_layout.xml - 위젯 레이아웃 파일
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" android:background="@drawable/w_back01"
android:padding="5px" android:gravity="center" android:id="@+id/ID_LAYOUT">

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:id="@+id/ID_LAYOUT1">

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:gravity="center"
android:layout_marginTop="5px" android:layout_marginLeft="0px">

<ImageView android:id="@+id/ID_IMG1" android:src="@drawable/n_kr"
android:adjustViewBounds="true" android:layout_width="16dip"
android:layout_height="10dip" android:visibility="visible"
android:gravity="center_vertical|center_horizontal"
android:layout_marginBottom="5px" android:layout_marginLeft="5px"
android:layout_marginTop="5px" />

<TextView android:id="@+id/ID_SIGN1" android:text="USD"
android:textSize="5pt" android:textStyle="bold"
android:layout_height="wrap_content" android:gravity="center_vertical|center_horizontal"
android:visibility="visible" android:layout_width="25dip" />

<TextView android:id="@+id/ID_BUY1" android:layout_width="47dip"
android:layout_height="wrap_content" android:visibility="visible"
android:text="0000.00" android:gravity="center_vertical|center_horizontal"
android:textSize="5pt" />

<TextView android:id="@+id/ID_SELL1" android:layout_width="47dip"
android:layout_height="wrap_content" android:visibility="visible"
android:text="0000.00" android:gravity="center_vertical|center_horizontal"
android:textSize="5pt" />

</LinearLayout>

</LinearLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="horizontal" android:id="@+id/ID_LAYOUT2"
android:layout_marginTop="0px" android:layout_marginRight="10px">

<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:visibility="visible"
android:text="ANDROES" android:gravity="center_vertical"
android:layout_marginLeft="20px" android:textSize="5pt"
android:textStyle="bold" />

<TextView android:layout_width="fill_parent" android:text="00-00 00:00"
android:layout_height="wrap_content" android:textSize="5pt"
android:gravity="right" android:id="@+id/ID_DATE" android:textStyle="bold" />

</LinearLayout>
< /LinearLayout>

> UpdateService.java - 주기적인 실행을 위한 서비스 파일
package com.androes.exrate;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import android.app.AlarmManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;

public class UpdateService extends Service {
private static int[] sAppWidgetIds;
private static final String TAG = "Androes UpdateService";

private DateFormat format = SimpleDateFormat.getTimeInstance(
SimpleDateFormat.MEDIUM, Locale.getDefault());
private static Context context;

public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);

SimpleDateFormat formatStr = new SimpleDateFormat("HHmm", Locale.KOREA);
Integer onlyHour = Integer.parseInt(formatStr.format(new Date()));
Logger.i(TAG, "Androes Onstart: " + onlyHour + " ... " + format.format(new Date()));
ExrateWidgetProvider.xmlViewParser(context);
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

public static void regAppWidgetIds(Context _context, int[] widgetIds) {
context = _context;
sAppWidgetIds = widgetIds;
}
}

* ExrateWidgetProvider.java - 위젯 메인 파일
package com.androes.exrate;

import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import com.androes.exrate.Main.XmlData;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
import android.widget.Toast;

public class ExrateWidgetProvider extends AppWidgetProvider {
static final String TAG = "ANDROES";
private static AppWidgetManager appWidgetManager;
Intent intent;
PendingIntent pendingIntent;

public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to
// crash).
String action = intent.getAction();
if (action.equals("CLICK_RELOAD")) {
Logger.d("Androes", "ACTION_CLICK_RELOAD");
Toast.makeText(context, "Data Collecting", Toast.LENGTH_SHORT).show();
xmlViewParser(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Logger.d("Androes", "ACTION_APPWIDGET_UPDATE");
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras
.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager
.getInstance(context), appWidgetIds);
}
}
} else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Logger.d("Androes", "ACTION_APPWIDGET_DELETED");

final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
/*
* Bundle extras = intent.getExtras(); if (extras != null &&
* extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) { final
* int appWidgetId = extras
* .getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
* this.onDeleted(context, new int[] { appWidgetId }); }
*/
} else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
Logger.d(TAG, "ACTION_APPWIDGET_ENABLED");

this.onEnabled(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
Logger.d(TAG, "ACTION_APPWIDGET_DISABLED");

this.onDisabled(context);
} else {
super.onReceive(context, intent);
}
}

// @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);

Logger.d(TAG, "onUpdate");
if (appWidgetIds == null) {
appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(
context, ExrateWidgetProvider.class));
}

if (appWidgetIds == null) {
Logger.d(TAG, "onUpdate appwidgetIds is null");
return;
}
this.appWidgetManager = appWidgetManager;
UpdateService.regAppWidgetIds(context, appWidgetIds);

// updatePeriodMillis 옵션 미사용시 아래 스크립트 이용
// StartUpdate(context, appWidgetIds);

// updatePeriodMillis 옵션 사용시
xmlViewParser(context);
}

/*
* public static void buildUpdate(Context context, String time_str, int
* appWidgetId) { Logger.e(TAG,time_str); RemoteViews views= new
* RemoteViews(context.getPackageName(), R.layout.time_layout);
* views.setTextViewText(R.id.now_time, (CharSequence)time_str);
* appWidgetManager.updateAppWidget(appWidgetId, views); }
*/
public static void StartUpdate(Context context, int[] appWidgetsIds) {
Intent updateIntent = new Intent(context, UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
updateIntent, 0);
if (pendingIntent == null)
Logger.e(TAG, "pendingIntent is null");
else
Logger.e(TAG, "pendingIntent is not null");

Calendar cal = Calendar.getInstance();
// Schedule alarm, and force the device awake for this update
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

int delay = 10000; // 10초: 10000 / 30분: 1800000
alarmManager.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(), delay, pendingIntent);
}

public static void xmlViewParser(Context context) {
RemoteViews remoteViews;
ComponentName watchWidget;

// TODO Auto-generated method stub
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.exratewidget_layout);
....

Intent intent = new Intent(context, ExrateWidgetProvider.class);
intent.setAction("CLICK_RELOAD");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,intent, 0);
remoteViews.setOnClickPendingIntent(R.id.ID_LAYOUT, pendingIntent);

watchWidget = new ComponentName(context, ExrateWidgetProvider.class);
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
}


updatePeriodMillis 옵션 사용하지 않고 수동으로 업데이트를 이용하고자 할 경우에는

AndroidManifest.xml내 service 항목과 ExrateWidgetProvider.java내 StartUpdate(), UpdateService.java 는 필요없습니다.
2011/12/06 11:35 2011/12/06 11:35

일단 퍼미션을 지정합니다.

<!-- Boot Config -->
<receiver android:name=".BootBroadcast" android:enabled="true"
android:exported="false"
android:label="androesServiceManager"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>

BooBroadcast.java
public class BootBroadcast extends BroadcastReceiver {
static final String TAG = "ANDROES";
private final String BOOT_ACTION = "android.intent.action.BOOT_COMPLETED";

@Override
public void onReceive(Context context, Intent intent) {
//리시버가 발동시 나의 서비스 실행
Logger.d(TAG, "BOOT_ACTION : onReceive");
if (intent.getAction().equals(BOOT_ACTION)) {

// boot 시 shared값을 못불러오네~음
final SharedPreferences prefs = context.getSharedPreferences("androesPrefName", 0);
String status = prefs.getString("status", "");

Logger.d(TAG, "BOOT_ACTION : " + status);
if (status.equals("start")) {
// Logger.d(TAG, "BOOT_ACTION : cylost service ON");
context.startService(new Intent(context, androesService.class));
} else {
// Logger.d(TAG, "BOOT_ACTION : cylost service OFF");
}
}
}
}

2011/12/06 11:35 2011/12/06 11:35

방법은 Call Log 가져오는 방법과 유사합니다.

AndroidManifest.xml 에 퍼미션 추가

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

PhoneBook.java
private Cursor getURI()
{
// 주소록 URI
Uri people = Contacts.CONTENT_URI;

// 검색할 컬럼 정하기
String[] projection = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.HAS_PHONE_NUMBER };

// 쿼리 날려서 커서 얻기
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";

// managedquery 는 activity 메소드이므로 아래와 같이 처리함
return getContentResolver().query(people, projection, null, selectionArgs, sortOrder);
// return managedQuery(people, projection, null, selectionArgs, sortOrder);
}

public void phoneBook() {
try {
Cursor cursor = getURI(); // 전화번호부 가져오기

int end = cursor.getCount(); // 전화번호부의 갯수 세기
Logger.d("ANDROES", "end = "+end);

String [] name = new String[end]; // 전화번호부의 이름을 저장할 배열 선언
String [] phone = new String[end]; // 전화번호부의 이름을 저장할 배열 선언
int count = 0;

if(cursor.moveToFirst())
{
// 컬럼명으로 컬럼 인덱스 찾기
int idIndex = cursor.getColumnIndex("_id");

do
{
tempItem = new TempItem();

// 요소값 얻기
int id = cursor.getInt(idIndex);
String phoneChk = cursor.getString(2);
if (phoneChk.equals("1")) {
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = " + id, null, null);

while (phones.moveToNext()) {
phone[count] = phones
.getString(phones
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
}
name[count] = cursor.getString(1);

// 개별 연락처 삭제
// rowNum = getBaseContext().getContentResolver().delete(RawContacts.CONTENT_URI, RawContacts._ID+ " =" + id,null);

// LogCat에 로그 남기기
// Logger.i("ANDROES", "id=" + id +", name["+count+"]=" + name[count]+", phone["+count+"]=" + phone[count]);
count++;

} while(cursor.moveToNext() || count > end);
}
} catch (IOException e) {
e.printStackTrace();
}
}

2011/12/06 11:34 2011/12/06 11:34

AndroidManifest.xml

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

ReadGooleAccount.java
AccountManager mgr = AccountManager.get(this);
Account[] accts = mgr.getAccounts();
final int count = accts.length;
Account acct = null;

for(int i=0;i<count;i++) {
acct = accts[i];
Logger.d("ANDROES", "Account - name="+acct.name+", type="+acct.type);
}

관련사이트)
http://blog.naver.com/cwj3688?Redirect=Log&logNo=20121677208
http://blog.naver.com/cwj3688?Redirect=Log&logNo=20123259451
2011/12/06 11:34 2011/12/06 11:34

AndroidManifest.xml 에 퍼미션 추가

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

SMSList.java
public static final String MESSAGE_TYPE_INBOX = "1";
public static final String MESSAGE_TYPE_SENT = "2";
public static final String MESSAGE_TYPE_CONVERSATIONS = "3";
public static final String MESSAGE_TYPE_NEW = "new";

public void SMSList() {
try {
// Retrieve All SMS
/*
Inbox = "content://sms/inbox"
Failed = "content://sms/failed"
Queued = "content://sms/queued"
Sent = "content://sms/sent"
Draft = "content://sms/draft"
Outbox = "content://sms/outbox"
Undelivered = "content://sms/undelivered"
All = "content://sms/all"
Conversations = "content://sms/conversations"

addressCol= mCurSms.getColumnIndex("address");
personCol= mCurSms.getColumnIndex("person");
dateCol = mCurSms.getColumnIndex("date");
protocolCol= mCurSms.getColumnIndex("protocol");
readCol = mCurSms.getColumnIndex("read");
statusCol = mCurSms.getColumnIndex("status");
typeCol = mCurSms.getColumnIndex("type");
subjectCol = mCurSms.getColumnIndex("subject");
bodyCol = mCurSms.getColumnIndex("body");
*/
Uri allMessage = Uri.parse("content://sms/");
cur = this.getContentResolver().query(allMessage, null, null, null, null);
count = cur.getCount();
Logger.i( TAG , "SMS count = " + count);
String row = "";
String msg = "";
String date = "";
String protocol = "";
while (cur.moveToNext()) {
row = cur.getString(cur.getColumnIndex("address"));
msg = cur.getString(cur.getColumnIndex("body"));
date = cur.getString(cur.getColumnIndex("date"));
protocol = cur.getString(cur.getColumnIndex("protocol"));
// Logger.d( TAG , "SMS PROTOCOL = " + protocol);

String type = "";
if (protocol == MESSAGE_TYPE_SENT) type = "sent";
else if (protocol == MESSAGE_TYPE_INBOX) type = "receive";
else if (protocol == MESSAGE_TYPE_CONVERSATIONS) type = "conversations";
else if (protocol == null) type = "send";

Logger.i( TAG , "SMS Phone: " + row + " / Mesg: " + msg + " / Type: " + type + " / Date: " + date);
}
} catch (IOException e) {
e.printStackTrace();
}
}

public void SMSDelete() {
Uri deleteUri = Uri.parse("content://sms");
int count = 0;
Cursor c = this.getContentResolver().query(deleteUri, null, null,
null, null);
while (c.moveToNext()) {
try {
// Delete the SMS
String pid = c.getString(0);
// Get id;
String uri = "content://sms/" + pid;
// count = this.getContentResolver().delete(Uri.parse(uri),null, null);
} catch (Exception e) {
}
}
}

참고사이트)
http://blog.naver.com/algorithmlab?Redirect=Log&logNo=70098254191
http://mobdev.olin.edu/mobdevwiki/FrontPage/Tutorials/SMS%20Messaging#Catch_the_new_SMS_message
http://stackoverflow.com/questions/2584058/android-querying-the-sms-contentprovider
http://youropensource.com/projects/559-get-All-SMS-messages-in-the-Android
http://stackoverflow.com/questions/2183680/delete-sms-in-android-1-5
 

2011/12/06 11:34 2011/12/06 11:34

SharedPreferences : 화면의 내용이 초기화 되지 않고 유지되는 기능이다.

일반적으로 설정 기능을 구현하고자 할 때 주로 사용되는 것으로 유용하게 쓰일것으로 생각되어 포스팅합니다.

* SampleSharedPreferences.java

package com.androes.exrate;
...
public class SampleSharedPreferences extends Activity {
private SharedPreferences pref = null;
private EditText nameEditText = null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// 입력된 정보를 저장하기 위해 Class & Method 호출
pref = getSharedPreferences("com.androes.whitepaper",Activity.MODE_PRIVATE);

nameEditText = (EditText) findViewById(R.id.nameEditText);

// 값 호출
nameEditText.setText(pref.getString("nation", ""));
}
...
@Override
protected void onStop() {
super.onStop();
// 저장을 위해 Edit객체 호출
SharedPreferences.Editor editor = pref.edit();
// 값 변경
editor.putString("nation", nameEditText.getText().toString());
// 변경된 값 저장
editor.commit();
}
}

SharedPreferences 는 임시변수나 작은 크기의 설정 및 환경변수 정보를 저장하기 적합하지만,

다양하고 용량이 큰 데이터 저장시에는 파일 및 sqlite를 사용하는것을 권장합니다.

* 데이터 저장위치(DDMS 나 adb 툴을 이용)
/data/data/패키지명/shared_prefs/패키지명.xml

ex. /data/data/com.androes.exrate/shared_prefs/com.androes.exrate.xml

# cat com.androes.exrate.xml
< ?xml version='1.0' encoding='utf-8' standalone='yes' ?>
< map>
< string name="nation">KOR</string>
< /map>

단, 위 파일은 루팅하셔야만 확인할 수 있다는 점 잊지마세요^^
저도 루팅 안하고 이거 찾느라 바보같이 쌩쇼를 했네요..ㅠㅠ

그럼 님들 저처럼 삽질 마시고 유용하게 잘 사용하시길...^^

p.s) Summary
// WRITE
SharedPreferences prefs = getSharedPreferences("CyLostPrefName", MODE_PRIVATE);
Editor ed = prefs.edit();
ed.putString("gmailID", "snazzy7979");
ed.putString("gmailPWD", "pwd12345");
ed.commit();

// REMOVE
SharedPreferences prefs = getSharedPreferences("androesPrefName", MODE_PRIVATE);
Editor ed = prefs.edit();
ed.remove("gmailID");
ed.remove("gmailPWD");
// ed.clear();
ed.commit();

// READ
SharedPreferences prefs = getSharedPreferences("androesPrefName", MODE_PRIVATE);
String gmailID = prefs.getString("gmailID", "");
String gmailPWD = prefs.getString("gmailPWD", "");

2011/12/06 11:33 2011/12/06 11:33

private void saveView( View view )

{

String path =

Environment.getExternalStorageDirectory().getAbsolutePath();

Bitmap b = Bitmap.createBitmap(

view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);

if(b!=null){

try {

File f = new File(path+"/notes");

f.mkdir();

File f2 = new File(path + "/notes/"+title+".png");

Canvas c = new Canvas( b );

view.draw( c );

FileOutputStream fos = new FileOutputStream(f2);

if ( fos != null )

{

b.compress(Bitmap.CompressFormat.PNG, 100, fos );

fos.close();

}

//setWallpaper( b );

} catch( Exception e ){

Log.e("testSaveView", "Exception: " + e.toString() );

}

}

}

2011/12/06 11:33 2011/12/06 11:33
안드로이드 Intent에서 앱을 호출하는 방법을 정리 합니다.

연락처 Intent

  • 연락처 조회
intent = new Intent(Intent.ACTION_VIEW, 
Uri.parse("content://contacts/people/" +
String.valueOf(contact.getId())));
startActivity(intent);
  • 연락처 등록
intent = new Intent(Intent.ACTION_INSERT, 
Uri.parse("content://contacts/people"));
startActivity(intent);
  • 연락처 수정
intent = new Intent(Intent.ACTION_EDIT, 
Uri.parse("content://contacts/people/" +
String.valueOf(contact.getId())));
startActivity(intent);
  • 연락처 삭제
intent = new Intent(Intent.ACTION_DELETE, 
Uri.parse("content://contacts/people/" +
String.valueOf(contact.getId())));
startActivity(intent);

전화 Intent

  • 권한 설정 (AndroidManifest.xml)
전화 걸기         : CALL_PHONE = "android.permission.CALL_PHONE"
긴급 통화 : CALL_PRIVILEGED =
"android.permission.CALL_PRIVILEGED"
폰 상태 읽기 : READ_PHONE_STATE =
"android.permission.READ_PHONE_STATE"
폰 상태 수정 : MODIFY_PHONE_STATE =
"android.permission.MODIFY_PHONE_STATE"
브로드케스팅 수신 : PROCESS_OUTGOING_CALLS =
"android.permission.PROCESS_OUTGOING_CALLS"
전화 걸기 이전 : ACTION_NEW_OUTGOING_CALL =
"android.intent.action.NEW_OUTGOING_CALL"
  • 전화걸기 화면
Intent intent = new Intent(Intent.ACTION_DIAL, 
Uri.parse("tel:" + TelNumber));
startActivity(intent);
  • 전화걸기
Intent intent = new Intent(Intent.ACTION_CALL, 
Uri.parse("tel:" + TelNumber));
startActivity(intent);

SMS Intent

  • 권한 설정 (AndroidManifest.xml)
수신 모니터링       : RECEIVE_SMS = "android.permission.RECEIVE_SMS"
읽기 가능 : READ_SMS = "android.permission.READ_SMS"
발송 가능 : SEND_SMS = "android.permission.SEND_SMS"
SMS Provider로 전송 : WRITE_SMS = "android.permission.WRITE_SMS"
: BROADCAST_SMS = "android.permission.BROADCAST_SMS"
  • SMS 발송 화면
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("sms_body", "The SMS text");
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);
  • SMS 보내기
Intent intent = new Intent(Intent.ACTION_SENDTO, 
Uri.parse("smsto://" + contact.getHandphone()));
intent.putExtra("sms_body", "The SMS text");
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);

이메일 Intent

  • 이메일 발송 화면
Intent intent = new Intent(Intent.ACTION_SENDTO, 
Uri.parse("mailto:" + contact.getEmail()));
startActivity(intent);

브라우저 Intent

  • Browser에서 URL 호출하기
new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com/"));
startActivity(intent);
  • 브라우저에서 검색
Intent intent = new Intent(Intent.ACT ION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, "검색어");
startActivity(intent);

지도 Intent

  • 지도 보기
Uri uri = Uri.parse ("geo: 38.00, -35.03");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

안드로이드 마켓 Intent

  • 안드로이드 마켓에서 Apps 검색
Uri uri = Uri.parse("market://search?q=pname:전제_패키지_명");  
//--- 예) market://search?q=pname:com.jopenbusiness.android.smartsearch
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
  • 안드로이드 마켓의 App 상세 화면
Uri uri = Uri.parse("market://details?id=전제_패키지_명");
//--- 예) market://details?id=com.jopenbusiness.android.smartsearch
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

갤럭시S의 Intent

  • 패키지명과 클래스명으로 App 호출
intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("패키지명", "전체_클래스명"));
startActivity(intent);
  • 전화, SMS
  • 전화번호부 : com.android.contacts, com.sec.android.app.contacts.PhoneBookTopMenuActivity
  • 전화 : com.sec.android.app.dialertab, com.sec.android.app.dialertab.DialerTabActivity
  • 최근기록 : com.sec.android.app.dialertab, com.sec.android.app.dialertab.DialerTabDialerActivity
  • 메시지 : com.sec.mms, com.sec.mms.Mms
  • 이메일 : com.android.email, com.android.email.activity.Welcome
  • 일정 : com.android.calendar, com.android.calendar.LaunchActivity
  • 인터넷 : com.android.browser, com.android.browser.BrowserActivity
  • Google의 Android용 앱
  • 검색 : com.google.android.googlequicksearchbox, com.google.android.googlequicksearchbox.SearchActivity
  • 음성 검색 : com.google.android.voicesearch, com.google.android.voicesearch.RecognitionActivity
  • Gmail : com.google.android.gm, com.google.android.gm.ConversationListActivityGmail
  • 지도 : com.google.android.apps.maps, com.google.android.maps.MapsActivity
  • 위치찾기 : com.google.android.apps.maps, com.google.android.maps.LatitudeActivity
  • YouTube : com.google.android.youtube, com.google.android.youtube.HomeActivity
  • 토크 : com.google.android.talk, com.google.android.talk.SigningInActivity
  • Goggles : com.google.android.apps.unveil, com.google.android.apps.unveil.CaptureActivity
  • Google 번역 : com.google.android.apps.translate, com.google.android.apps.translate.HomeActivity
  • Reader : com.google.android.apps.reader, com.google.android.apps.unveil.CaptureActivity
  • Voice : com.google.android.apps.googlevoice, com.google.android.apps.googlevoice.SplashActivity
  • Google 별지도 : com.google.android.stardroid, com.google.android.stardroid.activities.SplashScreenActivity
  • 카메라 : com.sec.android.app.camera, com.sec.android.app.camera.Camera
  • TV : com.sec.android.app.dmb, com.sec.android.app.dmb.activity.DMBFullScreenView
  • Android 관리
  • 환경 설정 : com.android.settings, com.android.settings.Settings
  • 작업 관리자 : com.sec.android.app.controlpanel, com.sec.android.app.controlpanel.activity.JobManagerActivity
  • 마켓 : com.android.vending, com.android.vending.AssetBrowserActivity
2011/12/06 11:32 2011/12/06 11:32

File apkFile = new File("/sdcard/ApkTest.apk");
Uri apkUri = Uri.fromFile(apkFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType( Uri.fromFile(apkFile), "application/vnd.android.package-archive");
startActivity(intent);

this.finish(); // 현재 액티비티 종료

2011/12/06 11:32 2011/12/06 11:32
릴리즈되는 이미지에서는 모든 system file 권한이 read only로 되어있기 때문에 권한 수정이 필요합니다.


/system/app 권한 풀기 방법 1


1. 올릴 apk 파일을 준비한다.

2. emulator를 실행시킬 때, 다음과 같이 파티션 정보를 준다.

> emulator -avd [avd 이름] -partition-size [size]

ex) avd 이름 : avd2

size : 128

====> 요놈을 안 해준상태로 밀어 넣으면 Out of memory 에러가 난다.

참고로, partition이 필요한 이유는 > adb shell df 로 확인할 수 있겠다.

3. remount 시키며 권한을 준다.

> adb remount rw

4. 밀어 넣는다.

> adb push [ .apk] /system/app

/system/app 권한 풀기 방법 2


1. 단말과 연결하신후 adb shell 커맨드에서 다음과 같이 입력하셔서 이미지 리마운트를 합니다.
# mount -w -o remount -t rfs /dev/stl5 /system

2. 그리고 기존에 apk가 깔려있는 폴더와 파일의 권한을 변경합니다.
# chmod 777 /system/app
# chmod 777 /system/app/MusicPlayer.apk (뮤직어플의 경우)

2011/12/06 11:32 2011/12/06 11:32
// 웹페이지 띄우기
Uri uri = Uri.parse("http://www.google.com");
Intent it = new Intent(Intent.ACTION_VIEW,uri);
startActivity
(it);

// 구글맵 띄우기
Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW,uri);
startActivity
(it);


// 구글 길찾기 띄우기
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=출발지주소&daddr=도착지주소&hl=ko");
Intent it = new Intent(Intent.ACTION_VIEW,URI);
startActivity
(it);


// 전화 걸기
Uri uri = Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL, uri);
startActivity
(it);


Uri uri = Uri.parse("tel.xxxxxx");
Intent it = new Intent(Intent.ACTION_CALL,uri);
// 퍼미션을 잊지 마세요. <uses-permission id="android.permission.CALL_PHONE" />


// SMS/MMS 발송
Intent it = new Intent(Intent.ACTION_VIEW);
it
.putExtra("sms_body", "The SMS text");
it
.setType("vnd.android-dir/mms-sms");
startActivity
(it);


// SMS 발송
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it
.putExtra("sms_body", "The SMS text");
startActivity
(it);


// MMS 발송
Uri uri = Uri.parse("content://media/external/images/media/23");
Intent it = new Intent(Intent.ACTION_SEND);
it
.putExtra("sms_body", "some text");
it
.putExtra(Intent.EXTRA_STREAM, uri);
it
.setType("image/png");
startActivity
(it);


// 이메일 발송
Uri uri = Uri.parse("mailto:xxx@abc.com");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
startActivity
(it);


Intent it = new Intent(Intent.ACTION_SEND);
it
.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");
it
.putExtra(Intent.EXTRA_TEXT, "The email body text");
it
.setType("text/plain");
startActivity
(Intent.createChooser(it, "Choose Email Client"));


Intent it = new Intent(Intent.ACTION_SEND);
String[] tos = {"me@abc.com"};
String[] ccs = {"you@abc.com"};
it
.putExtra(Intent.EXTRA_EMAIL, tos);
it
.putExtra(Intent.EXTRA_CC, ccs);
it
.putExtra(Intent.EXTRA_TEXT, "The email body text");
it
.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");
it
.setType("message/rfc822");
startActivity
(Intent.createChooser(it, "Choose Email Client"));


// extra 추가하기
Intent it = new Intent(Intent.ACTION_SEND);
it
.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");
it
.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");
sendIntent
.setType("audio/mp3");
startActivity
(Intent.createChooser(it, "Choose Email Client"));


// 미디어파일 플레이 하기
Intent it = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/song.mp3");
it
.setDataAndType(uri, "audio/mp3");
startActivity
(it);


Uri uri = Uri.withAppendedPath(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity
(it);


// 설치 어플 제거
Uri uri = Uri.fromParts("package", strPackageName, null);
Intent it = new Intent(Intent.ACTION_DELETE, uri);
startActivity
(it);


// APK파일을 통해 제거하기
Uri uninstallUri = Uri.fromParts("package", "xxx", null);
returnIt
= new Intent(Intent.ACTION_DELETE, uninstallUri);


// APK파일 설치
Uri installUri = Uri.fromParts("package", "xxx", null);
returnIt
= new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);


// 음악 파일 재생
Uri playUri = Uri.parse("file:///sdcard/download/everything.mp3");
returnIt
= new Intent(Intent.ACTION_VIEW, playUri);


// 첨부파일을 추가하여 메일 보내기
Intent it = new Intent(Intent.ACTION_SEND);
it
.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");
it
.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");
sendIntent
.setType("audio/mp3");
startActivity
(Intent.createChooser(it, "Choose Email Client"));


// 마켓에서 어플리케이션 검색
Uri uri = Uri.parse("market://search?q=pname:pkg_name");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity
(it);
// 패키지명은 어플리케이션의 전체 패키지명을 입력해야 합니다.


// 마켓 어플리케이션 상세 화면
Uri uri = Uri.parse("market://details?id=어플리케이션아이디");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity
(it);
// 아이디의 경우 마켓 퍼블리싱사이트의 어플을 선택후에 URL을 확인해보면 알 수 있습니다.


// 구글 검색
Intent intent = new Intent();
intent
.setAction(Intent.ACTION_WEB_SEARCH);
intent
.putExtra(SearchManager.QUERY,"searchString")
startActivity
(intent);

2011/12/06 11:31 2011/12/06 11:31

Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (file.getName().endsWith(".pdf")){

intent.setDataAndType(Uri.fromFile(file), "application/pdf");
}else if (file.getName().endsWith(".hwp")){
intent.setDataAndType(Uri.fromFile(file), "application/hwp");
}
try{
startActivity(intent);
}catch(ActivityNotFoundException e){
util.showLongToast("해당파일을 실항할 수 있는 어플리케이션이 없습니다.\n파일을 열 수 없습니다.");
e.printStackTrace();
}
2011/12/06 11:30 2011/12/06 11:30

SAMSUNG, LG, Smart Phone Android Open Source

삼성 안드로이드폰 소스 입니다.
LG 안드로이드 소스 입니다.
2011/12/06 11:30 2011/12/06 11:30
웹뷰를 이용한 간단한 어플을 하나 만들었는데.. 세션이 안되서 삽질을 많이 했습니다.
구글링을 통해 소스는 많이 있지만 자바코드에서 특정 웹페이지를 호출해서 session.getId() 를 찍어본 결과
호출 할 때 마다 다른 값이 나오더군요..뭐 근데.. 방법은 의외로 간단한 거였더군요.. 어쩌면 당연한 거였다는..

일단은 session.getId() 값이 같은 값이 나와야 합니다.. 이 값이 계속 바뀐다면 아무리 삽질을 한들 안되겠지요..
HttpClient httpclient = new DefaultHttpClient(); 이부분을 멤버변수로 바로 선언해 버립니다...
보통 다른 소스들은 호출할 때 마다 계속 재 생성하는데.. 재생성을 안하니 유지가 됩니다..ㅋ 그리고 JSESSIONID를 쿠키로 구우면 OK...

setSyncCookie() 안에 들어 있는 코드는 구글링을 통해 얻은 소스입니다.. 많이들 보셨을듯..하네요.. 바뀐부분은 List<Cookie> cookies = ((DefaultHttpClient)httpclient).getCookieStore().getCookies();
이부분인데.. HttpClient 는 getCookieStroe 메소드가 없어서 위처럼 형변환 해줬습니다..

쿠키로 구을 때 Log 찍어보시면 token값과 JSESSIONID도 같이 구워집니다..
그리고 CookieSyncManager.getInstance().startSync(); 가 되면
웹뷰와 연동 됩니다.. 이렇게 안하면 웹뷰에서 접속하는 것과 HttpClient에서 접속하는 것이 따로 놀더군요...

응용해 보시길 바랍니다.. 더 좋은 방법이 있다면 조언해주시구요^^


public class AndroidTet extends Activity {
public static WebView webview;
public HttpClient httpclient = new DefaultHttpClient(); //멤버변수로 선언
public CookieManager cookieManager;
public String domain = "http://192.168.0.44";
public void onCreate(Bundle savedInstanceState) {
...
CookieSyncManager.createInstance(this);
cookieManager = CookieManager.getInstance();
CookieSyncManager.getInstance().startSync();
...
setSyncCookie();
...
public void onResume() {
super.onResume();
CookieSyncManager.getInstance().startSync();
}

public void onPause() {
super.onPause();

if (CookieSyncManager.getInstance() != null) {
CookieSyncManager.getInstance().stopSync();
}
}
protected void onDestroy() {
super.onDestroy();

if (cookieManager != null) {
cookieManager.removeAllCookie();
}
}
public void setSyncCookie() {
Log.e("surosuro", "token transfer start ---------------------------");
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("token", "TEST");// 넘길 파라메터 값셋팅token=TEST

HttpParams params = new BasicHttpParams();

HttpPost post = new HttpPost(domain+/androidToken.jsp");
post.setParams(params);
HttpResponse response = null;
BasicResponseHandler myHandler = new BasicResponseHandler();
String endResult = null;

try {
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

try {
response = httpclient.execute(post);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

try {
endResult = myHandler.handleResponse(response);
} catch (HttpResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

List<Cookie> cookies = ((DefaultHttpClient)httpclient).getCookieStore().getCookies();

if (!cookies.isEmpty()) {
for (int i = 0; i < cookies.size(); i++) {
// cookie = cookies.get(i);
String cookieString = cookies.get(i).getName() + "="
+ cookies.get(i).getValue();
Log.e("surosuro", cookieString);
cookieManager.setCookie(domain, cookieString);
}
}
Thread.sleep(500);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
2011/12/06 11:29 2011/12/06 11:29
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "TubeMusic");
나중에 끝날때 wl.release() 호출해주심 됨다.
2011/12/06 11:29 2011/12/06 11:29

1. layout/main.xml의 progress bar의 android:progressDrawable 속성을 설정한다.

<ProgressBar

android:progressDrawable="@drawable/new_drawable"

...

</ProgressBar>

2. res/drawable/new_drawable.xml 파일을 생성한다.

각 스타일을 설정한다.

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

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+android:id/background" android:drawable="@drawable/backgroundProgress" />
<item android:id="@+android:id/secondaryProgress" android:drawable="@drawable/secondaryProgress" />
<item android:id="@+android:id/progress" android:drawable="@drawable/progress" />
</layer-list>

3. res/drawable 디렉토리 밑에 다음의 파일을 생성한다.

backgroundProgress.png

secondaryProgress.png

progress.png

png파일을 생성하지 않고 drawable로 작성하려면

2b. res/drawable/new_drawable.xml 파일을 다음과 같이 설정한다.

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

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@android:id/background">
<shape>
<corners android:radius="0dip" />
<gradient
android:startColor="#ff9d9e9d"
android:centerColor="#ff5a5d5a"
android:centerY="0.75"
android:endColor="#ff747674"
android:angle="270"
/>
</shape>
</item>

<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="0dip" />
<gradient
android:startColor="#80ffd300"
android:centerColor="#80ffb600"
android:centerY="0.75"
android:endColor="#a0ffcb00"
android:angle="270"
/>
</shape>
</clip>
</item>

<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="0dip" />
<gradient
android:startColor="#ff0000"
android:centerColor="#00ff00"
android:centerY="0.75"
android:endColor="#0000ff"
android:angle="0"
/>
<stroke android:width="3dp" color="#000000"/>
<padding android:left="10dp" android:top="10dp"
android:right="10dp" android:bottom="10dp" />
</shape>
</clip>
</item>
</layer-list>

2011/12/06 11:29 2011/12/06 11:29
VideoView를 사용하여 assets 디렉토리에 위치한 동영상을 실행하는 코드를 작성하였는데 계속 오류가 발생 했다.
프로젝트의 /assets/a.mp4에 파일을 저장했다.
Url fileuri= Uri.parse("file:///android_asset/a.mp4");
이 렣게 uri를 생성해서 실행했는데 오류가 발생 한다. 이유를 모르겠다. 구글검색으로 자료를 찾아봐도 해결책을 찾을 수 없었다. 그래서 파일의 위치를 res/raw/a.mp4 로 옮기었다.
그리고 다음과 같이 uri를 생성했다.
Uri videofile = Uri.parse("android.resource://"+getPackageName()+"/raw/a");

이렇게 했더니 정상적으로 동영상이 표시되었다.
아래는 전체 소스코드이다. 물론 layout/main.xml에 VideoView객체를 video_view라는 id로 추가했다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<VideoView android:id="@+id/video_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"></VideoView>
</LinearLayout>
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.widget.VideoView;
public class VideoPlayer extends Activity {
VideoView videoview ;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
videoview = (VideoView) findViewById(R.id.video_view);
try {
Uri videofile = Uri.parse("android.resource://"+getPackageName()+"/raw/a");
videoview.setVideoURI(videofile);
//descriptor.
videoview.start();
videoview.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
Log.d(getClass().getName(), "onCompletion()");
}
});
} catch (Exception ex) {
Log.d(getClass().getName(), "Video failed: '" + ex + "'" );
ex.printStackTrace();
}
}
}
2011/12/06 11:28 2011/12/06 11:28

바코드 스캔 라이브러리를 소개하려고 한다.

이름이 zxing이다.

소개를 그대로 인용하면


Multi-format 1D/2D barcode image processing library with clients for Android, Java

다중포멧 1D/ 2D 바코드 이미지 프로세싱 라이브러리, Android, Java 클라이언트 용



Project 사이트는 http://code.google.com/p/zxing/ 이다.

라이센스는 Apache License 2.0 이다.


android 뿐만아니라 iPhone같은 다양한 모바일 기기를 지원한다.


라이브러리를 받아서 빌드한뒤 샘플을 만들어 봤는데 1 시간 밖에 걸리지 않았다.


라이브러리 빌드를 위해서는 ant를 설치해야한다.


1. zxing의 core 디렉토리로 이동한뒤 ant명령어를 입력하여 빌드 한다 결과물로 core.jar가 생성된다.


2. Android 프로젝트를 생성한다.


3. Android 프로젝트의 lib 디렉토리를 생성한뒤 core.jar를 안드로이드 lib에 복사


4. Android 프로젝트의 build path에 /lib/core.jar를 추가해 준다.


5. zxing의 android-integration의 java파일을 Android 프로젝트의 src로 복사한다.


6. 적당한 패키지 경로를 수정해 준다.


7. 버튼을 추가하고 IntentIntegrator.initiateScan 바코드 스캔함수를 호출한다.


public class ZxingTest extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

View scan = findViewById(R.id.scan);

scan.setOnClickListener(new View.OnClickListener() {

  

   @Override

   public void onClick(View v) {

    IntentIntegrator.initiateScan(ZxingTest.this);

   }

  });

}

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

//super.onActivityResult(requestCode, resultCode, data);

IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);

if (scanResult != null) {

// handle scan result

TextView scanResultText = (TextView) findViewById(R.id.scanResult);

String text = "format="+scanResult.getFormatName()+", data:"+scanResult.getContents();

scanResultText.setText(text);

}

}

}



8. onActivityResult에서 결과를 받아서 처리한다
2011/12/06 11:28 2011/12/06 11:28

가끔씩 마주치게 되는 "OutOfMemoryError : bitmap size exceeds VM budget" 에러는 메모리 누수가 주요 원인이다.
이와 관련된 링크를 모아봤다.


* 액티비티가 멈출 때 비트맵을 재활용(즉 GC)되게 하라.
http://stackoverflow.com/questions/1949066/java-lang-outofmemoryerror-bitmap-size-exceeds-vm-budget-android
http://stackoverflow.com/questions/2191407/changing-imageview-content-causes-outofmemoryerror

- bitmap 이미지인 경우 recycle() 호출
- onPause에서 수행하는게 좋음

- ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();


* 이미지를 미리 줄여서 읽어들여라
http://chiyo85.tistory.com/entry/Android-Bitmap-Object-Resizing-Tip
http://www.memofy.com/memofy/show/1008ab7f2836ab7f01071c2dbfe138/outofmemory-exception-when-decoding-with-bitmapfactory

- BitmapFactory.Options.inSampleSize 활용


* Activity Context에 대한 참조(reference)를 오랫동안 유지하지 말아라
http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
(한글 번역 : http://blog.naver.com/huewu/110082062273 )

- Drawable.setCallback(null) 사용
- WeakReference를 가진 static 내부 클래스
- 이미지를 static 변수로 처리하지 말것.


* 외부 클래스의 상태에 의존하지 않는 내부 클래스는 static으로 선언하라
- 내부 클래스는 외부 클래스를 크게 만들며 또한 외부클래스 객체가 필요 이상으로 오래 살아있게 되어 메모리를
더 차지할 수 있다.
- 외부 클래스의 상태 필드에 접근하지 않는(즉, 외부 객체의 상태에 의존하지 않는) 내부 클래스는 static으로 선언.


* Attacking memory problems on Android
http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html

2011/12/06 11:27 2011/12/06 11:27

안드로이드 색상코드 형식

AA : 투명도, 00~FF(투명~불투명)
RR : 적색, 00~FF
GG : 녹색, 00~FF
BB : 청색, 00~FF

형식 종류
- #AARRGGBB
- #RRGGBB
- #RGB


이미지 배경을 투명하게하는 값

android:Background = "#00FF0000"

2011/12/06 11:25 2011/12/06 11:25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 웹페이지 띄우기
Uri uri = Uri.parse("http://www.google.com");
Intent it  = new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
// 구글맵 띄우기
Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW,uri);
startActivity(it);
// 구글 길찾기 띄우기
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=출발지주소&daddr=도착지주소&hl=ko");
Intent it = new Intent(Intent.ACTION_VIEW,URI);
startActivity(it);
// 전화 걸기
Uri uri = Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL, uri); 
startActivity(it); 
Uri uri = Uri.parse("tel.xxxxxx");
Intent it = new Intent(Intent.ACTION_CALL,uri);
// 퍼미션을 잊지 마세요. <uses-permission id="android.permission.CALL_PHONE" />
// SMS/MMS 발송
Intent it = new Intent(Intent.ACTION_VIEW);  
it.putExtra("sms_body", "The SMS text");  
it.setType("vnd.android-dir/mms-sms");  
startActivity(it); 
// SMS 발송
Uri uri = Uri.parse("smsto:0800000123");  
Intent it = new Intent(Intent.ACTION_SENDTO, uri);  
it.putExtra("sms_body", "The SMS text");  
startActivity(it); 
// MMS 발송
Uri uri = Uri.parse("content://media/external/images/media/23");  
Intent it = new Intent(Intent.ACTION_SEND);  
it.putExtra("sms_body", "some text");  
it.putExtra(Intent.EXTRA_STREAM, uri);  
it.setType("image/png");  
startActivity(it);
// 이메일 발송
Uri uri = Uri.parse("mailto:xxx@abc.com");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(it);
Intent it = new Intent(Intent.ACTION_SEND);  
it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");  
it.putExtra(Intent.EXTRA_TEXT, "The email body text");  
it.setType("text/plain");  
startActivity(Intent.createChooser(it, "Choose Email Client")); 
Intent it = new Intent(Intent.ACTION_SEND);    
String[] tos = {"me@abc.com"};    
String[] ccs = {"you@abc.com"};    
it.putExtra(Intent.EXTRA_EMAIL, tos);    
it.putExtra(Intent.EXTRA_CC, ccs);    
it.putExtra(Intent.EXTRA_TEXT, "The email body text");    
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");    
it.setType("message/rfc822");    
startActivity(Intent.createChooser(it, "Choose Email Client"));  
// extra 추가하기
Intent it = new Intent(Intent.ACTION_SEND);  
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");  
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");  
sendIntent.setType("audio/mp3");  
startActivity(Intent.createChooser(it, "Choose Email Client"));
// 미디어파일 플레이 하기
Intent it = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/song.mp3");
it.setDataAndType(uri, "audio/mp3");
startActivity(it);
Uri uri = Uri.withAppendedPath(
  MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");  
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
startActivity(it); 
// 설치 어플 제거
Uri uri = Uri.fromParts("package", strPackageName, null);  
Intent it = new Intent(Intent.ACTION_DELETE, uri);  
startActivity(it);
// APK파일을 통해 제거하기
Uri uninstallUri = Uri.fromParts("package", "xxx", null);
returnIt = new Intent(Intent.ACTION_DELETE, uninstallUri);
// APK파일 설치
Uri installUri = Uri.fromParts("package", "xxx", null);
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
// 음악 파일 재생
Uri playUri = Uri.parse("file:///sdcard/download/everything.mp3");
returnIt = new Intent(Intent.ACTION_VIEW, playUri);
// 첨부파일을 추가하여 메일 보내기
Intent it = new Intent(Intent.ACTION_SEND); 
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text"); 
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3"); 
sendIntent.setType("audio/mp3"); 
startActivity(Intent.createChooser(it, "Choose Email Client"));
// 마켓에서 어플리케이션 검색
Uri uri = Uri.parse("market://search?q=pname:pkg_name"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it); 
// 패키지명은 어플리케이션의 전체 패키지명을 입력해야 합니다.
// 마켓 어플리케이션 상세 화면
Uri uri = Uri.parse("market://details?id=어플리케이션아이디"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it);
// 아이디의 경우 마켓 퍼블리싱사이트의 어플을 선택후에 URL을 확인해보면 알 수 있습니다.
// 구글 검색
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY,"searchString")
startActivity(intent);

[출처] Intent 활용 예제|작성자 다빈

2011/12/06 11:25 2011/12/06 11:25

Audio :

*패키지의 assets/audio/oh.mp3 플레이

AssetFileDescriptor atd = getAssets().openFd("audio/oh.mp3");

MediaPlayer audio_play = new MediaPlayer();

audio_play.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());

afd.close();

audio_play.start();

*패키지의 res/raw/oh.mp3 플레이

MediaPlayer audio_play = MediaPlayer.create(context, R.raw.oh);

audio_play.start();

*파일시스템의 /data/oh.mp3 플레이

MediaPlayer audio_play = new MediaPlayer();

audio_play.setDataSource("/data/oh.mp3");

audio_play.start();

Video :

*파일시스템의 /data/oh.mp4 플레이

VideoView video = (VideoView)findViewById(R.id.video);

MediaController nc = new MediaController(this);

nc.setMediaPlayer(video);

video.setVideoPath("/data/oh.mp4");

video.start();

*패키지의 res/raw/oh.mp4 플레이

VideoView video = (VideoView) findViewById(R.id.video);
video.setVideoURI(Uri.parse("android.resource://패키지명/" + R.raw.파일이름));
video.setMediaController(new MediaController(this));
video.requestFocus();
video.start();

2011/12/06 11:24 2011/12/06 11:24
ArrayList<string> phone = new ArrayList<string>();
       
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while(cursor.moveToNext())
{
    int index = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
    String s = cursor.getString(index);
    phone.add(s);
}
       
Log.e("#####", phone.toString());
//결과 : [01011112222, 01033334444, 01055556666]
public static Map<String, String> getAddressBook(Context context)
{
    Map<String, String> result = new HashMap<String, String>();
    Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
    while(cursor.moveToNext())
    {
        int phone_idx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
        int name_idx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
        String phone = cursor.getString(phone_idx);
        String name = cursor.getString(name_idx);
        result.put(name, phone);
    }
   
    return result;
}

2011/12/06 11:24 2011/12/06 11:24

[01] UI 설계(Widget, View, Layout) 개론

View ---+--- View(Widget): 안드로이드가 제공하는 콘트롤, TextView, EditText, Button....
|
+--- Custom View: 기존의 View 를 상속받아 변경하거나 새롭게 생성.
|
+--- ViewGroup --- Layout: View를 배치하고 그룹화 함, LinearLayout, RelativeLayout, TableLayout....

1. UI 설계 방법
- 자바소스상에서 class상에 직접 지정하는 경우와 XML을 이용하는
방법이 있으며 개발과 유지보수의 장점 때문에 XML이 많이 사용된다.
XML을 UI 설계로 이용하는 대표적인 언어는 Flex등이 있다.

2. View
- Widget은 View로 되어있으며 View는 트리구조(상속)를 이루고 있다.
- 모든 안드로이드의 비주얼 콤포넌트는 View 클래스로부터 내려온다.
- View는 Widget, Control이라고 부르기도 한다.
- View --> ViewGroup --> Layout 의 관계를 갖는다.

3. Layout
- ViewGroup class의 확장
- View를 담는 컨테이너 역활을 함.

[02] View
- 화면을 구성하는 UI를 제공한다.
- API: http://developer.android.com/reference/android/view/View.html

1. 공통 XML 속성
- View는 많은 공통 속성과 메소드를 가지고 있는데 TextView의 속성이 대부분의 View에
그대로 적용된다.

android:id : View의 아이디
android:layout_width : 부모뷰를 기준으로 한 폭의 의 크기
android:layout_height : 부모뷰를 높이로한 뷰의 높이
android:padding : 외곽선과 뷰안의 내용과의 간격
android:visibility : 초기화면 표시 여부
android:background : Widget의 배경색을 #RGB, #ARGB, #RRGGBB, #AARRGGBB로 지정
android:clickable : 클릭 가능 여부
android:longClickable : 롱 클릭 가능 여부
android:focusable : 키보드 포커스의 가능 여부

2. 공통 메소드
setEnabled() : Widget의 사용 가능 여부 지정
isEnabled() : Widget이 사용 가능 상태인지 확인
requestFocus(): 해당 Widget으로 입력 포커스 이동
isFocused() : Widget이 현재 포커스를 가지고 있는지 확인
setFocus() : Widget에 focus를 지정
getParent() : 상위 위젯이나 컨테이너 추출
findViewById(): 지정한 id에 대한 위젯 추출
getRootView() : 최상위 컨테이너 추출

3. 레이블
- import android.widget.TextView;
- View --> TextView
- 단순한 문자열의 출력.
- 출력 문자열은 변경할 수 없다.

3. 버튼
- import android.widget.Button;
- 터치 및 클릭 이벤트를 처리한다.
- View --> TextView --> Button
- TextView를 상속받으며 사용자의 tab(터치) 이벤트를 주로 처리한다.

4. 이미지
- import android.widget.ImageView;
- View --> ImageView: 이미지를 넣을 수 있는 View Class
- View --> ImageView --> ImageButton: 이미지를 넣을 수 있는 버튼 클래스

5. 입력 필드
- import android.widget.EditText;
- View --> TextView --> EditText
- 문자를 입력 받을 수 있는 기능 제공

1) XML 속성
android:autoText : 자동 철자 교정
android:capitalize : 첫 글자를 대문자로 변환
android:digits : 특정 숫자만 입력
android:singleLine : 여러줄을 입력받을 것인지 지정
android:numeric : 숫자만 입력 가능
android:password : 패스워드 입력 형태 지정
android:phoneNumber: 전화번호 입력
android:inputMethod: 특정 형식의 입력 지정

6. 체크 박스
- import android.widget.CheckBox;
- View --> TextView --> Button --> CompoundButton
--> RadioButton --> CheckBox
- 취미같은 다중 선택 가능

7. 라디오 버튼
- import android.widget.RadioButton;
- View --> TextView --> Button --> CompoundButton --> RadioButton
- 성별같은 단일 선택만 가능
- RadioGroup에 의해 RadioButton의 그룹화 가능

[03] Layout
- android.view.ViewGroup class 상속 받음.

1. 공통 특성
android:layout_height : 부모 뷰 기준, 자식 뷰에 적용, 뷰의 높이.
android:layout_width : 부모 뷰 기준, 자식 뷰에 적용, 뷰의 너비, 자식뷰의 경우 필수임.
android:layout_margin : 부모 뷰 기준, 뷰의 상하 좌우의 여백 공간.
android:layout_marginTop : 부모 뷰 기준, 뷰의 위쪽 여백 공간.
android:layout_marginBottom: 부모 뷰 기준, 뷰의 아래쪽 여백 공간.
android:layout_marginRight : 부모 뷰 기준, 뷰의 오른쪽 여백 공간 .
android:layout_marginLeft : 부모 뷰 기준, 뷰의 왼쪽 여백 공간.
android:gravity : 자식 뷰 기준, View로 부터의 위치.
android:layout_gravity : 부모 뷰 기준, 현재 View의 위치.

2. AbsoluteLayout: View가 x,y좌표에 의해서 배치, 레이아웃이 깨질 수 있고 화면의
구성요소가 서로 잘 맞아 떨어저야함으로 잘 사용되지 않는다.
안드로이드 SDK 1.5 R1부터 폐기 대상으로 분류되어 더이상 사용되지 않는다.

3. FrameLayout: View를 좌측 상단에 배치, 여러개의 View가 있는 경우 겹쳐서 보임으로
Layout의 효과는 떨어짐, 사용 빈도는 적음.

4. LinearLayout: 직선형의 배치 방식, View를 수평 또는 수직의 라인을 View를 배치,
입력 양식에 많이 사용된다.
android:orientation: 뷰가 출력될 방향을 지정한다.

5. RelativeLayout: 다른 콘트롤에 비례해서 콘트롤의 위치 지정.
- 뷰를 위치를 설정하기위해 여러번 선언 가능.

- 부모뷰를 기준으로
android:layout_marginRight="20px" : 뷰를 부모의 오른쪽에서 일정 간격 떨어짐
android:layout_marginTop="60px" : 뷰를 부모의 위쪽에서 일정 간격 떨어짐
android:layout_alignParentTop="true" : 뷰를 부모에서 상단에 배치
android:layout_centerHorizontal="true" : 뷰를 부모에서 수평 중앙에 배치
android:layout_alignParentRight="true" : 뷰를 부모에서 오른쪽에 배치
android:layout_alignParentLeft="true" : 뷰를 부모에서 왼쪽에 배치
android:layout_centerVertical="true" : 뷰를 부모에서 수직 중앙에 배치
android:layout_centerInParent="true" : 뷰를 수직, 수평의 중앙에 배치
android:layout_alignParentBottom="true": 뷰를 부모에서 하단에 배치

- 주변에 배치된 뷰를 기준으로
android:layout_toRightOf="@id/idname" : 주변 특정 뷰의 오른쪽에 배치
android:layout_toLeftOf="@id/idname" : 주변 특정 뷰의 외쪽에 배치
android:layout_alignBaseline="@id/idname": 주변 특정 뷰와 같은 라인에 배치
android:layout_below="@id/idname" : 주변 특정 뷰의 아래에 배치
android:layout_above="@id/idname" : 주변 특정 뷰의 위에 배치
android:layout_alignRight="@id/idname" : 주변 특정 뷰의 오른쪽에 배치
android:layout_alignLeft="@id/idname" : 주변 특정 뷰의 왼쪽에 배치
android:layout_alignTop="@id/idname" : 주변 특정 뷰의 위쪽에 배치

- 예) [TEL][SMS]의 경우
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnSms"
android:layout_marginLeft="1px"
android:text="SMS"
android:textSize="16px"
android:layout_alignParentRight="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnTel"
android:layout_marginLeft="6px"
android:text="TEL"
android:textSize="16px"
android:layout_toLeftOf="@id/btnSms"
/>

6. TableLayout: 행과 열의 격자를 이용해 View의 배치.
- 아래의 레이아웃 특성을 조합하여 사용

android:stretchColumns="1" : 두번째 컴럼의 크기를 남는 공간으로 늘림(확장).
android:stretchColumns=" 늘리려는 column 번호(0~), *"
android:shrinkColumns=" 필요한 공간만 사용하고 줄이고자 하는 column 번호(0~), *"

android:stretchColumns="1" android:shrinkColumns="*": 모든 컬럼의 값을 전부
필요한 만큼만 사용하고 1번째 컬럼을 남은 공간으로 전부 확대

android:collapseColumns=" 숨기고자 하는 column 번호(0~), *"

android:layout_span=" 차지하려는 Column 수", 셀 합치기
android:layout_span="2": 컬럼 2개를 합침.



이름:[ ]의 경우 2번째 입력란을 확대함.
android:layout_marginTop="60px" : 뷰를 부모의 위쪽에서 일정 간격 떨어짐
android:layout_alignParentTop="true" : 뷰를 부모에서 상단에 배치
android:layout_centerHorizontal="true" : 뷰를 부모에서 수평 중앙에 배치
android:layout_alignParentRight="true" : 뷰를 부모에서 오른쪽에 배치
android:layout_alignParentLeft="true" : 뷰를 부모에서 왼쪽에 배치
android:layout_centerVertical="true" : 뷰를 부모에서 수직 중앙에 배치
android:layout_centerInParent="true" : 뷰를 수직, 수평의 중앙에 배치
android:layout_alignParentBottom="true": 뷰를 부모에서 하단에 배치


2011/12/06 11:23 2011/12/06 11:23

1. ViewPage Layout 기술

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

<LinearLayout

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

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

>

<android.support.v4.view.ViewPager

android:id="@+android:id/viewpager"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

</LinearLayout>

2. FragmentActivity 기술
/**
*
*/
package com.andy.fragments.viewpager;
import java.util.List;
import java.util.Vector;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import com.andy.R;
import com.andy.fragments.tabs.Tab1Fragment;
import com.andy.fragments.tabs.Tab2Fragment;
import com.andy.fragments.tabs.Tab3Fragment;
/**
* The <code>ViewPagerFragmentActivity</code> class is the fragment activity hosting the ViewPager
* @author mwho
*/
public class ViewPagerFragmentActivity extends FragmentActivity{
/** maintains the pager adapter*/
private PagerAdapter mPagerAdapter;
/* (non-Javadoc)
* @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  super.setContentView(R.layout.viewpager_layout);
  //initialsie the pager
  this.initialisePaging();
}
/**
* Initialise the fragments to be paged
*/
private void initialisePaging() {
  List<Fragment> fragments = new Vector<Fragment>();
  fragments.add(Fragment.instantiate(this, Tab1Fragment.class.getName()));
  fragments.add(Fragment.instantiate(this, Tab2Fragment.class.getName()));
  fragments.add(Fragment.instantiate(this, Tab3Fragment.class.getName()));
  this.mPagerAdapter = new PagerAdapter(super.getSupportFragmentManager(), fragments);
  //
  ViewPager pager = (ViewPager)super.findViewById(R.id.viewpager);
  pager.setAdapter(this.mPagerAdapter);
}
}
3. PageAdapter 기술
/**
*
*/
package com.andy.fragments.viewpager;
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
/**
* The <code>PagerAdapter</code> serves the fragments when paging.
* @author mwho
*/
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
/**
* @param fm
* @param fragments
*/
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
  super(fm);
  this.fragments = fragments;
}
/* (non-Javadoc)
* @see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
@Override
public Fragment getItem(int position) {
  return this.fragments.get(position);
}
/* (non-Javadoc)
* @see android.support.v4.view.PagerAdapter#getCount()
*/
@Override
public int getCount() {
  return this.fragments.size();
}
}
4.실행
왼쪽에서 오른쪽은 빨강->녹색->파랑
2011/12/06 11:23 2011/12/06 11:23

사용자 삽입 이미지

이것은 안드로이드 Tab UI 를 구성하는 구성요소 입니다.

안드로이드 Tab UI 는 이와같이 세 개의 다른 클래스들의 집합으로 이루어져 있습니다.

TabHost 는 TabUI 를 구성하는 전체 틀 입니다.

여기에는 특정한 android:id 를 가지는 TabWidget 와 TabSpec 이 포함되어야 합니다.

TabWidget 는 TabUI 에서 선택하는 버튼이 나오는 부분을 말하며 이것의 android:id 는 반드시 “@android:id/tabs” 가 되어야 합니다.

TabSpec 는 하나의 탭을 구성하는 구성요소들의 집합으로서 View 의 하위구성요소가 아니기 때문에 layout xml 파일에 직접 추가될 수 없습니다. 그래서 java 소스코드상으로 추가해 주어야 하며 layout xml 에는 이를 표시하기 위하여“@android:id/tabcontent” 라는 android:id 를 가지는 Layout View 가 추가되어 있어야 합니다.

이렇게 글로만 보면 상당히 어렵게 보이는데 이것을 쉽게 쓰도록 만들어 놓은것이 바로 TabActivity 클래스 입니다.

아래는 TabActivity 를 이용하여 layout xml 파일 없이 TabUI 를 구성하는 예 입니다.

package net.cranix.android.cranixcontact;

import android.app.TabActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TabHost;

public class CranixContact extends TabActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

final TabHost tabHost = getTabHost();
tabHost.addTab(
tabHost.newTabSpec("tab1")
.setIndicator("Contacts")
.setContent(new Intent(this,ContactsTabActivity.class))
);
tabHost.addTab(
tabHost.newTabSpec("tab2")
.setIndicator("Calllog")
.setContent(new Intent(this,CalllogTabActivity.class))
);
}
}

위의 소스에는 layout xml 파일이 사용되는 부분이 없지만 TabActivity 내부적으로 기본적인 layout 을 사용하고 있습니다.

그렇다면 TabActivity 에서 사용하는 기본적인 layout 은 무엇일까요?

안드로이드 소스를 직접 다운받아서 xml 파일을 뒤져보면 아래와 같은 레이아웃 파일을 발견할 수 있습니다.

<TabHost xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0" />
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"/>
</LinearLayout>
</TabHost>

이것이 바로 TabActivity 클래스가 기본으로 사용하는 layout xml 파일입니다.

위에서 말로 설명한 부분에 나와있는 대로 구성되어있는것을 확인할 수 있습니다.

TabActivity 를 상속받은 Activity 라면 기본적으로 위와같은 layout xml 파일이 contentView 로 자동으로 셋팅됩니다.

이것을 염두해 두고 개발을 해야지 오류를 피할수 있습니다.

그럼 내가만든 layout xml 을 TabActivity 에 띄우는 예제를 만들어 보겠습니다.

먼저 layout 을 구성합니다 파일이름은 main.xml 입니다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:text="@+id/TextView01"
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:text="@+id/Button01"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>

이것을 TabActivity 에 띄우는 java 코드는 아래와 같습니다.

package net.cranix.android.testtabactivity;

import android.app.TabActivity;
import android.os.Bundle;
import android.widget.TabHost;

public class TestTabActivity extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
final TabHost tabHost = getTabHost();

getLayoutInflater().inflate(R.layout.main, tabHost.getTabContentView());

tabHost.addTab(tabHost.newTabSpec("tab1").setIndicator("tab1").setContent(R.id.TextView01));
tabHost.addTab(tabHost.newTabSpec("tab2").setIndicator("tab2").setContent(R.id.Button01));
}
}
TabSpec.setContent 에 view 를 지정하려면 반드시 @”android:id/tabcontent” 의 하위 뷰 여야 합니다.
그래서 자신이 만든 layout 을 기존에 있던 tabHost 의 tabContentView 에 inflate 를 활용하여 붙혀주는 것 입니다.

위의 굵은 글씨에 의해 inflat 가 실행된 이후에는 layout 을 아래와 같이 인식합니다.

<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_weight="0" />
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<TextView android:text="@+id/TextView01"
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button android:text="@+id/Button01"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
</TabHost>

결국 위와 같이 tabSpec 을 만들때 setContent 에다가 자신이 만든 layout 을 사용할 수 있게 되는것 입니다.

이렇게 TabActivity 가 돌아가는 구조를 알아내면 응용도 가능합니다.

아래 예제는 안드로이드 TabUI 에서 TabWidget 이 아래에 있도록 구성한 Activity 입니다.

먼저 layout 파일은 아래와같이 구성합니다.

<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<TextView android:text="@+id/TextView01"
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button android:text="@+id/Button01"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
<TabWidget android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_weight="0" />
</LinearLayout>
</TabHost>

그다음 Activity 파일은 아래와 같이 구성합니다.

package net.cranix.android.testtabactivity;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TabHost;

public class TestTabActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TabHost tabHost = (TabHost) findViewById(R.id.tabhost);
tabHost.setup();

tabHost.addTab(tabHost.newTabSpec("tab1").setIndicator("tab1").setContent(R.id.TextView01));
tabHost.addTab(tabHost.newTabSpec("tab2").setIndicator("tab2").setContent(R.id.Button01));
}
}

실행해 보면 아래와 같이 탭이 아래에 있는것을 볼 수 있습니다.

사용자 삽입 이미지

여기서 중요한 것은 TabActivity 가 아니라 그냥 Activity 를 이용하였다는 것과 레이아웃 파일을 안드로이드 TabUI 에 맞게 직접 구성했다는점 입니다.

이렇게 TabActivity 를 통하지 않고 Tab 을 구현할 경우에는 반드시 tabHost.setup() 을 호출해 주어야 합니다.

여기서 TabHost view 하위에 있는 기본 뷰를 탐색해서 셋팅해 주게 됩니다.

즉 이 함수를 거치면 tabHost.getTabWidget() 과 tabHost.getTabContentView() 같은 메소드를 사용할때 null 이 반환되지 않게 됩니다.

결국 안드로이드 TabUI 는 기본적인 안드로이드 UI 구조를 가지고 TabUI 를 구성하기 편하게 상속하여 재작성 한 것입니다.

2011/12/06 11:22 2011/12/06 11:22

* 안드로이드을빨리이해하기위한방법

1. OpenSource활용

- http://code.google.com/hosting/

- http://google.com/codesearch

2. Reverse Engineering 활용하여 학습하기

- 자바 클래스 리버싱

사용자 삽입 이미지

- 안드로이드 실행파일 구조와 리버싱

사용자 삽입 이미지

사용자 삽입 이미지

* APK파일 디컴파일(Decompile)하는 방법

1. 디컴파일을 위한 툴 설치

1.ApkTool

- 다운로드 : http://code.google.com/p/android-apktool/downloads/list

사용자 삽입 이미지

- 압축풀기 : android-sdk가 설치된 platform-tools디렉토리안에 apktool디렉토리를 만들어 압축을 푼다.

apktool1.4.1.tar.bz2

apktool-install-windows-r04-brut1.tar.bz2

사용자 삽입 이미지

2.Dex2Jar

- 다운로드 : http://code.google.com/p/dex2jar/downloads/list

사용자 삽입 이미지


- 압축풀기 : android-sdk가 설치된 platform-tools디렉토리안에 dex2jar디렉토리를 만들어 압축을 푼다.

dex2jar-0.0.7.10-SNAPSHOT.zip

사용자 삽입 이미지

3.Java Decompiler

- 다운로드 : http://java.decompiler.free.fr/?q=jdgui

사용자 삽입 이미지

- 압축풀기 : 실행하기 편한 곳에 압축을 풀어 놓는다.

2. 환경변수 잡기

- adb, apktool, dex2jar 명령어를 사용하기 위한 환경변수를 잡는다.

%ANDROID_SDK%\platform-tools

%ANDROID_SDK%\platform-tools\apktool

%ANDROID_SDK%\platform-tools\dex2jar

 

사용자 삽입 이미지사용자 삽입 이미지

3. apk 파일 추출

- 폰에 설치된 모든 패키리를 리스트로 보인다.

사용자 삽입 이미지

- 리스트 중에 디컴파일 할 APK파일을 정한다.

사용자 삽입 이미지

- APK파일을 추출한다. (adb shell을 종료 후 수행한다.)

사용자 삽입 이미지

4. ApkTool 실행 (java파일 외 xml, image, db등을 추출할 수 있다.)

- apktool 명령어를 사용하여 apk파일에 있는 파일을 디코딩하여 out폴더에 내보낸다.

사용자 삽입 이미지

사용자 삽입 이미지

- xml파일을 열어 내용을 확인한다.

사용자 삽입 이미지

5. Dex2Jar 실행 (Java파일을 추출할 수 있다.)

- apk파일의 확장자를 zip으로 변경한다.

사용자 삽입 이미지

- 추출한 zip파일의 압축을 풀어 classes.dex 파일을 추출한다.

사용자 삽입 이미지

- Dex2Jar를 실행한다.

사용자 삽입 이미지

- Dex2Jar를 실행하면 아래 jar파일이 생긴다.

사용자 삽입 이미지

6. JD-GDI 실행

- JD-GDI실행하여 classes.dex.dex2jar파일을 열어 소스를 분석한다.

사용자 삽입 이미지

7. Enterprise Architect를 이용하여 클래스 다이어그램으로 분석한다.

- 다운로드 : http://www.sparxsystems.com.au/products/ea/trial.html

사용자 삽입 이미지

- 설치

사용자 삽입 이미지

- 실행

사용자 삽입 이미지

- Java Decompiler에서 open할 파일을 추출한다.

사용자 삽입 이미지

- zip파일을 압축을 푼다.

사용자 삽입 이미지

- Enterprise Architect에서 classes.dex.dex2jar.src디렉토리를 import 한다.

사용자 삽입 이미지

- 클래스 다이어그램을 보고 분석한다.

사용자 삽입 이미지

-----------------------------------------------------------------------------------------------------------------------

제 11회 한국자바개발자 컨퍼런스에서 트랙1, 3번째 세션에서 Reverse Engineering, 안드로이드 학습이란 주제로 발표를 진행합니다.

리버싱이 무엇인지, 안드로이드 리버싱을 통해서 어떻게 학습을 할 수 있는지 다양한 도구와 팁들을 소개합니다.

학습을 목적으로 리버스엔지니어링이 허용되지만 기술을 복제 유혹을 받을 수도 있습니다.

법적이나, 윤리적으로 문제가 된다는 것을 명심하고, 공부한 내용을 서로 공유하며 미소 지을 수 있는 개발문화가 되길 바래봅니다. :D

발표자료와 함께 사용된 동영상 공유합니다.

발표자료 : Reverse Engineering, 안드로이드 학습

자바 클래스 리버싱

안드로이드 실행파일 구조와 리버싱

Reverse Engineering 활용한 학습 예제

  1. 분석할 앱선정과 APK 파일 추출
  2. 디컴파일 후 분석
  3. 클래스 다이어그램으로 - Enterprise Architect
  4. 의존성 검사를 통해 쉽게 - xDepend
2011/12/06 11:18 2011/12/06 11:18
###########################################################################
### 쓴건 : 원격서버의 Virtualbox 웹관리를 위한 Phpvirtualbox 설치
### 쓴이 : 권성재 (nonots@hanmail.net, http://www.badaweb.co.kr)
### 쓴때 : 2011-12-13
###########################################################################

### 환경
- CentOS 5.x 64비트
- 아파치 2.2, PHP 5.2
- X 윈도우(GUI) 없이 텍스트모드(TUI)로 서버 실행중


1) 서버 CPU 가 가상화기술을 지원하는지 확인. 지원 안한다면 포기.
# egrep '(vmx|svm)' /proc/cpuinfo


2) php 확장기능에 soap, json 이 포함되어 있는지 확인. 없다면 추가 설치.
php 버전도 5.1 이상이어야 한다.
# php -i | egrep -i '(soap|json)'


3) dkms 패키지 설치
아래 링크에서 다운받아 설치
- http://rpmfind.net/linux/rpm2html/search.php?query=dkms
- http://linux.dell.com/dkms/permalink/
# rpm -Uvh dkms-~~~.rpm


4) VirtualBox 4.1 및 확장팩 설치
-http://download.virtualbox.org/virtualbox/
글쓰는 현재 최신버전이 4.1.6 이다. 확장팩도 같이 다운받는다
# rpm -Uvh VirtualBox-4.1-4.1.6_74727_rhel5-1.x86_64.rpm

-확장팩 설치
# VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-4.1.6-74713.vbox-extpack

부가기능 설치 위한 VBoxGuestAdditions_4.1.6.iso 파일도
다운받아 놓는다.

- vbox 사용자 추가하고 비밀번호 정한다. 비번을 kawkf123 이라고 하자
# useradd -g vboxusers vbox
# passwd vbox
만약 홈디렉토리를 /home/vbox 가 아니라 용량큰 다른 곳에 마운트한
/data/vbox 와 같은 위치를 사용한다면 -d /data/vbox 와 같이 추가

- /etc/vbox/vbox.cfg 파일 생성
[root@home3 ~]# cat /etc/vbox/vbox.cfg
VBOXWEB_USER=vbox
VBOXWEB_HOST=222.231.xxx.xxx
VBOXWEB_PORT=18083
VBOXWEB_LOGFILE=/var/log/vbox.log

와 같이 하고 반드시 /var/log/vbox.log 를 만들어줌
# touch /var/log/vbox.log
# chmod 600 /var/log/vbox.log
# chown vbox.vboxusers /var/log/vbox.log
이렇게 안만들어주고 데몬시작하니 말도 없이 까칠하게 생깜.
VBOXWEB_HOST=222.231.xxx.xxx 이 부분을
VBOXWEB_HOST=0.0.0.0 으로 하기도 한담.
18083 포트가 막혀있으면 열어준다.


5) Phpvirtualbox 4.1 설치
- http://code.google.com/p/phpvirtualbox/
위 링크에서 phpvirtualbox-4.1-5.zip 다운받아서 웹루트 적당한 곳에
압축을 푼다.
만약 Virtualbox 4.0 이 설치되었다면 phpvirtualbox 도 4.0 버전대를 사용
해야 할꺼다.
config.php-sample 파일을 config.php 로 복사한 후 편집기로 열어서 상단에

var $username = 'vbox';
var $password = 'kawkf123';
var $location = 'http://222.231.xxx.xxx:18083/';`

여기 username,password 는 4)에서 생성한 리눅스 서버 vbox 계정정보다.

이 웹접속 위치가 http://www.mysite.com/phpvirtualbox/index.php 라고 하자

어떤데서는 아래 명령어 줘야한다고함. vbox 계정으로 전환한 뒤 해야하거나.
# VBoxManage setproperty websrvauthlibrary null


6) 접속방법

# /etc/init.d/vboxballoonctrl-service start
# /etc/init.d/vboxdrv start
# /etc/init.d/vboxweb-service start
와 같이 데몬을 시작한다.
만약 위 3)에서 dkms 커널모듈이 설치가 불안정하게 되었다면
# /etc/init.d/vboxdrv setup 로 재설치할 수도 있다.
위 3 개 데몬 실행에 오류가 없고 18083 포트가 열렸고,프로세스가
# ps aux |grep vbox
vbox 32002 0.0 0.1 149588 4268 ? Sl 18:26 0:00 /usr/lib/virtualbox/vboxwebsrv --background -H 222.231.xxx.xxx -p 18083 -F /var/log/vbox.log
vbox 32007 0.0 0.0 119560 2200 ? S 18:26 0:00 /usr/lib/virtualbox/VBoxXPCOMIPCD
vbox 32012 0.0 0.1 140076 5620 ? Sl 18:26 0:00 /usr/lib/virtualbox/VBoxSVC --auto-shutdown
와 같이 보이면 일단 된거다.

http://www.mysite.com/phpvirtualbox/index.php 와 같이 접속하면
로그인 아이디 비번 물을꺼다.
여기에 위에서 vbox 서버 계정 정보를 넣으려고 삽질하면 안.된.다. 나처럼 -_-;;
아이디 비번에 모두 admin 이라고 넣어야 한다.
로그인후 메뉴에서 admin 비밀번호를 바꾸면 된다.
보이는 화면은 그냥 virtualbox 와 비슷할꺼다.
..
이제 알아서 직관적으로 사용하면 된다.




### 참고
http://codefat.com/tag/phpvirtualbox/
http://www.oss.kr/12792
2011/12/06 09:39 2011/12/06 09:39

[System Requirement]

Software requirements

Windows Server 2008 R2 or Windows Server 2008 SP2 이상

SQL 2005 640bit with SP2 or SQL 2008 64bit SP1 + 누적패치(hotfix) or SQL 2008 R2 64-bit

.NET 3.5 with SP1

Hardware requirements

This part is very short since it applies to

  • a single server with built-in database and
  • Server farm installations including a single server or multiple servers.

So according to Microsoft TechNet [1] you need:

  • Processor
    Beta:
    64-bit, dual processor, 3 GHz
    RTM: 64-bit, four cores
  • RAM
    4 GB for stand-alone or evaluation installation
    and 8 GB for single server and multiple server farm installation for production use
  • Hard disk
    80 GB
  • Other
    DVD drive

[설치 환경]

Windows Server 2008 R2

SQL Server 2008 SP1

[사전 준비 작업]

IIS 추가

응용 프로그램 서버 추가

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

SQL 2008 설치

사용자 삽입 이미지
 

SQL 2008 SP1: http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=66ab3dbb-bf3e-4f46-9559-ccc6a4f9dc19

IE ESC 구성 - 사용 안함

사용자 삽입 이미지
 

[SharePoint 2010 설치]

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

재부팅 후 프로그램 설치가 자동으로 진행

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

서버 팜 선택

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

[SharePoint 제품 구성 마법사]

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

SQL 2008 SP1 version: 10.0.2541.0

SQL 2008 SP1 누적패치(hotfix) 구하는 방법: http://support.microsoft.com/kb/970315

SQL 패치 후 SharePoint 제품 구성 마법사를 다시 실행하여 제품 구성을 다시 진행

패스워드 복잡성 및 8자 이상 요구

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

사용자 삽입 이미지
 

팀 사이트

사용자 삽입 이미지
 

2011/12/05 18:15 2011/12/05 18:15
최신 버전이 있긴 하지만. 최신 버전은 30일 날짜 제한이 있어서..
4.1을 올렸음.
2011/11/23 19:32 2011/11/23 19:32