- Focus

View.dispatchKeyEvent() : focus를 붙잡아서 처리할수 있다.

UI 이벤트 발생
-> Activity(dispatch~로 시작되는 method 호출)
-> View(호출)
-> Activity(on~로 시작되는 method 호출)

Android에서는 기본적으로 Focus 처리를 합리적으로 처리되게끔
되어 있으나, Focus 처리를 바꾸고 싶다면, 아래 XML속성으로 쉽게
변경할수 있다.
(nextFocusLeft, nextFocusRight, nextFocusUp, nextFocusDown)
ex)
<LinearLayout android:orientation="vertical" ... >
<Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... />
<Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... />
</LinearLayout>

View.requestFocus() ( = setFocus() ) 강제 포커스

<Intent>
대상이 명확할때는 intent filter가 필요없다(Explicit intent)
대상이 명확하지 않을때는 intent filter가 필요함(Implicit intent)

<Notification>
- Toast
- Status bar notification(맨위에 Title bar)
주로 Service(화면에 보이지 않는)에서 사용됨.

1. Get a reference to the NotificationManager.
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager =(NotificationManager)getSystemService(ns);

2.  Instantiate the Notification.
int icon = R.drawable.notification_icon;      // icon
CharSequence tickerText = "Hello";           // text
long when = System.currentTimeMillis();   // msg 발생 시간
Notification notification = new Notification(icon, tickerText, when);
notification.flags |= notification.FLAG_AUTO_CANCEL; <= 확인하면 위에
아이콘이 자동으로 사라짐!!!!

3. Define the Notification's expanded message and Intent.(이벤츠를 펼칠때)
Context context = getApplicationContext();
CharSequence contentTitle = "My notification";
CharSequence contentText = "Hello World!";
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

4 Pass the Notification to the NotificationManager.
private static final int HELLO_ID = 1;
mNotificationManager.notify(HELLO_ID, notification);

- Dialog notification

<AdapterView>

배열을 resource로 만들기.
contact의 데이터를 접근하려면 permission이 필요하다.
AndroidManifest.xml에서 uses-permission(android.permission.READ_CONTACTS)
을 추가한다.


Adapter는 데이터만이 아닌 +@(항목에 사용될 View)도 설정이 가능하다.
- ArrayAdapter : 배열 객체
ex)
Spinner s1 = (Spinner)findViewById(R.id.spinner1);
ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(
this, R.array.colors, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
s1.setAdapter(adapter);
final String[] arr = getResources().getStringArray(R.array.colors); 
s1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(SpinnerTest.this, arr[position] + " 선택됨",
Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});

resource data를 code level에서 접근하기
final String[] arr = getResources().getStringArray(R.array.colors);  

- SimpleCursorAdapter : database 데이터(Content Provider)
ex)
String[] PROJECTION = new String[] { People._ID, People.NAME };
Spinner s2 = (Spinner) findViewById(R.id.Spinner02);
Cursor cur = managedQuery(People.CONTENT_URI, PROJECTION, null, null, null);
SimpleCursorAdapter adapter2 = new SimpleCursorAdapter(this,
android.R.layout.simple_spinner_item, cur,
new String[] { People.NAME }, new int[] { android.R.id.text1 });
adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
s2.setAdapter(adapter2);

Contect Provider에 접근할때 URI로써 접근한다.
Avtivity.managedQuery() 통해서 가져옴.(Cursor 리턴)
항목선택에대한 처리는 AdapterView에서 처리한다.

<Data Storage>
각 응용프로그램의 데이터는 공유되지 않는다.
(단. Content provider를 통해서 접근 가능하다.)

- Preferences

 어플리케이션 데이터를 간단하게 저장(XML로저장된 파일)
DDMS-File Exporer-data-data-package 이름-shared_prefs 에 파일 존재함.

ex)
@Override protected void onPause() {
super.onPause();
// SharedPreferences setting =getPreferences(MODE_PRIVATE);
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
Editor editor = setting.edit();
editor.putString("mytext", edit.getText().toString());
editor.commit();
}

@Override protected void onResume() {
super.onResume();
// SharedPreferences setting =getPreferences(MODE_PRIVATE);
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
edit.setText(setting.getString("mytext", ""));
}

getPreferences() : 같은 응용프로그램내에서 다른 Activiry 접근 불가능.
getSharedPreferences() : 내응용프로그램내의 다른 Avtivity 들 간의 데이터 접근.

MODE_PRIVATE : 나혼자 사용하겠음. (-rw-rw---) 소유자:그룹:기타
MODE_WORLD_READABLE : 공유할수는 있지만 파일의 위치를 알아야하므로 비추

edit() 과 commit() 사이에 저장한다.

settings값들을 저장하기에 적합하다.

- Files
ex)
FileOutputStream out = null;
FileInputStream in = null;

try {
if (v == btnWrite) {
try {
out = openFileOutput("mydata.txt", MODE_PRIVATE);
out.write(text.getText().toString().getBytes());
}
catch (IOException e) {
e.printStackTrace();
}
}
else if (v == btnRead) {
try {
in = openFileInput("mydata.txt");
text.setText("");
final int length = 256;
byte[] buf = new byte[length];
int n;
String str;
while ((n = in.read(buf, 0, length)) != -1) {
str = new String(buf, 0, n);
text.append(str);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
}
catch (IOException e) {
e.printStackTrace();
}
}

openFileOutput() openFileInput() , 이름에는 Path sperator는 추가될수 없다.
(즉 "/sdcard/mytext.txt" 로 파일을 열수 없다.) 
아래와 같이 FileOutputStream constructor로서 path를 포함하여 파일을 열수 있다.
ex)
out = new FileOutputStream("/sdcard/mydata.txt");
in = new FileInputStream("/sdcard/mydata.txt");
(Java에서는 permission 개념이 없으므로 위의 Java class로 파일을 오픈하면,
 권한이 rw로 되어 있다.)

- Databases

SQLite3 명령어 : 따로 공부 필요함.
.schema : 테이블의 상태를 알수 있음.
.databases : 모든 테이블 보여줌
...

데이터베이스를 사용하고 싶다면.
1. SQLiteOpenHelper을 subclass 해서 몇가지 Method를 재정의한다.
ex)
private static class MyDatabaseHelper extends SQLiteOpenHelper {

@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
//DB 생성시
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
//DB 업데이트시 수정 작업을 도와줌
}
// 초기생성자의 매개변수는 사용자 임의로 변경가능하다.
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
}

SQLiteDatabase mDb = mDbHelper.getWritableDatabase(); // 실제 데이터베이스 생성

데이터 추가
ex)
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_TITLE, title);
initialValues.put(KEY_BODY, body);
mDb.insert(DATABASE_TABLE, null, initialValues);

데이터 삭제
ex)
mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;  
 
데이터 가져오기
ex)
mDb.query(DATABASE_TABLE, new String[] 
{KEY_ROWID, KEY_TITLE, KEY_BODY}, null, null, null, null, null);

데이터 업데이트
ex)
ContentValues args = new ContentValues();
args.put(KEY_TITLE, title);
args.put(KEY_BODY, body);
mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;

Notepad Tutorial 참조.


- Content Providers

데이터 공유 할수 있는 공식적인(표준적) 방법이다.

두가지 방법
1. 기존의 Content provider를 추가한다.
2. 새로운 Content provider를 생성한다.

Content provider는 단순한 테이블로 구성되어 있다.(공식적으로 id는 _ID로 되어 있다.)
Cursor를 통해서 record를 이동하여 참조할수 있다.

Database의 접근은 URI를 통해서 접근한다.(문자열. "content://"로 시작됨.)
(즉 DB정보를 몰라도 하나의 주소형태로 접근가능하다)

Android에서 기본적으로 제공하는 URI
android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI
android.provider.CallLog.Calls.CONTENT_URI
...

질의 방법에는 두가지가 있는데,ContentResolver.query(), Activity.managedQuery() 
Activity는 Cursor(resource)를 life cycle에 의해서 리소스 할당 해제가 자동으로 
관리되므로 Activity.managedQuery()를 주로 사용한다.
Activity.startManagingCursor(Cursor c);

실제 마지막 데이터에 접근하려면 id값이 추가로 필요한다.
ContentUris.withAppendedId(), Uri.withAppendedPath() 통해서 추가하여 URI로
사용된다.
public static Uri withAppendedId(Uri contentUri, long id)
public static Uri withAppendedPath(Uri baseUri, String pathSegment)

ex)
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
// id가 23인 people에 접근하는 URI
Cursor cur = managedQuery(myPerson, null, null, null, null);
// cur는 단 특정 id(23)을 지정하였으므로 하나의 내용만 가지고 있다.


ex) content 접근 예제(주의 permission 필요함 android.permission.READ_CONTACTS)
String[] projection = new String[] { People._ID, People.NAME, People.NUMBER };
// Get the base URI for the People table in the Contacts content
// provider.
Uri contacts = People.CONTENT_URI;
// Make the query.(오름차순)
Cursor managedCursor = managedQuery(contacts, projection, null,
null, People.NAME + " ASC");
getColumnData(managedCursor);

...


private void getColumnData(Cursor cur){
if (cur.moveToFirst()) {  // cursor의 맨앞으로
String name;
String phoneNumber;
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
do {
// Get the field values
name = cur.getString(nameColumn);
phoneNumber = cur.getString(phoneColumn);
// Do something with the values.
Log.d("test", "name="+name+", phone="+phoneNumber);
...
} while (cur.moveToNext());
}
}

Cursor는 가져온 데이터를 출력용으로 사용하고,
Data수정은 ContentResolver() 통해서 수정가능하다.

ex) data 수정
ContentValues values = new ContentValues(); // DB에 추가할때 사용됨.
// Add Abraham Lincoln to contacts and make him a favorite.
values.put(People.NAME, "Punbear");
// 1 = the new contact is added to favorites
// 0 = the new contact is not added to favorites
values.put(People.STARRED, 1);  // 즐겨찾기 추가
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
// 리턴되는 uri는 추가된 데이터의 URI

ex) data 추가(위의 코드와 연결됨)
Uri phoneUri = null;
Uri emailUri = null;
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "01047150000");
getContentResolver().insert(phoneUri, values);
// Now add an email address in the same way.
emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);
values.clear();
// ContactMethods.KIND is used to distinguish different kinds of
// contact methods, such as email, IM, etc.
values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);
values.put(People.ContactMethods.DATA, "punbear@gmail.com");
values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri, values);

Provider 종류 아래 URL 참조

Contacts : 연락처(1.6 버전 이하)
ContactsContract : 연락처(2.0 버전 이상)
MediaStore : 미디어관련
...

1.6까지는 web과 1개만 sync 되었었는데 2.0(API Level 5) 로 와서는 3개까지 지원됨.
실제로 전화기 관련 app 개발시 ContactsContract 를 통해서 작업해야함. Contact 는 참고!!

- NDK란??
NDK : Java에서 C code를 결합하는거 ?
NDK를 사용하는 주요이유는 기존에 사용하는 c코드 및 리눅스 레벨에서 사용되는 코드를
그대로 사용하기 위해서, OpenGL, Android 게임 프로그래밍등등..
JNI(C code Interfaces)

- Binder
- Service 
- AIDL(Android Interface Definition Language) (Java 문법 아님) 기법. 아래 URL 참조

Font는 res가 아닌 assets 폴더 안에다가 넣는다(자동화 되지 않음)
ex) Fontset
Typeface typeface = Typeface.createFromAsset(getContext(), getAssets(), "gulim.ttf");
setTypeface(typeface);

- 이클립스 단축키
F4 : class Hierarchy를 볼수 있음.