content: 
  package com.indiagenisys.whatsapp_stickers_handler;
  
  import android.content.ContentProvider;
  import android.content.ContentValues;
  import android.content.Context;
  import android.content.UriMatcher;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.ProviderInfo;
  import android.content.res.AssetFileDescriptor;
  import android.content.res.AssetManager;
  import android.database.Cursor;
  import android.database.MatrixCursor;
  import android.net.Uri;
  import android.os.ParcelFileDescriptor;
  import android.text.TextUtils;
  import android.util.Log;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  
  import org.json.JSONException;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Objects;
  
  import static com.indiagenisys.whatsapp_stickers_handler.ConfigFileManager.CONTENT_FILE_NAME;
  
  public class StickerContentProvider extends ContentProvider {
  
      /**
       * Do not change the strings listed below, as these are used by WhatsApp. And changing these will break the interface between sticker app and WhatsApp.
       */
      public static final String STICKER_PACK_IDENTIFIER_IN_QUERY = "sticker_pack_identifier";
      public static final String STICKER_PACK_NAME_IN_QUERY = "sticker_pack_name";
      public static final String STICKER_PACK_PUBLISHER_IN_QUERY = "sticker_pack_publisher";
      public static final String STICKER_PACK_ICON_IN_QUERY = "sticker_pack_icon";
      public static final String ANDROID_APP_DOWNLOAD_LINK_IN_QUERY = "android_play_store_link";
      public static final String IOS_APP_DOWNLOAD_LINK_IN_QUERY = "ios_app_download_link";
      public static final String PUBLISHER_EMAIL = "sticker_pack_publisher_email";
      public static final String PUBLISHER_WEBSITE = "sticker_pack_publisher_website";
      public static final String PRIVACY_POLICY_WEBSITE = "sticker_pack_privacy_policy_website";
      public static final String LICENSE_AGREEMENT_WEBSITE = "sticker_pack_license_agreement_website";
      public static final String IMAGE_DATA_VERSION = "image_data_version";
      public static final String AVOID_CACHE = "whatsapp_will_not_cache_stickers";
      public static final String ANIMATED_STICKER_PACK = "animated_sticker_pack";
  
      public static final String STICKER_FILE_NAME_IN_QUERY = "sticker_file_name";
      public static final String STICKER_FILE_EMOJI_IN_QUERY = "sticker_emoji";
  
      /**
       * Do not change the values in the UriMatcher because otherwise, WhatsApp will not be able to fetch the stickers from the ContentProvider.
       */
      private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
      public static final String METADATA = "metadata";
      private static final int METADATA_CODE = 1;
  
      private static final int METADATA_CODE_FOR_SINGLE_PACK = 2;
  
      static final String STICKERS = "stickers";
      private static final int STICKERS_CODE = 3;
  
      static final String STICKERS_ASSET = "stickers_asset";
      private static final int STICKERS_ASSET_CODE = 4;
  
      private static final int STICKER_PACK_TRAY_ICON_CODE = 5;
  
      public List<StickerPack> stickerPackList;
  
      @Override
      public boolean onCreate() {
          final String authority = WhatsappStickersHandlerPlugin.getContentProviderAuthority(getContext());
          if (!authority.startsWith(Objects.requireNonNull(getContext()).getPackageName())) {
              throw new IllegalStateException("your authority (" + authority + ") for the content provider should start with your package name: " + getContext().getPackageName());
          }
  
          // generate config JSON file if not exists
          try {
              ConfigFileManager.generateConfigFile(getContext());
          } catch (JSONException e) {
              e.printStackTrace();
          }
  
          //the call to get the metadata for the sticker packs.
          MATCHER.addURI(authority, METADATA, METADATA_CODE);
  
          //the call to get the metadata for single sticker pack. * represent the identifier
          MATCHER.addURI(authority, METADATA + "/*", METADATA_CODE_FOR_SINGLE_PACK);
  
          //gets the list of stickers for a sticker pack, * respresent the identifier.
          MATCHER.addURI(authority, STICKERS + "/*", STICKERS_CODE);
  
          // Gets the an asset from a sticker pack
          MATCHER.addURI(authority, STICKERS_ASSET + "/*/*", STICKERS_ASSET_CODE);
  
          return true;
      }
  
      @Override
      public Cursor query(@NonNull Uri uri, @Nullable String[] projection, String selection,
                          String[] selectionArgs, String sortOrder) {
          final int code = MATCHER.match(uri);
          if (code == METADATA_CODE) {
              return getPackForAllStickerPacks(uri);
          } else if (code == METADATA_CODE_FOR_SINGLE_PACK) {
              return getCursorForSingleStickerPack(uri);
          } else if (code == STICKERS_CODE) {
              return getStickersForAStickerPack(uri);
          } else {
              throw new IllegalArgumentException("Unknown URI: " + uri);
          }
      }
  
      @Nullable
      @Override
      public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) {
  //        if (uri != null)
  //            Log.e("URIIIIIIIIII", uri.toString());
  //        else {
  //            Log.e("URIIIIIIIIII", "NULLLLLLLL");
  //        }
          final int matchCode = MATCHER.match(uri);
          if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) {
              return getImageAsset(uri);
          }
          return null;
      }
  
  
      @Override
      public String getType(@NonNull Uri uri) {
          final Context context = getContext();
          assert context != null;
  
          final int matchCode = MATCHER.match(uri);
          switch (matchCode) {
              case METADATA_CODE:
                  return "vnd.android.cursor.dir/vnd." + WhatsappStickersHandlerPlugin.getContentProviderAuthority(context) + "." + METADATA;
              case METADATA_CODE_FOR_SINGLE_PACK:
                  return "vnd.android.cursor.item/vnd." + WhatsappStickersHandlerPlugin.getContentProviderAuthority(context) + "." + METADATA;
              case STICKERS_CODE:
                  return "vnd.android.cursor.dir/vnd." + WhatsappStickersHandlerPlugin.getContentProviderAuthority(context) + "." + STICKERS;
              case STICKERS_ASSET_CODE:
                  return "image/webp";
              case STICKER_PACK_TRAY_ICON_CODE:
                  return "image/png";
              default:
                  throw new IllegalArgumentException("Unknown URI: " + uri);
          }
      }
  
      private synchronized void readContentFile(@NonNull Context context) {
          final File file = new File(ConfigFileManager.getConfigFilePath(context));
          try (InputStream contentsInputStream = new FileInputStream(file)) {
              stickerPackList = ContentFileParser.parseStickerPacks(contentsInputStream);
          } catch (IOException | IllegalStateException e) {
              throw new RuntimeException(CONTENT_FILE_NAME + " file has some issues: " + e.getMessage(), e);
          }
      }
  
      private List<StickerPack> getStickerPackList() {
          //if (stickerPackList == null) {
          readContentFile(Objects.requireNonNull(getContext()));
          //}
          return stickerPackList;
      }
  
      private Cursor getPackForAllStickerPacks(@NonNull Uri uri) {
          return getStickerPackInfo(uri, getStickerPackList());
      }
  
      private Cursor getCursorForSingleStickerPack(@NonNull Uri uri) {
          final String identifier = uri.getLastPathSegment();
          for (StickerPack stickerPack : getStickerPackList()) {
              if (Objects.requireNonNull(identifier).equals(stickerPack.identifier)) {
                  return getStickerPackInfo(uri, Collections.singletonList(stickerPack));
              }
          }
          return getStickerPackInfo(uri, new ArrayList<>());
      }
  
      @NonNull
      private Cursor getStickerPackInfo(@NonNull Uri uri, @NonNull List<StickerPack> stickerPackList) {
          MatrixCursor cursor = new MatrixCursor(
                  new String[]{
                          STICKER_PACK_IDENTIFIER_IN_QUERY,
                          STICKER_PACK_NAME_IN_QUERY,
                          STICKER_PACK_PUBLISHER_IN_QUERY,
                          STICKER_PACK_ICON_IN_QUERY,
                          ANDROID_APP_DOWNLOAD_LINK_IN_QUERY,
                          IOS_APP_DOWNLOAD_LINK_IN_QUERY,
                          PUBLISHER_EMAIL,
                          PUBLISHER_WEBSITE,
                          PRIVACY_POLICY_WEBSITE,
                          LICENSE_AGREEMENT_WEBSITE,
                          IMAGE_DATA_VERSION,
                          AVOID_CACHE,
                          ANIMATED_STICKER_PACK
                  });
          for (StickerPack stickerPack : stickerPackList) {
              MatrixCursor.RowBuilder builder = cursor.newRow();
              builder.add(stickerPack.identifier);
              builder.add(stickerPack.name);
              builder.add(stickerPack.publisher);
              builder.add(stickerPack.trayImageFile);
              builder.add(stickerPack.androidPlayStoreLink);
              builder.add(stickerPack.iosAppStoreLink);
              builder.add(stickerPack.publisherEmail);
              builder.add(stickerPack.publisherWebsite);
              builder.add(stickerPack.privacyPolicyWebsite);
              builder.add(stickerPack.licenseAgreementWebsite);
              builder.add(stickerPack.imageDataVersion);
              builder.add(stickerPack.avoidCache ? 1 : 0);
              builder.add(stickerPack.animatedStickerPack ? 1 : 0);
          }
          cursor.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri);
          return cursor;
      }
  
      @NonNull
      private Cursor getStickersForAStickerPack(@NonNull Uri uri) {
          final String identifier = uri.getLastPathSegment();
          MatrixCursor cursor = new MatrixCursor(new String[]{STICKER_FILE_NAME_IN_QUERY, STICKER_FILE_EMOJI_IN_QUERY});
          for (StickerPack stickerPack : getStickerPackList()) {
              if (identifier.equals(stickerPack.identifier)) {
                  for (Sticker sticker : stickerPack.getStickers()) {
                      cursor.addRow(new Object[]{sticker.imageFileName, TextUtils.join(",", sticker.emojis)});
                  }
              }
          }
          cursor.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri);
          return cursor;
      }
  
      private AssetFileDescriptor getImageAsset(Uri uri) throws IllegalArgumentException {
          AssetManager am = Objects.requireNonNull(getContext()).getAssets();
          final List<String> pathSegments = uri.getPathSegments();
          if (pathSegments.size() != 3) {
              throw new IllegalArgumentException("path segments should be 3, uri is: " + uri);
          }
          String fileName = pathSegments.get(pathSegments.size() - 1);
          final String identifier = pathSegments.get(pathSegments.size() - 2);
          if (TextUtils.isEmpty(identifier)) {
              throw new IllegalArgumentException("identifier is empty, uri: " + uri);
          }
          if (TextUtils.isEmpty(fileName)) {
              throw new IllegalArgumentException("file name is empty, uri: " + uri);
          }
          //making sure the file that is trying to be fetched is in the list of stickers.
          for (StickerPack stickerPack : getStickerPackList()) {
              if (identifier.equals(stickerPack.identifier)) {
                  if (fileName.equals(stickerPack.trayImageFile)) {
                      return fetchFile(uri, am, fileName, identifier);
                  } else {
                      for (Sticker sticker : stickerPack.getStickers()) {
                          if (fileName.equals(sticker.imageFileName)) {
                              return fetchFile(uri, am, fileName, identifier);
                          }
                      }
                  }
              }
          }
          return null;
      }
  
      private AssetFileDescriptor fetchFile(@NonNull final Uri uri, @NonNull final AssetManager am,
                                            @NonNull final String fileName, @NonNull final String identifier) {
          return (fileName.contains("_SSP_")) ? fetchAssetFile(uri, am, fileName, identifier) : fetchNonAssetFile(uri, fileName, identifier);
      }
  
      private AssetFileDescriptor fetchNonAssetFile(final Uri uri, final String fileName, final String identifier) {
          try {
              String fname = fileName.replace("._.", File.separator);
              final File file = new File(fname);
              return new AssetFileDescriptor(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), 0,
                      AssetFileDescriptor.UNKNOWN_LENGTH);
          } catch (final IOException e) {
              Log.e(Objects.requireNonNull(getContext()).getPackageName(),
                      "IOException when getting asset file, uri:" + uri, e);
              return null;
          }
      }
  
      private AssetFileDescriptor fetchAssetFile(@NonNull final Uri uri, @NonNull final AssetManager am,
                                                 @NonNull final String fileName, @NonNull final String identifier) {
          try {
              String fname = fileName.replace("_SSP_", File.separator);
              String f = "flutter_assets/" + fname;
              return am.openFd(f);
          } catch (final IOException e) {
              Log.e(Objects.requireNonNull(getContext()).getPackageName(),
                      "IOException when getting asset file, uri:" + uri, e);
              return null;
          }
      }
  
  
      @Override
      public int delete(@NonNull Uri uri, @Nullable String selection, String[] selectionArgs) {
          throw new UnsupportedOperationException("Not supported");
      }
  
      @Override
      public Uri insert(@NonNull Uri uri, ContentValues values) {
          throw new UnsupportedOperationException("Not supported");
      }
  
      @Override
      public int update(@NonNull Uri uri, ContentValues values, String selection,
                        String[] selectionArgs) {
          throw new UnsupportedOperationException("Not supported");
      }
  }
  