Камера Android: намір даних повертає нуль


132

У мене є додаток для android, яке містить кілька заходів.

В одному з них я використовую кнопку, яка буде викликати камеру пристрою:

public void onClick(View view) {
    Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(photoIntent, IMAGE_CAPTURE);
}

У цій же діяльності я називаю OnActivityResultметод отримання результату зображення:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == IMAGE_CAPTURE) {
        if (resultCode == RESULT_OK) {
            Bitmap image = (Bitmap) data.getExtras().get("data");
            ImageView imageview = (ImageView) findViewById(R.id.pic);
            imageview.setImageBitmap(image);
        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, "CANCELED ", Toast.LENGTH_LONG).show();
        }
    }
}

Проблема полягає в тому, що намір dataє недійсним, і OnActivityResultметод перетворюється безпосередньо на програму, (resultCode == RESULT_CANCELED)а додаток повертається до попереднього рівня автівки.

Як я можу виправити цю проблему і після виклику на камеру додаток повертається до поточної активності, яка містить ImageViewзнімок, зроблений?

Дякую


подивіться на цю відповідь stackoverflow.com/questions/18120775/…
Praveen Sharma

Відповіді:


218

За замовчуванням програма для камери Android повертає ненульовий намір лише під час повернення мініатюри у поверненому намірі. Якщо ви передасте EXTRA_OUTPUTURI для запису, він поверне nullнаміри, і зображення буде в URI, який ви передали.

Ви можете перевірити це, переглянувши вихідний код програми камери на GitHub:

Я б здогадався, що ви або хочете EXTRA_OUTPUTякось проїхати , або додаток для камери на телефоні працює по-іншому.


12
як я можу виправити додаткову функціональність, яка дає ненульовий намір у камері onActivityResult
Abdul Wahab

Дуже корисна відповідь. Я хотів би, щоб я міг прийняти це для того, щоб інші могли знайти рішення ефективніше.
користувач1160020

1
Дивовижно! Велике спасибі за пояснення :) +100
Мухаммад Ріяз

Посилання, яке ви опублікували, більше не дійсне.

4
Чи не могли б ми змінити клас камери та просто змінити один метод, щоб дозволити обидва, чи це складніше?
Таннер Саммерс

16

Я знайшов просту відповідь. це працює!!

private void openCameraForResult(int requestCode){
    Intent photo = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Uri uri  = Uri.parse("file:///sdcard/photo.jpg");
    photo.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(photo,requestCode);
}

if (requestCode == CAMERA_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            File file = new File(Environment.getExternalStorageDirectory().getPath(), "photo.jpg");
            Uri uri = Uri.fromFile(file);
            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                bitmap = cropAndScale(bitmap, 300); // if you mind scaling
                profileImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

якщо ви хочете обрізати та масштабувати це зображення

public static  Bitmap cropAndScale (Bitmap source, int scale){
    int factor = source.getHeight() <= source.getWidth() ? source.getHeight(): source.getWidth();
    int longer = source.getHeight() >= source.getWidth() ? source.getHeight(): source.getWidth();
    int x = source.getHeight() >= source.getWidth() ?0:(longer-factor)/2;
    int y = source.getHeight() <= source.getWidth() ?0:(longer-factor)/2;
    source = Bitmap.createBitmap(source, x, y, factor, factor);
    source = Bitmap.createScaledBitmap(source, scale, scale, false);
    return source;
}

4
Він надає FileUriExposedExceptionсучасні версії SDK.
Souradeep Nanda

8

У мене виникли проблеми, intentце недійсне значення, але інформація, надіслана через це intent, не надходитьonActionActivit()

введіть тут опис зображення

Це краще рішення за допомогою getContentResolver () :

    private Uri imageUri;
    private ImageView myImageView;
    private Bitmap thumbnail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

      ...
      ...    
      ...
      myImageview = (ImageView) findViewById(R.id.pic); 

      values = new ContentValues();
      values.put(MediaStore.Images.Media.TITLE, "MyPicture");
      values.put(MediaStore.Images.Media.DESCRIPTION, "Photo taken on " + System.currentTimeMillis());
      imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
      startActivityForResult(intent, PICTURE_RESULT);

  }

onActivityResult()отримати растрове зображення , яке зберігається на getContentResolver () :

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_TAKE_PHOTO && resultCode == RESULT_OK) {

            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
                myImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

Introducir la descriptionpción de la imagen aquí Introducir la descriptionpción de la imagen aquí

Перевірте мій приклад у github:

https://github.com/Jorgesys/TakePicture


6

Простий робочий додаток для камери, уникаючи проблеми з нульовим наміром

- весь змінений код, включений у цю відповідь; близько до Android підручник

Я витрачав багато часу на це питання, тому вирішив створити обліковий запис і поділитися своїми результатами з вами.

Офіційний підручник для Android "Фотографування просто" виявився не зовсім таким, що обіцяв. Код за умови, що на моєму пристрої не працював: Samsung Galaxy S4 Mini GT-I9195 під керуванням Android версії 4.4.2 / KitKat / API Рівень 19.

Я зрозумів, що головна проблема полягала в наступному рядку в методі, який викликається під час зйомки фотографії ( dispatchTakePictureIntentу підручнику):

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

Це призвело до того, що намір згодом виявився onActivityResultнульовим.

Щоб вирішити цю проблему, я отримав багато натхнення з попередніх відповідей тут і декількох корисних дописів на Github (в основному, цей за допомогою deepwinter - велике спасибі йому; ви можете перевірити його відповідь і на тісно пов'язаній публікації ).

Дотримуючись цих приємних порад, я вибрав стратегію видалення згаданого putExtraрядка і зробив відповідне завдання повернення зробленого зображення з камери в методі onActivityResult (). Визначальними рядками коду для повернення растрового зображення, пов'язаного із зображенням, є:

        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }

Я створив зразковий додаток, який просто має можливість сфотографувати, зберегти його на SD-картці та відобразити. Я думаю, що це може бути корисним людям, що перебувають у тій же ситуації, що й я, коли я натрапив на це питання, оскільки нинішні пропозиції щодо допомоги в основному стосуються досить обширних постів про github, які займаються питанням, але їх не так просто наглядати за новичками, як я. Що стосується файлової системи Android Studio створюється за замовчуванням під час створення нового проекту, мені просто довелося змінити три файли за своїм призначенням:

Activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context="com.example.android.simpleworkingcameraapp.MainActivity">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="takePicAndDisplayIt"
    android:text="Take a pic and display it." />

<ImageView
    android:id="@+id/image1"
    android:layout_width="match_parent"
    android:layout_height="200dp" />

</LinearLayout>

MainActivity.java:

package com.example.android.simpleworkingcameraapp;

import android.content.Intent;
import android.graphics.Bitmap;
import android.media.Image;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

private ImageView image;
static final int REQUEST_TAKE_PHOTO = 1;
String mCurrentPhotoPath;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    image = (ImageView) findViewById(R.id.image1);
}

// copied from the android development pages; just added a Toast to show the storage location
private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmm").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
            imageFileName,  /* prefix */
            ".jpg",         /* suffix */
            storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = image.getAbsolutePath();
    Toast.makeText(this, mCurrentPhotoPath, Toast.LENGTH_LONG).show();
    return image;
}

public void takePicAndDisplayIt(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        File file = null;
        try {
            file = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
        }

        startActivityForResult(intent, REQUEST_TAKE_PHOTO);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
    if (requestCode == REQUEST_TAKE_PHOTO && resultcode == RESULT_OK) {
        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        image.setImageBitmap(bitmap);
    }
}
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.simpleworkingcameraapp">


<!--only added paragraph-->
<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  <!-- only crucial line to add; for me it still worked without the other lines in this paragraph -->
<uses-permission android:name="android.permission.CAMERA" />


<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=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

</manifest>

Зауважте, що знайдене нами рішення для проблеми також призвело до спрощення файлу маніфесту андроїд: зміни, запропоновані підручником для Android з точки зору додавання постачальника, більше не потрібні, оскільки я не використовую жодного в своєму коді Java. Отже, до файлу маніфесту потрібно було додати лише кілька стандартних рядків - переважно щодо дозволів.

Крім того, може бути корисним зазначити, що автоімпорт Android Studio може не працювати java.text.SimpleDateFormatі java.util.Date. Мені довелося імпортувати обидва вручну.


це рішення вимагає дозволу ЗБЕРІГАННЯ, що не дуже добре підходить.
shanraisshan

4
У запитанні чітко зазначено, що параметр Intent з назвою "data" передається в onActivityResult () є нульовим, тоді як ваше рішення піде вперед і намагається його використовувати.
Макс

2

Можливо, тому, що у вас було щось подібне?

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);                        
Uri fileUri =  CommonUtilities.getTBCameraOutputMediaFileUri();                  
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);                        
startActivityForResult(takePictureIntent, 2);

Однак не потрібно вводити додатковий вихід у наміри, оскільки тоді дані переходять у URI замість змінної даних. З цієї причини вам потрібно взяти два рядки посередині, щоб у вас є

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, 2);

Саме це спричинило проблему для мене, сподіваюся, що це допомогло.


12
Ця відповідь вводить в оману. Ви отримаєте мініатюру замість повнорозмірної фотографії, не ставлячи MediaStore.EXTRA_OUTPUT. Це виділено в документах та інших відповідях.
користувач2417480

2
Абсолютно ця відповідь вводить в оману.
IndexOutOfDevelopersException

це хибна відповідь.
Abdulla Thanseeh

0

Наступний код працює для мене:

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 2);

І ось результат:

protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent)
{ 
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    if(resultCode == RESULT_OK)
    {
        Uri selectedImage = imageReturnedIntent.getData();
        ImageView photo = (ImageView) findViewById(R.id.add_contact_label_photo);
        Bitmap mBitmap = null;
        try
        {
            mBitmap = Media.getBitmap(this.getContentResolver(), selectedImage);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

Я перевірив його, але це те саме питання :(
soft_developer

1
я отримую imageReturnedIntent.getData()значення null. Я створюю намір такий самий, як і ваш. Не вкладає зайвих парам.
Джейдіп Калкані

0

Для доступу до камери та фотографування та встановлення ImageView на Android

Ви повинні використовувати Uri file = Uri.fromFile(getOutputMediaFile());для зефіру.

Використовуйте посилання нижче, щоб отримати шлях

https://androidkennel.org/android-camera-access-tutorial/


0

Код Котліна, який працює для мене:

 private fun takePhotoFromCamera() {
          val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE)
        startActivityForResult(intent, PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA)
      }

І отримайте результат:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
 if (requestCode == PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA) {
         if (resultCode == Activity.RESULT_OK) {
           val photo: Bitmap? =  MediaStore.Images.Media.getBitmap(this.contentResolver, Uri.parse( data!!.dataString)   )
            // Do something here : set image to an ImageView or save it ..   
              imgV_pic.imageBitmap = photo 
        } else if (resultCode == Activity.RESULT_CANCELED) {
            Log.i(TAG, "Camera  , RESULT_CANCELED ")
        }

    }

}

і не забудьте оголосити код запиту:

companion object {
 const val PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA = 300
  }

0

Після багато спроб та вивчення я зміг це зрозуміти. По-перше, змінні дані з Intent завжди будуть нульовими, тому перевірка на наявність !nullзбоїв у вашій програмі так довго, що ви передаєте URI для startActivityForResult. Дотримуйтесь наведеного нижче прикладу. Я буду використовувати Котлін.

  1. Відкрийте намір камери

    fun addBathroomPhoto(){
    addbathroomphoto.setOnClickListener{
    
        request_capture_image=2
    
        var takePictureIntent:Intent?
        takePictureIntent =Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if(takePictureIntent.resolveActivity(activity?.getPackageManager()) != null){
    
            val photoFile: File? = try {
                createImageFile()
            } catch (ex: IOException) {
                // Error occurred while creating the File
    
                null
            }
    
            if (photoFile != null) {
                val photoURI: Uri = FileProvider.getUriForFile(
                    activity!!,
                    "ogavenue.ng.hotelroomkeeping.fileprovider",photoFile)
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    photoURI);
                startActivityForResult(takePictureIntent,
                    request_capture_image);
            }
    
    
        }
    
    }

    } `

  2. Створіть createImageFile (), але ви ОБОВ'ЯЗКОВО зробити змінну imageFilePath глобальною. Приклад того, як його створити, ви знайдете на офіційній документації Android і досить простий

  3. Будьте наміри

     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    
    if (requestCode == 1 && resultCode == RESULT_OK) {
        add_room_photo_txt.text=""
        var myBitmap=BitmapFactory.decodeFile(imageFilePath)
        addroomphoto.setImageBitmap(myBitmap)
        var file=File(imageFilePath)
        var fis=FileInputStream(file)
        var bm = BitmapFactory.decodeStream(fis);
        roomphoto=getBytesFromBitmap(bm) }}
  4. Метод getBytesFromBitmap

      fun getBytesFromBitmap(bitmap:Bitmap):ByteArray{
    
      var stream=ByteArrayOutputStream()
      bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
      return stream.toByteArray();
      }

Я сподіваюся, що це допомагає.


0

Коли ми зафіксуємо зображення з камери в android, то Uri або data.getdata()стане нульовим. у нас є два рішення для вирішення цього питання.

  1. Ми можемо отримати шлях Uri із зображення Bitmap
  2. Ми можемо отримати курсор Урі від курсору.

Я буду реалізовувати всі методи тут. Будь ласка, уважно дивіться та читайте їх:

Спочатку я розповім, як отримати Uri з Bitmap Image: Повний код:

Спочатку ми зафіксуємо зображення за допомогою Intent, яке буде однаковим для обох методів, тому цей код я напишу один раз лише тут:

// Capture Image
captureImg.setOnClickListener(new View.OnClickListener(){
    @Override public void onClick(View view){
        Intent intent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if(intent.resolveActivity(getPackageManager())!=null){
            startActivityForResult(intent,reqcode);
        }

    }
});  

Тепер ми впровадимо OnActivityResult :-( Це буде однаково для обох вище двох способів): -

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == reqcode && resultCode == RESULT_OK)
    {

        Bitmap photo = (Bitmap) data.getExtras().get("data");
        ImageView.setImageBitmap(photo);

        // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
        Uri tempUri = getImageUri(getApplicationContext(), photo);

        //Show Uri path based on Image
        Toast.makeText(LiveImage.this, "Here " + tempUri, Toast.LENGTH_LONG).show();

        //Show Uri path based on Cursor Content Resolver
        Toast.makeText(this, "Real path for URI : " + getRealPathFromURI(tempUri), Toast.LENGTH_SHORT).show();
    }
    else
    {
        Toast.makeText(this, "Failed To Capture Image", Toast.LENGTH_SHORT).show();
    }
}     

Тепер ми створимо все вищевказаний метод для створення Uri з методів Image та Cursor за допомогою класів:

Тепер шлях URI від Bitmap Image

private Uri getImageUri(Context applicationContext, Bitmap photo)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(LiveImage.this.getContentResolver(), photo, "Title", null);
    return Uri.parse(path);
}

\ Uri з Реального шляху збереженого зображення

public String getRealPathFromURI(Uri uri)
{
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
    return cursor.getString(idx);
}

-1

Коли ми знімаємо зображення з камери в Android, то Uriабо data.getdata()стає нульовим. У нас є два рішення для вирішення цього питання.

  1. Отримайте шлях Uri із зображення Bitmap
  2. Отримайте шлях курсору з курсору.

Ось як отримати Uri з Bitmap Image. Спочатку зафіксуйте зображення за допомогою наміру, яке буде однаковим для обох методів:

// Capture Image
captureImg.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, reqcode);
        }
    }
});

Тепер реалізуйте OnActivityResult, що буде однаковим для обох методів:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==reqcode && resultCode==RESULT_OK)
    {
        Bitmap photo = (Bitmap) data.getExtras().get("data");
        ImageView.setImageBitmap(photo);

        // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
        Uri tempUri = getImageUri(getApplicationContext(), photo);

        // Show Uri path based on Image
        Toast.makeText(LiveImage.this,"Here "+ tempUri, Toast.LENGTH_LONG).show();

        // Show Uri path based on Cursor Content Resolver
        Toast.makeText(this, "Real path for URI : "+getRealPathFromURI(tempUri), Toast.LENGTH_SHORT).show();
    }
    else
    {
        Toast.makeText(this, "Failed To Capture Image", Toast.LENGTH_SHORT).show();
    }
}

Тепер створіть усі вищезазначені методи для створення Uri з методів зображення та курсору:

Шлях Урі з растрового зображення:

private Uri getImageUri(Context applicationContext, Bitmap photo) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(LiveImage.this.getContentResolver(), photo, "Title", null);
    return Uri.parse(path);
}

Урі з Реального шляху збереженого зображення:

public String getRealPathFromURI(Uri uri) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
    return cursor.getString(idx);
}

Хтось зміг це вирішити? Будь ласка, опублікуйте своє рішення. Документ Android, очевидно, не зміг вирішити це питання
Gstuntz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.