You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改WhatsApp StickerContentProvider从Firebase加载贴纸的方法

从Firebase Storage加载贴纸到WhatsApp的ContentProvider修改方案

核心修改思路

原StickerContentProvider依赖本地assets目录加载贴纸,我们需要替换为从Firebase获取贴纸元数据(包信息、贴纸URL),并将贴纸文件缓存到应用私有目录后提供给WhatsApp调用。


步骤1:添加Firebase依赖

在build.gradle中加入Firebase Storage和Firestore(用于存储贴纸包元数据)的依赖:

implementation 'com.google.firebase:firebase-storage:20.3.0'
implementation 'com.google.firebase:firebase-firestore:24.10.0'

步骤2:修改ContentProvider核心逻辑

2.1 添加Firebase及缓存相关成员变量

在StickerContentProvider类中添加:

private FirebaseFirestore db;
private FirebaseStorage storage;
private File stickerCacheDir;

2.2 初始化Firebase与缓存目录

重写onCreate方法,初始化Firebase并创建缓存目录,同时调整UriMatcher规则的添加方式:

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

    // 初始化Firebase
    FirebaseApp.initializeApp(getContext());
    db = FirebaseFirestore.getInstance();
    storage = FirebaseStorage.getInstance();
    // 创建应用私有缓存目录
    stickerCacheDir = new File(getContext().getCacheDir(), "wa_stickers");
    if (!stickerCacheDir.exists()) {
        stickerCacheDir.mkdirs();
    }

    // 添加基础Uri匹配规则
    MATCHER.addURI(authority, METADATA, METADATA_CODE);
    MATCHER.addURI(authority, METADATA + "/*", METADATA_CODE_FOR_SINGLE_PACK);
    MATCHER.addURI(authority, STICKERS + "/*", STICKERS_CODE);

    // 异步加载Firebase中的贴纸包数据,加载完成后动态添加文件Uri规则并通知刷新
    loadStickerPacksFromFirebase();

    return true;
}

2.3 实现从Firebase加载贴纸包元数据

添加方法从Firestore读取贴纸包信息(需提前在Firestore中创建sticker_packs集合,文档结构与StickerPack类字段对应,额外添加trayImageUrl和贴纸的imageUrl字段):

private void loadStickerPacksFromFirebase() {
    db.collection("sticker_packs")
            .get()
            .addOnCompleteListener(task -> {
                if (task.isSuccessful() && task.getResult() != null) {
                    stickerPackList = new ArrayList<>();
                    String authority = BuildConfig.CONTENT_PROVIDER_AUTHORITY;
                    for (QueryDocumentSnapshot doc : task.getResult()) {
                        StickerPack pack = doc.toObject(StickerPack.class);
                        stickerPackList.add(pack);
                        // 动态添加托盘图标和贴纸的Uri匹配规则
                        MATCHER.addURI(authority, STICKERS_ASSET + "/" + pack.identifier + "/" + pack.trayImageFile, STICKER_PACK_TRAY_ICON_CODE);
                        for (Sticker sticker : pack.getStickers()) {
                            MATCHER.addURI(authority, STICKERS_ASSET + "/" + pack.identifier + "/" + sticker.imageFileName, STICKERS_ASSET_CODE);
                        }
                    }
                    // 通知ContentResolver数据更新,触发WhatsApp刷新贴纸列表
                    getContext().getContentResolver().notifyChange(AUTHORITY_URI, null);
                } else {
                    Log.e("StickerProvider", "Failed to load sticker packs", task.getException());
                }
            });
}

2.4 修改贴纸包列表获取逻辑

替换原getStickerPackList方法,避免空指针并支持异步加载:

private List<StickerPack> getStickerPackList() {
    if (stickerPackList == null) {
        stickerPackList = new ArrayList<>();
    }
    return stickerPackList;
}

2.5 替换贴纸文件获取逻辑

移除原openAssetFile、getImageAsset、fetchFile中与assets相关的代码,改为从Firebase下载并缓存文件:

@Nullable
@Override
public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
    final int matchCode = MATCHER.match(uri);
    if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) {
        return getCachedStickerFile(uri);
    }
    return super.openAssetFile(uri, mode);
}

private AssetFileDescriptor getCachedStickerFile(Uri uri) throws FileNotFoundException {
    List<String> pathSegments = uri.getPathSegments();
    if (pathSegments.size() != 3) {
        throw new FileNotFoundException("Invalid URI: " + uri);
    }
    String packId = pathSegments.get(1);
    String fileName = pathSegments.get(2);

    // 查找对应文件的Firebase Storage URL
    String fileUrl = null;
    for (StickerPack pack : stickerPackList) {
        if (pack.identifier.equals(packId)) {
            if (pack.trayImageFile.equals(fileName)) {
                fileUrl = pack.trayImageUrl; // 需在StickerPack类中添加该字段
                break;
            }
            for (Sticker sticker : pack.getStickers()) {
                if (sticker.imageFileName.equals(fileName)) {
                    fileUrl = sticker.imageUrl; // 需在Sticker类中添加该字段
                    break;
                }
            }
            if (fileUrl != null) break;
        }
    }

    if (fileUrl == null) {
        throw new FileNotFoundException("File not found: " + uri);
    }

    // 检查缓存文件是否存在,不存在则从Firebase同步下载
    File cacheFile = new File(stickerCacheDir, packId + "_" + fileName);
    if (!cacheFile.exists()) {
        try {
            StorageReference fileRef = storage.getReferenceFromUrl(fileUrl);
            // 同步等待下载完成(ContentProvider调用为同步,必须阻塞等待)
            Tasks.await(fileRef.getFile(cacheFile));
        } catch (ExecutionException | InterruptedException e) {
            throw new FileNotFoundException("Download failed: " + e.getMessage());
        }
    }

    // 返回文件描述符给WhatsApp
    ParcelFileDescriptor pfd = ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.MODE_READ_ONLY);
    return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
}

2.6 清理冗余代码

删除原readContentFile、getImageAsset、fetchFile方法,以及与CONTENT_FILE_NAME相关的常量定义。


步骤3:调整数据模型

修改StickerPack和Sticker类,添加Firebase URL字段:

// StickerPack类新增字段
private String trayImageUrl;

// Sticker类新增字段
private String imageUrl;

步骤4:Firebase配置

  1. Firestore规则:允许读取sticker_packs集合
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /sticker_packs/{pack} {
          allow read: if true;
        }
      }
    }
    
  2. Storage规则:允许读取贴纸文件路径
    rules_version = '2';
    service firebase.storage {
      match /b/{bucket}/o {
        match /stickers/{allPaths=**} {
          allow read: if true;
        }
      }
    }
    

注意事项

  • 同步下载可能导致短时阻塞,建议优化缓存策略(如提前预加载热门贴纸包)
  • 添加网络权限<uses-permission android:name="android.permission.INTERNET"/>到AndroidManifest.xml
  • 贴纸文件需符合WhatsApp规范:格式为WebP(静态)或APNG/GIF(动态),尺寸不超过512x512px,文件大小不超过100KB

内容的提问来源于stack exchange,提问作者Furqan Zaidi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:17:01