How to create sticker content povider for dynamic Android WhatsApp Sticker App

597 Views Asked by At

hey guys I am creating a WhatsApp sticker app that fetch stickers from Firebase cloud Firestore and display it into recycler view. Each recycler view contains list of stickers from each sticker pack. On clicking recycler view I am storing the images from firebase into my app external directory but I dont know how to expose these stickers that are stored inside my app directory to WhatsApp. I tried creating Json file and stored it inside the same directory where I have stored my stickers but it was of no use. I am stuck on what to do next. I am sharing the code that I have tried. Any help is highly appreciated.

MainActivity.java:

public class MainActivity extends AppCompatActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    CollectionReference stickerPacksRef = db.collection("stickers_pack");
    RecyclerView recyclerView = findViewById(R.id.recycler_view);

    recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

    stickerPacksRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
            List<StickerPacks> stickerPacks = new ArrayList<>();
            for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
                String name = documentSnapshot.getString("name");
                List<String> stickerUrls = (List<String>) documentSnapshot.get("stickers");
                StickerPacks stickerPack = new StickerPacks(name, stickerUrls);
                stickerPacks.add(stickerPack);
            }
         
            StickerPacksAdapter adapter = new StickerPacksAdapter(MainActivity.this, stickerPacks);
            recyclerView.setAdapter(adapter);
        }
    });

}

}

StickerContentProvider.java:

 public class StickerContentProvider extends ContentProvider {

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_AGREENMENT_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";
private static final String CONTENT_FILE_NAME = "contents.json";

public static final Uri AUTHORITY_URI = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(BuildConfig.CONTENT_PROVIDER_AUTHORITY).appendPath(StickerContentProvider.METADATA).build();

/**
 * 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);
private 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;

private List<StickerPack> stickerPackList;

@Override
public boolean onCreate() {
    final String authority = BuildConfig.CONTENT_PROVIDER_AUTHORITY;
    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());
    }
    MATCHER.addURI(authority, METADATA, METADATA_CODE);

    MATCHER.addURI(authority, METADATA + "/*", METADATA_CODE_FOR_SINGLE_PACK);
    MATCHER.addURI(authority, STICKERS + "/*", STICKERS_CODE);
    File contentsFile=new File(getContext().getExternalFilesDir(""),"Danish/"+CONTENT_FILE_NAME);
    if(contentsFile.exists()) {
        for (StickerPack stickerPack : getStickerPackList()) {
            MATCHER.addURI(authority, STICKERS_ASSET + "/" + stickerPack.identifier + "/" + stickerPack.trayImageFile, STICKER_PACK_TRAY_ICON_CODE);
            for (Sticker sticker : stickerPack.getStickers()) {
                MATCHER.addURI(authority, STICKERS_ASSET + "/" + stickerPack.identifier + "/" + sticker.imageFileName, 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) {
    final int matchCode = MATCHER.match(uri);
    if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) {
        try {
            return getImageAsset(uri);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    return null;
}


@Override
public String getType(@NonNull Uri uri) {
    final int matchCode = MATCHER.match(uri);
    switch (matchCode) {
        case METADATA_CODE:
            return "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA;
        case METADATA_CODE_FOR_SINGLE_PACK:
            return "vnd.android.cursor.item/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA;
        case STICKERS_CODE:
            return "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + 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) {
    File contentsFile=new File(context.getExternalFilesDir(""),"Danish/"+CONTENT_FILE_NAME);
    if(contentsFile.exists()) {
        try (FileInputStream contentsInputStream = new FileInputStream(contentsFile)) {
            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 (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_AGREENMENT_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, FileNotFoundException {
    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 Uri uri, @NonNull AssetManager am, @NonNull String fileName, @NonNull String identifier) throws FileNotFoundException {
    final File cacheFile = getContext().getExternalFilesDir("");
    final File file = new File(cacheFile, "Danish/"+fileName);


    return new AssetFileDescriptor(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), 0, AssetFileDescriptor.UNKNOWN_LENGTH);

}


@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");
}

}

StickerPacksAdapter.java:

  class StickerPacksAdapter extends RecyclerView.Adapter<StickerPacksAdapter.StickerPackViewHolder> {
private List<StickerPacks> stickerPacks;
private Context context;

public StickerPacksAdapter(Context context, List<StickerPacks> stickerPacks) {
    this.context = context;
    this.stickerPacks = stickerPacks;
}
@NonNull
@Override
public StickerPackViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sticker_pack, parent, false);
    return new StickerPackViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull StickerPackViewHolder holder, int position) {
    StickerPacks stickerPacks = this.stickerPacks.get(position);
    holder.nameTextView.setText(stickerPacks.getName());
    List<String> stickerUrls = stickerPacks.getStickerUrls();
    for (int i = 0; i < stickerUrls.size(); i++) {
        String stickerUrl = stickerUrls.get(i);
        ImageView imageView = holder.stickerImageViews[i];
        if(imageView!=null)
        Glide.with(holder.itemView.getContext()).load(stickerUrl).into(imageView);
    }
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            List<String> stickerUrls = stickerPacks.getStickerUrls();
            String stickerPackName = stickerPacks.getName();
            File directory = new File(context.getExternalFilesDir(""), stickerPackName);
            if (!directory.exists()) {
                directory.mkdirs();
            }
            for (int i = 0; i < stickerUrls.size(); i++) {
                String stickerUrl = stickerUrls.get(i);
                String fileName = "sticker" + i + ".webp"; // you can choose your own file name

                File file = new File(directory, fileName);

                FirebaseStorage.getInstance().getReferenceFromUrl(stickerUrl).getDownloadUrl()
                        .addOnSuccessListener(new OnSuccessListener<Uri>() {
                            @Override
                            public void onSuccess(Uri uri) {
                                String url = uri.toString();
                                new AsyncTask<Void, Void, Void>() {
                                    @SuppressLint("StaticFieldLeak")
                                    @Override
                                    protected Void doInBackground(Void... voids) {
                                        OkHttpClient client = new OkHttpClient();
                                        Request request = new Request.Builder().url(url).build();
                                        try (Response response = client.newCall(request).execute()) {
                                            if (!response.isSuccessful()) {
                                                throw new IOException("Unexpected code " + response);
                                            }
                                            try (InputStream inputStream = response.body().byteStream()) {
                                                FileOutputStream outputStream = new FileOutputStream(file);
                                                byte[] buffer = new byte[4096];
                                                int bytesRead;
                                                while ((bytesRead = inputStream.read(buffer)) != -1) {
                                                    outputStream.write(buffer, 0, bytesRead);
                                                }
                                                outputStream.flush();
                                                outputStream.close();
                                            }
                                            JSONObject json = new JSONObject();
                                            try {
                                                json.put("android_play_store_link", "");
                                                json.put("ios_app_store_link", "");
                                            }catch (JSONException e)
                                            {
                                                e.printStackTrace();
                                            }
                                            JSONArray stickerPacks = new JSONArray();
                                            JSONObject stickerPack = new JSONObject();
                                            try {
                                                stickerPack.put("identifier", stickerPackName.toLowerCase());
                                                stickerPack.put("name", stickerPackName);
                                                stickerPack.put("publisher", stickerPackName);
                                                stickerPack.put("tray_image_file", "sticker0.webp");
                                                stickerPack.put("image_data_version", "1");
                                                stickerPack.put("avoid_cache", false);
                                                stickerPack.put("publisher_email", "");
                                                stickerPack.put("publisher_website", "");
                                                stickerPack.put("privacy_policy_website", "");
                                                stickerPack.put("license_agreement_website", "");
                                                stickerPack.put("animated_sticker_pack", false);
                                            } catch (JSONException e) {
                                                e.printStackTrace();
                                            }

                                            JSONArray stickersArray = new JSONArray();
                                            int i=0;
                                            for (String stickerUrl : stickerUrls) {
                                                JSONObject sticker = new JSONObject();
                                                try {
                                                    sticker.put("image_file", "sticker"+i+".webp");
                                                    sticker.put("emojis", new JSONArray().put("").put("").put("❤️"));
                                                    stickersArray.put(sticker);
                                                    i++;
                                                } catch (JSONException e) {
                                                    e.printStackTrace();
                                                }
                                            }
                                            try {
                                                stickerPack.put("stickers", stickersArray);
                                                stickerPacks.put(stickerPack);
                                                json.put("sticker_packs", stickerPacks);
                                                File file = new File(context.getExternalFilesDir("")+"/"+stickerPackName, "contents.json");
                                                try (FileOutputStream outputStream = new FileOutputStream(file)) {
                                                    Log.d("TAG", "entered: ");
                                                    outputStream.write(json.toString().getBytes());
                                                    outputStream.close();
                                                } catch (IOException e) {
                                                    e.printStackTrace();
                                                }
                                            } catch (JSONException e) {
                                                e.printStackTrace();
                                            }
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                        return null;
                                    }
                                }.execute();
                            }
                        });
            }

            Intent intent = new Intent();
            intent.setAction("com.whatsapp.intent.action.ENABLE_STICKER_PACK");
            intent.putExtra("sticker_pack_id", stickerPackName);
            intent.putExtra("sticker_pack_authority", BuildConfig.CONTENT_PROVIDER_AUTHORITY);
            intent.putExtra("sticker_pack_name", stickerPackName);
            try {
                context.startActivity(intent);
            } catch (ActivityNotFoundException e) {
                e.printStackTrace();    }
        }
        });
}

@Override
public int getItemCount() {
    return stickerPacks.size();
}

public class StickerPackViewHolder extends RecyclerView.ViewHolder {
    private TextView nameTextView;
    private ImageView[] stickerImageViews;

    public StickerPackViewHolder(@NonNull View itemView) {
        super(itemView);
        nameTextView = itemView.findViewById(R.id.sticker_pack_name_text_view);
        stickerImageViews = new ImageView[30];
        for (int i = 0; i < 4; i++) {
            stickerImageViews[i] = itemView.findViewById(getStickerImageViewId(i));
        }
    }

    private int getStickerImageViewId(int index) {
        switch (index) {
            case 0:
                return R.id.sticker1;
            case 1:
                return R.id.sticker2;
            case 2:
                return R.id.sticker3;
            case 3:
                return R.id.sticker4;
            // and so on, up to 30
            default:
                throw new IllegalArgumentException("Invalid sticker image view index: " + index);
        }
    }

}

}

0

There are 0 best solutions below