Проблема орієнтації камери в Android


100

Я будую додаток, який використовує камеру для фотографування. Ось мій вихідний код для цього:

        File file = new File(Environment.getExternalStorageDirectory(),
            imageFileName);
    imageFilePath = file.getPath();
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    //Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
    startActivityForResult(intent, ACTIVITY_NATIVE_CAMERA_AQUIRE);

За onActivityResult()методом я використовую BitmapFactory.decodeStream()для підбору зображення.

Коли я запускаю свою програму на Nexus one, вона працює добре. Але коли я працюю на Samsung Galaxy S або HTC Inspire 4G, напрямок зображення не правильний.

  • Знімаючи в режимі портрет, реальне зображення (збережіть на SD-картці) завжди обертається на 90 градусів.

попередній перегляд зображення після знятого реальне зображення на SD-картці

Попередній перегляд зображення після пострілу --------- Реальне зображення на SD-картці

  • Знімайте в ландшафтному режимі, все добре.

Попередній перегляд зображення після знятого Реальне зображення на SD-картці

Попередній перегляд зображення після пострілу --------- Реальне зображення на SD-картці


1
setRotation (90) працював для мене в Samsung Galaxy Nexus, поки він не обертав зображення в Xperia S.
StarDust

хтось може мені допомогти в цьому? У мене така ж проблема stackoverflow.com/questions/28379130 / ...



Відповіді:


50

Тут є досить багато подібних тем та питань. Оскільки ви не пишете власну камеру, я думаю, що це зводиться до цього:

деякі пристрої обертають зображення перед збереженням, а інші просто додають тег орієнтації у дані exif фотографії.

Я рекомендую перевірити дані exif фотографії та звернути особливу увагу

ExifInterface exif = new ExifInterface(SourceFileName);     //Since API Level 5
String exifOrientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);

Оскільки фотографія відображається правильно у вашому додатку, я не впевнений, де проблема, але це, безумовно, повинно поставити вас на правильний шлях!


33
На деяких пристроях це не працює, воно повертає 0 для всіх орієнтацій. Я знаю, що це відбувається на Galaxy S Infuse і Sony Xperia Arc, і S II. Цікавим є те, що коли ці самі зображення вибираються з галереї , постачальник вмісту має належне орієнтаційне значення. Будь-які ідеї?
Тольга Е

3
@Abhijit Так, я спробував вирішити це (відкритий квиток з Android тощо), і я думаю, що знайшов розумне рішення обробляти обидва телефони правильною та несправною інформацією про орієнтацію. Перевірте мою детальну відповідь, яку я розмістив на власне запитання тут; stackoverflow.com/a/8864367/137404
Tolga E

1
@ramz Я спробував це рішення знайти. але він повертає 0 для всіх орієнтацій. Чи маєте ви уявлення, чому він повертає 0 для всіх орієнтацій.
Дорі

1
Причина цього рішення не працює - іноді неправильний "шлях", який використовується в конструкторі ExifInterface. Переважно на KitKat. Дивіться тут, як отримати правильний шлях: stackoverflow.com/a/20559175/690777
peter.bartos

1
Він завжди повертає 0 (ExifInterface.ORIENTATION_UNDEFINED) на моєму Samsung Galaxy S4 ...
valerybodak

28

Я щойно стикався з тією ж проблемою і використовував це для виправлення орієнтації:

public void fixOrientation() {
    if (mBitmap.getWidth() > mBitmap.getHeight()) {
        Matrix matrix = new Matrix();
        matrix.postRotate(90);
        mBitmap = Bitmap.createBitmap(mBitmap , 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), matrix, true);
    }
}

Якщо ширина Bitmap більше, ніж висота, повернене зображення знаходиться в альбомному вигляді, тому я обертаю його на 90 градусів.

Сподіваюся, що це допоможе комусь іншому в цьому питанні.


18
Що робити, якщо зображення насправді було зроблено в пейзажі? Ваш код все одно буде обертати його. Це не відповідь на запитання.
Володимир Палант

1
Моя програма примушує портрет, так що це не проблема. Я включив тут лише це альтернативне рішення проблеми.

5
Але якщо додаток змушує портрет ви все одно можете сфотографувати пейзаж (ширина> висота), і він буде повернутий ... Це, принаймні, те, що я отримую налаштування screenOrientation = "портрет" для всього ... камера все одно може зробити пейзаж фото.
Ixx

повну та правильну відповідь тут присутній stackoverflow.com/questions/6069122 / ...
Shirish Herwade

21

Необхідні дві речі:

  1. Попередній перегляд камери повинен відповідати обертанню. Встановити це за допомогоюcamera.setDisplayOrientation(result);

  2. Збережіть знімок, знятий у якості попереднього перегляду камери. Зробіть це через Camera.Parameters.

    int mRotation = getCameraDisplayOrientation();
    
    Camera.Parameters parameters = camera.getParameters();
    
    parameters.setRotation(mRotation); //set rotation to save the picture
    
    camera.setDisplayOrientation(result); //set the rotation for preview camera
    
    camera.setParameters(parameters);

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


це воно! Camera.parameters дуже зручно зберігати знімки, не надаючи проміжну растрову карту
rupps

Будь ласка, позначте це як найпростішу відповідь! Це зробіть роботу! Дуже радий цьому простому рішенню
Кай Бургхардт

Якщо цей фрагмент буде прийнятий таким, який є, ми могли би просто сказати parameters.setRotation(result): ні?
Метт Логан

10
Це передбачає, що ви безпосередньо використовуєте клас Camera, ви не можете вказати ті самі параметри, коли ви використовуєте намір ACTION_IMAGE_CAPTURE.
даху

1
Що таке результат і отриматиCameraDisplayOrientation ()?
venkat

10
            int rotate = 0;
            try {
                File imageFile = new File(sourcepath);
                ExifInterface exif = new ExifInterface(
                        imageFile.getAbsolutePath());
                int orientation = exif.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION,
                        ExifInterface.ORIENTATION_NORMAL);

                switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            Matrix matrix = new Matrix();
    matrix.postRotate(rotate);
    bitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

Було б корисно мати трохи пояснень, що робить ваш код, і як він робить щось інакше, ніж інші відповіді (питання все-таки вже досить старе, а інші відповіді, отже, ймовірно, досить зрілі).
кодування

7

Іншим варіантом є поворот растрової карти на екрані результатів так:

ImageView img=(ImageView)findViewById(R.id.ImageView01);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.refresh);
// Getting width & height of the given image.
int w = bmp.getWidth();
int h = bmp.getHeight();
// Setting post rotate to 90
Matrix mtx = new Matrix();
mtx.postRotate(90);
// Rotating Bitmap
Bitmap rotatedBMP = Bitmap.createBitmap(bmp, 0, 0, w, h, mtx, true);
BitmapDrawable bmd = new BitmapDrawable(rotatedBMP);

img.setImageDrawable(bmd);

30
Такий підхід не буде працювати, оскільки він також обертає зображення з пристроїв, які належним чином обробляють орієнтацію.
hanspeide

повну та правильну відповідь тут присутній stackoverflow.com/questions/6069122 / ...
Shirish Herwade

3

У мене також є така ж проблема для деяких пристроїв:

private void rotateImage(final String path) {

    Bitmap scaledBitmap = Bitmap.createScaledBitmap(Conasants.bm1, 1000,
            700, true);
    Bitmap rotatedBitmap = null;
    try {
        ExifInterface ei = new ExifInterface(path);
        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
        Matrix matrix = new Matrix();
        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            matrix.postRotate(90);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            matrix.postRotate(180);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            matrix.postRotate(270);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        default:
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        }
    } catch (Throwable e) {
        e.printStackTrace();
    }
    cropImage.setImageBitmap(rotatedBitmap);
    rotatedBitmap = null;
    Conasants.bm1 = null;
}

1

Спробуйте таким чином: статичний Uri image_uri; статична растрова снимка taken_image = null;

            image_uri=fileUri; // file where image has been saved

      taken_image=BitmapFactory.decodeFile(image_uri.getPath());
      try
        {
            ExifInterface exif = new ExifInterface(image_uri.getPath()); 

            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);


            switch(orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 90);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 180);

                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 270);

                    break;
                case ExifInterface.ORIENTATION_NORMAL:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 0);

                    break;
            }

        }
        catch (OutOfMemoryError e)
        {
            Toast.makeText(getActivity(),e+"\"memory exception occured\"",Toast.LENGTH_LONG).show();


        }



public Bitmap RotateBitmap(Bitmap source, float angle) {
      Matrix matrix = new Matrix();
      matrix.postRotate(angle);

      round_Image = source;
      round_Image = Bitmap.createBitmap(source, 0, 0, source.getWidth(),   source.getHeight(), matrix, true);


  return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);

}


1

Більше не перевіряйте дані exif фотографії. Ідіть легко з Glide .

Google представив нам бібліотеку завантажувачів зображень для Android, розроблену bumptech під назвою Glide як бібліотеку, яку рекомендує Google. Дотепер він використовувався у багатьох проектах з відкритим кодом Google, включаючи офіційний додаток Google I / O 2014.

Наприклад: Glide.with (контекст) .load (uri) .into (imageview);

Докладніше: https://github.com/bumptech/glide


1
public void setCameraPicOrientation(){
        int rotate = 0;
        try {
            File imageFile = new File(mCurrentPhotoPath);
            ExifInterface exif = new ExifInterface(
                    imageFile.getAbsolutePath());
            int orientation = exif.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);

            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(rotate);
        int targetW = 640;
        int targetH = 640;

        /* Get the size of the image */
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        /* Figure out which way needs to be reduced less */
        int scaleFactor = 1;
        if ((targetW > 0) || (targetH > 0)) {
            scaleFactor = Math.min(photoW/targetW, photoH/targetH);
        }

        /* Set bitmap options to scale the image decode target */
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;

        /* Decode the JPEG file into a Bitmap */
        Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        bitmap= Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            /* Associate the Bitmap to the ImageView */
      imageView.setImageBitmap(bitmap);
    }

Сподіваюся, це допоможе !! Дякую


0
    public static  int mOrientation =  1;

    OrientationEventListener myOrientationEventListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.takephoto);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);


        myOrientationEventListener
        = new OrientationEventListener(getApplicationContext()) {

            @Override
            public void onOrientationChanged(int o) {
                // TODO Auto-generated method stub
                if(!isTablet(getApplicationContext()))
                {
                    if(o<=285 && o>=80)
                        mOrientation = 2;
                    else
                        mOrientation = 1;
                }
                else
                {
                    if(o<=285 && o>=80)
                        mOrientation = 1;
                    else
                        mOrientation = 2;
                }

            }
        };

        myOrientationEventListener.enable();

    }



    public static boolean isTablet(Context context) {
        return (context.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK)
                >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }

}

Я сподіваюся, що це допоможе. Дякую!


0

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

private static final String[] CONTENT_ORIENTATION = new String[] {
        MediaStore.Images.ImageColumns.ORIENTATION
};

static int getExifOrientation(ContentResolver contentResolver, Uri uri) {
    Cursor cursor = null;

    try {
        cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
        if (cursor == null || !cursor.moveToFirst()) {
            return 0;
        }
        return cursor.getInt(0);
    } catch (RuntimeException ignored) {
        // If the orientation column doesn't exist, assume no rotation.
        return 0;
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

сподіваюся, що це допоможе :)


0

Спробуйте це у поверненому зміненому виклику:

Camera.Parameters parameters=mCamera.getParameters();
if(this.getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
    parameters.setRotation(90);
}else{
    parameters.setRotation(0);
}
mCamera.setParameters(parameters);

0

// натискання кнопки

btnCamera.setOnClickListener( new View.OnClickListener() {
        @Override
        public void onClick(View view) {

                try {
                    ContentValues values;
                    values = new ContentValues();
                    values.put(MediaStore.Images.Media.TITLE, "New Picture");
                    values.put(MediaStore.Images.Media.DESCRIPTION, "From your Camera");
                    imageUri = context.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, CAMERA_REQUEST);
                }catch (Exception e){}

            });

// метод onActivityResult

   if (requestCode==CAMERA_REQUEST){
        try {
            if (imageUri!=null) {
                path = String.valueOf(new File(FilePath.getPath(context, imageUri)));
                   }  
        }catch (Exception e){
            toast("please try again "+e.getMessage());
            Log.e("image error",e.getMessage());
        }
    }

// створити файловий шлях класу

FilePath публічного класу {

public static String getPath(final Context context, final Uri uri) {

    // check here to KITKAT or new version
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/"
                        + split[1];
            }
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] { split[1] };

            return getDataColumn(context, contentUri, selection,
                    selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context
 *            The context.
 * @param uri
 *            The Uri to query.
 * @param selection
 *            (Optional) Filter used in the query.
 * @param selectionArgs
 *            (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri,
                                   String selection, String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = { column };

    try {
        cursor = context.getContentResolver().query(uri, projection,
                selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri
            .getAuthority());
}

}


-1

Код функціонально для пейзажу та портрета @frontCameraID = змінна отримала його метод класичний для показу потрібної камери

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    if(holder.getSurface() == null) {
        return;
    }
    try{
        camera.stopPreview();
    } catch (Exception e){
    }

    try{

        int orientation = getDisplayOrientation(frontCameraID);

        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }

        parameters.setRotation(rotationPicture);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(orientation);
        camera.startPreview();

    } catch (Exception e) {
        Log.i("ERROR", "Camera error changed: " + e.getMessage());
    }
}

Спосіб отримання орієнтації y обертання для збереження зображення та орієнтації дисплея @result = орієнтація на поданні попереднього перегляду камери @rotationPicture = поворот, необхідний для правильного збереження зображення

private int getDisplayOrientation(int cameraId) {

    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = ((Activity) context).getWindowManager().getDefaultDisplay().getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0: degrees = 0; break;
        case Surface.ROTATION_90: degrees = 90; break;
        case Surface.ROTATION_180: degrees = 180; break;
        case Surface.ROTATION_270: degrees = 270; break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;
        rotationPicture = (360 - result) % 360;
    } else {
        result = (info.orientation - degrees + 360) % 360;
        rotationPicture = result;
    }

    return result;
}

Хтось питання про код, будь ласка, скажіть мені.


-2

два рішення в одній лінії, що використовують бібліотеку Picasso та glide

Провівши багато часу з великою кількістю рішень для проблеми обертання зображення, я нарешті знайшов два простих рішення. Нам не потрібно робити ніяких додаткових робіт. Пікассо і Glide - це дуже потужна бібліотека для обробки зображень, що містяться у вашому додатку. Він буде читати зображення EXIF ​​даних і автоматично повертати зображення.

Використання бібліотеки "Glide" https://github.com/bumptech/glide

Glide.with(this).load("http url or sdcard url").into(imgageView);

Використання бібліотеки Picasso https://github.com/square/picasso

Picasso.with(context).load("http url or sdcard url").into(imageView);

Ласкаво просимо до SO. Будь ласка, утримуйтесь від прохання про отримання грошових коштів: meta.stackexchange.com/questions/194061/…
січня
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.