gt:         protected ArrayList<String> filters = new ArrayList<>();
context: /*
   * This is the source code of Telegram for Android v. 3.x.x.
   * It is licensed under GNU GPL v. 2 or later.
   * You should have received a copy of the license in this archive (see LICENSE).
   *
   * Copyright Nikolai Kudashov, 2013-2017.
   */
  
  package org.telegram.messenger;
  
  import android.app.ActivityManager;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.graphics.Matrix;
  import android.graphics.drawable.BitmapDrawable;
  import android.media.ExifInterface;
  import android.media.ThumbnailUtils;
  import android.net.Uri;
  import android.os.AsyncTask;
  import android.os.Build;
  import android.os.Environment;
  import android.provider.MediaStore;
  import android.text.TextUtils;
  
  import org.telegram.messenger.secretmedia.EncryptedFileInputStream;
  import org.telegram.tgnet.ConnectionsManager;
  import org.telegram.tgnet.TLObject;
  import org.telegram.tgnet.TLRPC;
  import org.telegram.ui.Components.AnimatedFileDrawable;
  
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.RandomAccessFile;
  import java.net.HttpURLConnection;
  import java.net.SocketException;
  import java.net.SocketTimeoutException;
  import java.net.URL;
  import java.net.URLConnection;
  import java.net.UnknownHostException;
  import java.nio.ByteBuffer;
  import java.nio.channels.FileChannel;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  public class ImageLoader {
  
      private HashMap<String, Integer> bitmapUseCounts = new HashMap<>();
      private LruCache memCache;
      private HashMap<String, CacheImage> imageLoadingByUrl = new HashMap<>();
      private HashMap<String, CacheImage> imageLoadingByKeys = new HashMap<>();
      private HashMap<Integer, CacheImage> imageLoadingByTag = new HashMap<>();
      private HashMap<String, ThumbGenerateInfo> waitingForQualityThumb = new HashMap<>();
      private HashMap<Integer, String> waitingForQualityThumbByTag = new HashMap<>();
      private LinkedList<HttpImageTask> httpTasks = new LinkedList<>();
      private DispatchQueue cacheOutQueue = new DispatchQueue("cacheOutQueue");
      private DispatchQueue cacheThumbOutQueue = new DispatchQueue("cacheThumbOutQueue");
      private DispatchQueue thumbGeneratingQueue = new DispatchQueue("thumbGeneratingQueue");
      private DispatchQueue imageLoadQueue = new DispatchQueue("imageLoadQueue");
      private ConcurrentHashMap<String, Float> fileProgresses = new ConcurrentHashMap<>();
      private HashMap<String, ThumbGenerateTask> thumbGenerateTasks = new HashMap<>();
      private HashMap<String, Integer> forceLoadingImages = new HashMap<>();
      private static byte[] bytes;
      private static byte[] bytesThumb;
      private static byte[] header = new byte[12];
      private static byte[] headerThumb = new byte[12];
      private int currentHttpTasksCount = 0;
  
      private LinkedList<HttpFileTask> httpFileLoadTasks = new LinkedList<>();
      private HashMap<String, HttpFileTask> httpFileLoadTasksByKeys = new HashMap<>();
      private HashMap<String, Runnable> retryHttpsTasks = new HashMap<>();
      private int currentHttpFileLoadTasksCount = 0;
  
      private String ignoreRemoval = null;
  
      private volatile long lastCacheOutTime = 0;
      private int lastImageNum = 0;
      private long lastProgressUpdateTime = 0;
  
      private File telegramPath = null;
  
      private class ThumbGenerateInfo {
          private int count;
          private TLRPC.FileLocation fileLocation;
          private String filter;
      }
  
      private class HttpFileTask extends AsyncTask<Void, Void, Boolean> {
  
          private String url;
          private File tempFile;
          private String ext;
          private int fileSize;
          private RandomAccessFile fileOutputStream = null;
          private boolean canRetry = true;
          private long lastProgressTime;
  
          public HttpFileTask(String url, File tempFile, String ext) {
              this.url = url;
              this.tempFile = tempFile;
              this.ext = ext;
          }
  
          private void reportProgress(final float progress) {
              long currentTime = System.currentTimeMillis();
              if (progress == 1 || lastProgressTime == 0 || lastProgressTime < currentTime - 500) {
                  lastProgressTime = currentTime;
                  Utilities.stageQueue.postRunnable(new Runnable() {
                      @Override
                      public void run() {
                          fileProgresses.put(url, progress);
                          AndroidUtilities.runOnUIThread(new Runnable() {
                              @Override
                              public void run() {
                                  NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileLoadProgressChanged, url, progress);
                              }
                          });
                      }
                  });
              }
          }
  
          protected Boolean doInBackground(Void... voids) {
              InputStream httpConnectionStream = null;
              boolean done = false;
  
              URLConnection httpConnection = null;
              try {
                  URL downloadUrl = new URL(url);
                  httpConnection = downloadUrl.openConnection();
                  httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1");
                  //httpConnection.addRequestProperty("Referer", "google.com");
                  httpConnection.setConnectTimeout(5000);
                  httpConnection.setReadTimeout(5000);
                  if (httpConnection instanceof HttpURLConnection) {
                      HttpURLConnection httpURLConnection = (HttpURLConnection) httpConnection;
                      httpURLConnection.setInstanceFollowRedirects(true);
                      int status = httpURLConnection.getResponseCode();
                      if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
                          String newUrl = httpURLConnection.getHeaderField("Location");
                          String cookies = httpURLConnection.getHeaderField("Set-Cookie");
                          downloadUrl = new URL(newUrl);
                          httpConnection = downloadUrl.openConnection();
                          httpConnection.setRequestProperty("Cookie", cookies);
                          httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1");
                          //httpConnection.addRequestProperty("Referer", "google.com");
                      }
                  }
                  httpConnection.connect();
                  httpConnectionStream = httpConnection.getInputStream();
  
                  fileOutputStream = new RandomAccessFile(tempFile, "rws");
              } catch (Throwable e) {
                  if (e instanceof SocketTimeoutException) {
                      if (ConnectionsManager.isNetworkOnline()) {
                          canRetry = false;
                      }
                  } else if (e instanceof UnknownHostException) {
                      canRetry = false;
                  } else if (e instanceof SocketException) {
                      if (e.getMessage() != null && e.getMessage().contains("ECONNRESET")) {
                          canRetry = false;
                      }
                  } else if (e instanceof FileNotFoundException) {
                      canRetry = false;
                  }
                  FileLog.e(e);
              }
  
              if (canRetry) {
                  try {
                      if (httpConnection != null && httpConnection instanceof HttpURLConnection) {
                          int code = ((HttpURLConnection) httpConnection).getResponseCode();
                          if (code != HttpURLConnection.HTTP_OK && code != HttpURLConnection.HTTP_ACCEPTED && code != HttpURLConnection.HTTP_NOT_MODIFIED) {
                              canRetry = false;
                          }
                      }
                  } catch (Exception e) {
                      FileLog.e(e);
                  }
                  if (httpConnection != null) {
                      try {
                          Map<String, List<String>> headerFields = httpConnection.getHeaderFields();
                          if (headerFields != null) {
                              List values = headerFields.get("content-Length");
                              if (values != null && !values.isEmpty()) {
                                  String length = (String) values.get(0);
                                  if (length != null) {
                                      fileSize = Utilities.parseInt(length);
                                  }
                              }
                          }
                      } catch (Exception e) {
                          FileLog.e(e);
                      }
                  }
  
                  if (httpConnectionStream != null) {
                      try {
                          byte[] data = new byte[1024 * 32];
                          int totalLoaded = 0;
                          while (true) {
                              if (isCancelled()) {
                                  break;
                              }
                              try {
                                  int read = httpConnectionStream.read(data);
                                  if (read > 0) {
                                      fileOutputStream.write(data, 0, read);
                                      totalLoaded += read;
                                      if (fileSize > 0) {
                                          reportProgress(totalLoaded / (float) fileSize);
                                      }
                                  } else if (read == -1) {
                                      done = true;
                                      if (fileSize != 0) {
                                          reportProgress(1.0f);
                                      }
                                      break;
                                  } else {
                                      break;
                                  }
                              } catch (Exception e) {
                                  FileLog.e(e);
                                  break;
                              }
                          }
                      } catch (Throwable e) {
                          FileLog.e(e);
                      }
                  }
  
                  try {
                      if (fileOutputStream != null) {
                          fileOutputStream.close();
                          fileOutputStream = null;
                      }
                  } catch (Throwable e) {
                      FileLog.e(e);
                  }
  
                  try {
                      if (httpConnectionStream != null) {
                          httpConnectionStream.close();
                      }
                  } catch (Throwable e) {
                      FileLog.e(e);
                  }
              }
  
              return done;
          }
  
          @Override
          protected void onPostExecute(Boolean result) {
              runHttpFileLoadTasks(this, result ? 2 : 1);
          }
  
          @Override
          protected void onCancelled() {
              runHttpFileLoadTasks(this, 2);
          }
      }
  
      private class HttpImageTask extends AsyncTask<Void, Void, Boolean> {
  
          private CacheImage cacheImage = null;
          private RandomAccessFile fileOutputStream = null;
          private int imageSize;
          private long lastProgressTime;
          private boolean canRetry = true;
          private HttpURLConnection httpConnection = null;
  
          public HttpImageTask(CacheImage cacheImage, int size) {
              this.cacheImage = cacheImage;
              imageSize = size;
          }
  
          private void reportProgress(final float progress) {
              long currentTime = System.currentTimeMillis();
              if (progress == 1 || lastProgressTime == 0 || lastProgressTime < currentTime - 500) {
                  lastProgressTime = currentTime;
                  Utilities.stageQueue.postRunnable(new Runnable() {
                      @Override
                      public void run() {
                          fileProgresses.put(cacheImage.url, progress);
                          AndroidUtilities.runOnUIThread(new Runnable() {
                              @Override
                              public void run() {
                                  NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileLoadProgressChanged, cacheImage.url, progress);
                              }
                          });
                      }
                  });
              }
          }
  
          protected Boolean doInBackground(Void... voids) {
              InputStream httpConnectionStream = null;
              boolean done = false;
  
              if (!isCancelled()) {
                  try {
                      URL downloadUrl = new URL(cacheImage.httpUrl);
                      httpConnection = (HttpURLConnection) downloadUrl.openConnection();
                      httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1");
                      //httpConnection.addRequestProperty("Referer", "google.com");
                      httpConnection.setConnectTimeout(5000);
                      httpConnection.setReadTimeout(5000);
                      httpConnection.setInstanceFollowRedirects(true);
                      if (!isCancelled()) {
                          httpConnection.connect();
                          httpConnectionStream = httpConnection.getInputStream();
                          fileOutputStream = new RandomAccessFile(cacheImage.tempFilePath, "rws");
                      }
                  } catch (Throwable e) {
                      if (e instanceof SocketTimeoutException) {
                          if (ConnectionsManager.isNetworkOnline()) {
                              canRetry = false;
                          }
                      } else if (e instanceof UnknownHostException) {
                          canRetry = false;
                      } else if (e instanceof SocketException) {
                          if (e.getMessage() != null && e.getMessage().contains("ECONNRESET")) {
                              canRetry = false;
                          }
                      } else if (e instanceof FileNotFoundException) {
                          canRetry = false;
                      }
                      FileLog.e(e);
                  }
              }
  
              if (!isCancelled()) {
                  try {
                      if (httpConnection != null && httpConnection instanceof HttpURLConnection) {
                          int code = httpConnection.getResponseCode();
                          if (code != HttpURLConnection.HTTP_OK && code != HttpURLConnection.HTTP_ACCEPTED && code != HttpURLConnection.HTTP_NOT_MODIFIED) {
                              canRetry = false;
                          }
                      }
                  } catch (Exception e) {
                      FileLog.e(e);
                  }
                  if (imageSize == 0 && httpConnection != null) {
                      try {
                          Map<String, List<String>> headerFields = httpConnection.getHeaderFields();
                          if (headerFields != null) {
                              List values = headerFields.get("content-Length");
                              if (values != null && !values.isEmpty()) {
                                  String length = (String) values.get(0);
                                  if (length != null) {
                                      imageSize = Utilities.parseInt(length);
                                  }
                              }
                          }
                      } catch (Exception e) {
                          FileLog.e(e);
                      }
                  }
  
                  if (httpConnectionStream != null) {
                      try {
                          byte[] data = new byte[1024 * 8];
                          int totalLoaded = 0;
                          while (true) {
                              if (isCancelled()) {
                                  break;
                              }
                              try {
                                  int read = httpConnectionStream.read(data);
                                  if (read > 0) {
                                      totalLoaded += read;
                                      fileOutputStream.write(data, 0, read);
                                      if (imageSize != 0) {
                                          reportProgress(totalLoaded / (float) imageSize);
                                      }
                                  } else if (read == -1) {
                                      done = true;
                                      if (imageSize != 0) {
                                          reportProgress(1.0f);
                                      }
                                      break;
                                  } else {
                                      break;
                                  }
                              } catch (Exception e) {
                                  FileLog.e(e);
                                  break;
                              }
                          }
                      } catch (Throwable e) {
                          FileLog.e(e);
                      }
                  }
              }
  
              try {
                  if (fileOutputStream != null) {
                      fileOutputStream.close();
                      fileOutputStream = null;
                  }
              } catch (Throwable e) {
                  FileLog.e(e);
              }
              try {
                  if (httpConnection != null) {
                      httpConnection.disconnect();
                  }
              } catch (Throwable ignore) {
  
              }
  
              try {
                  if (httpConnectionStream != null) {
                      httpConnectionStream.close();
                  }
              } catch (Throwable e) {
                  FileLog.e(e);
              }
  
              if (done) {
                  if (cacheImage.tempFilePath != null) {
                      if (!cacheImage.tempFilePath.renameTo(cacheImage.finalFilePath)) {
                          cacheImage.finalFilePath = cacheImage.tempFilePath;
                      }
                  }
              }
  
              return done;
          }
  
          @Override
          protected void onPostExecute(final Boolean result) {
              if (result || !canRetry) {
                  fileDidLoaded(cacheImage.url, cacheImage.finalFilePath, FileLoader.MEDIA_DIR_IMAGE);
              } else {
                  httpFileLoadError(cacheImage.url);
              }
              Utilities.stageQueue.postRunnable(new Runnable() {
                  @Override
                  public void run() {
                      fileProgresses.remove(cacheImage.url);
                      AndroidUtilities.runOnUIThread(new Runnable() {
                          @Override
                          public void run() {
                              if (result) {
                                  NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidLoaded, cacheImage.url);
                              } else {
                                  NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailedLoad, cacheImage.url, 2);
                              }
                          }
                      });
                  }
              });
              imageLoadQueue.postRunnable(new Runnable() {
                  @Override
                  public void run() {
                      runHttpTasks(true);
                  }
              });
          }
  
          @Override
          protected void onCancelled() {
              imageLoadQueue.postRunnable(new Runnable() {
                  @Override
                  public void run() {
                      runHttpTasks(true);
                  }
              });
              Utilities.stageQueue.postRunnable(new Runnable() {
                  @Override
                  public void run() {
                      fileProgresses.remove(cacheImage.url);
                      AndroidUtilities.runOnUIThread(new Runnable() {
                          @Override
                          public void run() {
                              NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailedLoad, cacheImage.url, 1);
                          }
                      });
                  }
              });
          }
      }
  
      private class ThumbGenerateTask implements Runnable {
  
          private File originalPath;
          private int mediaType;
          private TLRPC.FileLocation thumbLocation;
          private String filter;
  
          public ThumbGenerateTask(int type, File path, TLRPC.FileLocation location, String f) {
              mediaType = type;
              originalPath = path;
              thumbLocation = location;
              filter = f;
          }
  
          private void removeTask() {
              if (thumbLocation == null) {
                  return;
              }
              final String name = FileLoader.getAttachFileName(thumbLocation);
              imageLoadQueue.postRunnable(new Runnable() {
                  @Override
                  public void run() {
                      thumbGenerateTasks.remove(name);
                  }
              });
          }
  
          @Override
          public void run() {
              try {
                  if (thumbLocation == null) {
                      removeTask();
                      return;
                  }
                  final String key = thumbLocation.volume_id + "_" + thumbLocation.local_id;
                  File thumbFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), "q_" + key + ".jpg");
                  if (thumbFile.exists() || !originalPath.exists()) {
                      removeTask();
                      return;
                  }
                  int size = Math.min(180, Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 4);
                  Bitmap originalBitmap = null;
                  if (mediaType == FileLoader.MEDIA_DIR_IMAGE) {
                      originalBitmap = ImageLoader.loadBitmap(originalPath.toString(), null, size, size, false);
                  } else if (mediaType == FileLoader.MEDIA_DIR_VIDEO) {
                      originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), MediaStore.Video.Thumbnails.MINI_KIND);
                  } else if (mediaType == FileLoader.MEDIA_DIR_DOCUMENT) {
                      String path = originalPath.toString().toLowerCase();
                      if (!path.endsWith(".jpg") && !path.endsWith(".jpeg") && !path.endsWith(".png") && !path.endsWith(".gif")) {
                          removeTask();
                          return;
                      }
                      originalBitmap = ImageLoader.loadBitmap(path, null, size, size, false);
                  }
                  if (originalBitmap == null) {
                      removeTask();
                      return;
                  }
  
                  int w = originalBitmap.getWidth();
                  int h = originalBitmap.getHeight();
                  if (w == 0 || h == 0) {
                      removeTask();
                      return;
                  }
                  float scaleFactor = Math.min((float) w / size, (float) h / size);
                  Bitmap scaledBitmap = Bitmaps.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true);
                  if (scaledBitmap != originalBitmap) {
                      originalBitmap.recycle();
                  }
                  originalBitmap = scaledBitmap;
                  FileOutputStream stream = new FileOutputStream(thumbFile);
                  originalBitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream);
                  try {
                      stream.close();
                  } catch (Exception e) {
                      FileLog.e(e);
                  }
                  final BitmapDrawable bitmapDrawable = new BitmapDrawable(originalBitmap);
                  AndroidUtilities.runOnUIThread(new Runnable() {
                      @Override
                      public void run() {
                          removeTask();
  
                          String kf = key;
                          if (filter != null) {
                              kf += "@" + filter;
                          }
                          NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageThumbGenerated, bitmapDrawable, kf);
                          /*BitmapDrawable old = memCache.get(kf);
                          if (old != null) {
                              Bitmap image = old.getBitmap();
                              if (runtimeHack != null) {
                                  runtimeHack.trackAlloc(image.getRowBytes() * image.getHeight());
                              }
                              if (!image.isRecycled()) {
                                  image.recycle();
                              }
                          }*/
                          memCache.put(kf, bitmapDrawable);
                      }
                  });
              } catch (Throwable e) {
                  FileLog.e(e);
                  removeTask();
              }
          }
      }
  
      private class CacheOutTask implements Runnable {
          private Thread runningThread;
          private final Object sync = new Object();
  
          private CacheImage cacheImage;
          private boolean isCancelled;
  
          public CacheOutTask(CacheImage image) {
              cacheImage = image;
          }
  
          @Override
          public void run() {
              synchronized (sync) {
                  runningThread = Thread.currentThread();
                  Thread.interrupted();
                  if (isCancelled) {
                      return;
                  }
              }
  
              if (cacheImage.animatedFile) {
                  synchronized (sync) {
                      if (isCancelled) {
                          return;
                      }
                  }
                  AnimatedFileDrawable fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, cacheImage.filter != null && cacheImage.filter.equals("d"));
                  Thread.interrupted();
                  onPostExecute(fileDrawable);
              } else {
                  Long mediaId = null;
                  boolean mediaIsVideo = false;
                  Bitmap image = null;
                  File cacheFileFinal = cacheImage.finalFilePath;
                  boolean inEncryptedFile = cacheImage.encryptionKeyPath != null && cacheFileFinal != null && cacheFileFinal.getAbsolutePath().endsWith(".enc");
                  boolean canDeleteFile = true;
                  boolean useNativeWebpLoaded = false;
  
                  if (Build.VERSION.SDK_INT < 19) {
                      RandomAccessFile randomAccessFile = null;
                      try {
                          randomAccessFile = new RandomAccessFile(cacheFileFinal, "r");
                          byte[] bytes;
                          if (cacheImage.selfThumb) {
                              bytes = headerThumb;
                          } else {
                              bytes = header;
                          }
                          randomAccessFile.readFully(bytes, 0, bytes.length);
                          String str = new String(bytes).toLowerCase();
                          str = str.toLowerCase();
                          if (str.startsWith("riff") && str.endsWith("webp")) {
                              useNativeWebpLoaded = true;
                          }
                          randomAccessFile.close();
                      } catch (Exception e) {
                          FileLog.e(e);
                      } finally {
                          if (randomAccessFile != null) {
                              try {
                                  randomAccessFile.close();
                              } catch (Exception e) {
                                  FileLog.e(e);
                              }
                          }
                      }
                  }
  
                  if (cacheImage.selfThumb) {
                      int blurType = 0;
                      if (cacheImage.filter != null) {
                          if (cacheImage.filter.contains("b2")) {
                              blurType = 3;
                          } else if (cacheImage.filter.contains("b1")) {
                              blurType = 2;
                          } else if (cacheImage.filter.contains("b")) {
                              blurType = 1;
                          }
                      }
  
                      try {
                          lastCacheOutTime = System.currentTimeMillis();
                          synchronized (sync) {
                              if (isCancelled) {
                                  return;
                              }
                          }
  
                          BitmapFactory.Options opts = new BitmapFactory.Options();
                          opts.inSampleSize = 1;
  
                          if (Build.VERSION.SDK_INT < 21) {
                              opts.inPurgeable = true;
                          }
  
                          if (useNativeWebpLoaded) {
                              RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r");
                              ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length());
  
                              BitmapFactory.Options bmOptions = new BitmapFactory.Options();
                              bmOptions.inJustDecodeBounds = true;
                              Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true);
                              image = Bitmaps.createBitmap(bmOptions.outWidth, bmOptions.outHeight, Bitmap.Config.ARGB_8888);
  
                              Utilities.loadWebpImage(image, buffer, buffer.limit(), null, !opts.inPurgeable);
                              file.close();
                          } else {
                              if (opts.inPurgeable) {
                                  RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r");
                                  int len = (int) f.length();
                                  byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null;
                                  if (data == null) {
                                      bytesThumb = data = new byte[len];
                                  }
                                  f.readFully(data, 0, len);
                                  f.close();
                                  if (inEncryptedFile) {
                                      EncryptedFileInputStream.decryptBytesWithKeyFile(data, 0, len, cacheImage.encryptionKeyPath);
                                  }
                                  image = BitmapFactory.decodeByteArray(data, 0, len, opts);
                              } else {
                                  FileInputStream is;
                                  if (inEncryptedFile) {
                                      is = new EncryptedFileInputStream(cacheFileFinal, cacheImage.encryptionKeyPath);
                                  } else {
                                      is = new FileInputStream(cacheFileFinal);
                                  }
                                  image = BitmapFactory.decodeStream(is, null, opts);
                                  is.close();
                              }
                          }
  
                          if (image == null) {
                              if (cacheFileFinal.length() == 0 || cacheImage.filter == null) {
                                  cacheFileFinal.delete();
                              }
                          } else {
                              if (blurType == 1) {
                                  if (image.getConfig() == Bitmap.Config.ARGB_8888) {
                                      Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                  }
                              } else if (blurType == 2) {
                                  if (image.getConfig() == Bitmap.Config.ARGB_8888) {
                                      Utilities.blurBitmap(image, 1, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                  }
                              } else if (blurType == 3) {
                                  if (image.getConfig() == Bitmap.Config.ARGB_8888) {
                                      Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                      Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                      Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                  }
                              } else if (blurType == 0 && opts.inPurgeable) {
                                  Utilities.pinBitmap(image);
                              }
                          }
                      } catch (Throwable e) {
                          FileLog.e(e);
                      }
                  } else {
                      try {
                          String mediaThumbPath = null;
                          if (cacheImage.httpUrl != null) {
                              if (cacheImage.httpUrl.startsWith("thumb://")) {
                                  int idx = cacheImage.httpUrl.indexOf(":", 8);
                                  if (idx >= 0) {
                                      mediaId = Long.parseLong(cacheImage.httpUrl.substring(8, idx));
                                      mediaIsVideo = false;
                                      mediaThumbPath = cacheImage.httpUrl.substring(idx + 1);
                                  }
                                  canDeleteFile = false;
                              } else if (cacheImage.httpUrl.startsWith("vthumb://")) {
                                  int idx = cacheImage.httpUrl.indexOf(":", 9);
                                  if (idx >= 0) {
                                      mediaId = Long.parseLong(cacheImage.httpUrl.substring(9, idx));
                                      mediaIsVideo = true;
                                  }
                                  canDeleteFile = false;
                              } else if (!cacheImage.httpUrl.startsWith("http")) {
                                  canDeleteFile = false;
                              }
                          }
  
                          int delay = 20;
                          if (mediaId != null) {
                              delay = 0;
                          }
                          if (delay != 0 && lastCacheOutTime != 0 && lastCacheOutTime > System.currentTimeMillis() - delay && Build.VERSION.SDK_INT < 21) {
                              Thread.sleep(delay);
                          }
                          lastCacheOutTime = System.currentTimeMillis();
                          synchronized (sync) {
                              if (isCancelled) {
                                  return;
                              }
                          }
  
                          BitmapFactory.Options opts = new BitmapFactory.Options();
                          opts.inSampleSize = 1;
  
                          float w_filter = 0;
                          float h_filter = 0;
                          boolean blur = false;
                          if (cacheImage.filter != null) {
                              String args[] = cacheImage.filter.split("_");
                              if (args.length >= 2) {
                                  w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density;
                                  h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density;
                              }
                              if (cacheImage.filter.contains("b")) {
                                  blur = true;
                              }
                              if (w_filter != 0 && h_filter != 0) {
                                  opts.inJustDecodeBounds = true;
  
                                  if (mediaId != null && mediaThumbPath == null) {
                                      if (mediaIsVideo) {
                                          MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts);
                                      } else {
                                          MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts);
                                      }
                                  } else {
                                      FileInputStream is;
                                      if (inEncryptedFile) {
                                          is = new EncryptedFileInputStream(cacheFileFinal, cacheImage.encryptionKeyPath);
                                      } else {
                                          is = new FileInputStream(cacheFileFinal);
                                      }
                                      image = BitmapFactory.decodeStream(is, null, opts);
                                      is.close();
                                  }
  
                                  float photoW = opts.outWidth;
                                  float photoH = opts.outHeight;
                                  float scaleFactor = Math.max(photoW / w_filter, photoH / h_filter);
                                  if (scaleFactor < 1) {
                                      scaleFactor = 1;
                                  }
                                  opts.inJustDecodeBounds = false;
                                  opts.inSampleSize = (int) scaleFactor;
                              }
                          } else if (mediaThumbPath != null) {
                              opts.inJustDecodeBounds = true;
                              FileInputStream is = new FileInputStream(cacheFileFinal);
                              image = BitmapFactory.decodeStream(is, null, opts);
                              is.close();
                              float photoW = opts.outWidth;
                              float photoH = opts.outHeight;
                              float scaleFactor = Math.max(photoW / 512, photoH / 384);
                              if (scaleFactor < 1) {
                                  scaleFactor = 1;
                              }
                              opts.inJustDecodeBounds = false;
                              int sample = 1;
                              do {
                                  sample *= 2;
                              } while (sample * 2 < scaleFactor);
                              opts.inSampleSize = sample;
                          }
                          synchronized (sync) {
                              if (isCancelled) {
                                  return;
                              }
                          }
  
                          if (cacheImage.filter == null || blur || cacheImage.httpUrl != null) {
                              opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
                          } else {
                              opts.inPreferredConfig = Bitmap.Config.RGB_565;
                          }
                          if (Build.VERSION.SDK_INT < 21) {
                              opts.inPurgeable = true;
                          }
  
                          opts.inDither = false;
                          if (mediaId != null && mediaThumbPath == null) {
                              if (mediaIsVideo) {
                                  image = MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts);
                              } else {
                                  image = MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts);
                              }
                          }
                          if (image == null) {
                              if (useNativeWebpLoaded) {
                                  RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r");
                                  ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length());
  
                                  BitmapFactory.Options bmOptions = new BitmapFactory.Options();
                                  bmOptions.inJustDecodeBounds = true;
                                  Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true);
                                  image = Bitmaps.createBitmap(bmOptions.outWidth, bmOptions.outHeight, Bitmap.Config.ARGB_8888);
  
                                  Utilities.loadWebpImage(image, buffer, buffer.limit(), null, !opts.inPurgeable);
                                  file.close();
                              } else {
                                  if (opts.inPurgeable) {
                                      RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r");
                                      int len = (int) f.length();
                                      byte[] data = bytes != null && bytes.length >= len ? bytes : null;
                                      if (data == null) {
                                          bytes = data = new byte[len];
                                      }
                                      f.readFully(data, 0, len);
                                      f.close();
                                      if (inEncryptedFile) {
                                          EncryptedFileInputStream.decryptBytesWithKeyFile(data, 0, len, cacheImage.encryptionKeyPath);
                                      }
                                      image = BitmapFactory.decodeByteArray(data, 0, len, opts);
                                  } else {
                                      FileInputStream is;
                                      if (inEncryptedFile) {
                                          is = new EncryptedFileInputStream(cacheFileFinal, cacheImage.encryptionKeyPath);
                                      } else {
                                          is = new FileInputStream(cacheFileFinal);
                                      }
                                      image = BitmapFactory.decodeStream(is, null, opts);
                                      is.close();
                                  }
                              }
                          }
                          if (image == null) {
                              if (canDeleteFile && (cacheFileFinal.length() == 0 || cacheImage.filter == null)) {
                                  cacheFileFinal.delete();
                              }
                          } else {
                              boolean blured = false;
                              if (cacheImage.filter != null) {
                                  float bitmapW = image.getWidth();
                                  float bitmapH = image.getHeight();
                                  if (!opts.inPurgeable && w_filter != 0 && bitmapW != w_filter && bitmapW > w_filter + 20) {
                                      float scaleFactor = bitmapW / w_filter;
                                      Bitmap scaledBitmap = Bitmaps.createScaledBitmap(image, (int) w_filter, (int) (bitmapH / scaleFactor), true);
                                      if (image != scaledBitmap) {
                                          image.recycle();
                                          image = scaledBitmap;
                                      }
                                  }
                                  if (image != null && blur && bitmapH < 100 && bitmapW < 100) {
                                      if (image.getConfig() == Bitmap.Config.ARGB_8888) {
                                          Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes());
                                      }
                                      blured = true;
                                  }
                              }
                              if (!blured && opts.inPurgeable) {
                                  Utilities.pinBitmap(image);
                              }
                          }
                      } catch (Throwable ignore) {
  
                      }
                  }
                  Thread.interrupted();
                  onPostExecute(image != null ? new BitmapDrawable(image) : null);
              }
          }
  
          private void onPostExecute(final BitmapDrawable bitmapDrawable) {
              AndroidUtilities.runOnUIThread(new Runnable() {
                  @Override
                  public void run() {
                      BitmapDrawable toSet = null;
                      if (bitmapDrawable instanceof AnimatedFileDrawable) {
                          toSet = bitmapDrawable;
                      } else if (bitmapDrawable != null) {
                          toSet = memCache.get(cacheImage.key);
                          if (toSet == null) {
                              memCache.put(cacheImage.key, bitmapDrawable);
                              toSet = bitmapDrawable;
                          } else {
                              Bitmap image = bitmapDrawable.getBitmap();
                              image.recycle();
                          }
                      }
                      final BitmapDrawable toSetFinal = toSet;
                      imageLoadQueue.postRunnable(new Runnable() {
                          @Override
                          public void run() {
                              cacheImage.setImageAndClear(toSetFinal);
                          }
                      });
                  }
              });
          }
  
          public void cancel() {
              synchronized (sync) {
                  try {
                      isCancelled = true;
                      if (runningThread != null) {
                          runningThread.interrupt();
                      }
                  } catch (Exception e) {
                      //don't promt
                  }
              }
          }
      }
  
      private class CacheImage {
          protected String key;
          protected String url;
          protected String filter;
          protected String ext;
          protected TLObject location;
          protected boolean animatedFile;
          protected boolean selfThumb;
  
          protected File finalFilePath;
          protected File tempFilePath;
          protected File encryptionKeyPath;
  
          protected String httpUrl;
          protected HttpImageTask httpTask;
          protected CacheOutTask cacheTask;
  
          protected ArrayList<ImageReceiver> imageReceiverArray = new ArrayList<>();
          protected ArrayList<String> keys = new ArrayList<>();
  