Selaa lähdekoodia

增加firebase & kochava

zk 1 vuosi sitten
vanhempi
commit
74258c5766

+ 16 - 0
app/build.gradle

@@ -212,4 +212,20 @@ dependencies {
     //google结算库
     implementation "com.android.billingclient:billing:$rootProject.billing_version"
 
+
+    //Kochava
+    dependencies {
+        def kochava_tracker_version = "4.3.0"
+        implementation("com.kochava.tracker:tracker:$kochava_tracker_version") // Required
+        implementation("com.kochava.tracker:events:$kochava_tracker_version") // Optional
+        implementation("com.kochava.tracker:engagement:$kochava_tracker_version") // Optional
+        implementation("com.kochava.tracker:datapointnetwork:$kochava_tracker_version") // Optional
+        implementation("com.kochava.tracker:legacyreferrer:$kochava_tracker_version") // Optional
+    }
+
+    //firebase
+    implementation platform('com.google.firebase:firebase-bom:30.4.1')
+    implementation 'com.google.firebase:firebase-crashlytics'
+    implementation 'com.google.firebase:firebase-analytics'
+    implementation 'com.google.firebase:firebase-messaging'
 }

+ 29 - 0
app/google-services.json

@@ -0,0 +1,29 @@
+{
+  "project_info": {
+    "project_number": "280685621253",
+    "project_id": "ai-vocie",
+    "storage_bucket": "ai-vocie.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:280685621253:android:fdc3cfb6d988444ec32c6c",
+        "android_client_info": {
+          "package_name": "com.funnyvoiceai.clonevoice"
+        }
+      },
+      "oauth_client": [],
+      "api_key": [
+        {
+          "current_key": "AIzaSyC4jOavCaZJyf0OH2HF6cKx7CDY8_btn6s"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": []
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 1 - 0
app/src/main/java/com/atmob/voiceai/data/consts/ErrorCode.java

@@ -2,4 +2,5 @@ package com.atmob.voiceai.data.consts;
 
 public interface ErrorCode {
 
+    int ERROR_NOT_SUBSCRIBED = 4002;
 }

+ 40 - 0
app/src/main/java/com/atmob/voiceai/data/consts/EventId.java

@@ -0,0 +1,40 @@
+package com.atmob.voiceai.data.consts;
+
+
+public interface EventId {
+
+    String guide_001 = "guide_001"; //   引导页1	进入页面
+    String guide_002 = "guide_002"; //   引导页2	进入页面
+    String start_001 = "start_001"; //   开始页	进入页面
+    String startbutton_001 = "startbutton_001"; //   开始页【Start have fun】按钮	按钮点击
+    String hometab_001 = "hometab_001"; //   Voice AI页	进入页面
+    String homeprobutton_001 = "homeprobutton_001"; //   顶部栏目PRO按钮	按钮点击
+    String homegetmore_001 = "homegetmore_001"; //   【Voice AI】页get more	按钮点击
+    String homelistclick_001 = "homelistclick_001"; //   角色语音试播	按钮点击,随便点哪个头像都可以
+    String homeadd_001 = "homeadd_001"; //   【Voice AI】页+add	按钮点击
+    String homegenerate_001 = "homegenerate_001"; //   【Voice AI】页generate按钮	按钮点击
+    String generating_001 = "generating_001"; //   AI voice生成中页	进入页面
+    String generateok_001 = "generateok_001"; //   AI voice生成完成页	进入页面
+    String generateoklist_001 = "generateoklist_001"; //   AI voice生成完成页角色点击	点击选中,随便点哪个头像都可以
+    String generateoksave_001 = "generateoksave_001"; //   AI voice生成完成页-save按钮	按钮点击
+    String generateokshare_001 = "generateokshare_001"; //   AI voice生成完成页-share按钮	按钮点击
+    String cloneundone_001 = "cloneundone_001"; //   clone voice页-未完成克隆	进入页面
+    String clonedone_001 = "clonedone_001"; //   clone voice页-已完成克隆	进入页面
+    String cloneupload_001 = "cloneupload_001"; //   clone voice页-upload file	按钮点击
+    String clonerecord_001 = "clonerecord_001"; //   clone voice页-record	按钮点击
+    String cloneuse_001 = "cloneuse_001"; //   clone voice页-use clone voice	按钮点击
+    String clonedelete_001 = "clonedelete_001"; //   clone voice页-delete	按钮点击
+    String history_001 = "history_001"; //   history页	进入页面
+    String historyclick_001 = "historyclick_001"; //   history页-点击列表数据项	按钮点击
+    String purchase_001 = "purchase_001"; //   订阅页面	进入页面
+    String purchaseclose_001 = "purchaseclose_001"; //   订阅页面左上角X按钮	按钮点击
+    String purchaseweek_001 = "purchaseweek_001"; //   订阅页面-订阅项1	点击选中
+    String purchasemonth_001 = "purchasemonth_001"; //   订阅页面-订阅项2	点击选中
+    String purchaseyear_001 = "purchaseyear_001"; //   订阅页面-订阅项3	点击选中
+    String purchaseweeksub_001 = "purchaseweeksub_001"; //   订阅页面-订阅项1-Subscribe	按钮点击
+    String purchasemonthsub_001 = "purchasemonthsub_001"; //   订阅页面-订阅项2-Subscribe	按钮点击
+    String purchaseyearsub_001 = "purchaseyearsub_001"; //   订阅页面-订阅项3-Subscribe 按钮点击
+    String retainstay_001 = "retainstay_001"; //   挽留弹窗-stay按钮	按钮点击
+    String retainmiss_001 = "retainmiss_001"; //   挽留弹窗-miss	按钮点击
+
+}

+ 5 - 0
app/src/main/java/com/atmob/voiceai/data/repositories/VoiceAIRepository.java

@@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData;
 
 import com.atmob.app.lib.handler.RxHttpHandler;
 import com.atmob.app.lib.livedata.SingleLiveEvent;
+import com.atmob.common.logging.AtmobLog;
 import com.atmob.common.runtime.ContextUtil;
 import com.atmob.voiceai.R;
 import com.atmob.voiceai.data.api.AtmobApi;
@@ -19,6 +20,7 @@ import com.atmob.voiceai.data.api.response.TextToSpeechResponse;
 import com.atmob.voiceai.data.api.response.VoiceInfoResponse;
 import com.atmob.voiceai.data.api.response.VoiceListResponse;
 import com.atmob.voiceai.data.api.response.VoiceTypeResponse;
+import com.atmob.voiceai.data.consts.ErrorCode;
 import com.atmob.voiceai.utils.BoxingUtil;
 import com.atmob.voiceai.utils.ThreePair;
 
@@ -187,6 +189,9 @@ public class VoiceAIRepository {
                 if (throwable instanceof RxHttpHandler.ServerErrorException) {
                     RxHttpHandler.ServerErrorException serverErrorException = (RxHttpHandler.ServerErrorException) throwable;
                     textToSpeechState.setValue(new ThreePair<>(TextToSpeechState.ERROR, serverErrorException.getMsg(), null));
+                    if (serverErrorException.getCode() == ErrorCode.ERROR_NOT_SUBSCRIBED) {
+                        memberRepository.refreshUserData();
+                    }
                 } else {
                     textToSpeechState.setValue(new ThreePair<>(TextToSpeechState.ERROR, ContextUtil.getContext().getString(R.string.generate_error), null));
                 }

+ 89 - 0
app/src/main/java/com/atmob/voiceai/handlers/EventHandler.java

@@ -0,0 +1,89 @@
+package com.atmob.voiceai.handlers;
+
+import android.app.Application;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+
+import com.atmob.common.runtime.ContextUtil;
+import com.atmob.voiceai.BuildConfig;
+import com.atmob.voiceai.sdk.firebase.FirebaseHelper;
+import com.atmob.voiceai.utils.ToastUtil;
+
+import java.util.Set;
+
+import dagger.hilt.EntryPoint;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EntryPointAccessors;
+import dagger.hilt.components.SingletonComponent;
+
+public final class EventHandler {
+
+    private static final boolean isToastReport = false;
+
+    private static volatile EventHandler INSTANCE;
+    private final FirebaseHelper firebaseHelper;
+    private static Handler MAIN_HANDLER;
+
+    static {
+        if (isToastReport) {
+            MAIN_HANDLER = new Handler(Looper.getMainLooper());
+        }
+    }
+
+    private EventHandler() {
+        Application application = ContextUtil.getApplication();
+        EventHandlerEntryPoint entryPoint = EntryPointAccessors.fromApplication(application, EventHandlerEntryPoint.class);
+        firebaseHelper = entryPoint.firebaseHelper();
+    }
+
+    private static EventHandler getInstance() {
+        if (INSTANCE == null) {
+            synchronized (EventHandler.class) {
+                if (INSTANCE == null) {
+                    INSTANCE = new EventHandler();
+                }
+            }
+        }
+        return INSTANCE;
+    }
+
+    public static void report(String eventId) {
+        report(eventId, null);
+    }
+
+    public static void report(String eventId, Bundle params) {
+        if (TextUtils.isEmpty(eventId)) {
+            return;
+        }
+        if (isToastReport) {
+            MAIN_HANDLER.post(() -> {
+                StringBuilder sb = new StringBuilder();
+                sb.append(eventId);
+                if (params != null) {
+                    sb.append(" ");
+                    Set<String> keySet = params.keySet();
+                    for (String key : keySet) {
+                        sb.append(key);
+                        sb.append(":");
+                        sb.append(params.get(key));
+                        sb.append(" ");
+                    }
+                }
+                ToastUtil.show(sb.toString(), ToastUtil.LENGTH_SHORT);
+            });
+        }
+        if (BuildConfig.DEBUG) {
+            return;
+        }
+        getInstance().firebaseHelper.reportEvent(eventId, params);
+    }
+
+
+    @EntryPoint
+    @InstallIn(SingletonComponent.class)
+    interface EventHandlerEntryPoint {
+        FirebaseHelper firebaseHelper();
+    }
+}

+ 101 - 0
app/src/main/java/com/atmob/voiceai/sdk/firebase/FirebaseHelper.java

@@ -0,0 +1,101 @@
+package com.atmob.voiceai.sdk.firebase;
+
+import android.annotation.SuppressLint;
+import android.app.Application;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.atmob.common.logging.AtmobLog;
+import com.atmob.voiceai.data.api.AtmobApi;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.analytics.FirebaseAnalytics;
+import com.google.firebase.messaging.FirebaseMessaging;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import atmob.reactivex.rxjava3.internal.functions.Functions;
+import atmob.rxjava.utils.RxJavaUtil;
+
+@Singleton
+public class FirebaseHelper {
+
+    private static final String TAG = FirebaseHelper.class.getSimpleName();
+    private FirebaseApp firebaseApp;
+    private final FirebaseAnalytics firebaseAnalytics;
+    private final ArrayList<OnMessagingTokenChangeListener> onMessagingTokenChangeListeners;
+    private final AtmobApi atmobApi;
+
+    @Inject
+    public FirebaseHelper(Application application, AtmobApi atmobApi) {
+        firebaseApp = FirebaseApp.initializeApp(application);
+        onMessagingTokenChangeListeners = new ArrayList<>(2);
+        firebaseAnalytics = FirebaseAnalytics.getInstance(application);
+        this.atmobApi = atmobApi;
+
+        getMessagingToken(this::uploadFirebaseToken);
+    }
+
+
+    private void uploadFirebaseToken(String token) {
+
+    }
+
+    public void getMessagingToken(OnSuccessListener<String> onSuccessListener) {
+        FirebaseMessaging.getInstance().getToken()
+                .addOnSuccessListener(s -> {
+                    if (onSuccessListener != null) {
+                        onSuccessListener.onSuccess(s);
+                    }
+                });
+    }
+
+    void onMessagingTokenChanged(String newToken) {
+        for (OnMessagingTokenChangeListener onMessagingTokenChangeListener : onMessagingTokenChangeListeners) {
+            onMessagingTokenChangeListener.onNewToken(newToken);
+        }
+        uploadFirebaseToken(newToken);
+    }
+
+    public void reportEvent(@NonNull String eventName, @Nullable Bundle params) {
+        firebaseAnalytics.logEvent(eventName, params);
+    }
+
+    public void addOnMessagingTokenChangeListener(OnMessagingTokenChangeListener listener) {
+        if (listener != null) {
+            onMessagingTokenChangeListeners.add(listener);
+        }
+    }
+
+    public void removeOnMessagingTokenChangeListener(OnMessagingTokenChangeListener listener) {
+        onMessagingTokenChangeListeners.remove(listener);
+    }
+
+    public void unsubscribeTopic(String topic) {
+        if (topic == null || TextUtils.isEmpty(topic.trim())) {
+            return;
+        }
+        FirebaseMessaging.getInstance().unsubscribeFromTopic(topic)
+                .addOnSuccessListener(unused -> AtmobLog.d(TAG, "unsubscribeTopic(%s) onSuccess.", topic))
+                .addOnFailureListener(e -> AtmobLog.e(TAG, "unsubscribeTopic(%s) onFailure.", e, topic));
+    }
+
+    public void subscribeTopic(String topic) {
+        if (topic == null || TextUtils.isEmpty(topic.trim())) {
+            return;
+        }
+        FirebaseMessaging.getInstance().subscribeToTopic(topic)
+                .addOnSuccessListener(unused -> AtmobLog.d(TAG, "subscribeTopic(%s) onSuccess.", topic))
+                .addOnFailureListener(e -> AtmobLog.e(TAG, "subscribeTopic(%s) onFailure.", e, topic));
+    }
+
+    public interface OnMessagingTokenChangeListener {
+        void onNewToken(String token);
+    }
+}

+ 103 - 0
app/src/main/java/com/atmob/voiceai/sdk/kochava/KochavaHelper.java

@@ -0,0 +1,103 @@
+package com.atmob.voiceai.sdk.kochava;
+
+import android.app.Application;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.atmob.common.runtime.ContextUtil;
+import com.atmob.common.runtime.ProcessUtil;
+import com.atmob.voiceai.BuildConfig;
+import com.atmob.voiceai.sdk.firebase.FirebaseHelper;
+import com.kochava.tracker.Tracker;
+import com.kochava.tracker.attribution.InstallAttributionApi;
+import com.kochava.tracker.engagement.Engagement;
+import com.kochava.tracker.log.LogLevel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class KochavaHelper {
+
+    private static final String APP_GUID = "kojunk-cleaner-psanv5l5";
+    private static final List<AttributionResultCallback> callbacks = new ArrayList<>(5);
+    private static final Handler handler = new Handler(Looper.getMainLooper());
+    private static Boolean attributed;
+
+    public static void init(Application application, FirebaseHelper firebaseHelper) {
+        if (BuildConfig.DEBUG) {
+            Tracker.getInstance().setLogLevel(LogLevel.TRACE);
+        } else {
+            Tracker.getInstance().setLogLevel(LogLevel.INFO);
+        }
+        Tracker.getInstance().startWithAppGuid(application, APP_GUID);
+
+        firebaseHelper.getMessagingToken(token -> Engagement.getInstance().registerPushToken(token));
+        firebaseHelper.addOnMessagingTokenChangeListener(token -> Engagement.getInstance().registerPushToken(token));
+
+        // This callback handler will fire on every launch. Optionally check the isRetrieved if you wish to only parse these results once.
+        InstallAttributionApi currentInstallAttribution = Tracker.getInstance().getInstallAttribution();
+        if (!currentInstallAttribution.isRetrieved()) {
+            Tracker.getInstance().retrieveInstallAttribution(KochavaHelper::onAttributionResult);
+        } else {
+            onAttributionResult(currentInstallAttribution);
+        }
+    }
+
+    private static void onAttributionResult(InstallAttributionApi installAttributionApi) {
+        synchronized (KochavaHelper.class) {
+            attributed = installAttributionApi.isAttributed();
+            Iterator<AttributionResultCallback> iterator = callbacks.iterator();
+            while (iterator.hasNext()) {
+                AttributionResultCallback callback = iterator.next();
+                if (callback == null) {
+                    iterator.remove();
+                    continue;
+                }
+                handler.post(() -> callback.onResult(attributed));
+                iterator.remove();
+            }
+        }
+    }
+
+    public static void registerAttributionResultCallback(AttributionResultCallback callback) {
+        if (!ProcessUtil.isMainProcess(ContextUtil.getContext())) {
+            return;
+        }
+        if (callback == null) {
+            return;
+        }
+        if (attributed != null) {
+            callback.onResult(attributed);
+            return;
+        }
+        synchronized (KochavaHelper.class) {
+            if (attributed != null) {
+                callback.onResult(attributed);
+                return;
+            }
+            callbacks.add(callback);
+        }
+    }
+
+    public static void unregisterAttributionResultCallback(AttributionResultCallback callback) {
+        if (!ProcessUtil.isMainProcess(ContextUtil.getContext())) {
+            return;
+        }
+        if (callback == null) {
+            return;
+        }
+        synchronized (KochavaHelper.class) {
+            callbacks.remove(callback);
+        }
+    }
+
+    public static boolean isAttributed() {
+        return attributed != null && attributed;
+    }
+
+    @FunctionalInterface
+    public interface AttributionResultCallback {
+        void onResult(boolean attributed);
+    }
+}

+ 4 - 2
build.gradle

@@ -6,8 +6,8 @@ buildscript {
         minSdkVersion = 21
         targetSdkVersion = 33
 
-        versionCode = 99
-        versionName = "1.43.26"
+        versionCode = 100
+        versionName = "1.43.27"
 
         hilt_version = '2.41'
         lifecycle_version = "2.6.1"
@@ -39,6 +39,8 @@ buildscript {
 
         classpath 'com.github.megatronking.stringfog:gradle-plugin:4.0.1'
         classpath 'com.github.megatronking.stringfog:xor:4.0.1'
+
+        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
     }
 
     configurations.configureEach {

+ 3 - 1
gradle.properties

@@ -23,4 +23,6 @@ android.enableJetifier=true
 android.injected.testOnly=false
 test_server_host=https://central-test.atmob.com
 prod_server_host=http://project-api.atmob.com
-local_server_host=http://192.168.10.171:8880
+local_server_host=http://192.168.10.171:8880
+#kochava
+kochava_guid=kofunny-voice-ai-5dkleu5