[Android App BLE] #3 BLE를 위한 권한 설정 및 화면 구성

in #kr6 years ago (edited)

이전글 - [Android App BLE] #2 Bluetooth Low Energy (BLE) 장치와 Bluetooth Classic

다음을 주로 참고하여 작성하였습니다.
Bluetooth Low Energy on Android, Part 1


출처: https://simbeez.com/


개념은 대충 잊어버리시고, 이제 즐거운 개발을 해봅시다.

개발환경은 다음과 같습니다.

  • Ubuntu 16.04
  • Android Studio 3.1.3
  • Android Version: 8.0

우분투나 윈도우즈나 큰 차이는 없을거 같습니다. 저는 주로 Eclipse로 안드로이드 앱을 개발했는데 안드로이드 스튜디오는 아직 낯서네요. 그냥 닥치고 개발해 보겠습니다.

빈 액티비티로 프로젝트 생성하는 것은 생략합니다. 액티비티 이름은 Default로 MainActivity를 사용합니다. #1편에서 다뤘듯이 처음 프로젝트 생성하고 GUI가 제대로 표시되지 않는 문제가 있으니 #1편 내용을 참고하세요.

코딩은 되도록이면 MainActivity에서 대부분 구현하려고 합니다. 원래는 기능별로 나눠야 하는데 말이죠. 처음 접하기에는 나눠져 있는 것보다 한 곳에 합쳐져 있는 것이 이해하긴 쉬운거 같습니다.

Permission 설정

  • 설정 파일: AndroidManifest.xml
    위 파일에 아래와 같은 내용을 추가합니다.
    (html comment removed:  BLE permission )
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

BLUETOOTH관련 권한 설정은 이해가 가는데, LOCATION 사용 권한은 의아할 수 있습니다. BLE 장치의 주요 기능 중 하나가 위치 기반 서비스 입니다. 즉 비콘과 같이 위치 정보를 제공하는 것이 많기 때문에 구글에서 BLE를 사용하려면 LOCATION관련 권한도 요청하기로 정했다고 합니다!

GUI 구성

GUI는 대략 이런 느낌입니다.

디자인, 꾸미기 이런거 없습니다. 저는 유독 디자인에 약합니다 ㅜ.ㅜ 디자이너 필요합니다~

  • Text View: 상태와 장치로 부터 읽은 값을 표시
  • START SCAN: BLE 장치 스캔 후 특정 MAC 주소와 일치되는 장치에 연결. 연결되면 자동으로 스캔 종료.
  • STOP CONNECTION: 장치와 연결 종료
  • SEND DATA: 주변장치에 제어 명령 전송
  • SHOW PAIRED DEVICES: 페어링된 장치 목록 표시

위 버튼의 내용을 살펴보면 구현하고자 하는 것은 모든 BLE 장치를 찾아서 일일이 클릭해서 접속하는 것이 아니라, 특정 장치를 찾고, 거기에 접속하는 것입니다. 그런 후에 적절한 제어 명령을 전송하는 기능을 포함하려고 합니다. 페어링된 장치 표시는 부가적인 걸로 보시면 되겠습니다. 스캔으로 자동 접속이 잘 안될 경우 페어링된 장치를 클릭하여 접속하는 기능을 구현할 것입니다.

제가 찾으려고 했던 부분이 바로 이겁니다. 특정 장치와 빠르게 연결하는 것! 대부분의 코드가 스캔하고, 장치 선택하고, 접속하는 내용을 다루고 있습니다. 사실 어떤 장치를 사용할 때 모두 스캔하는 것은 필요가 없습니다. 통신하고 싶은 장치만 스캔해서 접속하면 되니까요.

GUI 구성을 위해 다음과 같이 Relative Layout을 사용합니다.

  • 파일: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_marginTop="10dp"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="0.1"
                android:text="Status: "
                android:ellipsize="end"
                android:maxLines="1"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/tv_status"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="0.9"
                android:ellipsize="end"
                android:maxLines="1"
                android:text="<Status>" />

        </LinearLayout>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_marginTop="10dp"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="0.1"
                android:text="Read: "
                android:ellipsize="end"
                android:maxLines="1"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/tv_read"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="0.9"
                android:ellipsize="end"
                android:maxLines="1"
                android:text="<Read>" />

        </LinearLayout>


        <Button
            android:id="@+id/btn_scan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start Scan"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="179dp" />

        <Button
            android:id="@+id/btn_stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stop Connection"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="114dp" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send Data"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="257dp" />

        <Button
            android:id="@+id/btn_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Show Paired Devices"
            tools:layout_editor_absoluteX="108dp"
            tools:layout_editor_absoluteY="179dp" />

    </LinearLayout>

</RelativeLayout>

스튜디오 3.1.3에서는 ConstraintLayout이 디폴트인데, 여기서는 RelativeLayout을 사용하기로 합니다. 구성에 대해서는 따로 설명하지 않겠습니다. id부분만 주의깊게 봐주세요. GUI에 접근하기 위해서 이 id가 사용되니까요. id 이름 정하기가 좀 까리합니다. 그냥 변수명 만들듯이 작명했습니다.

GUI Control Variables

GUI 텍스트, 버튼들을 제어하는 변수들을 선언합니다.
-파일: MainActivity.java

public class MainActivity extends AppCompatActivity {
    //// GUI variables
    // text view for status
    private TextView tv_status_;
    // text view for read
    private TextView tv_read_;
    // button for start scan
    private Button btn_scan_;
    // button for stop connection
    private Button btn_stop_;
    // button for send data
    private Button btn_send_;
    // button for show paired devices
    private Button btn_show_;
(생략)

변수명이 좀 이상합니다. 저는 C++에서 주로 개발을 해와서, 구글의 C++ 코딩 가이드를 따르고 있습니다. 변수명은 적절하게 바꾸시면 됩니다. 아마 많은 분들이 변수명이 거슬려서 바꾸실 것입니다. 개인적으로 대세 안드로이드 코딩 스타일이 좀 별로 입니다. 변수명을 다 붙여 쓰다 보니 무슨 의미인지 바로 파악하기 어려울 때가 많습니다. 그래서 저는 언더바를 사용하는 스타일로 가겠습니다.

onCreate 함수

onCreate함수에 아래와 같이 GUI 컨트롤 변수들을 GUI 컴포넌트와 연결합니다.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //// get instances of gui objects
        // status textview
        tv_status_= findViewById( R.id.tv_status );
        // read textview
        tv_read_= findViewById( R.id.tv_read );
        // scan button
        btn_scan_= findViewById( R.id.btn_scan );
        // stop button
        btn_stop_= findViewById( R.id.btn_stop );
        // send button
        btn_send_= findViewById( R.id.btn_send );
        // show button
        btn_show_= findViewById( R.id.btn_show );
    }

이로써 화면 구성은 마무리 되었습니다. 한 번 Build -> Make Project 해봅니다.

앗! 에러가 발생하네요.

error: not well-formed (invalid token). Message{kind=ERROR, text=error: not well-formed (invalid token)., sources=[/home/etain/AndroidStudioProjects/BLEControl/app/src/main/res/layout/activity_main.xml:34], original message=, tool name=Optional.of(AAPT)}

Error: java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details

오류는 activity_main.xml의 다음 부분에서 발생했습니다.
android:text="<Read>" />
꺾쇠를 사용해서 그런가 봅니다. 아래와 같이 수정합니다.
android:text="&lt;Read>" />

Status 부분도 아래와 같이 수정합니다.
android:text="&lt;Status>" />

빌드 및 설치

이제 다시 빌드해 봅니다. 휴~ 제대로 빌드가 됩니다. 그리고 자신의 스마트폰에 설치해서 확인합니다. 이부분은 생략하겠습니다. 스마트폰에서 확인하려면 먼저 스마트폰을 개발자 옵션을 활성화 시켜야 합니다. 그런 후에 USB Debugging 옵션을 켜줘야 합니다.

안드로이드 스마프폰이 없거나 번거로우신 분은 Virtual Device를 설치하여 확인할 수 있습니다.

Android Virtual Device에 설치후 앱 실행 화면입니다.


다음에는 스캔하기 위한 준비사항과 스캔에 대해서 코딩해 보겠습니다.

오늘의 실습: 자신만의 코딩 스타일을 만들어 보세요.

Sort:  

날이 너무 덥습니다......덥다 ㅠ

감사합니다! 잘 보고 있습니당

Coin Marketplace

STEEM 0.18
TRX 0.13
JST 0.029
BTC 58216.77
ETH 3142.31
USDT 1.00
SBD 2.23