Веб Камера может понадобиться в самый неожиданный момент. Например, кто-то повадился посещать Вашу дачу без спроса. Можно провести Интернет на дачу, арендовать "белый" IP-адрес, поставить IP-видеокамеру ...
Или установить на смартфон какое-то приложение, которое превратит его в Веб Камеру, будет закачивать фото или видео на сервер разработчика, где Вы будете просматривать его в личном кабинете. Ну, и не только Вы, но и владелец сервера. Опять же, не понятно, нет ли в этом приложении бэкдора...
А можно сделать иначе и бесплатно. Не проще, но программисты поймут выгоду.
Курсовой Проект в исходных кодах
Веб Камера в исходных кодах на JAVA. Полностью весь проект для Android Studio - такой, как его сделал Разработчик. С комментариями.
Загружаем в Android Studio, подключаем смартфон по USB шнурку, компилируем, инсталлируем, пользуемся.
Теперь КАК?
В приложении используется стандартный Apache FTP Client. Соответствующие библиотеки включены в проект. Если есть сомнения, можете их скачать сами и заменить - проект полностью доступен - http://shmeleff.com/PhotoWebCamSHMELEFF.zip
Все остальные части проекта доступны для ознакомления.
Преимущества проекта
Вы можете инспектировать, модернизировать и развивать проект сами. Это настоящий Open Source Project без ограничений. Можете добавить свои функции. Например, сделать и отправить фото, если пришла SMS с заданным кодом. Или добавить клиента для работы с Watsapp
Нет дополнительных серверов, обязательных для хранения изображений или управления. Можно отправлять фотов свой Телеграм канал или на свой персональный web сервер.
Для упрощения проекта приложение работает только в вертикальной ориентации экрана. Для выполнения функций этого достаточно. В верхней части экрана отображается телеметрия - сообщения о работе приложения.
Файл разметки activity_main.xml для главного экрана приложения - http://shmeleff.com/photocam/activity_main.xml
Верхний TextView используется для телеметрии - текстовых системных сообщений. Есть ротация и скроллинг.
TextureView расположенный в центре экрана предназначен для работы с изображением с фотокамеры.
В нижней части главного экрана приложения - блоки кнопок управления.
Курсовой проект
Java код главного класса - http://shmeleff.com/photocam/MainActivity.java
Здесь выполняются все основные действия: запрашиваются разрешения (помимо файла Манифеста), создается и запускается таймер, используется фотокамера, вызывается FTP клиент, обрабатываются кнопки, читаются данные настроек (из ресурсов).
При смене версий OS Android иногда Google меняет правила получения разрешений для работы с фотокамерой и с записью и чтением данных в память. Причем, менялись правила радикально.
http://oflameron.com Guide
Вообще-то надо было работу с фотокамерой вынести в отдельный класс.
Main Screen
Вверху - телеметрия - системные сообщения приложения.
Ниже - фото
Ещё ниже - кнопки управления
START/STOP - запуск и остановка процесса фото-регистрации
FRONT CAM/REAR CAM - переключение между фотокамерами
CAM LOCAL - сохранять фото в памяти смартфона
FTP SETUP - настройка подключения FTP клиента
DARK - сделать темный экран
GUIDE - онлайн руководство
Icons
В приложении загружается некоторое количество иконок. Они иллюстрируют GUIDE, приложение и некоторые функции.
Всего 3 экрана, 4 java файла.
главный экран activity_main.xml
экран настроек FTP activity_f_t_p.xml
встроенное руководство print_activity.xml
В AndroidManifest.xml даются разрешения и объявляются Activity
http://shmeleff.com/photocam/AndroidManifest.xml
Скриншот файла ниже
Файл MyFTPClientFunctions.java - стандарnный набор методов для работы с Apache FTP Client
Файл MainActivity.java - главная Активити - вся работа с фотокамерой, GUI, разрешения, таймер, асинхронный вывод на экран, вызов FTP функций
Файл FTPActivity.java - активити для настройки FTP клиента
Файл PrintActivity.java - встроенное руководство Администратора
Почему проект именно Фото Веб Камеры?
при длительном наблюдении фотографии сэкономят огромное количество мобильного трафика Интернет, не требуют большой пропускной способности и большого дискового пространства для хранения
в большинстве случаев Photo Web Cam это дополнительное средство наблюдения к имеющимся видеокамерам - корпоративным, муниципальным
наблюдать за дачей зимой в режиме онлайн-видео? Это никто не будет отсматривать
вокруг полно бросовых устаревших или поврежденных смартфонов с старыми батареями, поцарапанными корпусами и стоящих "копейки". Поставив на них Ваше приложение клиент получит интеллектуальное средство наблюдения. Дешевле, чем с классической IP-веб камерой. Даже, если эту веб камеру похитят, это будет потеря всего 1-2 тысяч рублей.
изображения записываются только на выбранный Вами сервер (или в Ваш Телеграм канал - далее будет показано как это сделать), а не на сервер разработчика в Ваш "личный кабинет"
это отличный курсовой проект. Преподавателю будет интересно и Вам тоже.
это отличная заготовка, стартап для Вашего приложения. Автором разрешено развивать свои версии приложения
Для загрузки фотографий на сервер используется известный Apache FTP Client
Просто взят стандартный код и подключены библиотеки.
Apache библиотеки надо разместить в папке
Вторая активити - для настройки параметров приложения. Содержит много элементов EditText для ввода параметров. Имеет табличную разметку
Описание полей ввода параметров
EditText7 - "Per seconds 500+ Sample: 520" - Указывается время между фотоснимками. При использовании мобильного Интернета 4G рекомендуется не менее 500 секунд. Это время зависит от типа подключения к Интернету. WiFi или 5G позволяют делать снимки чаще. Следует учитывать, что в этом случае ротация фотоснимков на сервере будет происходить быстрее и надо буде либо чаще их просматривать, либо увеличивать количество снимков в серии (что увеличит необходимый размер дискового пространства).
EditText8 - Start Time HH. Sample: 7 - время начала фотосессии. Начало светлого времени суток в часах. Например, 7 утра.
EditText10 - End time HH. Sample: 20 - время окончания фотосессии. Окончание светлого времени суток. Например, 20 часов
EditText1 - FTP Server. Sample: ftp.dtn.com - адрес веб сервера (веб хостинга или ftp сервера), на который будут загружаться фотографии. Может быть и IP адресом в некоторых случаях.
EditText14 - Filename. Sample: picture - шаблон имени файла изображения на веб сервере, из которого будут создаваться имена фалов фотографий - picture1.jpg, picture2.jpg и т.д. добавлением порядкового номера и расширения.
EditText15 - Images Number. Sample: 10 - количество фотографий в серии (сессии), после чего они будут перезаписываться на веб сервере.
EditText12 - Login. Sample: admin - login для подключения к хостингу (веб серверу)
EditText3 - Password. Sample: u54eU@ - пароль для подключения к хостингу.
EditText4 - Folder. Sample: public_html - имя папки на веб сервере (хостинге), в которую надо записывать фотографии, чтобы они были доступны для просмотра веб броузером. Дело в том, что у многих веб серверов папка, к которой подключается FTP клиент, не совпадает с папкой, в которой лежат веб странички для просмотра вебброузером. Вот эту разницу и надо здесь указать, чтобы FTP клиент в неё перешел перед загрузкой файлов.
В FTPActivity.java - настраиваются параметры, а используются в другой Активити. Как они сохраняются в приложении и передаются?
//==========================================================
// Записать FTP настройки в ресурс
//==========================================================
public void onButton1Click(View view) {
sPref = getSharedPreferences("MyPref", MODE_PRIVATE); //с помощью метода getPreferences получаем объект sPref класса SharedPreferences
//Константа MODE_PRIVATE используется для настройки доступа и означает, что после
//сохранения, данные будут видны только этому приложению
EditText FTPeditText = (EditText)findViewById(R.id.editText1); //Текстовое поле для ввода FTP сервера
host = FTPeditText.getText().toString();
EditText LOGINeditText = (EditText)findViewById(R.id.editText2); //Текстовое поле для ввода FTP сервера
username = LOGINeditText.getText().toString();
Log.d(TAG, "==| FTP Username in Resources |==: " + username); // Логин FTP сервера
EditText PASSWORDeditText = (EditText)findViewById(R.id.editText3); //Текстовое поле для ввода FTP сервера
password = PASSWORDeditText.getText().toString();
Log.d(TAG, "==| FTP Username in Resources |==: " + password); // Пароль FTP сервера
EditText DESDIReditText = (EditText)findViewById(R.id.editText4); //Текстовое поле для ввода FTP сервера
desDirectory = DESDIReditText.getText().toString();
Log.d(TAG, "==| FTP Folder in Resources |==: " + desDirectory); // Папка FTP сервера
EditText FTIMEeditText = (EditText)findViewById(R.id.editText7); //Текстовое поле для ввода перилда повтора
ftime = FTIMEeditText.getText().toString();
Log.d(TAG, "==| Ftime in Resources |==: " + ftime); // Время, через которое повторять снимок
EditText STIMEeditText = (EditText)findViewById(R.id.editText8); //Текстовое поле для ввода времени начала сессии
stime = STIMEeditText.getText().toString();
Log.d(TAG, "==| Stime in Resources |==: " + stime); // Время старта сессии
EditText ETIMEeditText = (EditText)findViewById(R.id.editText10); //Текстовое поле для ввода времени окончания сессии
etime = ETIMEeditText.getText().toString();
Log.d(TAG, "==| Etime in Resources |==: " + etime); // Время окончания сессии
EditText FNAMEeditText = (EditText)findViewById(R.id.editText14); //Текстовое поле для ввода имени файла фотографии
desFileName = FNAMEeditText.getText().toString();
Log.d(TAG, "==| Image Fname in Resources |==: " + desFileName); // Имя файла фотограии без номера и расширения
EditText INUMBEReditText = (EditText)findViewById(R.id.editText15); //Текстовое поле для ввода имени файла фотографии
inumber = INUMBEReditText.getText().toString();
Log.d(TAG, "==| Image Number in Resources |==: " + inumber); // Количество фотографий в сессию
//==========================================
// Запись в Ресурс
//==========================================
//Создаем объект Editor для создания пар имя-значение:
Editor ed = sPref.edit(); //чтобы редактировать данные, необходим объект Editor – получаем его из sPref
ed.putString(FTP_TEXT, host); //В метод putString указываем наименование переменной – это константа FTP_TEXT, и значение
Log.d(TAG, "==|| HOST ||==: " + host); // Адрес FTP сервера
ed.putString(LOGIN_TEXT, username); //В метод putString указываем наименование переменной – это константа LOGIN_TEXT, и значение
ed.putString(PASSWORD_TEXT, password); //В метод putString указываем наименование переменной – это константа PASSWORD_TEXT, и значение
ed.putString(FOLDER_TEXT, desDirectory); //В метод putString указываем наименование переменной – это константа FOLDER_TEXT, и значение
ed.putString(FTIME_TEXT, ftime); //В метод putString указываем наименование переменной – это константа FTIME_TEXT, и значение
ed.putString(STIME_TEXT, stime); //В метод putString указываем наименование переменной – это константа STIME_TEXT, и значение
ed.putString(ETIME_TEXT, etime); //В метод putString указываем наименование переменной – это константа ETIME_TEXT, и значение
ed.putString(FNAME_TEXT, desFileName); //В метод putString указываем наименование переменной – это константа FNAME_TEXT, и значение
ed.putString(INUMBER_TEXT, inumber); //В метод putString указываем наименование переменной – это константа INUMBER_TEXT, и значение
ed.commit(); // Сохранение данных в ресурсах
Log.d(TAG, "==| DAT Resources Parth |==: " + Environment.getDataDirectory()); // Каталог на устройстве, в который пишутся данные
//==========================================
// Читать Ресурс
//==========================================
sPref = getSharedPreferences("MyPref",MODE_PRIVATE);
String savedText = sPref.getString(FTP_TEXT, "");
Log.d(TAG, "== FTP Server in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(LOGIN_TEXT, "");
Log.d(TAG, "== LOGIN in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(PASSWORD_TEXT, "");
Log.d(TAG, "== PASSWORD in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(FOLDER_TEXT, "");
Log.d(TAG, "== FOLDER in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(FTIME_TEXT, "");
Log.d(TAG, "== FTIME in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(STIME_TEXT, "");
Log.d(TAG, "== STIME in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(ETIME_TEXT, "");
Log.d(TAG, "== ETIME in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(FNAME_TEXT, "");
Log.d(TAG, "== FNAME in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
savedText = sPref.getString(INUMBER_TEXT, "");
Log.d(TAG, "== IMAGE NUMBER in Resources ==: " + savedText); // Пишем сохраненный текст из Ресурсов
}
Настройки сохраняются сразу. Пароль не шифруется и не маскируется звездочками.
Посмотреть данные можно через Resource Manager
Через Device Manager можно посмотреть, что записано в ресурсах на смартфоне. Для этого он должен быть подключен по USB и использоваться для отладки
Это необходимо для того, чтобы делать темный экран и потом восстанавливать яркость. Темный экран приложение должно использовать наибольшее время - для экономии ресурсов батареи и экрана и меньшего привлечения внимания.
f.brightness2 = android.provider.Settings.System.getInt(getContentResolver(),android.provider.Settings.System.SCREEN_BRIGHTNESS);
f.brightness3 = android.provider.Settings.System.getInt(getContentResolver(),android.provider.Settings.System.SCREEN_BRIGHTNESS);
Сохранить текущее значение яркости.
Установить новое значение яркости экрана
context = getApplicationContext();
Intent brightnessIntent = this.getIntent();
float brightness = brightnessIntent.getFloatExtra("brightness value", f.brightness2); //<-- 1-225
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = brightness;
getWindow().setAttributes(lp);
public static void verifyStoragePermissions - получение разрешений на фотокамеру. Без этого ничего работать не будет. Вообще, правила получения и управления разрешениями много раз менялись. Это надо отслеживать.
public String ping - при старте программа Пингует некий IP-адрес и определяет доступность Интернета. Сообщение выдается в телеметрию. Такая проверка мобильного Интернета проще.
http://shmeleff.com/PhotoWebCamSHMELEFF.zip
public void onPause - выполняется, когда пауза в работе фотокамеры. Пишет текст в телеметрию и вызывает stopBackgroundThread()
public void onResume - возобновляет съемку. Вызывает startBackgroundThread()
public void handleMessage - написать телеметрию
public void handleMessage - написать сообщение
public void onButton4Click - перейти к Activity с настройками FTP клиента
public void onButtonGuide - перейти к Activity с кратким руководством
public void onButton4eClick - сделать темный экран / вернуть в исходное состояние.
public void onButton5Click - выйти из приложения
public void onButtonClick - обработка нажатия кнопок
public void ifFTP - проверка доступности FTP подключения
public void onFTP - подключение к FTP серверу (WEB-серверу с FTP загрузкой файлов)
public void TextViewImages - добавить имидж в TextView
public void onButton1Click - Записать FTP настройки в ресурс
public void onButton3Click - Показать MAIN Activity
public void onButtonGuide - Показать Print Activity - встроенный GUIDE
public boolean ftpConnect - подключение к FIP серверу
public boolean ftpDisconnect - отключение FTP сервера
public boolean ftpUpload - загрузка изображений на хостинг (FTP сервер)
Показывает небольшое иллюстрированное руководство пользователя
Activity, элементы, разметка
В приложении используется только вертикальная ориентация экрана. Переход в горизонтальную заблокирован. Это существенно всё упростило. Т.к. приложение имеет сугубо техническое назначение (наблюдение) и длительное время работает без участия пользователя, нет смысла усложнять интерфейс и код.
Main Activity
Ничего особенного нет.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
android:orientation="vertical"
android:background="#fdf5e6"
tools:context=".MainActivity">
<!-- PhotoWebCamSHMELEFF - Valery Shmelev JAVA Application -->
<!-- Полностью работоспособный проект (шаблон) Фото Веб Камеры с FTP загрузкой картинок -->
<LinearLayout
android:id="@+id/MainForm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom|center_horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*"
android:id="@+id/tableLayout1">
<!-- Здесь пишем сообщения о работе приложения - Телеметрию -->
<TableRow
android:id="@+id/tableRow1h"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/TextView7e"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:inputType="textMultiLine"
android:textSize="15dp"
android:autoLink="web"
android:textColorLink="#0066cc"
android:layout_span="3"
android:textColor="#005678"
android:maxLines="7"
android:lines="7"
android:gravity="bottom"
android:text="Telemetry:\n" />
</TableRow>
<!-- Здесь получаем картинку с фотокамеры -->
<TableRow
android:id="@+id/tableRow1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextureView
android:id="@+id/textureView"
android:layout_width="348dp"
android:layout_height="348dp"
android:layout_marginTop="16dp"
android:layout_span="3"
android:rotation="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.49"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</TableRow>
<!-- Кнопка начала сессии или паузы -->
<TableRow
android:id="@+id/tableRow1e"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button1e"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_span="3"
android:onClick="onButtonClick"
android:text="START/STOP" />
</TableRow>
<!-- Выбрать FRONT фотокамеру -->
<TableRow
android:id="@+id/tableRow5"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="FRONT CAM" />
<!-- Выбрать REAR фотокамеру -->
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="REAR CAM" />
<!-- Записывать фото только в память смартфона -->
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CAM LOCAL" />
</TableRow>
<!-- Перейти к настройкам FTP клиента (Apache FTP Client) -->
<TableRow
android:id="@+id/tableRow6"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/button4"
android:layout_width="149dp"
android:layout_height="wrap_content"
android:onClick="onButton4Click"
android:text="FTP Setup" />
<!-- Затемнить/Восстановить яркость экрана приложения -->
<Button
android:id="@+id/button4e"
android:layout_width="149dp"
android:layout_height="wrap_content"
android:onClick="onButton4eClick"
android:text="Dark" />
<!-- Выйти из приложения -->
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onButton5Click"
android:text="Exit" />
</TableRow>
<!-- Показать подробное руководство -->
<TableRow
android:id="@+id/tableRow7e"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/buttonGuide"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_span="3"
android:onClick="onButtonGuide"
android:text="GUIDE" />
</TableRow>
<!-- Нижняя строка для сообщений -->
<TableRow
android:id="@+id/tableRow7"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/webGuide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColorLink="#0066cc"
android:textStyle="bold"
android:layout_span="3"
android:lines="1"
android:textSize="18sp"
android:scrollbars = "vertical"
android:text=" Web GUIDE " />
</TableRow>
<!-- Пустая строка -->
<TableRow
android:id="@+id/tableRow8"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/TextView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="3"
android:lines="1"
android:text=" " />
</TableRow>
</TableLayout>
</LinearLayout>
<!-- PhotoWebCamSHMELEFF - Valery Shmelev JAVA Developer -->
<!-- https://oflameron.imgbb.com/ -->
</androidx.constraintlayout.widget.ConstraintLayout>
Print_Activity
Встроенное руководство
С точки зрения разметки, в встроенном руководстве нет ничего особенного.
Текст руководства добавлен в Activity программным способом в HTML формате
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append(Html.fromHtml("<b>Admin GUIDE</b>:<br><br> Setup PAGE<br><br> <b>Per seconds</b> 500+ - Time between photos in sec<br> <b>Start time HH</b> - Start time of the photo session. In hours<br> <b>End time HH</b> - End time of the photo session. In hours (Daylight hours)<br> <b>FTP server</b> - ftp address Sample build.shmelev.com<br> <b>Filename</b> - Template for the file name on the web server<br> <b>Images number</b> - Maximum number of photos in rotation<br> <b>Login</b> - user name<br> <b>Password</b> - ftp password<br> <br> ", Html.FROM_HTML_MODE_COMPACT));
HTML формат позволяет разместить в руководстве картинки и веб-ссылки.
Пример добавления картинки R.drawable.bigint
ImageSpan imageSpan2 = new ImageSpan(this, R.drawable.bigint);
spannableString2.setSpan(imageSpan2, spannableString2.length() - 1, spannableString2.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.append(spannableString2);
В встроенное руководство можно добавлять HTML ссылки на дополнительные справочные данные, блог или форум, зеркала для скачивания
Пример использования HTML разметки:
spannableStringBuilder.append(spannableString2);
spannableStringBuilder.append(Html.fromHtml("<br><br> <b>BigIntPrimeNumber.apk</b> - search 3 mod 4 BigINT quality prime numbers 1024 bit size<br><br>", Html.FROM_HTML_MODE_COMPACT));
Добавить сюда веб ссылку проще простого. Перепишем вторую строку в таком виде:
spannableStringBuilder.append(Html.fromHtml("<br><br> <b>BigIntPrimeNumber.apk</b> - search 3 mod 4 BigINT quality prime numbers 1024 bit size<br><br><a href='https://ru.pinterest.com/arrayphotocintrol/_pins/'>AI Language Models</a><br><br>", Html.FROM_HTML_MODE_COMPACT));
PhotoWebCamSHMELEFF
Перспективы приложения
Рассмотрим что можно сделать для приложения и что это даст для пользователя.
Добавление фотографий на веб сервер - это редкое решение для таких приложений. Чаще всего предлагается централизованный сервер с личным кабинетом. Для пользователя так, конечно, проще. Но ведь кто-то сможет вместе с Вами смотреть за Вашими объектами и знать где и когда Вас не будет...
В то же время, если Вы настроили загрузку на веб сервер, то только Вы сами управляете информацией.
Но работа с веб сервером не очень удобна. Есть более удобный вариант - создать свой Telegram канал и помещать снимки туда.