content: package im.actor.core.utils;
  
  import android.content.Context;
  import android.database.Cursor;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.net.Uri;
  import android.os.Environment;
  import android.provider.MediaStore;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import im.actor.core.viewmodel.GalleryVM;
  import im.actor.runtime.Log;
  import im.actor.runtime.actors.Actor;
  import im.actor.runtime.collections.SparseArray;
  
  public class GalleryScannerActor extends Actor {
  
      public static final String TAG = "GALLERY_SCANNER";
      public static final int CHECK_NEW_DELAY = 1000;
      public static final int SCAN_DELAY = 100;
      Context context;
      Uri uri;
      Cursor cursor;
      int offset = 0;
      int column_index_data, column_index_folder_name, column_index_date, column_index_id, column_index_folder_id;
  
      ArrayList<String> listOfAllImages = new ArrayList<>();
      ArrayList<String> newMedia = new ArrayList<>();
  
      String absolutePathOfImage = null;
      GalleryVM galleryVM;
  
      private static final int SCAN_COUNT = 10;
  
      private boolean visible = false;
      private boolean scanned = false;
      private String[] projection;
      private boolean inited = false;
      private Bitmap bitmap;
  
      public GalleryScannerActor(Context context, GalleryVM galleryVM) {
          this.context = context;
          this.galleryVM = galleryVM;
      }
  
      @Override
      public void preStart() {
          uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
      }
  
      private void initScan() {
          Log.d(TAG, "init scan");
          projection = new String[]{MediaStore.Images.Media.DATA,
                  MediaStore.Images.Media._ID,
                  MediaStore.Images.Media.BUCKET_ID,
                  MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
                  MediaStore.Images.Media.DATE_TAKEN};
  
          cursor = getQuery();
  
          if (cursor != null && cursor.getCount() > 0) {
              column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
              column_index_id = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
              column_index_folder_id = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID);
              column_index_date = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN);
              column_index_folder_name = cursor
                      .getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
  
  
              Log.d(TAG, "init scan ok - starting scan iterations");
              inited = true;
              self().send(new Scan());
          } else {
              Log.d(TAG, "init scan - no media, let's check it in " + CHECK_NEW_DELAY);
              if (visible) {
                  schedule(new InitScan(), CHECK_NEW_DELAY);
              }
          }
  
      }
  
      private Cursor getQuery() {
          try {
              return context.getContentResolver().query(uri, projection, null,
                      null, MediaStore.Images.Media.DATE_TAKEN + " DESC");
          } catch (Exception e) {
              return null;
          }
      }
  
      private void scan() {
  
          Log.d(TAG, "scan");
  
          for (int i = 0; i <= SCAN_COUNT && cursor.moveToNext(); i++) {
              absolutePathOfImage = cursor.getString(column_index_data);
              listOfAllImages.add(absolutePathOfImage);
          }
  
          offset += SCAN_COUNT;
          scanned = offset >= cursor.getCount();
  
          //
          // Update VM after 1st batch, every 100 photos, when scanned to end
          //
          if (offset == SCAN_COUNT || offset % 100 == 0 || scanned) {
              Log.d(TAG, "scan - update vm, offset - " + offset);
              galleryVM.getGalleryMediaPath().change(new ArrayList<String>(listOfAllImages));
          }
  
          if (visible) {
              if (!scanned) {
                  schedule(new Scan(), SCAN_DELAY);
              } else {
                  schedule(new CheckNew(), CHECK_NEW_DELAY);
              }
          }
      }
  
      private void checkNew() {
          Log.d(TAG, "checkNew");
          cursor = getQuery();
          newMedia.clear();
          boolean firstCycle = true;
          while (cursor != null && cursor.moveToNext()) {
              absolutePathOfImage = cursor.getString(column_index_data);
              if (!listOfAllImages.contains(absolutePathOfImage)) {
                  if (firstCycle) {
                      firstCycle = false;
                      bitmap = BitmapFactory.decodeFile(absolutePathOfImage);
                      if (bitmap != null) {
                          bitmap.recycle();
                          newMedia.add(absolutePathOfImage);
                          Log.d(TAG, "checkNew - new media");
  
                      } else {
                          Log.d(TAG, "checkNew - new media writing, breaking for wait");
  
                          break;
                      }
                  } else {
                      newMedia.add(absolutePathOfImage);
                  }
  
              } else {
                  Log.d(TAG, "checkNew - found old media, break");
  
                  break;
              }
          }
          if (newMedia.size() > 0) {
              listOfAllImages.addAll(0, newMedia);
              galleryVM.getGalleryMediaPath().change(new ArrayList<>(listOfAllImages));
              Log.d(TAG, "checkNew - new media add - " + newMedia.size());
          }
          if (visible) {
              Log.d(TAG, "checkNew - visible, schedule next check in " + CHECK_NEW_DELAY);
              schedule(new CheckNew(), CHECK_NEW_DELAY);
          }
      }
  
      @Override
      public void onReceive(Object message) {
          if (message instanceof Scan) {
              scan();
          } else if (message instanceof InitScan) {
              initScan();
          } else if (message instanceof CheckNew) {
              checkNew();
          } else if (message instanceof Show) {
              visible = true;
              if (scanned) {
                  self().send(new CheckNew());
              } else {
                  if (!inited) {
                      self().send(new InitScan());
                  }
              }
          } else if (message instanceof Hide) {
              visible = false;
          }
      }
  
      private static class Scan {
  
      }
  
      private static class InitScan {
  
      }
  
      private static class CheckNew {
  
      }
  
      public static class Show {
  
      }
  
      public static class Hide {
  
      }
  }
  