정리가 필요한 카테고리(추후 정리)/Android, iOS

코틀린(Kotlin)으로 작성한 안드로이드 블루투스 리모컨 예제

TwinParadox 2018. 9. 15. 21:51
728x90

https://twinparadox.tistory.com/132



위 링크에 올린 블루투스 리모컨으로 조종하는 아두이노 선풍기에 어플리케이션 코드가 없었다.


프로젝트를 정리해 올리면서 아두이노 스케치 코드는 올렸는데, 어플리케이션 코드는 찾지 못해서 올리지 못한 상태였는데 최근 코틀린을 배우면서, 코틀린도 연습할 겸, 잃어버린 프로젝트의 주권(?)을 되찾을 겸, 리모컨을 다시 만들었다.



소스에 대해 간략히 설명하자면, SelectDeviceActivity는, 블루투스 장치를 검색 및 선택하는 액티비티고, ControlActivity는 해당 장치를 컨트롤하는 액티비티로, 여기서는 리모컨 역할을 하는 액티비티다.



깃허브 주소 : https://github.com/Twinparadox/ArduinoFan-with-RemoteController/tree/master/BluetoothRemoteController




build.gradle

apply plugin: 'com.android.application'
 
apply plugin: 'kotlin-android'
 
apply plugin: 'kotlin-android-extensions'
 
android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "twinparadox.bluetoothremotecontroller"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    compile "org.jetbrains.anko:anko-common:0.8.3"
}
cs



AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="twinparadox.bluetoothremotecontroller">
 
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SelectDeviceActiviy">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ControlActivity"
            android:screenOrientation="portrait" >
        </activity>
    </application>
 
</manifest>
cs




control_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto">
 
    <android.support.constraint.ConstraintLayout
        android:id="@+id/wind_power"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp">
 
        <Button
            android:id="@+id/wind_high"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/high"
            app:layout_constraintRight_toRightOf="parent"/>
 
        <Button
            android:id="@+id/wind_mid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/mid"
            app:layout_constraintLeft_toLeftOf="@+id/wind_low"
            app:layout_constraintRight_toRightOf="@+id/wind_high" />
 
        <Button
            android:id="@+id/wind_low"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/low"
            app:layout_constraintLeft_toLeftOf="parent"/>
    </android.support.constraint.ConstraintLayout>
 
    <android.support.constraint.ConstraintLayout
        android:id="@+id/wind_stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        app:layout_constraintTop_toBottomOf="@+id/wind_power"
        >
 
        <Button
            android:id="@+id/wind_off"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/windstop"/>
    </android.support.constraint.ConstraintLayout>
 
    <android.support.constraint.ConstraintLayout
        android:id="@+id/rotation_angle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        app:layout_constraintTop_toBottomOf="@+id/wind_stop">
 
        <Button
            android:id="@+id/angle_60"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/angle60"
            app:layout_constraintLeft_toLeftOf="parent"/>
 
        <Button
            android:id="@+id/angle_120"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/angle120"
            app:layout_constraintLeft_toLeftOf="@+id/angle_60"
            app:layout_constraintRight_toRightOf="@+id/angle_180" />
 
        <Button
            android:id="@+id/angle_180"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/angle180"
            app:layout_constraintRight_toRightOf="parent"/>
 
    </android.support.constraint.ConstraintLayout>
 
    <android.support.constraint.ConstraintLayout
        android:id="@+id/rotation_stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        app:layout_constraintTop_toBottomOf="@+id/rotation_angle">
 
        <Button
            android:id="@+id/rotation_off"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/rotationstop"/>
 
    </android.support.constraint.ConstraintLayout>
 
    <android.support.constraint.ConstraintLayout
        android:id="@+id/disconnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        app:layout_constraintTop_toBottomOf="@+id/rotation_stop">
 
        <Button
            android:id="@+id/disconnect_device"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/disconnect"/>
 
    </android.support.constraint.ConstraintLayout>
 
</android.support.constraint.ConstraintLayout>
cs



ControlActivity.kt

package twinparadox.bluetoothremotecontroller
import android.app.ProgressDialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.os.AsyncTask
import android.os.Bundle
import android.os.PersistableBundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import kotlinx.android.synthetic.main.control_layout.*
import java.io.IOException
import java.util.*
class ControlActivity: AppCompatActivity() {
    companion object {
        var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
        var m_bluetoothSocket: BluetoothSocket? = null
        lateinit var m_progress:ProgressDialog
        lateinit var m_bluetoothAdapter:BluetoothAdapter
        var m_isConnected:Boolean = false
        lateinit var m_address:String
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.control_layout)
        m_address=intent.getStringExtra(SelectDeviceActiviy.EXTRA_ADDRESS)
        ConnectToDevice(this).execute()
        wind_off.setOnClickListener{ sendCommand("off") }
        wind_low.setOnClickListener{ sendCommand("on1") }
        wind_mid.setOnClickListener { sendCommand("on2") }
        wind_high.setOnClickListener { sendCommand("on3") }
        rotation_off.setOnClickListener { sendCommand("off4") }
        angle_60.setOnClickListener { sendCommand("on4") }
        angle_120.setOnClickListener { sendCommand("on5") }
        angle_180.setOnClickListener { sendCommand("on6") }
        disconnect_device.setOnClickListener{ disconnect() }
    }
    private fun sendCommand(input:String) {
        if(m_bluetoothSocket != null) {
            try {
                m_bluetoothSocket!!.outputStream.write(input.toByteArray())
            }
            catch (e:IOException) {
                e.printStackTrace()
            }
        }
    }
    private fun disconnect() {
        if(m_bluetoothSocket != null) {
            try {
                m_bluetoothSocket!!.close()
                m_bluetoothSocket = null
                m_isConnected = false
            }
            catch (e:IOException) {
                e.printStackTrace()
            }
        }
        finish()
    }
    private class ConnectToDevice(c: Context): AsyncTask<Void, Void, String>() {
        private var connectSuccess:Boolean = true
        private val context:Context
        init {
            this.context=c
        }
        override fun onPreExecute() {
            super.onPreExecute()
            m_progress= ProgressDialog.show(context,"Connecting...","please wait")
        }
        override fun doInBackground(vararg params: Void?): String? {
            try {
                if(m_bluetoothSocket == null || !m_isConnected) {
                    m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
                    val device:BluetoothDevice = m_bluetoothAdapter.getRemoteDevice(m_address)
                    m_bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(m_myUUID)
                    BluetoothAdapter.getDefaultAdapter().cancelDiscovery()
                    m_bluetoothSocket!!.connect()
                }
            }
            catch(e:IOException) {
                connectSuccess=false
                e.printStackTrace()
            }
            return null
        }
        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            if(!connectSuccess) {
                Log.i("data","coudln't connect")
            }
            else {
                m_isConnected=true
            }
            m_progress.dismiss()
        }
    }
}
cs



select_device_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SelectDeviceActiviy">
 
    <ListView
        android:id="@+id/select_device_list"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/select_device_refresh"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginBottom="10dp"/>
 
    <Button
        android:id="@+id/select_device_refresh"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="20dp"
        android:text="@string/refresh"/>
 
</android.support.constraint.ConstraintLayout>
cs



SelectDeviceActivity.kt

package twinparadox.bluetoothremotecontroller
import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.AdapterView
import android.widget.ArrayAdapter
import kotlinx.android.synthetic.main.select_device_layout.*
import org.jetbrains.anko.toast
class SelectDeviceActiviy : AppCompatActivity() {
    private var m_bluetoothAdapter:BluetoothAdapter? = null
    private lateinit var m_pairedDevices:Set<BluetoothDevice>
    private val REQUEST_ENABLE_BLUETOOTH = 1
    companion object {
        val EXTRA_ADDRESS: String = "Device_address"
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.select_device_layout)
        m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        if(m_bluetoothAdapter == null) {
            toast("This device dosen't support bluetooth")
            return
        }
        if(!m_bluetoothAdapter!!.isEnabled) {
            val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
            startActivityForResult(enableBluetoothIntent, REQUEST_ENABLE_BLUETOOTH)
        }
        select_device_refresh.setOnClickListener{ pairedDeviceList() }
    }
    private fun pairedDeviceList() {
        m_pairedDevices = m_bluetoothAdapter!!.bondedDevices
        val deviceList : ArrayList<BluetoothDevice> = ArrayList()
        val nameList : ArrayList<String> = ArrayList()
        if(!m_pairedDevices.isEmpty()) {
            for(device:BluetoothDevice in m_pairedDevices) {
                deviceList.add(device)
                nameList.add(device.name+"("+device.address+")")
                Log.i("device",""+device.name)
            }
        }
        else {
            toast("Paired devices not found")
        }
        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1,nameList)
        select_device_list.adapter = adapter
        select_device_list.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            val device: BluetoothDevice = deviceList[position]
            val address: String = device.address
            val intent = Intent(this, ControlActivity::class.java)
            intent.putExtra(EXTRA_ADDRESS,address)
            startActivity(intent)
        }
    }
    override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if(requestCode == REQUEST_ENABLE_BLUETOOTH) {
            if(resultCode == Activity.RESULT_OK) {
                if(m_bluetoothAdapter!!.isEnabled) {
                    toast("Bluetooth has been enabled")
                }
                else {
                    toast("Bluetooth has benn disabled")
                }
            }
            else if(resultCode == Activity.RESULT_CANCELED) {
                toast("Bluetooth enabling has benn canceled")
            }
        }
    }
}
cs








728x90
728x90