Táto stránka pre svoju správnu funkčnosť vyžaduje súbory cookies. Slúžia na authentifikáciu návštevníka, analýzu návštevnosti a reklamnú personalizáciu.
logo
Prihlásenie / Registrácia
mobile

Zavrieť
 

Bezpečnostná kamera z Android telefónu

V článku si opäť ukážeme, že staré mobilné telefóny nemusia nevyhnutne skončiť v elektroodpade.Tentokrát si zo starého Androidového telefónu vytvoríme jednoduchú bezpečnostnú kameru ovládanú cez SMS.
Pár týždňov dozadu mi do "zbierky" pribudol ďalší starší mobilný telefón, ktorý už mal čo to za sebou. Navzdory tomu bol stále v relatívne dobrom stave a po menšej oprave fotoaparátu (poškriabaný objektív) je stále použiteľný. Niekoľko dní na to som niekde zachytil reportáž o bezpečnostných kamerách v nejakom meste a hneď ma aj napadlo ako svoj nový prírastok v zbierke využiť.

Už v minulosti som narazil na nejakú appku, čo umožnila používať telefón ako web kameru. Snímala obraz z fotoaparátu a cez zabudovaný web server sa bolo možné na ňu pripojiť a sledovať, čo sa pred telefónom deje. To bolo síce použiteľné, avšak na prístup k takémuto zariadeniu potrebujeme priamo prístup do siete, kde sa nachádza, alebo toto zariadenie nejakým spôsobom sprístupniť na internete. Samotné zariadenie musí mať dostatočne rýchle a nie príliš dátovo obmedzené pripojenie.

Riešenie, ktoré popíšem v tomto článku bude fungovať na trochu inom princípe, no svoj účel bude spĺňať.
Predstavme si situáciu, že na pár dní idete mimo bydlisko a chceli by ste mat možnosť skontrolovať, či je u Vás doma všetko OK. Riešenie by zároveň malo byť čo najmenej finančne náročné a jednoduché na implementáciu.

Dosť bolo rečí, ideme na to.

Popis riešenia

Celé riešenie bude založené na Android aplikácii, ktorá na požiadanie (cez SMS) urobí fotku a pošle ju mailom na vopred zadanú adresu. Aplikácia sama o sebe nič nerobí, len čaká na inštrukcie, nepotrebuje pre svoj beh žiadny server (keďže sa na ňu nikto nepripája) a zároveň jej stačí aj pomalé pripojenie na internet (kľudne aj mobilné), keďže nehrá rolu či fotografia príde o jednu či pätnásť sekúnd.Takto "obohatený" telefón už len stačí vhodne umiestniť. Ak má dobrú batériu môže byť teoreticky aj v exteriéri, ak je k dispozícii dostatočné pokrytie signálom od mobilného operátora.

Ak sa pýtate: "Ok, ale na to, aby telefón mohol prijímať SMS alebo odosielať nejaké mobilné dáta musí mať predsa aktívnu SIM kartu a tá nebude zadarmo". Áno, máte pravdu. Avšak, nie je to taký problém ako by sa mohlo zdať. V rámci hľadania riešenia som bol navštíviť pobočku svojho "kyslíkového" mobilného operátora a majú v ponuke aj program, v ktorom sa platí len za spotrebované služby, žiadny paušál ani časovo obmedzený predplatený kredit. A v prípade, ak bude mobil používať nejakú WIFI, tak teoreticky nespotrebuje žiadne platené služby (prijaté SMS sú samozrejme zadarmo). Niečo obdobné by v ponuke mohli mať aj ostatný mobilný operátori.

Začneme vytvorením nového projektu v Android Studio-u, nazveme ho SecCam a package name nastavíme sk.codeblog.seccam.

Obsluha SMS

Ďalej potrebujeme systému povedať, že naša aplikácia je schopná obsluhovať SMS. Aplikácia musí byť z bezpečnostných dôvodov nastavená ako štandardná aplikácia pre SMS, preto si na to (aspoň formálne) vytvoríme potrebné zázemie. (bližšie som o tom hovoril v článku Hlasovanie cez SMS na WEB stránku > link <, odporúčam prečítať).

V projekte si vytvoríme súbory SmsReceiver.java, MmsReceiver.java, RepondViaMessage.java, SendToActivity.java s nasledujúcim obsahom:

SmsReceiver.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;

public class SmsReceiver extends BroadcastReceiver {

    private final String PDUS = "pdus";

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();

        if (extras == null) {
            return;
        }

        Object[] smsExtras = (Object[]) extras.get(PDUS);

        for (Object smsExtra : smsExtras) {
            byte[] smsBytes = (byte[]) smsExtra;

            SmsMessage smsMessage = SmsMessage.createFromPdu(smsBytes);

            String body = smsMessage.getMessageBody().toUpperCase();
            String phoneNumber = smsMessage.getOriginatingAddress();

            //Tu bude kod na spracovanie obsahu spravy
        }
    }
}

MmsReceiver.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

    }
}

RepondViaMessage.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.app.IntentService;
import android.content.Intent;

public class RespondViaMessage extends IntentService {
    public RespondViaMessage() {
        super(RespondViaMessage.class.getName());

        setIntentRedelivery(false);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

    }
}

SendToActivity.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.app.Activity;

public class SendToActivity extends Activity {

}

Obsah súborov MmsReceiver.java, RepondViaMessage.java a SendToActivity.java má len formálne náležitosti, nie je potrebné sa im nejako extra venovať. Čo však dôležité je, je obsah súboru SmsReceiver.java kde sme si pridali spracovanie prichádzajúcej SMS správy, resp načítanie textu správy do premennej body a jej odosielateľa do phoneNumber. S týmito údajmi budeme pracovať ďalej, keď budeme mat pripravenú výkonnú časť našej aplikácie.

S konfiguráciou aplikácie sa momentálne nemá význam zaoberať, no pre poriadok použijeme triedu, kde si všetky dôležité nastavenia "natvrdo" zapíšeme (samozrejme nič vám nebráni urobiť si na to rozumnejšiu funkcionalitu). Súbor s triedou nazveme Config.java a vyplníme v ňom potrebné údaje:

Zdrojový kód:
package sk.codeblog.seccam;

public class Config {
    public static final String MailFrom = "vasmail@gmail.com";
    public static final String MailPassword = "vaseheslo";
    public static final String MailTo = "adresat@server.sk";
    public static final String MailSubject = "CODEBlog.sk SecCam";

    public static final String AuthorizedPhoneNumber = "+4219XXZZZYYY";
}

Význam konštánt MailFrom, MailPassword,MailTo a MailSubject je jasný, konštanta AuthorizedPhoneNumber by mala obsahovať telefónne číslo, ktoré je oprávnené posielať príkazy.

Odoslanie mailu

V nasledujúcich triedach budeme potrebovať knižnicu javamail-android, ktorú je možné stiahnuť zo stránok google: > link <. Všetky tri súbory je nutné skopírovať do priečinka libs v projekte a cez menu Add as Library v rightclick menu zaregistrovať v projekte.

Pokračujeme zadefinovaním triedy GMailer, ktorá bude v samostatnom vlákne posielať nami vytvorené fotografie pomocou SMTP servra GMail-u. Tento spôsob odosielania mailov považuje GMail za menej bezpečný a preto ho treba povoliť v nastaveniach GMail-u (> link <). Každopádne odporúčam si na odosielanie týchto mailov vytvoriť osobitné konto.

GMailer.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.util.Log;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

public class GMailer extends Thread {

    private static final String TAG = "GMailer";

    private String _text = "";
    private String _attachment = "";

    public GMailer(String Attachment) {
        _text = GetActualDateTime();
        _attachment = Attachment;
    }

    private String GetActualDateTime()
    {
        return (new SimpleDateFormat("dd.MM.yyyy HH:mm:ss")).format(new Date());
    }

    @Override
    public void run() {
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", "smtp.gmail.com");
        props.put("mail.smtp.port", "587");

        Session session = Session.getInstance(props,
                new javax.mail.Authenticator() {
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(Config.MailFrom, Config.MailPassword);
                    }
                });
        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(Config.MailFrom));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(Config.MailTo));
            message.setSubject(Config.MailSubject);
            message.setText(_text);

            Multipart multipart = new MimeMultipart();

            MimeBodyPart messageBodyPart = new MimeBodyPart();

            File AttachFile = new File(_attachment);
            String fileName = AttachFile.getName();

            DataSource source = new FileDataSource(_attachment);
            messageBodyPart.setDataHandler(new DataHandler(source));
            messageBodyPart.setFileName(fileName);
            multipart.addBodyPart(messageBodyPart);

            message.setContent(multipart);

            Transport.send(message);

            //Vymaz odoslaneho suboru
            (new File(_attachment)).delete();


        } catch (MessagingException e) {
            Log.e(TAG, e.getMessage());
        }catch (Exception GenEx)
        {
            Log.e(TAG, GenEx.getMessage());
        }
    }
}

Konštruktor triedy prijíma parameter s cestou súboru na odoslanie. Ostatné potrebné údaje bude čerpať z triedy Config. Zdrojový súbor prílohy je po odoslaní vymazaný.

Získanie fotografie

No, odosielať maily už vieme a teraz začne ta pravá "zábava". Ideme k totiž k tomu hlavnému a to získaniu snímky z fotoaparátu nášho telefónu. Po nejakom čase hľadania vhodného riešenia som sa dopracoval ku "Camera2 API", ktoré je prístupné od verzie Android API 21 (Android 5.0 Lollipop), avšak narazil som na informáciu o tom, že nie všetky zariadenia ho podporujú.Osobne som sa s tým ešte nestretol, avšak je vhodné to spomenúť.

Princíp fungovania bude nasledovný: Najskôr je potrebné si celú funkčnosť inicializovať, na čo si vytvoríme metódu Init, ktorá sa bude vykonávať pred prvým pokusom o vytvorenie snímky. Pomocou objektu CameraManager získame zoznam dostupných kamier/fotoaparátov, s ktorého si následne pomocou objektu CameraCharacteristics vyberieme pre nás ten najvhodnejší. V mojom prípade je najvhodnejšie umiestnenie kamery na zadnej strane telefónu (CameraCharacteristics.LENS_FACING_BACK), no k dispozícii môže byť aj predná (LENS_FACING_FRONT) alebo externá kamera (LENS_FACING_EXTERNAL).

Ak sme vhodnú kameru našli, odložíme si jej ID do privátnej premennej _cameraid, pomocou ktorej si ju v budúcnosti otvoríme a použijeme.

Posledným krokom inicializácie je zistenie najvyššieho rozlíšenia snímku, ktoré nám kamera poskytuje. Pre tento účel si vytvoríme metódu GetCameraResolution, ktorá ziska z objektu CameraCharacteristics zoznam podporovaných rozlíšení a vyhľadá v nich ten najväčší.

Keď máme inicializáciu dokončenú, môžeme na požiadanie vytvoriť snímku. Vzhľadom na to, že kamera nemôže byť používaná na viacerých miestach naraz, budeme si referenciu na objekt otvorenej kamery odkladať do privátnej premennej _camera, aby sa program nepokúšal kameru opätovne otvoriť. To však neznamená, že bude kamera otvorená po celú dobu (pri občasnom odfotení snímku to ani nemá zmysel). Hneď ako bude aktuálne požadovaná snímka snímka zosnímaná, bude kamera zatvorená a premenná _camera vyprázdnená.

Vráťme sa ale k samotnému snímaniu... Vytvoríme si metódu TakePhoto, v ktorej si najskôr cez CameraManager nami vybranú (premenná _cameraid) kameru otvoríme, priradíme callback/handler na event zmeny jej stavu cameraStateCallback (ktorý si za chvíľu popíšeme) a vytvoríme si privátnu premennú _imgreader typu ImageReader, ktorej nastavime rozmery vybraného rozlíšenia kamery. požadovaný formát obrázka a priradíme callback na event dostupnosti snímku onImageAvailableListener.

Callback cameraStateCallback musí preťažovať metódy onOpened, onDisconnected a onError. Pre nás je teraz najdôležitejšia metóda onOpened, ktorá je zavolaná v čase, keď sa kameru podarilo úspešne otvoriť. V tejto metóde získame referenciu na otvorenú kameru a vytvoríme CaptureSession s použitím premennej _imgreader a nového callback-u sessionStateCallback, ktorý bude odchytávať zmenu stavu session.

Nasleduje onImageAvailableListener, ktorý nám pomocou metódy SaveImage uloží získanú snímku do súboru. Súbor sa následne pomocou už definovanej SendPhoto odošle na mail a zavolaním metódy CloseCamera uzavrie kameru, session a reader.

Ostal nám ešte jeden callback a to sessionStateCallback, ktorý musí obsahovať onReady, onConfigured a onConfigureFailed. V metóde onReady si pomocou objektu CaptureRequest vytvorime požiadavku na snímku a cez premennú session typu CameraCaptureSession ju odošleme.

Každá snímka uložená do súboru bude mať unikátny názov, ktorý vygenerujeme z aktuálneho dátumu a času v metóde GetFileName.

Túto logiku zapracujeme do nového súboru Actions.java nasledovne:

Zdrojový kód:
package sk.codeblog.seccam;

import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Environment;
import android.util.Log;
import android.util.Size;

import androidx.annotation.NonNull;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import static android.content.Context.CAMERA_SERVICE;

public class Actions {

    private static final String TAG = "Actions";

    private static Context _context = null;
    private static CameraDevice _camera;
    private static CameraCaptureSession _capturesession;
    private static ImageReader _imgreader;
    private static String _cameraid = "";
    private static Size _camerasize = null;

    public static void Init(Context cntx) {

        //Ak uz bolo inicializovane nepokracujeme
        if (_context != null)
            return;

        _context = cntx;

        InitCamera();
    }

    public static void TakePhoto() {
        //Ak nebola najdena vhodna kamera koncime
        if (_cameraid.equals("") || _camerasize == null) {
            return;
        }

        //Ak existuje referencia na objekt kamery, pravdepodobne este nedobehla predchadzajuca akcia
        if (_camera != null) {
            return;
        }

        try {
            CameraManager manager = (CameraManager) _context.getSystemService(CAMERA_SERVICE);
            manager.openCamera(_cameraid, cameraStateCallback, null);

            _imgreader = ImageReader.newInstance(_camerasize.getWidth(), _camerasize.getHeight(), ImageFormat.JPEG, 1);
            _imgreader.setOnImageAvailableListener(onImageAvailableListener, null);

        } catch (CameraAccessException camEx) {
            Log.e(TAG, camEx.getMessage());
        } catch (SecurityException secEx) {
            Log.e(TAG, secEx.getMessage());
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage());
        }
    }

    private static void SendPhoto(String file) {
        GMailer th = new GMailer(file);
        th.start();
    }

    private static void InitCamera() {
        try {
            CameraManager manager = (CameraManager) _context.getSystemService(CAMERA_SERVICE);

            //Ziskame zoznam kamier
            String[] Cameras = manager.getCameraIdList();
            for (String CameraId : Cameras) {

                //Ziskame informacie o konkretnej kamere
                CameraCharacteristics CameraInfo = manager.getCameraCharacteristics(CameraId);
                //Orientacia kamery
                int LensFacing = CameraInfo.get(CameraCharacteristics.LENS_FACING);

                //Hladame zadnu kameru
                if (LensFacing == CameraCharacteristics.LENS_FACING_BACK) {

                    //Pokusime sa zistit najvacsie mozne rozlisenie snimkov
                    Size CameraSize = GetCameraResolution(CameraInfo);
                    if (CameraSize != null) {
                        _cameraid = CameraId;
                        _camerasize = CameraSize;
                        break;
                    }
                }
            }
        } catch (CameraAccessException camEx) {
            Log.e(TAG, camEx.getMessage());
        } catch (SecurityException secEx) {
            Log.e(TAG, secEx.getMessage());
        }
    }

    //Vyhladame najvacsie mozne rozlisenie fotografie
    private static Size GetCameraResolution(CameraCharacteristics CameraInfo) {
        StreamConfigurationMap streamConfigurationMap = CameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        Size[] Sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
        if (Sizes == null)
            return null;

        int Max = 0;
        Size MaxSize = null;

        for (Size CurrSize : Sizes) {
            int Width = CurrSize.getWidth();
            if (Width > Max) {
                Max = Width;
                MaxSize = CurrSize;
            }
        }
        return MaxSize;
    }

    private static CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            Log.d(TAG, "CameraDevice.StateCallback onOpened");
            try {
                _camera = camera;
                _camera.createCaptureSession(Arrays.asList(_imgreader.getSurface()), sessionStateCallback, null);
            } catch (CameraAccessException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.w(TAG, "CameraDevice.StateCallback onDisconnected");
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.e(TAG, "CameraDevice.StateCallback onError " + error);
        }
    };

    private static ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.d(TAG, "onImageAvailable");
            Image img = reader.acquireLatestImage();
            if (img != null) {
                String FileName = SaveImage(img);
                if (FileName != null)
                {
                    SendPhoto(FileName);
                }
                img.close();
            }

            //Uzavrieme kameru
            CloseCamera();
        }
    };

    private static void CloseCamera()
    {
        _camera.close();
        _camera = null;

        _capturesession.close();
        _capturesession = null;

        _imgreader.close();
        _imgreader = null;
    }

    private static CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {

        @Override
        public void onReady(CameraCaptureSession session) {
            _capturesession = session;
            try {

                CaptureRequest.Builder builder = _camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                builder.addTarget(_imgreader.getSurface());
                CaptureRequest Request = builder.build();

                session.capture(Request, null, null);
            } catch (CameraAccessException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        @Override
        public void onConfigured(CameraCaptureSession session) {

        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
        }
    };

    private static String GetFileName() {
        Date ActDate = new Date();
        GregorianCalendar Cal = new GregorianCalendar();
        Cal.setTime(ActDate);

        return String.format("%04d%02d%02d_%02d%02d%02d_%03d.jpg",
                Cal.get(Calendar.YEAR), Cal.get(Calendar.MONTH) + 1, Cal.get(Calendar.DAY_OF_MONTH),
                Cal.get(Calendar.HOUR_OF_DAY), Cal.get(Calendar.MINUTE), Cal.get(Calendar.SECOND),
                Cal.get(Calendar.MILLISECOND));
    }

    private static String SaveImage(Image image) {

        boolean IsOK = false;

        //Process image data
        ByteBuffer buffer;
        byte[] bytes;

        String FileName = Environment.getExternalStorageDirectory() + "/Pictures/photo_" + GetFileName() + ".jpg";

        //getApplicationInfo().dataDir;
        File file = new File(FileName);
        FileOutputStream output = null;

        if (image.getFormat() == ImageFormat.JPEG) {
            buffer = image.getPlanes()[0].getBuffer();
            bytes = new byte[buffer.remaining()]; // makes byte array large enough to hold image
            buffer.get(bytes); // copies image from buffer to byte array
            try {
                output = new FileOutputStream(file);
                output.write(bytes);    // write the byte array to file

                IsOK = true;
            } catch (FileNotFoundException e) {
                Log.e(TAG, e.getMessage());
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            } finally {
                image.close(); // close this to free up buffer for other images
                if (output != null) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        if (IsOK)
        {
            return FileName;
        }
        else
        {
            return null;
        }
    }
}

Výkonný kód už máme vytvorený, môžeme teraz jeho volanie pridať do SmsReceiver.java

Zdrojový kód:
public void onReceive(Context context, Intent intent) {

...

    String body = smsMessage.getMessageBody().toUpperCase();
    phoneNumber = smsMessage.getOriginatingAddress();

    if (phoneNumber.equals(Config.AuthorizedPhoneNumber)) {
        switch (body) {
            case "PHOTO": {
                Actions.Init(context);
                Actions.TakePhoto();
            }
            break;
    
            //sem mozno pridat pripadne nove funkcnosti
        }
    }

...

}

AndroidManifest.xml a oprávnenia

Pokračujeme pridaním hlavnej Activity našeho projektu.
Poslúži nám na kontrolu (aj prípadné vyžiadanie) chýbajúcich oprávnení aplikácie a otestovanie funkčnosti aplikácie.

activity_main.xml

Zdrojový kód:
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout android:id="@+id/relativeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Test" />
</RelativeLayout>

MainActivity.java

Zdrojový kód:
package sk.codeblog.seccam;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (checkPermission())
        {
         Init();
        }

        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                Actions.TakePhoto();
            }
        });
    }

    private void Init()
    {
        Actions.Init(getApplicationContext());
    }

    private boolean checkPermission() {
        ArrayList<String> promptPermissions = new ArrayList<String>();

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.ACCESS_NETWORK_STATE);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.CAMERA);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.INTERNET);
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.READ_SMS);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.SEND_SMS);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
            promptPermissions.add(Manifest.permission.RECEIVE_SMS);
        }

        if (promptPermissions.size() == 0) {
            return true;
        }

        ActivityCompat.requestPermissions(this, promptPermissions.toArray(new String[promptPermissions.size()]), 0);
        return false;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        boolean AllAccepted = true;

        for (int Result: grantResults) {
            if (Result < 0)
            {
                AllAccepted = false;
                Toast.makeText(this, "Všetky oprávnenia musia byť schválené.", Toast.LENGTH_SHORT).show();
                break;
            }
        }

        if (AllAccepted)
        {
            Init();
        }
    }
}

Po vizuálnej stránke má naša Activity len jedno tlačidlo, ktorým sa aktivuje funkčnost zaznamenania a odoslania snímku. Po programovej stránke je tu metóda checkPermission, ktorá overí, či má aplikácia všetky požadované oprávnenia. Ak tieto oprávnenia nemáme, vypýtame si ich.

A ostal nám už len AndroidManifest.xml, v ktorom si všetko dáme pekne dokopy:

Zdrojový kód:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="sk.codeblog.seccam">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />

    <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="sk.codeblog.seccam.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- BroadcastReceiver prijimajuci SMS spravy-->
        <receiver android:name="sk.codeblog.seccam.SmsReceiver"
            android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>

        <!-- BroadcastReceiver prijimajuci MMS spravy-->
        <receiver android:name="sk.codeblog.seccam.MmsReceiver"
            android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>

        <!-- Activity na odosielanie SMS/MMS-->
        <activity android:name="sk.codeblog.seccam.SendToActivity" >
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>

        <!-- Service na "quick response"-->
        <service android:name="sk.codeblog.seccam.RespondViaMessage"
            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>
    </application>

</manifest>

Záverom

Projekt môžeme skompilovať a vyskúšat na našom Android telefóne. Po prvej inštalácii je ešte potrebné nastaviť aplikáciu ako defaultnú pre SMS, povoliť všetky oprávnenia pri štarte MainActivity a bezpečnostná kamera je pripravená.

Zdrojový ḱód projektu je možné stiahnúť z: > link <

Použité zdroje

Take photo in service using Camera2 API - stackoverflow.com > link <
Sending Email in Android using JavaMail API without using the default/built-in app - stackoverflow.com > link <

Codeblog
Diskusia

Žiadne príspevky v diskusii.

Nový príspevok

Na prispievanie do diskusie musíte byť prihlásený.