title: Android : Compress images issue
body: <p>I want to compress images before uploading them to the server, without losing quality.</p>
  <p>my principle is to scale the image to a height and width that does not exceed 4000 pixels. for example, if the height is 5000 pixels, then I change it to 4000, and I change the width also in proportion to the height. otherwise I keep the current dimensions if they do not exceed 4000.</p>
  <p>then I compress the bitmap to a preset quality of 80%.</p>
  <p>it works fine in most smartphones, and I got good quality with a very small image size (1MB around).</p>
  <p>the problem here is that it doesn't work in a very limited number of smartphones. I find that the size of the image is not changed (6-5MB), and in the exif data the height and the width are -1</p>
  <p>this is my compression algorithm</p>
  <pre><code>private fun compressImage(
      source : Uri,
      maxSize : Int,
      quality : Int,
      fileNameSuffix : String
  ) : File?
  {
      // Create bitmap from uri.
      val sourceAsBitmap : Bitmap = getBitmapFromUri(source) ?: return null
      
      // The width and height of the original image.
      val sourceWidth : Float = sourceAsBitmap.width.toFloat()
      val sourceHeight : Float = sourceAsBitmap.height.toFloat()
      
      // #######################################################################################
      
      // The height and width of the image after scale must not exceed 4000 pixels.
      // So here, we determine if the current height and width exceed 4000, we choose the 4000,
      // otherwise, we keep the current dimensions.
      // After determining the max between the height and width, we calculate their proportion 
      // to correctly scale the image
      
      val minWidth : Float = min(sourceWidth, maxSize.toFloat()) / sourceWidth
      val minHeight : Float = min(sourceHeight, maxSize.toFloat()) / sourceHeight
      
      val percentage : Float = min(minWidth, minHeight)
      
      // The chosen width and height that will be used to scale the image.
      val newWidth : Int = (sourceWidth * percentage).toInt()
      val newHeight : Int = (sourceHeight * percentage).toInt()
      
      // #######################################################################################
      
      // Scale image to chosen dimensions.
      val scaledBitmap : Bitmap = Bitmap.createScaledBitmap(sourceAsBitmap, newWidth, newHeight, true)
      
      // Compress the image to the chosen quality and format.
      val outputStream = ByteArrayOutputStream()
      scaledBitmap.compress(CompressFormat.JPEG, quality, outputStream)
      
      // Create file for the scaled &amp; compressed image.
      val compressedFile : File = createFile(outputStream.toByteArray(), source, fileNameSuffix) ?: return null
      
      copyExifData(source, compressedFile, newWidth, newHeight)
      
      outputStream.close()
      
      return compressedFile
  }
  
  
  private fun createFile(byteArray : ByteArray, source : Uri, nameSuffix : String) : File?
  {
      return try
      {
          val sourceAsDocumentFile : DocumentFile? = DocumentFile.fromSingleUri(context, source)
          
          val sourceFileName : String = sourceAsDocumentFile?.name?.substringBefore(&quot;.&quot;) ?: System.currentTimeMillis().toString()
          
          val finalFileName = &quot;$sourceFileName-$nameSuffix.jpg&quot;
          
          val file : File = StorageUtils.createFile(finalFileName)
          
          val fileOutputStream = FileOutputStream(file)
          fileOutputStream.write(byteArray)
          fileOutputStream.close()
          
          file
      }
      catch (exception : Exception)
      {
          ExceptionLogger.print(&quot;failed to create file&quot;, exception)
          null
      }
  }
  
  
  private fun copyExifData(source : Uri, destination : File, width : Int, height : Int)
  {
      try
      {
          val sourceFileDescriptor : FileDescriptor = context.contentResolver?.openFileDescriptor(source, &quot;r&quot;)?.fileDescriptor ?: return
          
          val sourceFileExif = ExifInterface(sourceFileDescriptor)
          
          val destinationFileExif = ExifInterface(destination)
          
          destinationFileExif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, height.toString())
          destinationFileExif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, width.toString())
          
          val orientation : String = sourceFileExif.getAttribute(ExifInterface.TAG_ORIENTATION) ?: ExifInterface.ORIENTATION_NORMAL.toString()
          
          destinationFileExif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation)
          
          sourceFileExif.getAttribute(ExifInterface.TAG_DATETIME)?.let {
              destinationFileExif.setAttribute(ExifInterface.TAG_DATETIME, it)
          }
          
          sourceFileExif.getAttribute(ExifInterface.TAG_GPS_ALTITUDE)?.let {
              destinationFileExif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, it)
          }
          
          sourceFileExif.getAttribute(ExifInterface.TAG_GPS_LATITUDE)?.let {
              destinationFileExif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, it)
          }
          
          sourceFileExif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE)?.let {
              destinationFileExif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, it)
          }
          
          destinationFileExif.saveAttributes()
      }
      catch (exception : Exception)
      {
          ExceptionLogger.print(&quot;failed to copy exif data&quot;, exception)
      }
  }
  
  
  private fun getBitmapFromUri(uri : Uri) : Bitmap?
  {
      return try
      {
          val parcelFileDescriptor : ParcelFileDescriptor = context.contentResolver?.openFileDescriptor(uri, &quot;r&quot;) ?: return null
          val fileDescriptor : FileDescriptor = parcelFileDescriptor.fileDescriptor
          val image : Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
          parcelFileDescriptor.close()
          image
      }
      catch (exception : Exception)
      {
          ExceptionLogger.print(&quot;error decoding image using bitmap factory&quot;, exception)
          null
      }
  }
  </code></pre>
  <p>can anyone help me to solve this problem?
  i also want to know what is the difference between these three method for getting bitmap from uri? and which one is better?</p>
  <p>method 1 :</p>
  <pre><code>val bitmap = if (VERSION.SDK_INT &gt;= VERSION_CODES.P)
  {
      val source : ImageDecoder.Source = ImageDecoder.createSource(context.contentResolver, uri)
      ImageDecoder.decodeBitmap(source)
  }
  else
  {
      MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
  }
  </code></pre>
  <p>methode 2 :</p>
  <pre><code>val parcelFileDescriptor : ParcelFileDescriptor = context.contentResolver?.openFileDescriptor(uri, &quot;r&quot;) ?: return null
  val fileDescriptor : FileDescriptor = parcelFileDescriptor.fileDescriptor
  val image : Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
  parcelFileDescriptor.close()
  </code></pre>
  <p>methode 3 :</p>
  <pre><code>val bitmap = BitmapFactory.decodeFile(uri.path)
  </code></pre>