'개발'에 해당되는 글 188건
- 2011.06.13 jQuery select box jQuery checkbox 15
- 2011.03.21 [android] C2DM을 이용한 push notification 12
- 2011.03.21 android blowser history 정보 조회 4
- 2011.03.21 webview plugin 설정 6
- 2011.02.16 안드로이드에서의 dp, dpi, px 8
- 2011.01.05 안드로이드 수신/발신 정보 가져오기 android call log 9
- 2011.01.03 다이얼로그 생성시 BadTokenException 이슈 3
- 2010.12.28 안드로이드 Intent에서 앱 호출하는 방법을 정리 16
- 2010.12.09 Android Bitmap Object Resizing Tip 1
- 2010.12.08 이클립스 명령어 단축기
jQuery select box jQuery checkbox
jQuery로 선택된 값 읽기
$("#select_box option:selected").val();
$("select[name=name]").val();
jQuery로 선택된 내용 읽기
$("#select_box option:selected").text();
선택된 위치
var index = $("#test option").index($("#test option:selected"));
-------------------------------------------------------------------
$("#myselect").append("<option value='1'>Apples</option>");
$("#myselect").append("<option value='2'>After Apples</option>");
// Add options to the start of a select
$("#myselect").prepend("<option value='0'>Before Apples</option>");
// Replace all the options with new options
$("#myselect").html("<option value='1'>Some oranges</option><option value='2'>More Oranges</option><option value='3'>Even more oranges</option>");
// Replace items at a certain index
$("#myselect option:eq(1)").replaceWith("<option value='2'>Some apples</option>");
$("#myselect option:eq(2)").replaceWith("<option value='3'>Some bananas</option>");
// Set the element at index 2 to be selected
$("#myselect option:eq(2)").attr("selected", "selected");
// Set the selected element by text
$("#myselect").val("Some oranges").attr("selected", "selected");
// Set the selected element by value
$("#myselect").val("2");
// Remove an item at a particular index
$("#myselect option:eq(0)").remove();
// Remove first item
$("#myselect option:first").remove();
// Remove last item
$("#myselect option:last").remove();
// Get the text of the selected item
alert($("#myselect option:selected").text());
// Get the value of the selected item
alert($("#myselect option:selected").val());
// Get the index of the selected item
alert($("#myselect option").index($("#myselect option:selected")));
// Alternative way to get the selected item
alert($("#myselect option:selected").prevAll().size());
// Insert an item in after a particular position
$("#myselect option:eq(0)").after("<option value='4'>Some pears</option>");
// Insert an item in before a particular position
$("#myselect option:eq(3)").before("<option value='5'>Some apricots</option>");
// Getting values when item is selected
$("#myselect").change(function() {
alert($(this).val());
alert($(this).children("option:selected").text());
});
var checkedValue = $('#checkboxToggle').attr('checked') ? 'checked' : '';
$(':checkbox').attr('checked', checkedValue);
[android] C2DM을 이용한 push notification
C2DM 소개
등록
AUTH 정보 받기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 |
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class Auth_android { private static String EMAIL = "메일주소" ; private static String PASS = "메일주소 비밀번호" ; private static String SOURCE = "알아보기 쉽게 적힌 프로그램 정보(예 : company-project-version. apple-iphone-4.2.1)" ; public static void main( String[] args ) throws Exception { try { StringBuffer postDataBuilder = new StringBuffer(); postDataBuilder.append( "Email=" + EMAIL); postDataBuilder.append( "&Passwd=" + PASS); postDataBuilder.append( "&accountType=GOOGLE" ); postDataBuilder.append( "&source=" + SOURCE); postDataBuilder.append( "&service=ac2dm" ); byte [] postData = postDataBuilder.toString().getBytes( "UTF8" ); URL url = new URL(HOST); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput( true ); conn.setUseCaches( false ); conn.setRequestMethod( "POST" ); conn.setRequestProperty( "Content-Type" , "application/x-www-form-urlencoded" ); conn.setRequestProperty( "Content-Length" ,Integer.toString(postData.length)); OutputStream out = conn.getOutputStream(); out.write(postData); out.close(); BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null ) { System.out.println(inputLine); } } catch (Exception e) { e.printStackTrace(); } } } // 결과 SID=DQAAAL0AAADVvGAVXO....... LSID=DQAAAL8AAAAp5iSaE8h......... Auth=DQAAAL8AAADrYkFbucd............ // 요것만 사용함 |
registration_id 받기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 |
<? xml version = "1.0" encoding = "utf-8" ?> package = "mint.TestC2dm" android:versionCode = "1" android:versionName = "1.0" android:minSdkVersion = "8" > < permission android:name = "mint.TestC2dm.permission.C2D_MESSAGE" android:protectionLevel = "signature" /> < uses-permission android:name = "mint.TestC2dm.permission.C2D_MESSAGE" /> < uses-permission android:name = "com.google.android.c2dm.permission.RECEIVE" /> < uses-permission android:name = "android.permission.INTERNET" /> < uses-permission android:name = "android.permission.WAKE_LOCK" /> < uses-permission android:name = "android.permission.GET_ACCOUNTS" /> < uses-permission android:name = "android.permission.USE_CREDENTIALS" /> < uses-permission android:name = "android.permission.READ_PHONE_STATE" /> < application android:icon = "@drawable/icon" android:label = "@string/app_name" > < activity android:name = ".TestC2dm" android:theme = "@android:style/Theme.Black.NoTitleBar" android:label = "@string/app_name" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > < receiver android:name = ".C2DMReceiver" android:permission = "com.google.android.c2dm.permission.SEND" > < intent-filter > < action android:name = "com.google.android.c2dm.intent.RECEIVE" /> < category android:name = "mint.TestC2dm" /> </ intent-filter > < intent-filter > < action android:name = "com.google.android.c2dm.intent.REGISTRATION" /> < category android:name = "mint.TestC2dm" /> </ intent-filter > </ receiver > < activity android:name = ".showMsg" android:screenOrientation = "portrait" android:theme = "@android:style/Theme.Translucent" android:launchMode = "singleTask" > < intent-filter > < action android:name = "android.intent.action.AlertDialogs" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > </ application > </ manifest > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 |
package mint.TestC2dm; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.view.View; public class TestC2dm extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); } // 등록 public void onRegist(View v) { Intent registrationIntent = new Intent( "com.google.android.c2dm.intent.REGISTER" ); registrationIntent.putExtra( "app" , PendingIntent.getBroadcast( this , 0 , new Intent(), 0 )); registrationIntent.putExtra( "sender" , "메일주소" ); startService(registrationIntent); } // 해지 public void onUnregist(View v) { Intent unregIntent = new Intent( "com.google.android.c2dm.intent.UNREGISTER" ); unregIntent.putExtra( "app" , PendingIntent.getBroadcast( this , 0 , new Intent(), 0 )); startService(unregIntent); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
<? xml version = "1.0" encoding = "utf-8" ?> android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < Button android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "가입" android:onClick = "onRegist" /> < Button android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "해지" android:onClick = "onUnregist" /> </ LinearLayout >l |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 |
package mint.TestC2dm; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; public class C2DMReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e( "###############" , "onReceive" ); if (intent.getAction().equals( "com.google.android.c2dm.intent.REGISTRATION" )) { handleRegistration(context, intent); } else if (intent.getAction().equals( "com.google.android.c2dm.intent.RECEIVE" )) { handleMessage(context, intent); } } private void handleRegistration(Context context, Intent intent) { Log.e( "###############" , "handleRegistration" ); String registration = intent.getStringExtra( "registration_id" ); if (intent.getStringExtra( "error" ) != null ) { // Registration failed, should try again later. } else if (intent.getStringExtra( "unregistered" ) != null ) { Log.e( "@@@@@@@@unregistered" , "unregistered" ); } else if (registration != null ) { // Send the registration ID to the 3rd party site that is sending the messages. // This should be done in a separate thread. // When done, remember that all registration is done. Log.e( "@@@@@@@@registration_id" , registration); } } private void handleMessage(Context context, Intent intent) { Log.e( "###############" , "handleMessage" ); String title = intent.getStringExtra( "title" ); String msg = intent.getStringExtra( "msg" ); // 화면 깨우기 PushWakeLock.acquireCpuWakeLock(context); Intent i = new Intent(context, showMsg. class ); Bundle b = new Bundle(); b.putString( "title" , title); b.putString( "msg" , msg); i.putExtras(b); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 |
package mint.TestC2dm; import android.app.KeyguardManager; import android.content.Context; import android.os.PowerManager; import android.util.Log; class PushWakeLock { private static PowerManager.WakeLock sCpuWakeLock; private static KeyguardManager.KeyguardLock mKeyguardLock; private static boolean isScreenLock; static void acquireCpuWakeLock(Context context) { Log.e( "PushWakeLock" , "Acquiring cpu wake lock" ); if (sCpuWakeLock != null ) { return ; } PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); sCpuWakeLock = pm.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "I'm your father" ); sCpuWakeLock.acquire(); // KeyguardManager km = (KeyguardManager)context.getSystemService(context.KEYGUARD_SERVICE); // mKeyguardLock = km.newKeyguardLock("key guard"); // if (km.inKeyguardRestrictedInputMode()) { // mKeyguardLock.disableKeyguard(); // isScreenLock = true; // } else { // isScreenLock = false; // } } static void releaseCpuLock() { Log.e( "PushWakeLock" , "Releasing cpu wake lock" ); // if (isScreenLock) { // mKeyguardLock.reenableKeyguard(); // isScreenLock = false; // } if (sCpuWakeLock != null ) { sCpuWakeLock.release(); sCpuWakeLock = null ; } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 |
package mint.TestC2dm; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class showMsg extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); String title, msg; Bundle bun = getIntent().getExtras(); title = bun.getString( "title" ); msg = bun.getString( "msg" ); AlertDialog.Builder alertDialog = new AlertDialog.Builder(showMsg. this ); alertDialog.setPositiveButton( "닫기" , new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { PushWakeLock.releaseCpuLock(); showMsg. this .finish(); } }); alertDialog.setNegativeButton( "보기" , new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity( new Intent().setClassName(getPackageName(), getPackageName()+ ".TestC2dm" )); PushWakeLock.releaseCpuLock(); showMsg. this .finish(); } }); alertDialog.setTitle(title); alertDialog.setMessage(msg); alertDialog.show(); // 폰 설정의 조명시간을 가져와서 해당 시간만큼만 화면을 켠다. int defTimeOut = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, 15000 ); TimerTask task = new TimerTask() { @Override public void run() { PushWakeLock.releaseCpuLock(); } }; Timer timer = new Timer(); timer.schedule(task, defTimeOut); } } |
1
2
3
4
5 |
###############: onReceive ###############: handleRegistration registration_id: APA91bGxex5sJi5hbeQkGUaURZo8....... |
메세지 보내기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 |
import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; public class Push { private static String AUTH = "DQAAAL8AAADrYkFbucd............" ; // DB에 저장된 디바이스 토큰 목록 private static String[] arrId = { "APA91bGxex5sJi5hbeQkGUaURZo8......." // "APA91bHCRg6NhgMYv8Rbb2LVCoj4al......." }; public static void main( String[] args ) throws Exception { for ( int i= 0 ; i<arrId.length; i++) { androidPush(arrId[i], "제목" , "내용" ); } } public static void androidPush(String regId, String title, String msg) throws Exception { try { StringBuffer postDataBuilder = new StringBuffer(); postDataBuilder.append( "registration_id=" + regId); // 등록ID postDataBuilder.append( "&collapse_key=1" ); postDataBuilder.append( "&delay_while_idle=1" ); postDataBuilder.append( "&data.title=" + URLEncoder.encode(title, "UTF-8" )); // 제목 postDataBuilder.append( "&data.msg=" + URLEncoder.encode(msg, "UTF-8" )); // 내용 byte [] postData = postDataBuilder.toString().getBytes( "UTF8" ); URL url = new URL(HOST); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput( true ); conn.setUseCaches( false ); conn.setRequestMethod( "POST" ); conn.setRequestProperty( "Content-Type" , "application/x-www-form-urlencoded" ); conn.setRequestProperty( "Content-Length" ,Integer.toString(postData.length)); conn.setRequestProperty( "Authorization" , "GoogleLogin auth=" +AUTH); OutputStream out = conn.getOutputStream(); out.write(postData); out.close(); conn.getInputStream(); } catch (Exception e) { e.printStackTrace(); } } } |
결과
문제점
장점
android blowser history 정보 조회
mCur.moveToFirst();
if (mCur.moveToFirst() && mCur.getCount() > 0) {
while (mCur.isAfterLast() == false) {
Log.v("titleIdx", mCur.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
Log.v("urlIdx", mCur.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
mCur.moveToNext();
}
}
아카이브 파일
webview plugin 설정
Log.d("","type = " + type);
webview = (WebView) findViewById(R.id.webview); //webView 설정
if(type == 0 ){
webview.getSettings().setPluginState(PluginState.ON); //플러그인 사용
}else if(type == 1 ){
webview.getSettings().setPluginState(PluginState.OFF); //플러그인 미사용
}else if(type == 2 ){
webview.getSettings().setPluginState(PluginState.ON_DEMAND); //플러그인 사용자 요청시
}else {
}
아카이브 파일
안드로이드에서의 dp, dpi, px
dp를 평소에 즐겨사용했지만, 확실히 무슨 개념인지를 몰라 조사하던중에
잘 정리되어있는 블로그를 발견 !
수정(101130)
단순히 px을 dp로 변환하고자 한다면 dp = px * 0.66625로 계산한다.(해상도480*800 기준)
480*800 px => 320*533 dp이다.
출처 : http://blog.naver.com/dythmall?Redirect=Log&logNo=30096162077
dp (dip, density independent pixel) 은 안드로이드에서 여러 화면 크기를 서포트 해주기 위해서 만든 유닛이다.
[출처] 안드로이드의 dp와 px 그들은 무었인가?|작성자 dythmall
안드로이드 수신/발신 정보 가져오기 android call log
폰 사용량을 알기 위해 안드로이드 수신/발신 정보를 가져오려고 한다.
안드로이드 수신/발신 정보 가져오기 CallLog 이용
- private void getHistory() {
- String[] projection = { CallLog.Calls.CONTENT_TYPE, CallLog.Calls.NUMBER, CallLog.Calls.DURATION, CallLog.Calls.DATE };
Cursor cur = managedQuery(CallLog.Calls.CONTENT_URI, null, CallLog.Calls.TYPE + "= ?",
new String[]{ String.valueOf(CallLog.Calls.OUTGOING_TYPE) }, CallLog.Calls.DEFAULT_SORT_ORDER);
Log.d("db count=", String.valueOf(cur.getCount()));
Log.d("db count=", CallLog.Calls.CONTENT_ITEM_TYPE);
Log.d("db count=", CallLog.Calls.CONTENT_TYPE);
if(cur.moveToFirst() && cur.getCount() > 0) {
while(cur.isAfterLast() == false) {
StringBuffer sb = new StringBuffer();
sb.append("call type=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.TYPE)));
sb.append(", cashed name=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.CACHED_NAME)));
sb.append(", content number=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.NUMBER)));
sb.append(", duration=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.DURATION)));
sb.append(", new=").append(cur.getString(cur.getColumnIndex(CallLog.Calls.NEW)));
sb.append(", date=").append(timeToString(cur.getLong(cur.getColumnIndex(CallLog.Calls.DATE)))).append("]");
cur.moveToNext();
Log.d("call history[", sb.toString());
}
} - }
- androidManifest.xml에 추가
- <uses-permission android:name="android.permission.READ_CONTACTS" />
아주 편리하게도 api가 있다. CallLog를 이용해서 DB 에 저장된 내용을 가져올 수 있다.
주석 없이도 볼 수 있을 거란 생각에 주석은 생략한다.
CallLog의 경우 사용자가 전화통화내역 삭제를 누를 경우 볼 수 없다.
그리하야 별도로 call log 를 쌓으려고 한다.
방법은 직접 action을 감지하고, 통화 내역을 계산하여 별도의 DB에 쌓는 것이다.
안드로이드 수신/발신 정보 모니터링 하기
- private String LOG_TAG = "CheckCall";
private static int pState = TelephonyManager.CALL_STATE_IDLE; - public void onReceive(Context context, final Intent intent) {
TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telManager.listen(new PhoneStateListener(){
public void onCallStateChanged(int state, String incomingNumber){
if(state != pState){
if(state == TelephonyManager.CALL_STATE_IDLE){
Toast.makeText(mContext, "IDLE" + String.valueOf(System.currentTimeMillis()), Toast.LENGTH_SHORT).show();
}
else if(state == TelephonyManager.CALL_STATE_RINGING){
Toast.makeText(mContext, "RINGING" + String.valueOf(System.currentTimeMillis() +incomingNumber), Toast.LENGTH_SHORT).show();
}
else if(state == TelephonyManager.CALL_STATE_OFFHOOK){
Toast.makeText(mContext, "OFFHOOK" + String.valueOf(System.currentTimeMillis()), Toast.LENGTH_SHORT).show();
}
pState = state;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
if(Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())){
Log.i(LOG_TAG, "out=" + intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
}
} -
AndroidManifest.xml
-
<receiver android:name=class name>
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver> -
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
- 참고자료
이정도 코드 역시 주석이 없이도 쉬이 할 수 있을 것 같아서 그냥 붙여 넣었다. 매니페스트에 권한과 인텐트 필터 넣는 것을 몰라 한참을 리서치 했다.
-
state
- 발신시
-
Intent.ACTION_NEW_OUTGOING_CALL -> offhook -> idle
- 수신시
-
Ring -> offhook -> idle
원문 :http://whdnfl21.springnote.com/pages/6833219
다이얼로그 생성시 BadTokenException 이슈
요즘 어플리케이션 개발에 한창입니다. 점점 안드로이드 플랫폼에 익숙해져 가는것 같습니다. 그러나 때로는 명확해 보이는 코드가 오류를 던져 대면 당혹스러워 지는군요. 이번 경우가 그렀습니다.
오류 발생 코드
- public void onCreate( Bundle $bundle ) {
- super.onCreate( $bundle ) ;
- setContentView( R.layout.ui_setting ) ;
- Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;
- dialogShowBtn.setOnClickListener( this ) ;
- }
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch( v.getId() ) {
- case R.id.dialogShowBtn :
- CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ;
- customDialog.show() ;
- break ;
- }
- }
public void onCreate( Bundle $bundle ) { super.onCreate( $bundle ) ; setContentView( R.layout.ui_setting ) ; Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ; dialogShowBtn.setOnClickListener( this ) ; } public void onClick(View v) { // TODO Auto-generated method stub switch( v.getId() ) { case R.id.dialogShowBtn : CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ; customDialog.show() ; break ; } }
오류 메세지
Uncaught handler: thread main exiting due to uncaught exception android.view.WindowManager$BadTokenException: Unable to add window — token null is not for an application
해결책
- public void onCreate( Bundle $bundle ) {
- super.onCreate( $bundle ) ;
- setContentView( R.layout.ui_setting ) ;
- Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;
- dialogShowBtn.setOnClickListener( this ) ;
- }
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch( v.getId() ) {
- case R.id.dialogShowBtn :
- CustomDialog customDialog = new CustomDialog( this ) ;
- customDialog.show() ;
- break ;
- }
- }
public void onCreate( Bundle $bundle ) { super.onCreate( $bundle ) ; setContentView( R.layout.ui_setting ) ; Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ; dialogShowBtn.setOnClickListener( this ) ; } public void onClick(View v) { // TODO Auto-generated method stub switch( v.getId() ) { case R.id.dialogShowBtn : CustomDialog customDialog = new CustomDialog( this ) ; customDialog.show() ; break ; } }
토의
일단 다이얼로그의 생성자에 인자로 보내는 Context를 getApplicationContext()로 얻어서 넘기는 대신에, Activity 자신을 직접 넘김으로써 오류를 잡는데는 성공했지만 아직까지는 명확하게 왜 이렇게 해야 하는지 알아가는 중입니다. 다만, 레퍼런스를 읽어보면 다이얼로그는 Owner Activity에 종속적으로 실행이 된다고 하는데 위에 오류 발생하는 코드를 보면 Activity를 알 길이 없습니다.
하지만 그렇다고 아래와 같은 코드도 BadTokenException 오류를 발생하기는 마찬가지였습니다.
- public void onCreate( Bundle $bundle ) {
- super.onCreate( $bundle ) ;
- setContentView( R.layout.ui_setting ) ;
- Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ;
- dialogShowBtn.setOnClickListener( this ) ;
- }
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch( v.getId() ) {
- case R.id.dialogShowBtn :
- CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ;
- customDialog.setOwnerActivity( this ) ;
- customDialog.show() ;
- break ;
- }
- }
public void onCreate( Bundle $bundle ) { super.onCreate( $bundle ) ; setContentView( R.layout.ui_setting ) ; Button dialogShowBtn = (Button) findViewById( R.id.dialogShowBtn ) ; dialogShowBtn.setOnClickListener( this ) ; } public void onClick(View v) { // TODO Auto-generated method stub switch( v.getId() ) { case R.id.dialogShowBtn : CustomDialog customDialog = new CustomDialog( getApplicationContext() ) ; customDialog.setOwnerActivity( this ) ; customDialog.show() ; break ; } }
관련예제
안드로이드 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
출처 : http://www.jopenbusiness.com/tc/oss/entry/Android-Intent-%ED%99%9C%EC%9A%A9-%EC%82%AC%EB%A1%80
Android Bitmap Object Resizing Tip
[Intro]
Android에서 사용하는 이미지는 Bitmap이라는 클래스에서 다~ 알아서 해줍니다.
그리고 이런 Bitmap Object를 쉽게 만들 수 있도록 도와주는
BitmapFactory 클래스 라는 것도 있습니다.
BitmapFactory는 여러가지 소스로 부터 Bitmap Object를 만들어 주는 일을 하는데,
전부 static이며 decodeXXX 라는 이름을 가진 메소드들로 이루어져 있습니다.
XXX에는 어떤 것으로 부터 decode를 하여
Bitmap Object를 만들어 낼지에 대한 말들이 들어 가겠죠.
[Decoding Methods]
BitmapFactory.decodeByteArray() 메소드는 Camera.PictureCallback 으로 부터 받은
Jpeg 사진 데이터를 가지고 Bitmap으로 만들어 줄 때 많이 사용 합니다.
Camera.PictureCallback에서 들어오는 데이터가 byte[] 형식이기 때문에
저 메소드를 사용 해야 하는 것이죠.
BitmapFactory.decodeFile() 메소드는 파일을 그대로 읽어 옵니다.
내부적으로는 파일 경로를 가지고 FileInputStream을 만들어서 decodeStream을 합니다.
그냥 파일 경로만 쓰면 다 해주는게 편리 한 것이죠.
BitmapFactory.decodeResource() 메소드는 Resource로 부터 Bitmap을 만들어 내며
BitmapFactory.decodeStream() 메소드는 InputStream으로 부터 Bitmap을 만들어 냅니다.
뭐 그냥 이름만 봐도 알 수 있는 것들이지요.
[OutOfMemoryError??]
보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데,
그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면
간단하게 처리 할 수 있습니다.
그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
아래의 예를 한번 보시죠.
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);
이미지 파일로부터 Bitmap을 만든 다음에
다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
보통이라면 저렇게 하는게 맞습니다.
읽어서, 줄인다.
그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
지금 Dev Phone에서 카메라로 촬영하면
기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,
과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?
실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면
아래와 같은 황당한 메세지를 발견 할 수 있습니다.
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
네... OutOfMemory 입니다.
더 이상 무슨 말이 필요 하겠습니까...
메모리가 딸려서 처리를 제대로 못합니다.
이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지,
하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
이것은 엔지니어들이 해결해야 할 문제 겠죠...
하지만 메모리 에러를 피할 수 있는 방법이 있습니다.
[BitmapFactory.Options.inSampleSize]
BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
같은 이름이지만 Signature가 다른 메소드의 차이점은
BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.
BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.
여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.
inSampleSize 옵션은,
애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.
inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.
2의 지수만큼 비례할 때 가장 빠르다고 합니다.
2, 4, 8, 16... 정도 되겠죠?
그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
inSampleSize 옵션에 4를 주어서 decoding 한 다음에,
Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);
당연한 이야기 이겠지만,
내가 원하고자 하는 사이즈가 딱 1/4 크기라면
Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.
inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.
[Appendix]
inSampleSize 옵션을 사용하면
SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.
[Outro]
Android의 기본 어플리케이션 소스를 분석 하다보면
상당히 테크니컬한 기법들을 많이 얻을 수 있습니다.
어떻게 이런 방법으로 만들었나 싶을 정도로 매우 정교하고 복잡하게 만들어져 있지요.
참 대단한 것 같습니다.
아 그리고 왜 dstWidth와 dstHeight 변수 선언이 없냐고 따지시는 분들 설마 없겠죠?
출처 : http://blog.naver.com/visualc98/79874750
이클립스 명령어 단축기
----- 소스 네비게이션 -----
Ctrl + 마우스커서(혹은 F3) : 클래스나 메소드 혹은 멤버를 상세하게 검색하고자 할때
Alt + Left, Alt + Right : 이후, 이전
Ctrl + O : 해당 소스의 메소드 리스트를 확인하려 할때
F4 : 클래스명을 선택하고 누르면 해당 클래스의 Hierarchy 를 볼 수 있다.
Alt + <-(->) : 이전(다음) 작업 화면
----- 문자열 찾기 -----
Ctrl + K : 찾고자 하는 문자열을 블럭으로 설정한 후 키를 누른다.
Ctrl + Shift + K : 역으로 찾고자 하는 문자열을 찾아감.
Ctrl + J : 입력하면서 찾을 수 있음.
Ctrl + Shift + J : 입력하면서 거꾸로 찾아갈 수 있음.
Ctrl + F : 기본적으로 찾기
----- 소스 편집 -----
Ctrl + Space : 입력 보조장치(Content Assistance) 강제 호출 => 입력하는 도중엔 언제라도 강제 호출 가능하다.
F2 : 컴파일 에러의 빨간줄에 커서를 갖져다가 이 키를 누르면 에러의 원인에 대한 힌트를 제공한다.
Ctrl + L : 원하는 소스 라인으로 이동
로컬 히스토리 기능을 이용하면 이전에 편집했던 내용으로 변환이 가능하다.
Ctrl + Shift + Space : 메소드의 가로안에 커서를 놓고 이 키를 누르면 파라미터 타입 힌트를 볼 수 있다.
Ctrl + D : 한줄 삭제
Ctrl + W : 파일 닫기
Ctrl + I : 들여쓰기 자동 수정
Ctrl + Shift + / : 블록 주석(/* */)
Ctrl + Shift + \ : 블록 주석 제거
Ctrl + / : 여러줄이 한꺼번에 주석처리됨. 주석 해제하려면 반대로 하면 된다.
Alt + Up(Down) : 위(아래)줄과 바꾸기
Alt + Shift + 방향키 : 블록 선택하기
Ctrl + Shift + Space : 메소드의 파라메터 목록 보기
Ctrl + Shift + O : 자동으로 import 하기
Ctrl + Shift + F4 : 열린 파일 모두 닫기
Ctrl + M : 전체화면 토글
Ctrl + Alt + Up(Down) :
Ctrl + , or . : 다음 annotation(에러, 워닝, 북마크 가능)으로 점프
Ctrl + 1 : 퀵 픽스
F3 : 선언된 변수로 이동, 메소드 정의부로 이동
Ctrl + T : 하이어라키 �b업 창 띄우기(인터페이스 구현 클래스간 이동시 편리)
Ctrl + O : 메소드나 필드 이동하기
Ctrl + F6 : 창간 전환, UltraEdit 나 Editplus 의 Ctrl + Tab 과 같은 기능
----- 템플릿 사용 -----
sysout 입력한 후 Ctrl + Space 하면 System.out.println(); 으로 바뀐다.
try 입력한 후 Ctrl + Space 하면 try-catch 문이 완성된다.
for 입력한 후 Ctrl + Space 하면 여러가지 for 문을 완성할 수 있다.
템플릿을 수정하거나 추가하려면 환경설정/자바/편집기/템플릿 에서 할 수 있다.
----- 메소드 쉽게 생성하기 -----
클래스의 멤버를 일단 먼저 생성한다.
override 메소드를 구현하려면, 소스->메소드대체/구현 에서 해당 메소드를 체크한다.
기타 클래스의 멤버가 클래스의 오브젝트라면, 소스->위임메소드 생성에서 메소드를 선택한다.
----- organize import -----
자바파일을 여러개 선택한 후 소스->가져오기 체계화 해주면 모두 적용된다.
----- 소스 코드 형식 및 공통 주석 설정 -----
환경설정 -> 자바 -> 코드 스타일 -> 코드 포멧터 -> 가져오기 -> 프로파일.xml 을 불러다가 쓰면 된다.
또한 다수의 자바파일에 프로파일을 적용하려면 패키지 탐색기에서 패키지를 선택한 후 소스 -> 형식화를 선택하면 된다.
환경설정 -> 자바 -> 코드 스타일 -> 코드 템플리트 -> 가져오기 -> 템플리트.xml 을 불러다가 쓰면 된다.
----- 에디터 변환 -----
에디터가 여러 파일을 열어서 작업중일때 Ctrl + F6 키를 누르면 여러파일명이 나오고 F6키를 계속 누르면 아래로
Ctrl + Shift + F6 키를 누르면 위로 커서가 움직인다.
Ctrl + F7 : 뷰간 전환
Ctrl + F8 : 퍼스펙티브간 전환
F12 : 에디터로 포커스 위치
- 이클립스 자주쓰는 단축키 -
Ctrl + / : 주석 처리 - 한 라인/블록에 대해 주석 처리 (추가 및 제거)
Ctrl + L : 특정 라인으로 이동
Ctrl + F6 : Editor 창간의 이동
Ctrl + F7 : View 이동 메뉴
Ctrl + F8 : Prespectives 이동 메뉴
Ctrl + D : 한라인 삭제 - 커서가 위치한 라인 전체를 삭제 한다.
Ctrl + J : Incremental find 이클립스 하단 상태 표시줄에 Incremental find 라고 표시되어 한 글자자씩 누를 때 마다 코드내의 일치하는 문자열로 이동 , 다시 Ctrl + J 를 누르면 그 문자열과 일치 하는 부분을 위/아래 방향키로 탐색이 가능하다.
Ctrl + N : 새로운 파일 / 프로젝트 생성
Ctrl + 1 (빠른교정) - 문 맥에 맞게 소스 교정을 도와 준다. 변수를 선언하지 않고 썼을경우 빨간색 에러 표시되는데 이 단축키를 적용하면 변수에 맞는 선언이 추가 되도록 메뉴가 나타난다.
Ctrl + 0 : 클래스 구조를 트리로 보기
Ctrl + Space : Cotent Assist - 소스 구문에서 사용 가능한 메소드, 멤버들의 리스트 메뉴를 보여준다.
Ctrl + PageUp , Ctrl + PageDown : Edit 창 좌우 이동 - Edit 창이 여러개 띄워져 있을경우 Edit 창간의 이동 한다.
Ctrl + Shift + Down : 클래스 내에서 다음 멤버로 이동
Ctrl + Shift + M : 해당 객체의 Import 문을 자동 생성 - import 추가 할 객체에 커서를 위치 시키고 단축키를 누르면 자동적으로 import 문이 생성
Ctrl + Shift + O : import 문을 자동 생성 - 전체 소스 구문에서 import 안된 클래스의 import 문을 생성해 준다.
Ctrl + Shift + G : 해당 메서드 / 필드를 쓰이는 곳을 표시 - View 영역에 Search 탭에 해당 메서드 / 필드를 사용하는 클래스를 표시 해준다.
Alt + Shift + R : Refactoring (이름변경) - Refactoing 으로 전체 소스에서 이름변경에 의한 참조 정보를 변경해 준다.
F3 : 선언 위치로 이동
F11 : 디버깅 시작
F8 : 디버깅 계속
F6 : 디버깅 한줄씩 실행(step over)
F5 : 디버깅 한줄씩 실행 함수 내부로 들어감 (step into)
F12 : Editor 창으로 이동 (Debugging 등 자동적으로 포커스가 이동 됐을경우 편리)
Alt + Up , Alt + Down : 줄 바꿈 - 해당 라인을 위 / 아래로 이동 시킨다.
Alt + Shift + S : Source Menu - 소스메뉴 (Import 추가 , Comment 추가 , 각종 Generator 메뉴) 가 나타난다.
Alt + Shift + Up : 블록설정 - 소스 코드를 블록 단위로 설정해 준다.
Alt + Shift + Down : 블록해제 - 소스 코드를 블록 단위로 해제한다.
Alt + Shift + J : 주석 생성 - 해당 메서드/클래스에 대한 주석을 템플릿을 생성해 준다.
sysout + (Ctrl + Space) : System.out.println() 문장 삽입 - 코드 템플릿을 이용해서 소스 구문을 추가
(Windows -> Preferences -> JAVA -> Editor -> Templates 에서 자주 쓰는 소스 구문을 추가시키면 <템플릿 이름> + (Ctrl + Space) 로 소스 문장을 완성 시킬 수 있다.)
Alt + Shift + Z : Surround With 메뉴 - try / catch 문이나 for , do , while 등을 해당 블록에 감싸주는 메뉴가 나타난다.
Ctrl + Shift + F : 코드 포맷팅 - 코드 내용을 문법 템플릿에 맞게 포맷팅(들여쓰기) 해준다.
Ctrl + Alt + Down: 한줄 복사후 아래에 복사 넣기 - Copy&Paste 대체하는 단축키. 커서가 위치한 라인을 복사해 밑줄에 생성해 준다.
Ctrl + Shift +X : 대문자로 변환
Ctrl + Shift + Y : 소문자로 변환
Ctrl + Shift + L : 모든 단축키의 내용을 표시해준다.
Ctrl + Shift + B : 현재 커서 라인에 Break point 설정
Ctrl + Shift + T : 클래스 찾기