- Use at your own risk! lol, ㅋㅋㅋ
내가 작업한 것 중에서 유용한 Tip 들을 정리 해본다.
// ---------------------------------------------
// :: AndroidManifest.xml
// ---------------------------------------------
* android:sharedUserId="..."
* for Froyo (Android 2.2 API Level 8 ~)
android:installLocation = {
"internalOnly",
"preferExternal",
"auto"
};
android:installLocation="auto"
* android:launchMode="singleTask"
* Prevent to restart Activity Life-Cycle when slide-out QWERTY keypad
* Landscape/Portrait mode event
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation"
* android:theme="@android:style/Theme.Translucent"
// ---------------------------------------------
// ---------------------------------------------
// :: Slide Strings
// ---------------------------------------------
// NOTE: Slide Strings
android:ellipsize = {
"marquee" -> slide
"end" -> "..."
}
android:singleLine="true"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
// main.xml
// ----------------------------------------------
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12.00sp"
android:gravity="left|center_vertical"
android:height="20.0dp"
android:width="300.0dp"
android:singleLine="true"
android:ellipsize="end"
android:focusable="true"
android:focusableInTouchMode="true"
android:text="">
// TextView Click Event
// ----------------------------------------------
// TextViewStr1
View.OnClickListener TextViewStr1Listener = new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Log.d( "LOG", "TextViewStr1Listener: focus = false" );
//m_TextViewStr_xxx.clearFocus();
m_TextViewStr1.setEllipsize( TruncateAt.MARQUEE );
m_TextViewStr1.setFocusable( true );
};
};
m_TextViewStr1.setOnClickListener( TextViewStr1Listener );
// Focus
View.OnFocusChangeListener focusTextViewStr1Listener = new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
// TODO Auto-generated method stub
if( hasFocus ) {
Log.d( "LOG", "[FOCUS] TextViewStr1Listener: focus = true" );
//m_TextViewStr_xxx.clearFocus();
m_TextViewStr1.setEllipsize( TruncateAt.MARQUEE );
m_TextViewStr1.setFocusable( true );
}
else {
Log.d( "LOG", "[FOCUS] TextViewStr1Listener: focus = false" );
m_TextViewStr1.setEllipsize( TruncateAt.END );
}
}
};
m_TextViewStr1.setOnFocusChangeListener( focusTextViewStr1Listener );
// ---------------------------------------------
// ---------------------------------------------
// :: ViewControl Visibility
// ---------------------------------------------
// VISIBLE: 0x00000000, INVISBLE: 0x00000004, GONE: 0x00000008
ImageView.setVisibility( 0 );
ImageView.invalidate();
// ---------------------------------------------
// ---------------------------------------------
// :: Events
// ---------------------------------------------
@Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
Log.d( "FUNC", "onConfigurationChanged" );
if( m_isFullScreen ) {
Log.d( "LOG", "Full Screen -> restore" );
m_isFullScreen = false;
//getWindow().setFlags( WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR, WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR );
//setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT );
getWindow().clearFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN );
}
else {
Log.d( "LOG", "Full Screen" );
m_isFullScreen = true;
getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN );
}
}
/*
* Activity Status(Activity Lifecycle)
run1: app
onStart -> onResume
run2-1: app -> no work -> home
onSaveInstanceState -> onStop
run2-2: app
onStop -> onRestart -> onStart -> onResume
run3-1: work -> home
onSaveInstanceState -> onStop
run3-2: app
onRestart -> onStart -> onResume
*/
Figure Source: http://developer.android.com/guide/topics/fundamentals.html
// NOTE: Activity LifeCycle method 들의 한글 주석은 출처가 없다.
// onCreate가 호출이 된 후 GUI 상태 복구를 위해서 사용을 하는 함수
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// savedInstaceState로부터 GUI 상태를 복구를 한다.
// 인수로 받는 Bundle은 onCreate에도 전달된다.
Log.d( "FUNC", "onRestoreInstanceState" );
if( !m_isPauseBySystem ) {
Log.d( "PLAY", "keep state: pauseBySystem == false" );
return;
}
if( m_MediaPlayer == null ) {
Log.d( "LOG", "m_MediaPlayer == null" );
return;
}
else {
Log.d( "LOG", "now restart..." );
m_MediaPlayer.start();
}
}
/// visible lifetime으로 가기 전 activity 처리를 위해서 호출이 된다.
public void onRestart(){
super.onRestart();
// activity가 이미 화면에 보이고
// 여기서 변경된 것들만 읽어들인다.
Log.d( "FUNC", "onRestart" );
}
/// visible lifetime start시에 호출된다.
public void onStart(){
super.onStart();
// 여기서는 activity가 화면에 보이므로
// 필요한 GUI 변경 사항을 적용한다.
Log.d( "FUNC", "onStart" );
}
/// active lifetime시에 호출이 된다.
public void onResume(){
super.onResume();
/// 일시 중지된 모든 GUI update나 thread
/// activity에 의해서 필요하지만 activity가 비활성화
/// 되면서 일시 중단된 것들을 처리를 다시 시작을 할 때
Log.d( "FUNC", "onResume" );
Intent intt = new Intent( "com.android.music.musicservicecommand" );
intt.putExtra( "command", "pause" );
sendBroadcast( intt );
if( !m_isPauseBySystem ) {
Log.d( "PLAY", "keep state: pauseBySystem == false" );
return;
}
if( m_MediaPlayer == null ) {
Log.d( "LOG", "m_MediaPlayer == null" );
return;
}
else {
Log.d( "LOG", "now restart..." );
m_MediaPlayer.start();
}
// for Some Interrupt
// ...
}
/// GUI 상태를 저장을 하기 위해서 호출이된다.
public void onSaveInstanceState(Bundle savedInstanceState){
/// GUI state를 savedInstanceState에 저장한다.
/// 프로세스가 중료되거나 재시작될 경우
/// 이 번들이 onCreate에 전달이 되서
/// 그 시점부터 다시 시작이 될 것이다.
super.onSaveInstanceState(savedInstanceState);
Log.d( "FUNC", "onSaveInstanceState" );
if( m_MediaPlayer == null ) {
Log.d( "LOG", "m_MediaPlayer == null" );
return;
}
else {
Log.d( "LOG", "now pause... by System" );
m_isPauseBySystem = true;
m_MediaPlayer.pause();
// save m_MediaPlayer.getCurrentPosition()...
}
// for Some Interrupt
// ...
}
/// active lefetime 끝에서 호출된다.
public void onPuase() {
super.onPause();
/// activity가 포그라운드 상태가 아닐 경우
/// 다시 update 되거나 필요가 없는 GUI및 Thread
/// 프로세서 점유율이 높이는 처리는 일시 중지한다
Log.d( "FUNC", "onPuase" );
if( m_MediaPlayer == null ) {
Log.d( "LOG", "m_MediaPlayer == null" );
return;
}
else {
m_isPauseBySystem = true;
m_MediaPlayer.pause();
// save m_MediaPlayer.getCurrentPosition()...
}
// Try adding android:configChanges="keyboardHidden|orientation"
// to your AndroidManifest.xml. This tells the system what configuration changes
// you are going to handle yourself
// android:configChanges="keyboardHidden|orientation
// for Some Interrupt
// ...
}
/// visible lifetime 끝에서 호출
public void onStop() {
super.onStop();
/// 남아있는 GUI 업데이트나 쓰레드
/// activity가 화면에 보이지 않을 때 필요치 않은 처리를 일시 중단을 한다.
/// 이 메서드가 호출이 되고 난 뒤에는 프로세스가 종료될 가능성이 있으므로
/// 바뀐 모든 내용과 상태 변화를 지속시킨다.
Log.d( "FUNC", "onStop" );
}
// ---------------------------------------------
// ---------------------------------------------
// :: Surface and Media Player
// ---------------------------------------------
public class TESTAPP extends Activity implements
OnPreparedListener,
SurfaceHolder.Callback {
...
}
...
// SurfaceHolder
public void surfaceChanged(SurfaceHolder surfaceholder, int format, int width, int height) {
Log.d( "FUNC", "surfaceChanged()" );
}
public void surfaceDestroyed(SurfaceHolder surfaceholder) {
Log.d( "FUNC", "surfaceDestroyed()" );
if( m_MediaPlayer == null )
return;
else
m_MediaPlayer.pause();
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d( "FUNC", "surfaceCreated()" );
// mediaPlayer not null ?
// then start to play or pause
// NOTE: update surface
//m_MediaPlayer.setDisplay( holder );
/*
if( m_MediaPlayer == null ) {
Log.d( "LOG", "m_MediaPlayer == null" );
return;
}
else {
Log.d( "LOG", "now Playing..." );
m_MediaPlayer.setDisplay( holder );
//m_MediaPlayer.pause();
//m_MediaPlayer.start();
*/
if( m_MediaPlayer == null ) {
Log.d( "LOG", "null; playing..." );
return;
}
else {
Log.d( "LOG", "playing..." );
/*
if( m_MediaPlayer.getCurrentPosition() == 0 )
m_MediaPlayer.start();
}
else {
Log.d( "LOG", "use the holder" );
m_MediaPlayer.setDisplay( holder );
m_MediaPlayer.start();
*/
m_SurfaceHolder = holder;
// re-create MediaPlayer, reset SurfaceView, seekTo(...), ...
// PlayVideo(); // see below code Video Player with SurfaceView
}
}
}
public void onPrepared(MediaPlayer mediaplayer) {
Log.d( "FUNC", "MediaPlayer: onPrepared()" );
// mediaPlayer not null ?
// then start to play
if( m_MediaPlayer != null )
m_MediaPlayer.start();
else
Log.d( "LOG", "m_MediaPlayer == null" );
}
// ---------------------------------------------
// ---------------------------------------------
// :: Handler
// ---------------------------------------------
private Handler m_Handler = null;
{
m_Handler = new Handler();
m_Handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// update something
// or
// skip
};
};
}
// another
if( m_Handler != null ) {
Log.d( "LOG", "m_Handler != null" );
m_Handler.removeCallbacksAndMessages( null );
m_Handler = null;
}
m_Handler = new Handler();
m_Handler.postDelayed( new Runnable() {
public void run() {
Log.d( "RUNNABLE", "start" );
// Something...
Log.d( "THREAD", "finish" );
}
}, 5000
);
or
m_Handler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.d( "FUNC", "HANDLER: handleMessage()" );
switch( msg.what ) {
case 0:
// Something
break;
}
};
};
// ---------------------------------------------
// ---------------------------------------------
// :: Thread Start and Stop
// ---------------------------------------------
private void threadStop() {
Log.d( "FUNC", "threadStop()" );
m_ThreadRunFlag = false;
m_Thread = null;
}
private void threadStart() {
Log.d( "FUNC", "threadStart()" );
if( m_Thread != null ) {
Log.d( "THREAD", "m_Thread != NULL" );
return;
}
m_ThreadRunFlag = true;
m_Thread = new Thread() {
public void run() {
try {
// checks MediaPlayer Status
//
while( m_ThreadIsRunning == true ) {
Thread.sleep( 1000 );
// checks MediaPlayer Status
//
m_Handler.sendMessage( m_Handler.obtainMessage() );
}
m_ThreadRunFlag = false;
}
catch( Throwable t ) {
m_ThreadRunFlag = false;
}
}
if( m_Thread != null )
m_Thread.start();
else
Log.d( "THREAD", "m_Thread == NULL" );
};
// ---------------------------------------------
// ---------------------------------------------
// :: MessageBox
// ---------------------------------------------
private void dlgFail() {
final Builder winAlert;
Dialog winDialog;
LayoutInflater li = LayoutInflater.from( this );
View viewDlgFail = li.inflate( R.layout.dlgFail, null );
DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
// Button 1
if( which == -1 ) {
Log.d( "Button", "Selected Button 1" );
//Toast.makeText( __MyActivity__.this, "Selected Button 1", Toast.LENGTH_LONG ).show();
}
// Button 2
else if( which == -3 ) {
Log.d( "Button", "Selected Button 2" );
//Toast.makeText( __MyActivity__.this, "Selected Button 2", Toast.LENGTH_LONG ).show();
}
// Button 3
else if( which == -2 ) {
Log.d( "Button", "Selected Button 3" );
//Toast.makeText( __MyActivity__.this, "Selected Button 3", Toast.LENGTH_LONG ).show();
}
dialog.cancel();
// Quit
// System.exit( 1 );
return;
}
};
winAlert = new AlertDialog.Builder( this )
.setIcon( R.drawable.icon )
.setTitle( getResources().getString(R.string."__FAIL_MESSAGE_TITLE__") )
.setMessage( testGMP.this.m_strResMsg )
.setPositiveButton( getResources().getString(R.string."__FAIL_MESSAGE_BUTTON_OK__"), okListener )
.setView( viewDlgFail );
winDialog = winAlert.create();
winDialog.show();
}
// ---------------------------------------------
// ---------------------------------------------
// :: Resource to Export
// ---------------------------------------------
// Get current mounted SD card path
String strFilePathExt = Environment.getExternalStorageDirectory().getPath();
String strFilePath = strFilePathExt + "/";
String strFilename = "_export_filename_";
// Get SD card State
Environment.getExternalStorageState()
// Checks Internal Storage Free Space
{
File path = Environment.getDataDirectory();
StatFs stat = new StatFs( path.getPath() );
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
{
AssetFileDescriptor afd = getResources().openRawResourceFd( R.raw.__RAW_MEDIA_FILE__ );
if( (availableBlocks * blockSize) < afd.getLength() ) {
Toast.makeText( this, getResources().getString(Out of Internal Storage), Toast.LENGTH_LONG ).show();
afd.close();
return;
}
afd.close();
}
}
// Checks Mount State and External Storage Free Space
if( android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED) ) {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs( path.getPath() );
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
{
AssetFileDescriptor afd = getResources().openRawResourceFd( R.raw.__RAW_MEDIA_FILE__ );
if( (availableBlocks * blockSize) < afd.getLength() ) {
Toast.makeText( this, getResources().getString("Out of External Storage"), Toast.LENGTH_LONG ).show();
afd.close();
return;
}
afd.close();
}
}
// Resource file to Export to SD card
{
AssetFileDescriptor afd = this.getResources().openRawResourceFd( R.raw.__RAW_MEDIA_FILE__ );
if( (new File(strFilePath + strFilename).exists()) == true ) {
Log.d( "EXPORT", "Exist = " + strFilePath + strFilename + ", " + "Internal Resource File Size = " + afd.getLength() + ", External(Exported) Resource File Size = " + (new File(strFilePath + strFilename).length()) );
if( (new File(strFilePath + strFilename).length()) == afd.getLength()) {
Toast.makeText( this, getResources().getString("File existed already"), Toast.LENGTH_LONG ).show();
afd.close();
return;
}
}
byte[] buf_readBytes = new byte[1024];
int readBytes = 0;
int offset = 0;
int count = 1024;
OutputStream oStream = new FileOutputStream( strFilePath + strFilename );
FileInputStream fis = afd.createInputStream();
while( true ) {
int bytes = fis.read( buf_readBytes );
if( bytes <= 0 ) break;
oStream.write( buf_readBytes, 0, bytes );
}
oStream.flush();
oStream.close();
fis.close();
afd.close();
}
// ---------------------------------------------
// ---------------------------------------------
// :: Use Cache Directory and File
// ---------------------------------------------
private File m_TmpFile = null;
...
@Override
protected void onDestroy() {
super.onDestroy();
try {
if( m_TmpFile != null )
m_TmpFile.delete();
}
catch( Exception e ) {
Log.e( "FUNC", "onDestroy(): error: " + e.getMessage(), e );
}
}
{
try {
AssetFileDescriptor afd = getResources().openRawResourceFd( R.raw.__RAW_MEDIA_FILE__ );
FileInputStream fis = afd.createInputStream();
String strTmpFilename = "";
if( m_TmpFile != null )
m_TmpFile.delete();
Log.d( "CacheFile", "Full Resource Size = " + Long.toString(fis.getChannel().size()) );
Log.d( "CacheFile", "Current Resource Position = " + Long.toString(fis.getChannel().position()) );
Log.d( "CacheFile", "Current Resource Size = " + Long.toString(afd.getLength()) );
{
byte[] buf_readBytesFile = new byte[1024];
File cacheDir = getCacheDir();
String strFilename = "__CACHE_TMP_FILENAME__";
m_TmpFile = File.createTempFile( strFilename, ".tmp", cacheDir );
OutputStream oStream = new FileOutputStream( m_TmpFile );
Log.d( "CacheFile", "CacheTmpFilename = " + m_TmpFile.getPath() );
while( true ) {
int bytes = fis.read( buf_readBytesFile );
if( bytes <= 0 ) break;
oStream.write( buf_readBytesFile, 0, bytes );
}
oStream.flush();
strTmpFilename = m_TmpFile.getAbsolutePath();
oStream.close();
}
Log.d( "CacheFile", "CacheTmpFilename = " + strTmpFilename );
afd.close();
fis.close();
}
catch( Exception e ) {
// ...
}
}
// ---------------------------------------------
// ---------------------------------------------
// :: Play Video
// ---------------------------------------------
// See above code SurfaceView method
public class TESTAPP extends Activity implements
OnPreparedListener,
SurfaceHolder.Callback {
...
Intent intt = new Intent( "com.android.music.musicservicecommand" );
intt.putExtra( "command", "pause" );
sendBroadcast( intt );
}
...
private MediaPlayer m_MediaPlayer = null;
private SurfaceView m_SurfaceView = null;
private SurfaceHolder m_SurfaceHolder = null;
// VideoView
m_VideoView = (VideoView)findViewById( R.id.VideoView01 );
// SurfaceView
m_SurfaceView = (SurfaceView)findViewById( R.id.SurfaceView01 );
m_SurfaceHolder = m_SurfaceView.getHolder();
m_SurfaceHolder.addCallback( this );
m_SurfaceHolder.setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS );
m_SurfaceView.setClickable( false );
// Visibility
// VideoView
//m_VideoView.setVisibility( 0 );
//m_VideoView.invalidate();
// SurfaceView
m_SurfaceView.setVisibility( 0 );
m_SurfaceView.invalidate();
// Button: Play
Button btnPlay = (Button)findViewById( R.id.Button01 );
View.OnClickListener btnPlayListener = new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
if( m_MediaPlayer == null ) {
Log.d( "PLAY", "OK, First time" );
PlayVideo();
}
else {
PlayVideo();
}
}
};
btnPlay.setOnClickListener( btnPlayListener );
public void PlayVideo() {
if( m_MediaPlayer != null )
m_MediaPlayer.release();
m_MediaPlayer = null;
m_MediaPlayer = new MediaPlayer();
AssetFileDescriptor afd = getResources().openRawResourceFd( R.raw.__RAW_MEDIA_FILE__ );
if( m_MediaPlayer == null ) {
Log.d( "MediaPlayer", "m_MediaPlayer == NULL" );
return;
}
// Use ViewoView
/*
if( m_VideoView == null ) {
Log.d( "VideoView", "m_VideoView == NULL" );
return;
}
*/
// Use SurfaceView
if( m_SurfaceView == null ) {
Log.d( "SurfaceView", "m_SurfaceView == NULL" );
return;
}
m_MediaPlayer.setAudioStreamType( AudioManager.STREAM_MUSIC );
m_MediaPlayer.setScreenOnWhilePlaying( true );
try {
m_MediaPlayer.setDataSource( afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength() );
// Use VideoView
//m_VideoView.getHolder().addCallback( this );
//m_MediaPlayer.setDisplay( m_VideoView.getHolder() );
// Use Surface
m_MediaPlayer.setDisplay( m_SurfaceHolder );
m_MediaPlayer.prepare();
}
catch( IllegalStateException e ) {
// ...
}
catch( IOException e ) {
// ...
}
if( m_CurrentPosition > 0 )
m_MediaPlayer.seekTo( m_CurrentPosition );
if( !isPause )
m_MediaPlayer.start();
}
}
if( m_MediaPlayer != null ) {
m_MediaPlayer.release();
m_MediaPlayer = null;
}
// ---------------------------------------------
// ---------------------------------------------
// :: Play Video (VideoView)
// ---------------------------------------------
// VideoView
int m_CurrentPosition = 0;
... onSaveInstanceState( ... ) {
m_VideoView.pause();
m_CurrentPosition = m_VideoView.getCurrentPosition();
}
... onRestoreInstanceState( ... ) {
m_VideoView.seekTo( m_CurrentPosition );
m_VideoView.start();
}
if( m_MediaController == null )
m_MediaController = new MediaController( this );
Uri uriVideo = Uri.parse( "android.resource://com.test.TESTAPP/" + R.raw.xxx );
m_VideoView.setVideoURI( uriVideo );
m_MediaController.setSaveEnabled( true );
m_VideoView.setMediaController( m_MediaController );
m_MediaController.show();
m_VideoView.requestFocus();
m_VIdeoView.start();
// ---------------------------------------------
// ---------------------------------------------
// :: Media Scan (SD card)
// ---------------------------------------------
sendBroadcast( new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())) );
// ---------------------------------------------
// ---------------------------------------------
// :: Restrict characters length (Korean/Japanese)
// - EditText 에서 한글/일본어 입력 시 byte 수 제한하기.
// - 간단한 테스트는 Emulator 에서 일본어 입력기로 해보면 된다.
// ---------------------------------------------
m_Editbox = (EditText)findViewById( R.id.EditText01 );
m_Editbox.addTextChangedListener( new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
//Log.d("LOG", s.toString() );
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
//Log.d("LOG", s.toString() );
}
// byte to int, bytes to hex 는 internet 에서 그냥 찾아서 썼다.
// NOT my code (^^) --------------------
public int getInt(byte[] bytes) {
int newValue = 0;
newValue |= (((int) bytes[0]) << 24) & 0xFF000000;
newValue |= (((int) bytes[1]) << 16) & 0xFF0000;
newValue |= (((int) bytes[2]) << 8) & 0xFF00;
newValue |= (((int) bytes[3])) & 0xFF;
return newValue;
}
public String byteArrayToHex(byte[] ba) {
if (ba == null || ba.length == 0) {
return null;
}
StringBuffer sb = new StringBuffer(ba.length * 2);
String hexNumber;
for (int x = 0; x < ba.length; x++) {
hexNumber = "0" + Integer.toHexString(0xff & ba[x]);
sb.append(hexNumber.substring(hexNumber.length() - 2));
}
return sb.toString();
}
// NOT my code (^^) --------------------
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
//Toast.makeText( myTest1App.this, m_Editbox.getText().toString(), Toast.LENGTH_LONG ).show();
// 여기에선 8 bytes 를 제한한다.
// 주의할 점은 한글/일본어는 2 bytes 가 아닌 3 bytes 로 된다.
// 아래 log 에서 해당 문자와 hex code 를 보면 안다.
int max_len = 8; // restrict 8 bytes. (Korean/Japanese may has 3 bytes. NOT 2 bytes.)
if( s.length() > 0 ) {
int c = s.charAt( s.length()-1 );
String str = Character.toString( s.charAt(s.length()-1) );
int cLen = str.length();
Log.d( "LOG", "s len = " + s.toString().getBytes().length + ", s str = " + s.toString() + ", s hex = " + byteArrayToHex(s.toString().getBytes()) );
//Log.d( "LOG", "byte = " + s.toString().getBytes() + ", len = " + cLen + ", ascii = " + s.charAt(0) + ", dec = " + c + ", hex = " + byteArrayToHex(s.toString().getBytes()) );
Log.d( "LOG", "s.charAt(" + (s.length()-1) + ") -> " + ", = " + str + ", len = " + cLen + ", dec = " + c + ", bytes = " + str.getBytes() + ", len = " + str.getBytes().length + ", hex = " + byteArrayToHex(str.getBytes()) );
if( s.toString().getBytes().length > max_len ) {
s.delete( s.length()-1, s.length() );
m_TextView.setText( s.toString() );
}
}
}
}
);
// ---------------------------------------------
// ---------------------------------------------
// :: Indicator: Notification
// ---------------------------------------------
// http://developer.android.com/guide/topics/ui/notifiers/notifications.html
// http://osdir.com/ml/Android-Beginners/2010-07/msg00649.html
// Path: /android-sdk-windows/platforms/android-4/samples/ApiDemos/src/com/example/android/apis/app
// Source: RemoteService.java
// - AndroidManifest.xml
// - < activity ... android:launchMode="singleTask">
//
// Show a notification while this service is running.
private NotificationManager mNM = null;
private void showNotification() {
// Indicator: Notification
if( mNM != null ) {
mNM.cancel( R.string.app_name );
// Use custom view
//mNM.cancel( R.layout.local_service_controller );
}
mNM = (NotificationManager)getSystemService( NOTIFICATION_SERVICE );
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText( R.string.remote_service_started );
// Set the icon, scrolling text and timestamp
//Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis() );
Notification notification = new Notification( -1, text, System.currentTimeMillis() );
Intent intent = new Intent( this, testMyActivity.class );
intent.setFlags( Intent.FLAG_ACTIVITY_SINGLE_TOP );
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity( this, 0, intent, 0 );
// Set the info for the views that show in the notification panel.
//notification.setLatestEventInfo(this, getText(R.string.local_service_label),
notification.setLatestEventInfo( this, getText(R.string.Info), text, contentIntent );
/*
// Set custom view
* DELETE notification.setLatestEventInfo(...)
RemoteViews contentView = new RemoteViews( getPackageName(), R.layout.local_service_controller );
////contentView.setImageViewResource( R.id.image, R.drawable.notification_image );
////contentView.setTextViewText( R.id.text, "Hello, this message is in a custom expanded view" );
//contentView.setImageViewResource( R.id.image, R.drawable.imagefile );
contentView.setTextViewText( R.id.TextViewIndicator, getText(R.string.app_name) );
notification.contentView = contentView;
// Use custom view
notification.contentIntent = contentIntent;
*/
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
mNM.notify( R.string.remote_service_started, notification );
// Use custom view
//mNM.notify( R.layout.local_service_controller, notification );
}
// Use custom view (ImageView, TextView)
// local_service_controller.xml
// Change (, ) to <, >
(LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:padding="4dip"
android:id="@+id/LinearLayoutIndicator"
android:layout_width="fill_parent" android:layout_height="fill_parent")
(RelativeLayout android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_height="fill_parent")
(TextView
android:id="@+id/TextViewIndicator"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="local_service_controller")
(/TextView)
(Button android:id="@+id/start"
android:layout_below="@+id/TextViewIndicator"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="start_service")
(/Button)
(Button android:id="@+id/stop"
android:layout_below="@+id/TextViewIndicator"
android:layout_toRightOf="@+id/start"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="stop_service")
(/Button)
(/RelativeLayout)
(/LinearLayout)
// ---------------------------------------------
-----
Cheers,
June
댓글 없음:
댓글 쓰기