Заметки · 17.04.2023

Сим-сим, откройся!

Начну с обязательного предисловия.

Я ни в коем случае не являюсь разработчиком приложений для операционной системы Android. Более того, на момент написания поста я видел Android Studio третий раз в жизни и никогда не занимался созданием программ для Android. Пришлось штудировать матчасть.

Теперь по теме поста.

Попался мне в руки безымянный электронный замок с вызывной панелью, камерой и простым веб-сервером. На основной веб-странице, помимо фрейма с видеопотоком и настройками устройства, присутствует интересная кнопка. С её помощью можно открыть замок пройдя парольную авторизацию.

Мне стало интересно — а можно ли открывать замок, выполняя запрос напрямую из адресной строки браузера? Спустя пару чашек кофе, я откопал в коде ссылку на cgi-скрипт, открывающий дверь. Именно в этот момент мне пришла в голову идея создания приложения-ключа для Android.

Установив на компьютер Android Studio, я принялся за создание приложения с условным названием — OpenDoor. Коротко и ясно, — думалось мне.

А потом случилось это:

— А чего ты не назвал приложение Аллохомора?

VoatiK (но уже постфактум)

Как-то не вспомнилось… А ведь какое было бы название! И работает по тому же принципу, и звучит красиво, и отсылка опять же…

Продолжим! Языком разработки я выбрал Java. В качестве шаблона взял «Empty Activity» и Android 5.0 Lollipop — минимальной совместимой версией ОС. Так как OpenDoor будет работать с сетью, добавил в файл манифеста (AndroidManifest.xml) строку:

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

Во многих источниках упоминалось, что если приложение будет сталкиваться именно с http (открытый текстовый трафик), нужно добавить в манифест еще и это:

android:usesCleartextTraffic="true"

Итоговый файл AndroidManifest.xml выглядел вот так:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:usesCleartextTraffic="true"
        android:supportsRtl="true"
        android:theme="@style/Theme.OpenDoor"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Теперь про дизайн. В Android Studio за размещение элементов управления отвечает файл activity_main.xml. А раскрасить элементы можно, используя файлы со стилями, цветами и темами. Например, отключить action-bar приложения можно через themes.xml с помощью строки:

<style name="Theme.OpenDoor" parent="Theme.AppCompat.Light.NoActionBar">
Файл themes.xml

В файл colors.xml можно вносить цвета и давать им имена, чтобы ссылаться именно на название цвета, а не его код. Очень удобно — особенно — когда нужно что-то централизовано перекрасить.

Файл colors.xml

Стоит заметить, что представление файла activity_main.xml очень сильно напоминает старый добрый WYSIWYG редактор из тех времён, когда они ещё были в моде.

Совмещение вида разработки дизайна с помощью кода и визуального редактора

Для работы заветного приложения была нужна всего одна кнопка с надписью «Открыть дверь» и расположить её хотелось по самому центру экрана. Фон приложения мне виделся исключительно белым, что создавало бы эффект эмуляции фонарика в темное время суток.

Кнопку (объект Button) я разместил в LinearLayout с вертикальной ориентацией (android:orientation=»vertical») и центральной гравитацией объектов (android:gravity=»center»).

Чтобы прикрутить к нажатию кнопки функцию — я использовал конструкцию android:onClick=»OpenDoor», где OpenDoor — это имя вызываемой функции из файла MainActivity.java, но об этом чуть ниже.

Итоговый код activity_main.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=".MainActivity">
    <LinearLayout
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical" >
        <Button
            android:id="@+id/opendoorbtn"
            android:layout_width="266dp"
            android:layout_height="94dp"
            android:backgroundTint="@color/purple_700"
            android:onClick="OpenDoor"
            android:text="Открыть дверь"
            android:textAlignment="center"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:tooltipText="Нажмите для открытия двери" />
    </LinearLayout>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="bottom|end">
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="code by ngdream"
            android:textAlignment="center"
            android:textColor="@color/gray" />
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

В нижний LinearLayout добавил надпись «code by ngdream«, установив гравитацию по нижнему краю с помощью конструкции: android:gravity=»bottom|end».

Этап тестирования дизайна в эмуляторе

С дизайном более-менее разобрались. Кнопку нарисовали, раскрасили и разместили. Теперь оживим приложение.

Все методы я запихнул в файл — MainActivity.java.

Обращение к http сформировал на основе библиотеки Volley. Здесь (тык) можно подсмотреть пример простого запроса и подогнать его под свои нужды. Установку библиотеки в проект можно осуществить, добавив в файл build.gradle(:app) новую зависимость (блок dependencies):

dependencies {
    ...
    implementation 'com.android.volley:volley:1.2.1'
    ...
}
Вид файла build.grandle(:app)

Как только библиотека установится — можно писать код. Даже если залить в java-файл копипасту из официальной документации Volley и уже на месте править, Android Studio очень тонко намекнёт на импорт нужных классов.

Итоговый вариант MainActivity.java получился таким:

package ru.ngdream.opendoor;

// Импорт модулей в проект
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

public class MainActivity extends AppCompatActivity {

    // Действие при запуске приложения
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // Действие по кнопке
    public void OpenDoor(View view) {
        httpCall("http://192.168.0.100/web/cgi-bin/hi3510/doorUnlock.cgi");
    }

    // Всплывающее сообщение
    public void MessageView(String text) {
        Toast toast = Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT);
        toast.show();
    }

    // HTTP запрос
    public void httpCall(String url) {
        RequestQueue queue = Volley.newRequestQueue(this);
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        MessageView("Дверь открыта!");
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                MessageView("Что-то пошло не так...");
            }
        });
        queue.add(stringRequest);
    }

}

Для понимания логики: я добавил метод вывода всплывающих сообщений MessageView и прицепил его к событиям в методе httpCall, который, в свою очередь, выполняется в методе OpenDoor. Именно OpenDoor является титулярным методом и вызывается при нажатии на единственную кнопку.

Если всё прошло успешно и httpCall получил данные от заданного url — дверь откроется и внизу появится соответствующее сообщение. В противном случае вылезет надпись «что-то пошло не так…».

А теперь немного про иконку приложения. Android Studio умеет делать отличные иконки без использования графических редакторов.

Процесс изготовления иконки приложения

Достаточно указать цвет фона и выбрать из обширной коллекции клип-арта нужную пиктограмму. Также среда разработки умеет создавать вариации формы иконок для разных оболочек операционных систем.

Итоговый вид приложения в эмуляторе

Резюмируя, скажу, что порог вхождения в разработку приложений для Android крайне низок и за вечер можно спокойно собрать что-то подобное даже не разбираясь в дебрях и тонкостях Android Studio. Для разработки простого приложения хватит базовых знаний Java. Единственный очевидный минус этой среды разработки, с которым придётся мириться — это требования к железу.

P.S.: работа приложения гарантируется только если смартфон и электронный замок находятся в одной сети.