Пульт на Android

Все владельцы мобильных устройств фирмы Apple ещё морозным январем 2018го обзавелись приятным приложением под iOS для пилотирования квадрокоптеров с помощью WiFi. И вот, спустя год вышло такое же приложение но уже для другой операционной системы. Актуальную версию вы можете скачать тут .

Введение

В данной статье я расскажу вам о том, как можно написать свой или доработать уже имеющийся пульт для Андроид своими руками. Для работы будем использовать модный язык Kotlin, а в качестве среды разработки возьмем Android Studio. Для тех кто ни разу ей не пользовался рекомендую к ознакомлению следующие материалы. Весь код приложения можно найти тут. Если вы хотите сразу получить приложение с целью дальнейшей доработки, выполните следующую команду:

git clone https://github.com/Tennessium/android

Однако чтобы вы смогли полностью понять устройство приложения, я расскажу вам о каждом этапе создания проекта, как если бы вы делали его с нуля.

Обертка

Начнем с самого простого - внешнего вида нашего приложения. На гитхабе вы можете найти HTML, CSS и JavaScript файлы, это и есть веб страница с которой будет происходить управление коптером. Чтобы эта страница отображалась у нас в приложении надо:

  1. Создать папку assets в главной папке приложения app

  2. Добавить в нее файлы все файлы отсюда

Если вы дошли до этого этапа то у вас уже есть необходимая веб страница, поздравляю! Теперь нам надо её как-то отобразить в приложении. Для этого в классе вашего activity в методе onCreate необходимо написать следующий код:

main_web.loadUrl("file:///android_asset/index.html")

Где main_web - id вашего WebView, который должен находится в xml файле выбранного вами activity.

К сожалению, пульт для управления коптером требует всего экрана устройства, а элементы интерфейса системы мешают полноценному использованию программы. Для этого надо в начале метода onCreate вызвать следующую функцию:

private fun fullScreenCall() {
    window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
    if (Build.VERSION.SDK_INT < 19) {
        val v = this.window.decorView
        v.systemUiVisibility = View.GONE
    } else {
        //for higher api versions.
        val decorView = window.decorView
        val uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        decorView.systemUiVisibility = uiOptions
    }
}

Данная функция позволяет избавиться от элементов интерфейса системы. Идем дальше.

Вот так выглядит пульт на этом этапе:

Если вы запустите приложение, то заметите что стики не работают. Это происходит по тому, что на нашей странице отключен JavaScript. чтобы его включить надо прописать следующее:

    main_web.settings.apply {
    domStorageEnabled = true
    javaScriptEnabled = true
    loadWithOverviewMode = true
    useWideViewPort = true
    setSupportZoom(false)
}

Этим куском кода мы разрешаем странице использовать JavaScript и заодно готовимся к следующему этапу - логике.

Прием данных с веб страницы

Чтобы телефон мог принимать данные с HTML страницы, надо создать класс для взаимодействия с веб интерфейсом

class WebAppInterface(c: Context) {
    @JavascriptInterface
    public fun postMessage(message: String) {
        val data = JSONObject(message)
        send("255.255.255.255", 35602, pack(
        data.getInt("x").toShort(),
        data.getInt("y").toShort(),
        data.getInt("z").toShort(),
        data.getInt("r").toShort()))
    }
}

Данный класс будет получать сообщение с веб страницы отправленное методом postMessage, где аргумент message - сообщение от страницы.

Теперь надо связать классы WebAppInterface и MainActivity. Для этого надо всего лишь добавить одну строку в метод onCreate:

main_web.addJavascriptInterface(WebAppInterface(this), "appInterface")

Отправка данных на коптер

Важно! Для любой работы с интернетом на платформе Android в файле AndroidManifest.xml внутри тега manifest необходимо добавить такую строку:

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

Она дает вашему приложению доступ в интернет и возможность передавать данные по средствам WiFi. А как это делать, мы с вами сейчас и узнаем. Идём дальше!

Вы наверное заметили функцию send в классе WebAppInterface. Именно она отправляет данные на коптер. Давайте объявим ее вне классов:

fun send(host: String, port: Int, data: ByteArray, senderPort: Int = 0): Boolean {
    var ret = false
    var socket: DatagramSocket? = null
    try {
        socket = DatagramSocket(senderPort)
        val address = InetAddress.getByName(host)
        val packet = DatagramPacket(data, data.size, address, port)
        socket.send(packet)
        ret = true
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        socket?.close()
    }
    return ret
}

Данная функция отправляет данные при помощи протокола пользовательских датаграмм на коптер. Программа отправляет байты, поэтому неплохо бы было объявить функцию для создания массива байтов из четырех переменных:

fun pack(x: Short, y: Short, z: Short, r: Short): ByteArray {
    val pump_on_buf: ByteBuffer = ByteBuffer.allocate(8)
    pump_on_buf.putShort(r)
    pump_on_buf.putShort(z)
    pump_on_buf.putShort(y)
    pump_on_buf.putShort(x)
    return pump_on_buf.array().reversedArray()
}

Итог

Теперь ваше приложение имеет полный функционал аналога под iOS. Можете кастомизировать его как пожелаете. По любым вопросам о приложении можете обращаться в Телеграм @Tenessinum.

results matching ""

    No results matching ""