안녕하세요 오랜만에 포스팅을 하네요

개인적인 사정(면접, 과제)로 정신없이 지내다 보니 조금은 소홀해졌네요 ^^..

댓글로 질문하신 분도 꽤 있으신데 제가 관련 내용을 업데이트 못 해서 죄송합니다 ^^.. 빠른 시일 내에 하도록 하겠습니다!



Genymotion 설치 방법


안드로이드 프로그래밍을 하다 보면 스마트폰에 예제 어플들이 수도 없이 깔려서 거슬린 적이 많을 것이다. 하지만 느린 에뮬레이터를 쓸 바에는 불편함을 감수하고 스마트폰을 사용하는 분들이 많을 것이라고 생각된다. 하지만 이 에뮬레이터는 빠르다.

Genymotion 설치법에 대해서 알아보자!


1. 회원가입 및 다운로드

http://www.genymotion.com 에 들어가서 회원가입을 한다. GET GENYMOTION 클릭!



유료 버전이 기능이 더 많지만 일단은 Free 버전을 써보도록 하자! DOWNLOAD 클릭!



DOWNLOAD를 클릭하면 로그인 하라는 창이 뜬다. 아직 회원가입을 안했다면 회원가입을 한다. 계정이 있다면 로그인!



Enterprise size나 Usage type은 적당히 선택하도록 한다.


가입을 완료하면 방금 적었던 이메일 주소로 인증메일이 온다. 인증메일에서 확인을 하면 바로 실행프로그램을 다운받을수 있는 창이 뜬다. (https://cloud.genymotion.com/page/launchpad/download)



자신의 컴퓨터 사양에 맞는 버전을 다운받는다.



2. 설치

다운받은 설치 프로그램을 실행하여 설치를 하도록 하자




설치를 마치고 지니모션을 실행시킨다.  

VirtualBox도 설치가 될텐데 따로 설정할 부분은 없다.

(VirtualBox는 가상운영체제로 여기서 지니모션이 실행된다.)


3. 설정

사용할 에뮬레이터의 환경설정을 한다. 지니모션 첫 실행시 다음과 같은 화면이 뜬다.

새로운 디바이스를 만들도록하자.



Yes를 누르면 로그인을 하라는 화면이 뜰 것이고 로그인을 하면 다음과 같은 화면이 뜬다.

원하는 디바이스 환경을 찾아서 Next를 누르면 설치가 진행이 된다.



설치완료후 안드로이드 에뮬레이터로 사용하기 위해 세팅을 해준다.

Settings 메뉴에서 ADB메뉴에서 Android SDK 폴더의 경로를 지정해준다.




세팅완료후 Play버튼을 눌러 실행을 한다.



4. 에러해결

실행 했을때 다음과 같은 화면이 뜨는 경우가 있을것이다. 

MS 오피스가 설치된 PC에서 대부분 발생하는 문제라고 한다.

이 문제는 키보드 입력기를 "Microsoft 입력기"로 설정하면 된다.







위 그림과 같이 설정을 변경해주면 'Microsoft 입력기'키보드가 추가된다.

선택메뉴에서 'Microsoft 입력기'로 꼭 선택해주자

확인을 누르고 다시 지니모션을 실행해보자.


이제 정상적으로 실행이 된다! 다른 문제가 있다면 현재(2014.05.22)날짜 기준으로 VirtualBox를 최신버전으로 업데이트 하지 않았을 경우 또 다른 에러가 발생한다. 


설치나 설정을 모두다 완료했는데 안된다면 재부팅을 추천한다.







지금까지 블루투스를 활성화 하고 기기 검색을 하는 단계까지 진행하였다.


이전 글 : 블루투스 활성화 글 보기블루투스 기기 검색하기 글 보기


저번 프로젝트에 이어 블루투스 연결하는 단계를 포스팅 하겠다.


검색한 기기에 연결하기 위해서는 ConnectThread와 ConnectedThread에 대해서 알고 있어야 한다.

BluetoothService.java 파일에 ConnectThread와 ConnectedThread 클래스를 내부 클래스로 삽입해준다.


1. ConnectThread


BluetoothService.java

public class BluetoothService {
	... // 이전 부분 생략

	// RFCOMM Protocol
	private static final UUID MY_UUID = 
		UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	
	private BluetoothAdapter btAdapter;
	
	private ConnectThread mConnectThread;
	private ConnectedThread mConnectedThread;

	... // 이전 부분 생략
		
	private class ConnectThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final BluetoothDevice mmDevice;

		public ConnectThread(BluetoothDevice device) {
			mmDevice = device;
			BluetoothSocket tmp = null;
			
			// 디바이스 정보를 얻어서 BluetoothSocket 생성
			try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                Log.e(TAG, "create() failed", e);
            }
			mmSocket = tmp;
		}

		public void run() {
			Log.i(TAG, "BEGIN mConnectThread");
			setName("ConnectThread");
			
			// 연결을 시도하기 전에는 항상 기기 검색을 중지한다.
			// 기기 검색이 계속되면 연결속도가 느려지기 때문이다.
			btAdapter.cancelDiscovery();

			// BluetoothSocket 연결 시도
			try {
				// BluetoothSocket 연결 시도에 대한 return 값은 succes 또는 exception이다.
				mmSocket.connect();
				Log.d(TAG, "Connect Success");
				
			} catch (IOException e) {
				connectionFailed();		// 연결 실패시 불러오는 메소드
				Log.d(TAG, "Connect Fail");
				
				// socket을 닫는다.
				try {
					mmSocket.close();
				} catch (IOException e2) {
					Log.e(TAG,
							"unable to close() socket during connection failure",
							e2);
				}
				// 연결중? 혹은 연결 대기상태인 메소드를 호출한다.
				BluetoothService.this.start();
				return;
			}

			// ConnectThread 클래스를 reset한다.
			synchronized (BluetoothService.this) {
				mConnectThread = null;
			}

			// ConnectThread를 시작한다.
			connected(mmSocket, mmDevice);
		}

		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) {
				Log.e(TAG, "close() of connect socket failed", e);
			}
		}
	}
}


여기서 UUID라는 변수가 있는데 이것은 Bluetooth 통신 프로토콜이다. 블루투스 통신을 할 때 그에 맞는 프로토콜로 통신을 해 주어야 한다. 간단한 예로 블루투스 기능이 지원되는 이어폰과의 통신을 원한다면 그에 맞는 프로토콜을 사용하여야 하고, 스마트폰 끼리의 블루투스 통신은 또 그에 맞는 프로토콜을 사용해야 한다. 

지금 위에서 사용한 프로토콜은 아마 스마트폰끼리의 블루투스 통신을 하는데 사용하는 프로토콜일 것이다.


블루투스 프로토콜 종류 보기 - [안드로이드] 블루투스 프로토콜 UUID 리스트


2. ConnectedThread


위에서(ConnectThread) 다음단계가 ConnectedThread이다. 위 단계에서는 연결을 하는 중간단계 까지라고 생각하고, 이번 단계는 연결을 완료하는 단계라고 생각하면 될 것 같다. 위와 동일하게 BluetoothService 클래스에 내부 클래스로 정의한다.


BluetoothService.java

public class BluetoothService {
	... // 이 전 부분 생략
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;

	private ConnectThread mConnectThread; // 변수명 다시
	private ConnectedThread mConnectedThread; // 변수명 다시
	
	... // 이 전 부분 생략
	
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // BluetoothSocket의 inputstream 과 outputstream을 얻는다.
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // InputStream으로부터 값을 받는 읽는 부분(값을 받는다)
                    bytes = mmInStream.read(buffer);

                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         * @param buffer  The bytes to write
         */
        public void write(byte[] buffer) {
            try {
            	// 값을 쓰는 부분(값을 보낸다)
                mmOutStream.write(buffer);

            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
}



3. 마무리 작업


지금까지 잘 따라했다면 몇 가지 빨간줄이 있을 것이다. 이 빨간줄을 지워보자

기본 베이스는 처음에 말했듯이 안드로이드 기본 예제인 BluetoothChat이다.

BluetoothChat에서 다음과 같은 메소드를 모두 복사해서 BluetoothService.java에 붙여넣어 빨간줄을 없애보자


BluetoothService.java

public class BluetoothService {
	... // 이 전과 동일
	private int mState;

	// 상태를 나타내는 상태 변수
	private static final int STATE_NONE = 0; // we're doing nothing
	private static final int STATE_LISTEN = 1; // now listening for incoming connections
	private static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
	private static final int STATE_CONNECTED = 3; // now connected to a remote device

	... // 이 전과 동일

	// Bluetooth 상태 set
	private synchronized void setState(int state) {
		Log.d(TAG, "setState() " + mState + " -> " + state);
		mState = state;
	}

	// Bluetooth 상태 get
	public synchronized int getState() {
		return mState;
	}

	public synchronized void start() {
		Log.d(TAG, "start");

		// Cancel any thread attempting to make a connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}
	}

	// ConnectThread 초기화 device의 모든 연결 제거
	public synchronized void connect(BluetoothDevice device) {
		Log.d(TAG, "connect to: " + device);

		// Cancel any thread attempting to make a connection
		if (mState == STATE_CONNECTING) {
			if (mConnectThread == null) {

			} else {
				mConnectThread.cancel();
				mConnectThread = null;
			}
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to connect with the given device
		mConnectThread = new ConnectThread(device);

		mConnectThread.start();
		setState(STATE_CONNECTING);
	}

	// ConnectedThread 초기화
	public synchronized void connected(BluetoothSocket socket,
			BluetoothDevice device) {
		Log.d(TAG, "connected");

		// Cancel the thread that completed the connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to manage the connection and perform transmissions
		mConnectedThread = new ConnectedThread(socket);
		mConnectedThread.start();

		setState(STATE_CONNECTED);
	}

	// 모든 thread stop
	public synchronized void stop() {
		Log.d(TAG, "stop");

		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		if (mConnectedThread != null) {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		setState(STATE_NONE);
	}

	// 값을 쓰는 부분(보내는 부분)
	public void write(byte[] out) { // Create temporary object
		ConnectedThread r; // Synchronize a copy of the ConnectedThread
		synchronized (this) {
			if (mState != STATE_CONNECTED)
				return;
			r = mConnectedThread;
		} // Perform the write unsynchronized r.write(out); }
	}

	// 연결 실패했을때
	private void connectionFailed() {
		setState(STATE_LISTEN);
	}

	// 연결을 잃었을 때 
	private void connectionLost() {
		setState(STATE_LISTEN);

	}

	private class ConnectThread extends Thread {
		... 
	}

	private class ConnectedThread extends Thread {
		...
	}
}


EX_Bluetooth.zip



마지막에 설명을 쫌 대강대강 하고 넘어간 부분이 많은데, 일단은 현재까지의 프로젝트를 압축파일로 올리겠다.

이후 시간이 된다면 스마트폰 끼리의 블루투스 예제를 주석을 달아서 올리도록 하겠다. 

또한, 글쓴이는 임베디드 보드(동글)과 통신을 하였는데 이 부분에 대해서 궁금하신 분이 많다면 서둘러 예제 프로젝트를 올리도록 하겠다.

저번 포스팅에서는 블루투스를 활성화 하는 단계까지 진행하였다. 


이전 글 : 블루투스 활성화 글 보기 


이번에는 저번 프로젝트에 이어 기기 검색하는 부분부터 시작하겠다.


1. 기기 검색하기 - 프로젝트 파일 복사하기


기기 검색하기에 앞서 기기 검색하기 기능 실행시 보여줄 화면을 위해 안드로이드 블루투스 채팅 예제프로젝트에서 몇가지 파일을 가져오도록 하겠다. 번거로울수 있어서 아래 포스팅을 하였다. 접힌 내용을 펼쳐서 보길 바란다.

java 파일인 DeviceListActivity.java

레이아웃 xml 파일인 device_list.xml, device_name.xml

을 복사해서 붙여넣고 

[res]->[values]->[string.xml] 에 string 들을 추가해준다.(복사해서 붙여넣는다.)


.. 추가중


Manifest에 액티비티 추가(DeviceListActivity)를 해주는것도 잊지말자!


AndroidManifest.xml

        <activity android:name="ssm.bluetooth.MainActivity" 
                android:label="@string/app_name">
                    <intent-filter>
                        <action android:name="android.intent.action.MAIN">

                        <category android:name="android.intent.category.LAUNCHER">
                    </category>
                </action>
        </intent-filter>
        </activity>
        <!-- 여기서부터 추가 -->
        <activity android:name="ssm.bluetooth.DeviceListActivity" 
                android:theme="@android:style/Theme.DeviceDefault.Dialog">
        <!-- 여기까지 추가 -->


2. 기기 검색하기 - 코드 작성하기


기기 검색시 띄워줄 액티비티가 완성되었으니 이제 액티비티를 띄워줄 코드를 작성해보자

BluetoothService.java 파일에 scanDevice()라는 메소드를 생성하였다.


BluetoothService.java

public void scanDevice() {
		Log.d(TAG, "Scan Device");
		
		Intent serverIntent = new Intent(mActivity, DeviceListActivity.class);
		mActivity.startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
	}

startActivityForResult가 또 등장하였다!

저번 포스팅에서 봤던 녀석이 아닌가!

새로운 액티비티를 띄워서 처리된 결과값이 mActivity로 반환이 되는 코드이다.(mActivity는 MainActivity)


scanDevice() 메소드에서는 디바이스를 선택하는 액티비티를 띄워주고 선택하였을 경우 그 결과값을 MainActivity로 반환하고 있다. 


그렇다면 scanDevice()라는 기기를 검색을 요청하는 메소드는 어디에 추가를해야 될까?

기기의 블루투스 상태가 On 상태이고, 블루투스 연결 버튼을 눌렀을 경우,

또는 기기의 블루투스 상태가 Off상태 이었을때, 연결 버튼을 눌러 블루투스를 활성화 하였을 경우.

이 두가지 경우에 scanDevice()메소드를 호출하면 될 것이다. 

간단히 정리하자면 블루투스가 활성화 되어있는 상태에서 scanDevice()메소드를 호출하면 된다.




그럼 scanDevice()메소드를 호출하고, 기기를 선택하였을 경우 결과값을 반환 받았을때, MainActivity에서 처리해주는 소스를 작성해 보자.


MainActivity.java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult " + resultCode);
        
        switch (requestCode) {
        
        /** 추가된 부분 시작 **/
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
            	btService.getDeviceInfo(data);
            }
            break;
        /** 추가된 부분 끝 **/
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
            	// Next Step
            	btService.scanDevice();
            } else {

                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
	}

requestCode가 REQUEST_CONNECT_DEVICE인 경우 getDeviceInfo()메소드를 호출하도록 하였다.

getDeviceInfo()메소드는 아직 구현하지 않았다. 

이 메소드는 기기선택 액티비티에서 선택한 기기의 정보를 받아 getDeviceInfo()라는 메소드로 전달을 해주고 getDeviceInfo()메소드는 그 정보를 이용하여 블루투스연결을 시도할 예정이다.


3. 검색된 기기에 접속하기 - 1


검색된 기기에 접속하기 위해서 getDeviceInfo()메소드를 작성해보자.


BluetoothService.java

	public void getDeviceInfo(Intent data) {
		// Get the device MAC address
		String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
		// Get the BluetoothDevice object
		//BluetoothDevice device = btAdapter.getRemoteDevice(address);
		BluetoothDevice device = btAdapter.getRemoteDevice(address);
		
		Log.d(TAG, "Get Device Info \n" + "address : " + address);

		connect(device);
	}

address라는 String에는 선택한 기기의 주소가 담겨져 있고, 그 주소를 BluetoothDevice로 전달하여 connect()메소드로 전달하면 접속을 할 수 있다. connect()메소드는 아직 구현을 하지 않았다. 이제 connect()메소드만 구현하면 기기의 블루투스 연결은 가능할 것 이다.


4. 검색된 기기에 접속하기 - 2


이제부터는 많은 양의 복사 붙여넣기를 시도할 예정이다.

안드로이드 블루투스 채팅 예제의 BluetoothChatService의 connect()메소드를 붙여넣고, 이와 관련된 모든 소스를 붙여넣는다.

다음 포스팅을 보면서 진행하도록 하자.


다음 글 : 블루투스통신 연결하기 글 보기




안드로이드의 블루투스 통신 예제입니다.

안드로이드의 블루투스 레퍼런스를 참고하고 싶으시다면 다음 링크로 들어가시면 됩니다.


레퍼런스 참고 -> 블루투스 API


1. 블루투스 통신 예제



이 예제를 활용하여 수정한다면 블루투스 통신을 자유자재로 사용할 수 있다.

이제부터 이 예제를 활용하여 블루투스 통신을 하는 예제를 만들어 보겠다.


2. 새 프로젝트 생성


액티비티에 버튼을 생성하여, 버튼을 눌렀을 경우 블루투스 연결을 할 수 있도록 만들어볼 예정이다.


메인화면 레이아웃은 다음과 같이 버튼과 현재 상황을 알려주는 TextView로 만들었다.

main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/btn_connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="연결" />


    <TextView
        android:id="@+id/txt_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_connect"
        android:text="" />

</RelativeLayout>



기본 세팅이 끝났으니 이제부터 블루투스 통신에 관련된 코드를 작성해보겠다.


3. 권한 등록


제대로된 코딩을 시작하기 전에 권한등록(permission 등록)을 먼저 하도록 하자

블루투스 기능을 사용하기 위해서는 'BLUETOOTH'와 'BLUETOOTH_ADMIN'의 권한을 등록해줘야 한다.


BLUETOOTH : 커넥션 요구, 커넥션 수락, 데이터 전송 등의 블루투스 통신을 하기 위해 필요한 권한

BLUETOOTH_ADMIN : 디바이스 검색 시작, 블루투스 설정을 조작하기 위해 필요

(BLUETOOTH_ADMIN 권한을 사용하려면 BLUETOOTH 권한도 꼭 있어야 한다.)


Manifest 파일에 블루투스 권한 등록


AndroidManifest.xml

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


4. 새 Class 파일 생성


MainActivity에는 Button과 TextView만 배치해놓고 BluetoothService라는 Class를 만들도록 한다.

BluetoothService라는 Class에서 블루투스와 관련된 모든 작업을 처리할 예정이다. 

작업이 처리되는 과정은 핸들러를 통해서 MainActivity에 전달되어 상태값을 전달받아 TextView에 보여주거나 버튼의 상태를 변경해 주도록 할 것이다.


BluetoothService 생성자는 메인이 되는 MainActivity로 부터 Activity와 Handler 값을 받는다.


BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}

MainActivity.java

private BluetoothService btService = null;

private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.main);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}


5. 블루투스 활성화


블루투스를 활성화 하기 위해 BluetoothAdapter클래스를 사용해서 다음 두 단계의 작업을 순서대로 진행한다.


① 블루투스 지원 확인

② 블루투스 활성화


① 블루투스 지원 확인

블루투스 통신을 사용하기 전에 디바이스가 블루투스를 지원하는지 확인할 필요가 있다. 디바이스가 블루투스를 지원하는지 확인해보도록 하자


BluetoothService.java

public boolean getDeviceState() {
		Log.d(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}

getDeviceState() 라는 메소드를 만들어서 기기의 블루투스 지원여부를 확인 한다.

BluetoothAdapter가 null일 경우 블루투스 통신을 지원하지 않는 기기이다. (하지만 이러한 기기는 요즘 없을듯..?)


② 블루투스 활성화

getDeviceState()가 true를 반환할 경우 블루투스 활성화를 요청하도록 해보자.

블루투스 활성화 요청을 위해서 enableBluetooth()라는 메소드를 만들었다. 


BluetoothService.java

public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}

기기의 블루투스 상태가 On일 경우 다음 단계를 실행하면 되고, 

기기의 블루투스 상태가 Off일 경우 블루투스 활성화를 요청하는 알림창을 띄운다.


알림창에서 확인/취소를 선택할 경우 결과는 MainActivityonActivityResult()메소드로 들어온다. 

그 전에 startActivityForResult의 개념에 대해서 알고 있어야 이해하기 편한데, 이 것은 구글링을 통해서 알아보는것을 추천한다.


알림창의 확인/취소 결과는 MainActivity에 onActivityResult()메소드로 전달되는데 소스를 보면 다음과 같다.


MainActivity.java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        switch (requestCode) {          
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // 확인 눌렀을 때
                //Next Step
            } else {
                // 취소 눌렀을 때
                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
}


이제 MainActivity에서 버튼을 클릭했을 때 블루투스 활성화를 위한 이벤트 처리만 해주면 된다.


MainActivity.java

@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}

여기까지 블루투스를 활성화 하는 방법에 대해서 알아보았다.

블루투스가 활성화 되었을 때(resultCode가 Activity.RESULT_OK일때) 기기 검색을 통해 페어링을 시켜주면 되는데, 이 부분은 다음장에 포스팅 하도록 하겠다.


전체 소스는 다음과 같고, 프로젝트를 첨부파일로 올린다.


EX_Bluetooth(1).zip



MainActivity.java

public class MainActivity extends Activity implements OnClickListener {
	// Debugging
	private static final String TAG = "Main";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	// Layout
	private Button btn_Connect;
	private TextView txt_Result;
	
	private BluetoothService btService = null;
	
	
	private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.e(TAG, "onCreate");
		
		setContentView(R.layout.main);
		
		/** Main Layout **/
		btn_Connect = (Button) findViewById(R.id.btn_connect);
		txt_Result = (TextView) findViewById(R.id.txt_result);
		
		btn_Connect.setOnClickListener(this);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}

	@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult " + resultCode);
        
        switch (requestCode) {

        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
            	
            } else {

                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
	}

}


BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}
	
	/**
	 * Check the Bluetooth support
	 * @return boolean
	 */
	public boolean getDeviceState() {
		Log.i(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}
	
	/**
	 * Check the enabled Bluetooth
	 */
	public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}
	
}


다음 글 : 블루투스 기기 검색하기 글 보기



 안드로이드 프로그래밍에서 어플만의 UI를 갖는것은 필수라고 생각한다.

 앱의 기능은 구글링을 통해서 어느정도 구현을 할 수가 있는데, 똑같은 기능을 갖고 있는 앱 중에서 UI가 이쁜 앱과 UI가 평범한 앱중에는 당연히 UI가 이쁜 앱을 쓰지 않을까??

 독특한 특색을 갖는 UI를 꾸미기 위해서 이번에는 액션바를 활용해 보겠다. (커스텀하게 액션바를 만드는 내용은 다음에 시간날때 올리도록 하겠다..)


 글쓴이의 경우 앱에 액션바를 사용하였는데 액션바에 배경이미지를 넣고 사용하기 위해 커스텀하게 구현을 하였다. 버튼역시 테마에 맞게 이미지를 넣어 구현을 하였다. 


 이때 문제가 발생하였는데 이미지를 넣은 버튼을 배치했을 때 디바이스에 따라 해상도가 모두 다르기 때문에 버튼의 크기가 제 각각 이었다. 

 액션바의 크기는 모든 디바이스에서 비율이 똑같을 것이라고 생각하고 크기를 절대값으로 조절하였더니 문제가 생긴것이다. 해상도에 따라서 액션바의 크기도 제각각이었다. 그래서 액션바의 크기를 구해서 버튼크기를 조절하려고 했다. 그러나 액션바의 크기가 0으로 나와서 크기를 알 수 가 없었다.

 그렇다.. 액션바의 크기는 0이었던 것이다.. 

 하지만 액션바의 크기를 구할 수 있는 방법이 있다!!

 액티비티가 실행될 때 onCreate 안에서 액션바의 크기를 구하면 0이 나오지만, 스레드를 이용하면 크기를 구할수 있다.

 일단 Action Bar 와 Status Bar 크기를 구하는 코드부터 살펴보자


private void getStatusBarSize() {
	Rect rectgle = new Rect();
	Window window = getWindow();
	window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
	int StatusBarHeight = rectgle.top;
	int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
	int TitleBarHeight = contentViewTop - StatusBarHeight;
	
	Log.i("getHeight", "StatusBar Height= " + StatusBarHeight + " TitleBar Height = " + TitleBarHeight);
}

위의 메소드를 onCreate 안에서 호출하지말고 스레드를 통해서 호출한다.


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

	LinearLayout layout = new LinearLayout(this);
	layout.post(new Runnable() {
		@Override
		public void run() {
			getStatusBarSize();
		}
	});
}


스레드를 통해서 getStatusBarSize를 호출하면 크기를 구할 수 있다.


여기서 또 한가지 팁은 액션바 사이즈를 구해서 액션바 사이즈를 이용해서 액션바에 들어가는 버튼이나 이미지 크기를 조절하려면 onCreate안에서 계산하면 안된다. 스레드를 통해서 크기를 구하기 때문에 onCreate가 끝나고 크기가 구해질 수 가 있기 때문에 액션바에 들어가는 버튼의 크기를 조절하고자 한다면 스레드 안에서 getStatusBarSize()를 호출하고 난 뒤에 크기를 조절해 주도록 하자!


액션바를 커스텀하게 만드는 방법은 다음에 자세히 포스팅 하도록 하겠다.


android-ndk-r4-windows-crystax-4 이다.

NDK를 사용하고 안드로이드에 OpenCV를 올리기 위해서는 정식 NDK 버전이 아닌 crystax ndk 버전이 필요하다고 한다?? 정식 NDK로 잘 안되는 경우는 이 파일을 받아서 사용하길 바란다.


다운로드

안드로이드 앱의 폴더와 서버의 폴더 생성 시간을 비교하고 변경하여 동기화 하는 작업을 수행하는 도중 setLastModified() 메소드가 제대로 작동하지 않았다. 

폴더의 생성시간에 접근을 하는 메소드는 setLastModified()가 맞는데 몇번을 수행하여도 폴더의 시간이 변경되지 않았다.


setLastModified()의 반환형이 boolean 형임을 파악하고 Log를 계속 찍어보았다.

return 값은 계속해서 false...


실행한 디바이스는 갤럭시 탭 10.1로 안드로이드 4.0 버전이었다.


분명히 폴더시간이 바뀌어야 되는데 바뀌지 않았다. 구글링을 한 결과 setLastModified() 메소드가 일부 디바이스에서 작동하지 않는다는 답변이 있었다. 


아마도 삼성제품에 대다수 해당되는 것 같았다. 해킹이나 버그를 방지하기 위해서 접근하지 못하게 해놓았다는 답변이다. 정말 거지같다..


결국 SDcard의 폴더 생성시간은 변경할 수 없다.


폴더 생성시간을 변경을 하지는 못하고, 서버의 폴더 생성 시간과 비교를 하기 위해서는 따로 DB를 만들거나 또 다른 편법을 써야할 것 같다..


이 문제로 고민하는 분이 없길 바란다...

NDK를 이용한 기본 예제이다. 간단한 프로그래밍은 기본예제의 변형으로 가능할 것이다. 더 깊은 내용은 나중에 포스팅 하도록 하겠다.


NDK 설치 완료후 제대로 설치가 되었는지 이 예제를 통해서 확인해보길 바란다. 먼저 프로젝트를 하나 생성한다. 프로젝트명은 HelloJNI로 하였다.


프로젝트 생성후 jni라는 이름의 폴더를 하나 생성한다. 이 폴더안에 2개의 파일을 생성할 예정이다.

2개의 파일 이름은 Android.mkhello-jni.c 이다.


jni폴더 생성후 [마우스 오른쪽 클릭] -> [New] -> [File] 을 눌러 파일을 생성한다.




위의 그림의 방식처럼 hello-jni.c 와 Android.mk 파일을 만들었다면 두 파일에 내용을 넣는다. 먼저 hello-jni.c 파일부터 내용을 넣겟다.


hello-jni.c

#include <string.h>
#include <jni.h>

jstring Java_tjssm_hellojni_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )
{

    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

NDK 설치를 확인하기 위한 간단한 예제이기 때문에 자세한 설명은 생략하도록 하겠다. 

여기서 주의할 점은 jstring Java_tjssm_hellojni_MainActivity_stringFromJNI()이다.

jstring은 return 타입이고 그다음은 Java_패키지명1_패키지명2_class명_class에서만든메소드명() 이다.


ex) return타입이 jstring 이고 패키지명이 tjssm.hellojni이고 class명이 MainActivity이고, class에 만들어 놓은 메소드명이 stringFromJNI 이기때문에jstring Java_tjssm_hellojni_MainActivity_stringFromJNI() 이라고 쓴다.


여기서 주의할 점은 패키지명이나 class명에 '_'기호가 들어갈 경우 에러가 난다는 것이다. ndk를 이용한 프로그래밍에서는 '_'기호를 넣지 않도록 한다.


다음은 Android.mk 파일에 내용을 넣겠다.


Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

LOCAL_MODULE 은 c파일의 이름

LOCAL_SRC_FILES 에는 c파일의 이름에 .c를 붙여주면 된다.


다음은 JAVA소스이다. jni를 호출하는 MainActivity의 소스이다.


MainActivity.java

public class MainActivity extends Activity {
	
	private TextView tv;
	
	static {
		System.loadLibrary("hello-jni");
	}
	
	public native String stringFromJNI();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		tv = (TextView) findViewById(R.id.tv_result);
		tv.setText(stringFromJNI());
	}
}

System.loadLibrary에 이전에 만들었던 c파일의 이름인 hello-jni를 넣어준다. 

public native String stringFromJNI() 라는 메소드가 jni와 연결된다.

return 타입이 String 이므로 이전에 작성했던 hello-jni로부터 String을 return 받아 tv라는 TextView로 값이 전달된다.


위와 같이 소스를 작성하였다면 ndk-build를 실행한다. cygwin 폴더안의 android-ndk폴더를 환경변수로 등록을 하였다면 cmd 창을 실행하여 현재 작성한 프로젝트 파일의 경로로 이동한다. 그후에 ndk-build라는 명령어를 입력하면 현재 프로젝트의 libs->armeabi 라는 폴더가 생기며 폴더안에 파일이 생성되어 있을것이다.


이제 이 프로젝트를 실행시키면 다음과 같은 결과를 얻을 수 있다.



매번 cmd창을 이용하여 ndk build를 할 수는 없으니 다음에는 이클립스에서 ndk build하는 방법을 알아보도록 하겠다.



2014/01/27 - [Programming/안드로이드] - [안드로이드] Eclipse에서 NDK-build 하기



+ Recent posts