zk пре 1 година
родитељ
комит
8f0e1567f6

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

@@ -46,4 +46,9 @@ public interface EventId {
     String settingrateapp_001 = "settingrateapp_001"; //设置页-rate app按钮	按钮点击
     String settingcontact_001 = "settingcontact_001"; //设置页-contact按钮	按钮点击
     String settingpro_001 = "settingpro_001"; //设置页-PRO按钮	按钮点击
+
+    String openratewin_001 = "openratewin_001";//评分弹窗出现次数	弹窗出现
+    String ratestarnub_001 = "ratestarnub_001";//评分弹窗选星星	点击选中,带选中1-5哪个星星
+    String ratebadsubmit_001 = "ratebadsubmit_001";//评分弹窗提交(1-3星)	按钮点击,带提交时选中的星星数量、反馈的内容
+    String ratewinclose_001 = "ratewinclose_001";//评分弹窗关闭按钮	按钮点击
 }

+ 146 - 0
app/src/main/java/com/atmob/voiceai/dialog/ScoringDialog.java

@@ -0,0 +1,146 @@
+package com.atmob.voiceai.dialog;
+
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.atmob.app.lib.base.BaseDialog;
+import com.atmob.voiceai.R;
+import com.atmob.voiceai.data.consts.EventId;
+import com.atmob.voiceai.databinding.DialogScoringBinding;
+import com.atmob.voiceai.handlers.EventHandler;
+import com.atmob.voiceai.utils.AppUtil;
+import com.atmob.voiceai.utils.ScoringUtils;
+
+import java.util.concurrent.TimeUnit;
+
+import atmob.reactivex.rxjava3.disposables.Disposable;
+import atmob.reactivex.rxjava3.functions.Action;
+import atmob.rxjava.utils.RxJavaUtil;
+
+@BaseDialog.FullScreen(height = false)
+public class ScoringDialog extends BaseDialog<DialogScoringBinding> {
+
+    private static final long STAR_ANIM_PERIOD = 160;
+
+
+    private final LottieAnimationView[] stars;
+
+    private Disposable startAnimInterval;
+
+    private int starsNum;
+
+    public ScoringDialog(@NonNull Context context) {
+        super(context, R.style.Theme_Common_Dialog);
+        setCancelable(false);
+        stars = new LottieAnimationView[]{
+//                binding.scoringStar1,
+//                binding.scoringStar2,
+//                binding.scoringStar3,
+//                binding.scoringStar4,
+//                binding.scoringStar5
+        };
+        binding.setOnStarClickListener(v -> {
+            starsNum = getStars(v);
+            Bundle bundle = new Bundle();
+            bundle.putInt("stars", starsNum);
+            EventHandler.report(EventId.ratestarnub_001, bundle);
+            binding.setShowFeedback(starsNum <= 3);
+            starAnim(starsNum, () -> {
+                if (starsNum > 3) {
+                    requestGoogleStoreScoring();
+                }
+            });
+        });
+        binding.setOnCloseClickListener(v -> {
+            EventHandler.report(EventId.ratewinclose_001);
+            dismiss();
+        });
+        binding.setOnSubmitClickListener(v -> {
+            Bundle bundle = new Bundle();
+            bundle.putInt("stars", starsNum);
+//            bundle.putString("feedback", binding.feedback.getText().toString());
+            EventHandler.report(EventId.ratebadsubmit_001, bundle);
+            showFeedbackSuccess();
+        });
+    }
+
+    private void requestGoogleStoreScoring() {
+        if (AppUtil.isAppExist(AppUtil.GooglePlay)) {
+            AppUtil.launchGooglePlayDetail(getContext().getPackageName());
+            dismiss();
+        }
+        showFeedbackSuccess();
+    }
+
+    private void showFeedbackSuccess() {
+        ScoringUtils.isScoringSuccess();
+    }
+
+    private int getStars(View v) {
+        int id = v.getId();
+//        if (id == R.id.scoring_star_1) {
+//            return 1;
+//        } else if (id == R.id.scoring_star_2) {
+//            return 2;
+//        } else if (id == R.id.scoring_star_3) {
+//            return 3;
+//        } else if (id == R.id.scoring_star_4) {
+//            return 4;
+//        } else if (id == R.id.scoring_star_5) {
+//            return 5;
+//        }
+        return 5;
+    }
+
+    @Override
+    public void show() {
+        binding.setShowFeedback(false);
+        super.show();
+    }
+
+    @Override
+    protected void onDismiss() {
+        super.onDismiss();
+        if (startAnimInterval != null) {
+            startAnimInterval.dispose();
+        }
+        resetStar();
+    }
+
+    @Override
+    protected void onShow() {
+        super.onShow();
+        ScoringUtils.recordScoringShow();
+        starAnim(5, this::resetStar);
+        EventHandler.report(EventId.openratewin_001);
+    }
+
+    private void starAnim(int stars, Action onComplete) {
+        if (startAnimInterval != null) {
+            startAnimInterval.dispose();
+            resetStar();
+        }
+//        startAnimInterval = RxJavaUtil.interval(0, STAR_ANIM_PERIOD,
+//                stars * (STAR_ANIM_PERIOD - 1) + binding.scoringStar5.getDuration(), TimeUnit.MILLISECONDS, index -> {
+//                    if (index < stars && index < this.stars.length) {
+//                        this.stars[Math.toIntExact(index)].playAnimation();
+//                    }
+//                }, () -> {
+//                    if (onComplete != null) {
+//                        onComplete.run();
+//                    }
+//                });
+    }
+
+    private void resetStar() {
+        for (LottieAnimationView star : stars) {
+            star.cancelAnimation();
+            star.setProgress(0);
+        }
+    }
+}

+ 17 - 3
app/src/main/java/com/atmob/voiceai/module/result/VoiceResultActivity.java

@@ -23,11 +23,13 @@ import com.atmob.voiceai.data.api.bean.UserVoiceBean;
 import com.atmob.voiceai.data.api.bean.VoiceListBean;
 import com.atmob.voiceai.data.consts.EventId;
 import com.atmob.voiceai.databinding.ActivityVoiceResultBinding;
+import com.atmob.voiceai.dialog.ScoringDialog;
 import com.atmob.voiceai.handlers.EventHandler;
 import com.atmob.voiceai.module.main.MainActivity;
 import com.atmob.voiceai.module.voiceai.VoiceAIFragment;
 import com.atmob.voiceai.module.voiceai.VoiceAIListAdapter;
 import com.atmob.voiceai.utils.ToastUtil;
+import com.atmob.voiceai.utils.VoiceFileUtil;
 
 import dagger.hilt.android.AndroidEntryPoint;
 
@@ -41,6 +43,8 @@ public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding
 
     private VoiceRecommendAdapter voiceRecommendAdapter;
 
+    private ScoringDialog scoringDialog;
+
     public static void start(Context context, UserVoiceBean userVoiceBean) {
         Intent intent = new Intent(context, VoiceResultActivity.class);
         if (!(context instanceof Activity)) {
@@ -73,6 +77,17 @@ public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding
         initPlay();
     }
 
+
+    private void showScoringDialog() {
+        if (scoringDialog == null) {
+            scoringDialog = new ScoringDialog(this);
+        }
+        if (scoringDialog.isShowing()) {
+            return;
+        }
+        scoringDialog.show();
+    }
+
     private void intVoiceRecommendList() {
         voiceRecommendAdapter = new VoiceRecommendAdapter(this);
         voiceRecommendAdapter.setActionHandler(voiceListBean -> {
@@ -120,9 +135,6 @@ public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding
 
     private void initPlay() {
         binding.ivPlay.setOnClickListener(view -> {
-            if (player.isLoading()) {
-                return;
-            }
             if (player.isPlaying()) {
                 player.pause();
                 voiceResultViewModel.setVoicePlay(false);
@@ -184,6 +196,7 @@ public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding
         voiceResultViewModel.setCurrentDuration(0);
         player.pause();
         voiceResultViewModel.setVoicePlay(false);
+        voiceResultViewModel.checkShowScoring();
     }
 
     @Override
@@ -196,6 +209,7 @@ public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding
     }
 
     private void initObserver() {
+        voiceResultViewModel.getShowScoringEvent().observe(this, o -> showScoringDialog());
         voiceResultViewModel.getVoiceList().observe(this, list -> voiceRecommendAdapter.submit(list));
         voiceResultViewModel.getRefreshAudioCurrentProgress().observe(this, o -> {
             long currentPosition = player.getCurrentPosition();

+ 30 - 5
app/src/main/java/com/atmob/voiceai/module/result/VoiceResultViewModel.java

@@ -19,6 +19,7 @@ import com.atmob.voiceai.data.repositories.VoiceAIRepository;
 import com.atmob.voiceai.handlers.EventHandler;
 import com.atmob.voiceai.utils.DateUtil;
 import com.atmob.voiceai.utils.MediaStoreHelper;
+import com.atmob.voiceai.utils.ScoringUtils;
 import com.atmob.voiceai.utils.ShareUtils;
 import com.atmob.voiceai.utils.ToastUtil;
 import com.atmob.voiceai.utils.VoiceFileUtil;
@@ -29,6 +30,7 @@ import java.util.Random;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
@@ -37,7 +39,9 @@ import atmob.reactivex.rxjava3.annotations.NonNull;
 import atmob.reactivex.rxjava3.core.SingleObserver;
 import atmob.reactivex.rxjava3.core.SingleSource;
 import atmob.reactivex.rxjava3.disposables.Disposable;
+import atmob.reactivex.rxjava3.functions.Action;
 import atmob.reactivex.rxjava3.functions.Function;
+import atmob.rxjava.utils.RxJavaUtil;
 import dagger.hilt.android.lifecycle.HiltViewModel;
 
 @HiltViewModel
@@ -50,7 +54,7 @@ public class VoiceResultViewModel extends BaseViewModel {
     private final MutableLiveData<Long> currentDuration = new MutableLiveData<>();
     private final SingleLiveEvent<?> refreshAudioCurrentProgress = new SingleLiveEvent<>();
     private final MutableLiveData<List<VoiceListBean>> voiceList = new MutableLiveData<>();
-
+    private final SingleLiveEvent<?> showScoringEvent = new SingleLiveEvent<>();
     private final MutableLiveData<UserVoiceBean> userVoiceBean = new MutableLiveData<>();
     private final OkHttpClient okHttpClient;
     private boolean isSeekbarChanging;
@@ -58,18 +62,25 @@ public class VoiceResultViewModel extends BaseViewModel {
 
     private boolean downloadFileDisposable;
 
+    private boolean isCanShowScoring;
+
     @Inject
     public VoiceResultViewModel(VoiceAIRepository voiceAIRepository, OkHttpClient okHttpClient) {
         this.voiceAIRepository = voiceAIRepository;
         this.okHttpClient = okHttpClient;
         refreshVoiceRecommendList();
         EventHandler.report(EventId.generateok_001);
+        checkScoring();
     }
 
     public LiveData<List<VoiceListBean>> getVoiceList() {
         return voiceList;
     }
 
+    public LiveData<?> getShowScoringEvent() {
+        return showScoringEvent;
+    }
+
     public LiveData<Boolean> getIsPlay() {
         return isPlay;
     }
@@ -91,6 +102,17 @@ public class VoiceResultViewModel extends BaseViewModel {
     }
 
 
+    private void checkScoring() {
+        isCanShowScoring = ScoringUtils.checkShowScoring();
+        if (!isCanShowScoring) {
+            return;
+        }
+        addDisposable(RxJavaUtil.timer(5, TimeUnit.SECONDS, () -> {
+            isCanShowScoring = false;
+            showScoringEvent.call();
+        }));
+    }
+
     public void setVoicePlay(boolean isPlay) {
         this.isPlay.setValue(isPlay);
     }
@@ -193,10 +215,6 @@ public class VoiceResultViewModel extends BaseViewModel {
                 });
     }
 
-    private String getVoiceFileName() {
-        //月日年时分毫秒+3位随机数
-        return "VoiceAI_" + DateUtil.formatNormalDate("ddMMyyyyHHmmssSSS", System.currentTimeMillis()) + (new Random().nextInt(900) + 100) + ".mp3";
-    }
 
     public void onShareClick() {
         EventHandler.report(EventId.generateokshare_001);
@@ -248,4 +266,11 @@ public class VoiceResultViewModel extends BaseViewModel {
             timer = null;
         }
     }
+
+    public void checkShowScoring() {
+        if (isCanShowScoring) {
+            isCanShowScoring = false;
+            showScoringEvent.call();
+        }
+    }
 }

+ 11 - 1
app/src/main/java/com/atmob/voiceai/module/setting/SettingActivity.java

@@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
 
 import com.atmob.app.lib.base.BaseActivity;
 import com.atmob.voiceai.databinding.SettingActivityBinding;
+import com.atmob.voiceai.dialog.ScoringDialog;
 import com.atmob.voiceai.handlers.EventHandler;
 import com.atmob.voiceai.sdk.kochava.KochavaHelper;
 import com.atmob.voiceai.utils.ToastUtil;
@@ -30,6 +31,8 @@ public class SettingActivity extends BaseActivity<SettingActivityBinding> {
     private int attrTimes = 0;
     private Disposable attrSubscribe;
 
+    private ScoringDialog scoringDialog;
+
     public static void start(Context context) {
         Intent intent = new Intent(context, SettingActivity.class);
         if (!(context instanceof Activity)) {
@@ -47,7 +50,7 @@ public class SettingActivity extends BaseActivity<SettingActivityBinding> {
     }
 
     private void initObserver() {
-
+        settingViewModel.getShowScoringEvent().observe(this, o -> showScoringDialog());
     }
 
     private void initView() {
@@ -85,6 +88,13 @@ public class SettingActivity extends BaseActivity<SettingActivityBinding> {
         });
     }
 
+    private void showScoringDialog() {
+        if (scoringDialog == null) {
+            scoringDialog = new ScoringDialog(this);
+        }
+        scoringDialog.show();
+    }
+
     private void showAttrInfo() {
         StringBuffer sb = new StringBuffer();
         sb.append("ka attribute:");

+ 14 - 5
app/src/main/java/com/atmob/voiceai/module/setting/SettingViewModel.java

@@ -8,6 +8,7 @@ import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 
 import com.atmob.app.lib.base.BaseViewModel;
+import com.atmob.app.lib.livedata.SingleLiveEvent;
 import com.atmob.common.runtime.ActivityUtil;
 import com.atmob.common.runtime.ContextUtil;
 import com.atmob.voiceai.R;
@@ -18,6 +19,7 @@ import com.atmob.voiceai.handlers.EventHandler;
 import com.atmob.voiceai.module.subscription.SubscriptionPageActivity;
 import com.atmob.voiceai.utils.AppUtil;
 import com.atmob.voiceai.utils.CommonUtils;
+import com.atmob.voiceai.utils.ScoringUtils;
 import com.atmob.voiceai.utils.SystemUtil;
 import com.atmob.voiceai.utils.ToastUtil;
 
@@ -29,6 +31,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel;
 public class SettingViewModel extends BaseViewModel {
 
 
+    private final MutableLiveData<Boolean> isHideScoring = new MutableLiveData<>();
+    private final SingleLiveEvent<?> showScoringEvent = new SingleLiveEvent<>();
     private final MutableLiveData<String> versionName = new MutableLiveData<>();
     private final MutableLiveData<String> proName = new MutableLiveData<>();
     private final MemberRepository memberRepository;
@@ -39,6 +43,15 @@ public class SettingViewModel extends BaseViewModel {
         Context context = ContextUtil.getContext();
         versionName.setValue(context.getString(R.string.version_name, SystemUtil.getVersionName(context)));
         proName.setValue(context.getString(R.string.setting_app_name_pro, context.getString(R.string.app_name)));
+        isHideScoring.setValue(ScoringUtils.isHideScoring());
+    }
+
+    public LiveData<Boolean> getIsHideScoring() {
+        return isHideScoring;
+    }
+
+    public LiveData<?> getShowScoringEvent() {
+        return showScoringEvent;
     }
 
     public LiveData<String> getProName() {
@@ -60,11 +73,7 @@ public class SettingViewModel extends BaseViewModel {
 
     public void onRateClick() {
         EventHandler.report(EventId.settingrateapp_001);
-        if (AppUtil.isAppExist(AppUtil.GooglePlay)) {
-            AppUtil.launchGooglePlayDetail(ContextUtil.getContext().getPackageName());
-        } else {
-            ToastUtil.show(R.string.google_play_not_found, ToastUtil.LENGTH_SHORT);
-        }
+        showScoringEvent.call();
     }
 
 

+ 64 - 0
app/src/main/java/com/atmob/voiceai/utils/ScoringUtils.java

@@ -0,0 +1,64 @@
+package com.atmob.voiceai.utils;
+
+import android.text.format.DateUtils;
+
+import com.atmob.common.data.KVUtils;
+
+public class ScoringUtils {
+
+
+    public static final int KEY_SCORING_BURIAL = 10 * 60 * 1000;
+    public static final String KEY_SCORING_SUCCESS = "scoring_success";
+    public static final String KEY_SHOW_SCORING_TIMES_TOTAL = "show_scoring_times_total";
+    public static final String KEY_SHOW_SCORING_TIME = "show_scoring_time";
+    public static final String KEY_SHOW_SCORING_TIMES_TODAY = "show_scoring_times_today";
+
+    private static final int KEY_SHOW_SCORING_TIMES_TOTAL_DEFAULT = 10;
+    private static final int KEY_SHOW_SCORING_TIMES_TODAY_MAX = 2;
+
+
+    public static void recordScoringShow() {
+        int totalTimes = KVUtils.getDefault().getInt(KEY_SHOW_SCORING_TIMES_TOTAL, 0);
+        KVUtils.getDefault().putInt(KEY_SHOW_SCORING_TIMES_TOTAL, totalTimes + 1);
+
+        long lastShowTime = KVUtils.getDefault().getLong(KEY_SHOW_SCORING_TIME, 0);
+        if (DateUtils.isToday(lastShowTime)) {
+            int timesToday = KVUtils.getDefault().getInt(KEY_SHOW_SCORING_TIMES_TODAY, 0);
+            KVUtils.getDefault().putInt(KEY_SHOW_SCORING_TIMES_TODAY, timesToday + 1);
+        } else {
+            KVUtils.getDefault().putInt(KEY_SHOW_SCORING_TIMES_TODAY, 1);
+        }
+
+        KVUtils.getDefault().putLong(KEY_SHOW_SCORING_TIME, System.currentTimeMillis());
+    }
+
+    public static boolean isHideScoring() {
+        return KVUtils.getDefault().getBoolean(KEY_SCORING_SUCCESS, false);
+    }
+
+    public static void isScoringSuccess() {
+        KVUtils.getDefault().getBoolean(KEY_SCORING_SUCCESS, true);
+    }
+
+    public static boolean checkShowScoring() {
+        boolean scoringDone = KVUtils.getDefault().getBoolean(KEY_SCORING_SUCCESS, false);
+        if (scoringDone) {
+            return false;
+        }
+        int totalTimes = KVUtils.getDefault().getInt(KEY_SHOW_SCORING_TIMES_TOTAL, 0);
+        if (totalTimes >= KEY_SHOW_SCORING_TIMES_TOTAL_DEFAULT) {
+            return false;
+        }
+        int timesToday = KVUtils.getDefault().getInt(KEY_SHOW_SCORING_TIMES_TODAY, 0);
+        if (timesToday > KEY_SHOW_SCORING_TIMES_TODAY_MAX) {
+            return false;
+        }
+        long lastTime = KVUtils.getDefault().getLong(KEY_SHOW_SCORING_TIME, 0);
+        if ((System.currentTimeMillis() - lastTime) < KEY_SCORING_BURIAL) {
+            return false;
+        }
+        return true;
+    }
+
+
+}

+ 3 - 0
app/src/main/java/com/atmob/voiceai/utils/VoiceFileUtil.java

@@ -1,6 +1,8 @@
 package com.atmob.voiceai.utils;
 
 
+import android.net.Uri;
+
 import androidx.annotation.NonNull;
 
 
@@ -49,6 +51,7 @@ public class VoiceFileUtil {
         return new File(DownloadUtils.voiceFile, fileName);
     }
 
+
     private static String getFileName(@NonNull String url) {
         return url.substring(url.lastIndexOf("/") + 1);
     }

+ 5 - 0
app/src/main/res/drawable/bg_scoring_dialog.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="20dp" />
+    <solid android:color="#32333C" />
+</shape>

+ 57 - 0
app/src/main/res/layout/dialog_scoring.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <data>
+
+        <variable
+            name="showFeedback"
+            type="Boolean" />
+
+
+        <variable
+            name="onCloseClickListener"
+            type="android.view.View.OnClickListener" />
+
+        <variable
+            name="onSubmitClickListener"
+            type="android.view.View.OnClickListener" />
+
+        <variable
+            name="onStarClickListener"
+            type="android.view.View.OnClickListener" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <View
+            android:id="@+id/v_scoring_bg"
+            android:background="@drawable/bg_scoring_dialog"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintWidth_percent="0.8333333333333333"
+            app:layout_constraintDimensionRatio="3:4"
+            android:layout_width="0dp"
+            android:layout_height="0dp" />
+
+
+        <TextView
+            android:gravity="center"
+            android:text="@string/scoring_submit"
+            app:layout_constraintVertical_bias="0.9120879120879121"
+            android:background="@drawable/bg_voice_ai_btn"
+            app:layout_constraintWidth_percent="0.7111111111111111"
+            app:layout_constraintDimensionRatio="256:36"
+            app:layout_constraintStart_toStartOf="@+id/v_scoring_bg"
+            app:layout_constraintEnd_toEndOf="@+id/v_scoring_bg"
+            app:layout_constraintBottom_toBottomOf="@+id/v_scoring_bg"
+            app:layout_constraintTop_toTopOf="@+id/v_scoring_bg"
+            android:layout_width="0dp"
+            android:layout_height="0dp" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 1 - 0
app/src/main/res/layout/setting_activity.xml

@@ -119,6 +119,7 @@
             app:layout_constraintTop_toBottomOf="@+id/space3">
 
             <include
+                isGone="@{settingViewModel.isHideScoring}"
                 layout="@layout/layout_item_settings"
                 settingsIcon="@{@drawable/icon_setting_rate}"
                 settingsName="@{@string/setting_rate_app}"

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -116,4 +116,5 @@
     <string name="loading_txt">loading…</string>
     <string name="query_order_status_loading_txt">Processing</string>
     <string name="query_order_time_out">Network abnormality, please click to restore</string>
+    <string name="scoring_submit">Submit</string>
 </resources>