Browse Source

统一列表数据返回&增加等待界面

zk 1 year ago
parent
commit
84a9fab20c
27 changed files with 1739 additions and 124 deletions
  1. 8 2
      app/build.gradle
  2. 89 1
      app/proguard-rules.pro
  3. 6 0
      app/src/main/AndroidManifest.xml
  4. BIN
      app/src/main/assets/anim/voice_ai_clone_loading.mp4
  5. 1037 0
      app/src/main/assets/anim/voice_ai_example_loading.json
  6. 9 0
      app/src/main/java/com/atmob/voiceai/data/api/bean/VoiceListBean.java
  7. 14 11
      app/src/main/java/com/atmob/voiceai/data/api/request/BaseRequest.java
  8. 11 1
      app/src/main/java/com/atmob/voiceai/data/api/request/TextTosSpeechRequest.java
  9. 3 3
      app/src/main/java/com/atmob/voiceai/data/api/request/VoiceListRequest.java
  10. 0 9
      app/src/main/java/com/atmob/voiceai/data/api/response/VoiceInfoResponse.java
  11. 74 2
      app/src/main/java/com/atmob/voiceai/data/repositories/VoiceAIRepository.java
  12. 79 0
      app/src/main/java/com/atmob/voiceai/module/generating/VoiceGeneratingActivity.java
  13. 99 0
      app/src/main/java/com/atmob/voiceai/module/generating/VoiceGeneratingViewModel.java
  14. 38 0
      app/src/main/java/com/atmob/voiceai/module/result/VoiceResultActivity.java
  15. 16 0
      app/src/main/java/com/atmob/voiceai/module/result/VoiceResultViewModel.java
  16. 8 12
      app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIFragment.java
  17. 14 19
      app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIListAdapter.java
  18. 68 58
      app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIViewModel.java
  19. BIN
      app/src/main/res/drawable-xxhdpi/bg_generating_background1.webp
  20. BIN
      app/src/main/res/drawable-xxhdpi/bg_generating_background2.webp
  21. 1 1
      app/src/main/res/drawable/bg_voice_ai_list_check.xml
  22. 5 0
      app/src/main/res/drawable/bg_voice_playing.xml
  23. 125 0
      app/src/main/res/layout/activity_voice_generating.xml
  24. 6 0
      app/src/main/res/layout/activity_voice_result.xml
  25. 22 4
      app/src/main/res/layout/item_voice_ai_list.xml
  26. 5 0
      app/src/main/res/values/strings.xml
  27. 2 1
      build.gradle

+ 8 - 2
app/build.gradle

@@ -156,8 +156,14 @@ dependencies {
     implementation "com.gyf.immersionbar:immersionbar-components:$rootProject.immersionbar_version"
     implementation "com.gyf.immersionbar:immersionbar-components:$rootProject.immersionbar_version"
 
 
     //字符串加密算法
     //字符串加密算法
-    implementation "com.github.megatronking.stringfog:xor:$rootProject.stringfog_verstion"
+    implementation "com.github.megatronking.stringfog:xor:$rootProject.stringfog_version"
 
 
     //音频播放
     //音频播放
-    implementation "androidx.media3:media3-exoplayer:1.3.1"
+    implementation "androidx.media3:media3-exoplayer:$rootProject.media3_version"
+
+    //VapPlayer 动效
+    implementation "io.github.tencent:vap:2.0.28"
+
+    //lottie
+    implementation "com.airbnb.android:lottie:$rootProject.lottie_version"
 }
 }

+ 89 - 1
app/proguard-rules.pro

@@ -18,4 +18,92 @@
 
 
 # If you keep the line number information, uncomment this to
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
 # hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
+-dontshrink
+
+## Log
+-assumenosideeffects class android.util.Log {
+    public static *** d(...);
+    public static *** v(...);
+    public static *** w(...);
+    public static *** i(...);
+    public static *** wtf(...);
+}
+-assumenosideeffects class com.atmob.common.logging.AtmobLog {
+    public static *** d(...);
+    public static *** v(...);
+    public static *** w(...);
+    public static *** i(...);
+    public static *** wtf(...);
+}
+
+## native
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+## res
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+## ViewBinding & DataBinding
+-keep class * implements androidx.viewbinding.ViewBinding {
+    public inflate(android.view.LayoutInflater);
+    public inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean);
+}
+
+# common config end
+
+# RxJava start
+-dontwarn java.util.concurrent.Flow*
+# RxJava end
+
+# Glide start
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep class * extends com.bumptech.glide.module.AppGlideModule {
+ <init>(...);
+}
+-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
+  **[] $VALUES;
+  public *;
+}
+-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
+  *** rewind();
+}
+# Glide end
+
+# Turbo start
+-keep class com.plutus.common.turbo.beans.** {*;}
+# Turbo end
+
+
+#oaid miitmdid start
+-keep class com.bun.miitmdid.core.** {*;}
+-keep class com.bun.lib.**{*;}
+-keep class XI.CA.XI.**{*;}
+-keep class XI.K0.XI.**{*;}
+-keep class XI.XI.K0.**{*;}
+-keep class XI.vs.K0.**{*;}
+-keep class XI.xo.XI.XI.**{*;}
+-keep class com.asus.msa.SupplementaryDID.**{*;}
+-keep class com.asus.msa.sdid.**{*;}
+-keep class com.bun.lib.**{*;}
+-keep class com.bun.miitmdid.**{*;}
+-keep class com.huawei.hms.ads.identifier.**{*;}
+-keep class com.samsung.android.deviceidservice.**{*;}
+-keep class org.json.**{*;}
+-keep public class com.netease.nis.sdkwrapper.Utils {
+public <methods>;
+}
+#oaid miitmdid end
+
+
+#SVGA
+-keep class com.squareup.wire.** { *; }
+-keep class com.opensource.svgaplayer.proto.** { *; }
+
+-keepclassmembers class * extends android.webkit.WebChromeClient{
+    public void openFileChooser(...);
+}
+

+ 6 - 0
app/src/main/AndroidManifest.xml

@@ -42,6 +42,12 @@
         <activity
         <activity
             android:name=".module.subscription.SubscriptionPageActivity"
             android:name=".module.subscription.SubscriptionPageActivity"
             android:screenOrientation="portrait" />
             android:screenOrientation="portrait" />
+        <activity
+            android:name=".module.generating.VoiceGeneratingActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".module.result.VoiceResultActivity"
+            android:screenOrientation="portrait" />
     </application>
     </application>
 
 
 
 

BIN
app/src/main/assets/anim/voice_ai_clone_loading.mp4


File diff suppressed because it is too large
+ 1037 - 0
app/src/main/assets/anim/voice_ai_example_loading.json


+ 9 - 0
app/src/main/java/com/atmob/voiceai/data/api/bean/VoiceListBean.java

@@ -23,8 +23,12 @@ public class VoiceListBean extends BaseObservable {
     @SerializedName("hasPro")
     @SerializedName("hasPro")
     private boolean hasPro;
     private boolean hasPro;
 
 
+    @SerializedName("showAdd")
     private boolean isAddIcon;
     private boolean isAddIcon;
 
 
+    @SerializedName("type")
+    private int voiceType;
+
     private int viewType;
     private int viewType;
 
 
     private boolean check;
     private boolean check;
@@ -40,6 +44,11 @@ public class VoiceListBean extends BaseObservable {
         int PLAYING = 2;
         int PLAYING = 2;
     }
     }
 
 
+
+    public int getVoiceType() {
+        return voiceType;
+    }
+
     @Bindable
     @Bindable
     @VoicePlayState
     @VoicePlayState
     public int getVoicePlayState() {
     public int getVoicePlayState() {

+ 14 - 11
app/src/main/java/com/atmob/voiceai/data/api/request/BaseRequest.java

@@ -1,22 +1,25 @@
 package com.atmob.voiceai.data.api.request;
 package com.atmob.voiceai.data.api.request;
 
 
 import com.atmob.user.param.AtmobParams;
 import com.atmob.user.param.AtmobParams;
+import com.atmob.voiceai.BuildConfig;
+
+import java.lang.reflect.Field;
 
 
 public class BaseRequest extends AtmobParams {
 public class BaseRequest extends AtmobParams {
 
 
 
 
     public BaseRequest() {
     public BaseRequest() {
-//        if (BuildConfig.DEBUG) {
-//            try {
-//                Field androidId = AtmobParams.class.getDeclaredField("androidId");
-//                androidId.setAccessible(true);
-//                androidId.set(this, "6713123wdq22e1232");
-//            } catch (NoSuchFieldException e) {
-//
-//            } catch (IllegalAccessException e) {
-//
-//            }
-//        }
+        if (BuildConfig.DEBUG) {
+            try {
+                Field androidId = AtmobParams.class.getDeclaredField("androidId");
+                androidId.setAccessible(true);
+                androidId.set(this, "6713123wdq22e1232");
+            } catch (NoSuchFieldException e) {
+
+            } catch (IllegalAccessException e) {
+
+            }
+        }
 
 
     }
     }
 }
 }

+ 11 - 1
app/src/main/java/com/atmob/voiceai/data/api/request/TextTosSpeechRequest.java

@@ -7,14 +7,24 @@ public class TextTosSpeechRequest extends BaseRequest {
     @SerializedName("id")
     @SerializedName("id")
     private int id;
     private int id;
 
 
+
+    @SerializedName("type")
+    private int type;
+
     @SerializedName("content")
     @SerializedName("content")
     private String content;
     private String content;
 
 
-    public TextTosSpeechRequest(int id, String content) {
+
+    public TextTosSpeechRequest(int id, int type, String content) {
         this.id = id;
         this.id = id;
+        this.type = type;
         this.content = content;
         this.content = content;
     }
     }
 
 
+    public int getType() {
+        return type;
+    }
+
     public int getId() {
     public int getId() {
         return id;
         return id;
     }
     }

+ 3 - 3
app/src/main/java/com/atmob/voiceai/data/api/request/VoiceListRequest.java

@@ -5,13 +5,13 @@ import com.google.gson.annotations.SerializedName;
 public class VoiceListRequest extends BaseRequest {
 public class VoiceListRequest extends BaseRequest {
 
 
     @SerializedName("id")
     @SerializedName("id")
-    private int id;
+    private String id;
 
 
-    public VoiceListRequest(int id) {
+    public VoiceListRequest(String id) {
         this.id = id;
         this.id = id;
     }
     }
 
 
-    public int getId() {
+    public String getId() {
         return id;
         return id;
     }
     }
 }
 }

+ 0 - 9
app/src/main/java/com/atmob/voiceai/data/api/response/VoiceInfoResponse.java

@@ -14,8 +14,6 @@ public class VoiceInfoResponse {
     private String cloneVoiceAvatar;
     private String cloneVoiceAvatar;
     @SerializedName("cloneVoiceName")
     @SerializedName("cloneVoiceName")
     private String cloneVoiceName;
     private String cloneVoiceName;
-    @SerializedName("cloneVoiceList")
-    private List<CloneVoiceListBean> cloneVoiceList;
 
 
     public int getVoiceTimes() {
     public int getVoiceTimes() {
         return voiceTimes;
         return voiceTimes;
@@ -41,12 +39,5 @@ public class VoiceInfoResponse {
         this.cloneVoiceName = cloneVoiceName;
         this.cloneVoiceName = cloneVoiceName;
     }
     }
 
 
-    public List<CloneVoiceListBean> getCloneVoiceList() {
-        return cloneVoiceList;
-    }
-
-    public void setCloneVoiceList(List<CloneVoiceListBean> cloneVoiceList) {
-        this.cloneVoiceList = cloneVoiceList;
-    }
 
 
 }
 }

+ 74 - 2
app/src/main/java/com/atmob/voiceai/data/repositories/VoiceAIRepository.java

@@ -1,13 +1,19 @@
 package com.atmob.voiceai.data.repositories;
 package com.atmob.voiceai.data.repositories;
 
 
 
 
+import androidx.annotation.IntDef;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.MutableLiveData;
 
 
 import com.atmob.app.lib.handler.RxHttpHandler;
 import com.atmob.app.lib.handler.RxHttpHandler;
 import com.atmob.voiceai.data.api.AtmobApi;
 import com.atmob.voiceai.data.api.AtmobApi;
+import com.atmob.voiceai.data.api.GenerateApi;
+import com.atmob.voiceai.data.api.bean.UserVoiceBean;
+import com.atmob.voiceai.data.api.bean.VoiceListBean;
 import com.atmob.voiceai.data.api.request.BaseRequest;
 import com.atmob.voiceai.data.api.request.BaseRequest;
+import com.atmob.voiceai.data.api.request.TextTosSpeechRequest;
 import com.atmob.voiceai.data.api.request.VoiceListRequest;
 import com.atmob.voiceai.data.api.request.VoiceListRequest;
+import com.atmob.voiceai.data.api.response.TextToSpeechResponse;
 import com.atmob.voiceai.data.api.response.VoiceInfoResponse;
 import com.atmob.voiceai.data.api.response.VoiceInfoResponse;
 import com.atmob.voiceai.data.api.response.VoiceListResponse;
 import com.atmob.voiceai.data.api.response.VoiceListResponse;
 import com.atmob.voiceai.data.api.response.VoiceTypeResponse;
 import com.atmob.voiceai.data.api.response.VoiceTypeResponse;
@@ -18,7 +24,10 @@ import java.util.List;
 import javax.inject.Inject;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import javax.inject.Singleton;
 
 
+import atmob.reactivex.rxjava3.annotations.NonNull;
 import atmob.reactivex.rxjava3.core.Single;
 import atmob.reactivex.rxjava3.core.Single;
+import atmob.reactivex.rxjava3.core.SingleObserver;
+import atmob.reactivex.rxjava3.disposables.Disposable;
 import atmob.rxjava.utils.RxJavaUtil;
 import atmob.rxjava.utils.RxJavaUtil;
 
 
 @Singleton
 @Singleton
@@ -26,13 +35,42 @@ public class VoiceAIRepository {
 
 
 
 
     private final AtmobApi atmobApi;
     private final AtmobApi atmobApi;
+    private final GenerateApi generateApi;
 
 
+    private boolean textToSpeechRequest;
+
+    private VoiceListBean requestCloneBean;
+    private String textToSpeechTxt;
+
+    private final MutableLiveData<Integer> textToSpeechState = new MutableLiveData<>();
+
+    private final MutableLiveData<UserVoiceBean> resultBean = new MutableLiveData<>();
+
+
+    @IntDef({TextToSpeechState.GENERATING, TextToSpeechState.GENERATED, TextToSpeechState.ERROR})
+    public @interface TextToSpeechState {
+        int GENERATING = 1;
+        int GENERATED = 2;
+        int ERROR = 3;
+    }
 
 
     @Inject
     @Inject
-    public VoiceAIRepository(AtmobApi atmobApi) {
+    public VoiceAIRepository(AtmobApi atmobApi, GenerateApi generateApi) {
         this.atmobApi = atmobApi;
         this.atmobApi = atmobApi;
+        this.generateApi = generateApi;
+    }
+
+    public VoiceListBean getRequestCloneBean() {
+        return requestCloneBean;
     }
     }
 
 
+    public String getTextToSpeechTxt() {
+        return textToSpeechTxt;
+    }
+
+    public LiveData<Integer> getTextToSpeechState() {
+        return textToSpeechState;
+    }
 
 
     public Single<VoiceInfoResponse> requestVoiceInfo() {
     public Single<VoiceInfoResponse> requestVoiceInfo() {
         return atmobApi.getVoiceInfo(new BaseRequest())
         return atmobApi.getVoiceInfo(new BaseRequest())
@@ -46,11 +84,45 @@ public class VoiceAIRepository {
                 .compose(RxJavaUtil.SingleSchedule.io2Main());
                 .compose(RxJavaUtil.SingleSchedule.io2Main());
     }
     }
 
 
-    public Single<VoiceListResponse> requestVoiceList(int id) {
+    public Single<VoiceListResponse> requestVoiceList(String id) {
         return atmobApi.getVoiceList(new VoiceListRequest(id))
         return atmobApi.getVoiceList(new VoiceListRequest(id))
                 .compose(RxHttpHandler.handle(true))
                 .compose(RxHttpHandler.handle(true))
                 .compose(RxJavaUtil.SingleSchedule.io2Main());
                 .compose(RxJavaUtil.SingleSchedule.io2Main());
     }
     }
 
 
+    public void requestTextToSpeech(VoiceListBean bean, String content) {
+        if (bean == null || textToSpeechRequest) {
+            return;
+        }
+        requestCloneBean = bean;
+        textToSpeechTxt = content;
+        textToSpeech(bean.getId(), bean.getVoiceType(), content).subscribe(new SingleObserver<TextToSpeechResponse>() {
+            @Override
+            public void onSubscribe(@NonNull Disposable d) {
+                textToSpeechRequest = true;
+                textToSpeechState.setValue(TextToSpeechState.GENERATING);
+            }
+
+            @Override
+            public void onSuccess(@NonNull TextToSpeechResponse textToSpeechResponse) {
+                textToSpeechRequest = false;
+
+                textToSpeechState.setValue(TextToSpeechState.GENERATED);
+            }
+
+            @Override
+            public void onError(@NonNull Throwable e) {
+                textToSpeechRequest = false;
+                textToSpeechState.setValue(TextToSpeechState.ERROR);
+            }
+        });
+    }
+
+    private Single<TextToSpeechResponse> textToSpeech(int id, int type, String content) {
+        return generateApi.textToSpeech(new TextTosSpeechRequest(id, type, content))
+                .compose(RxHttpHandler.handle(true))
+                .compose(RxJavaUtil.SingleSchedule.io2Main());
+    }
+
 
 
 }
 }

+ 79 - 0
app/src/main/java/com/atmob/voiceai/module/generating/VoiceGeneratingActivity.java

@@ -0,0 +1,79 @@
+package com.atmob.voiceai.module.generating;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.app.lib.base.BaseActivity;
+import com.atmob.voiceai.R;
+import com.atmob.voiceai.data.repositories.VoiceAIRepository;
+import com.atmob.voiceai.databinding.ActivityVoiceGeneratingBinding;
+import com.atmob.voiceai.module.result.VoiceResultActivity;
+import com.atmob.voiceai.utils.ToastUtil;
+import com.tencent.qgame.animplayer.util.ScaleType;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+@AndroidEntryPoint
+public class VoiceGeneratingActivity extends BaseActivity<ActivityVoiceGeneratingBinding> {
+
+    private VoiceGeneratingViewModel voiceGeneratingViewModel;
+
+    public static void start(Context context) {
+        Intent intent = new Intent(context, VoiceGeneratingActivity.class);
+        if (!(context instanceof Activity)) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initView();
+        initObserver();
+        startGenerating();
+    }
+
+    private void initObserver() {
+        voiceGeneratingViewModel.getGenerateSuccess().observe(this, o -> {
+            VoiceResultActivity.start(this);
+            finish();
+        });
+        voiceGeneratingViewModel.getGenerateError().observe(this, o -> {
+            finish();
+            ToastUtil.show(R.string.generate_error, ToastUtil.LENGTH_SHORT);
+        });
+    }
+
+    private void startGenerating() {
+        binding.animView.setScaleType(ScaleType.FIT_XY);
+        binding.animView.setLoop(Integer.MAX_VALUE);
+        binding.animView.startPlay(this.getAssets(), "anim/voice_ai_clone_loading.mp4");
+    }
+
+    private void initView() {
+
+    }
+
+    @Override
+    protected void initViewModel() {
+        super.initViewModel();
+        voiceGeneratingViewModel = getViewModelProvider().get(VoiceGeneratingViewModel.class);
+        binding.setVoiceGeneratingViewModel(voiceGeneratingViewModel);
+    }
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+
+
+    @Override
+    public void onBackPressed() {
+
+    }
+}

+ 99 - 0
app/src/main/java/com/atmob/voiceai/module/generating/VoiceGeneratingViewModel.java

@@ -0,0 +1,99 @@
+package com.atmob.voiceai.module.generating;
+
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+
+import com.atmob.app.lib.base.BaseViewModel;
+import com.atmob.app.lib.livedata.SingleLiveEvent;
+import com.atmob.common.runtime.ContextUtil;
+import com.atmob.voiceai.R;
+import com.atmob.voiceai.data.api.bean.VoiceListBean;
+import com.atmob.voiceai.data.repositories.VoiceAIRepository;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import atmob.rxjava.utils.RxJavaUtil;
+import dagger.hilt.android.lifecycle.HiltViewModel;
+
+@HiltViewModel
+public class VoiceGeneratingViewModel extends BaseViewModel {
+
+    private final MutableLiveData<String> generatingTime = new MutableLiveData<>();
+
+    private final SingleLiveEvent<?> generateSuccess = new SingleLiveEvent<>();
+    private final SingleLiveEvent<?> generateError = new SingleLiveEvent<>();
+
+    private final VoiceAIRepository voiceAIRepository;
+
+    private final Observer<Integer> stateObserver;
+
+    @Inject
+    public VoiceGeneratingViewModel(VoiceAIRepository voiceAIRepository) {
+        this.voiceAIRepository = voiceAIRepository;
+        startCountDown();
+        stateObserver = state -> {
+            if (state == null) {
+                return;
+            }
+            if (state == VoiceAIRepository.TextToSpeechState.ERROR) {
+                generateError.call();
+            } else if (state == VoiceAIRepository.TextToSpeechState.GENERATED) {
+                generateSuccess.call();
+            }
+        };
+        voiceAIRepository.getTextToSpeechState().observeForever(stateObserver);
+    }
+
+    public LiveData<?> getGenerateSuccess() {
+        return generateSuccess;
+    }
+
+    public LiveData<?> getGenerateError() {
+        return generateError;
+    }
+
+    private void startCountDown() {
+        String textToSpeechTxt = voiceAIRepository.getTextToSpeechTxt();
+        if (TextUtils.isEmpty(textToSpeechTxt)) {
+            return;
+        }
+        int time = getRangeTime(textToSpeechTxt);
+        RxJavaUtil.interval(0, 1, time, TimeUnit.SECONDS, aLong -> generatingTime.setValue(ContextUtil.getContext().getString(R.string.generating_time, time - aLong)), () -> generatingTime.setValue(ContextUtil.getContext().getString(R.string.generating_time_countdown_over)));
+    }
+
+
+    private int getRangeTime(@NonNull String textToSpeechTxt) {
+        int length = textToSpeechTxt.length();
+
+        if (length <= 30) {
+            return 5;
+        } else if (length <= 80) {
+            return 10;
+        } else if (length <= 120) {
+            return 15;
+        } else {
+            return 20;
+        }
+    }
+
+
+    public LiveData<String> getGeneratingTime() {
+        return generatingTime;
+    }
+
+    public VoiceListBean getCloneBean() {
+        return voiceAIRepository.getRequestCloneBean();
+    }
+
+    @Override
+    protected void onCleared() {
+        super.onCleared();
+        voiceAIRepository.getTextToSpeechState().removeObserver(stateObserver);
+    }
+}

+ 38 - 0
app/src/main/java/com/atmob/voiceai/module/result/VoiceResultActivity.java

@@ -0,0 +1,38 @@
+package com.atmob.voiceai.module.result;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+
+import com.atmob.app.lib.base.BaseActivity;
+import com.atmob.voiceai.databinding.ActivityVoiceResultBinding;
+import com.atmob.voiceai.module.generating.VoiceGeneratingActivity;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+@AndroidEntryPoint
+public class VoiceResultActivity extends BaseActivity<ActivityVoiceResultBinding> {
+
+
+    private VoiceResultViewModel voiceResultViewModel;
+
+    public static void start(Context context) {
+        Intent intent = new Intent(context, VoiceResultActivity.class);
+        if (!(context instanceof Activity)) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        context.startActivity(intent);
+    }
+
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+
+    @Override
+    protected void initViewModel() {
+        super.initViewModel();
+        voiceResultViewModel = getViewModelProvider().get(VoiceResultViewModel.class);
+    }
+}

+ 16 - 0
app/src/main/java/com/atmob/voiceai/module/result/VoiceResultViewModel.java

@@ -0,0 +1,16 @@
+package com.atmob.voiceai.module.result;
+
+import com.atmob.app.lib.base.BaseViewModel;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.lifecycle.HiltViewModel;
+
+@HiltViewModel
+public class VoiceResultViewModel extends BaseViewModel {
+
+
+    @Inject
+    public VoiceResultViewModel() {
+    }
+}

+ 8 - 12
app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIFragment.java

@@ -1,7 +1,6 @@
 package com.atmob.voiceai.module.voiceai;
 package com.atmob.voiceai.module.voiceai;
 
 
 import android.annotation.SuppressLint;
 import android.annotation.SuppressLint;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View;
@@ -11,10 +10,8 @@ import androidx.annotation.Nullable;
 import androidx.media3.common.MediaItem;
 import androidx.media3.common.MediaItem;
 import androidx.media3.common.PlaybackException;
 import androidx.media3.common.PlaybackException;
 import androidx.media3.common.Player;
 import androidx.media3.common.Player;
-import androidx.media3.exoplayer.ExoPlaybackException;
 import androidx.media3.exoplayer.ExoPlayer;
 import androidx.media3.exoplayer.ExoPlayer;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
 
 
 import com.atmob.app.lib.base.BaseFragment;
 import com.atmob.app.lib.base.BaseFragment;
 import com.atmob.common.logging.AtmobLog;
 import com.atmob.common.logging.AtmobLog;
@@ -30,8 +27,6 @@ import com.atmob.voiceai.utils.ToastUtil;
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayout;
 import com.gyf.immersionbar.ImmersionBar;
 import com.gyf.immersionbar.ImmersionBar;
 
 
-import java.lang.reflect.Method;
-
 import dagger.hilt.android.AndroidEntryPoint;
 import dagger.hilt.android.AndroidEntryPoint;
 
 
 
 
@@ -52,9 +47,9 @@ public class VoiceAIFragment extends BaseFragment<FragmentVoiceAiBinding> {
     }
     }
 
 
     private void initObserver() {
     private void initObserver() {
+        voiceViewModel.getPlayExampleVoice().observe(getViewLifecycleOwner(), this::playExampleVoice);
         voiceViewModel.getShowGenerateAd().observe(getViewLifecycleOwner(), o -> {
         voiceViewModel.getShowGenerateAd().observe(getViewLifecycleOwner(), o -> {
             //TODO 显示广告
             //TODO 显示广告
-
             voiceViewModel.seeAdGenerate();
             voiceViewModel.seeAdGenerate();
         });
         });
         voiceViewModel.getVoiceDetailList().observe(getViewLifecycleOwner(), list -> voiceAIListAdapter.submit(list)
         voiceViewModel.getVoiceDetailList().observe(getViewLifecycleOwner(), list -> voiceAIListAdapter.submit(list)
@@ -74,8 +69,7 @@ public class VoiceAIFragment extends BaseFragment<FragmentVoiceAiBinding> {
                 binding.voiceTab.addTab(tab);
                 binding.voiceTab.addTab(tab);
                 itemBinding.getRoot().setOnClickListener(v -> {
                 itemBinding.getRoot().setOnClickListener(v -> {
                     tab.select();
                     tab.select();
-
-                    voiceViewModel.refreshVoiceList(typeListBean.getId());
+                    voiceViewModel.refreshVoiceList(String.valueOf(typeListBean.getId()));
                 });
                 });
                 if (i == 0) {
                 if (i == 0) {
                     itemBinding.getRoot().performClick();
                     itemBinding.getRoot().performClick();
@@ -103,9 +97,8 @@ public class VoiceAIFragment extends BaseFragment<FragmentVoiceAiBinding> {
             }
             }
 
 
             @Override
             @Override
-            public void choiceItemClick(@NonNull VoiceListBean voiceListBean) {
-                voiceViewModel.setChoiceVoiceBean(voiceListBean);
-                playExampleVoice(voiceListBean);
+            public void voiceItemClick(@NonNull VoiceListBean voiceListBean) {
+                voiceViewModel.voiceItemClick(voiceListBean);
             }
             }
         });
         });
         binding.ryVoiceView.setAdapter(voiceAIListAdapter);
         binding.ryVoiceView.setAdapter(voiceAIListAdapter);
@@ -124,10 +117,13 @@ public class VoiceAIFragment extends BaseFragment<FragmentVoiceAiBinding> {
         });
         });
     }
     }
 
 
-    private void playExampleVoice(@NonNull VoiceListBean voiceListBean) {
+    private void playExampleVoice(VoiceListBean voiceListBean) {
         if (player.isPlaying() || player.isLoading()) {
         if (player.isPlaying() || player.isLoading()) {
             player.stop();
             player.stop();
         }
         }
+        if (voiceListBean == null) {
+            return;
+        }
         Uri videoUri = Uri.parse(voiceListBean.getVoiceUrl());
         Uri videoUri = Uri.parse(voiceListBean.getVoiceUrl());
         MediaItem mediaItem = MediaItem.fromUri(videoUri);
         MediaItem mediaItem = MediaItem.fromUri(videoUri);
         player.setMediaItem(mediaItem);
         player.setMediaItem(mediaItem);

+ 14 - 19
app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIListAdapter.java

@@ -10,7 +10,6 @@ import androidx.recyclerview.widget.AsyncListDiffer;
 import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView;
 
 
-import com.atmob.common.logging.AtmobLog;
 import com.atmob.voiceai.data.api.bean.VoiceListBean;
 import com.atmob.voiceai.data.api.bean.VoiceListBean;
 import com.atmob.voiceai.databinding.ItemVoiceAiListBinding;
 import com.atmob.voiceai.databinding.ItemVoiceAiListBinding;
 import com.atmob.voiceai.databinding.ItemVoiceListFootBinding;
 import com.atmob.voiceai.databinding.ItemVoiceListFootBinding;
@@ -28,8 +27,6 @@ public class VoiceAIListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
 
 
     private ActionHandler actionHandler;
     private ActionHandler actionHandler;
 
 
-    private VoiceListBean checkBean;
-
     public void setActionHandler(ActionHandler actionHandler) {
     public void setActionHandler(ActionHandler actionHandler) {
         this.actionHandler = actionHandler;
         this.actionHandler = actionHandler;
     }
     }
@@ -39,17 +36,23 @@ public class VoiceAIListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
         this.listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<VoiceListBean>() {
         this.listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<VoiceListBean>() {
             @Override
             @Override
             public boolean areItemsTheSame(@NonNull VoiceListBean oldItem, @NonNull VoiceListBean newItem) {
             public boolean areItemsTheSame(@NonNull VoiceListBean oldItem, @NonNull VoiceListBean newItem) {
+                return oldItem.getId() == newItem.getId();
+            }
+
+            @Override
+            public boolean areContentsTheSame(@NonNull VoiceListBean oldItem, @NonNull VoiceListBean newItem) {
                 if (oldItem.getViewType() != newItem.getViewType()) {
                 if (oldItem.getViewType() != newItem.getViewType()) {
                     return false;
                     return false;
                 }
                 }
+                if (oldItem.getVoicePlayState() != newItem.getVoicePlayState()) {
+                    return false;
+                }
+                if (oldItem.isCheck() != newItem.isCheck()) {
+                    return false;
+                }
                 if (oldItem.isAddIcon() != newItem.isAddIcon()) {
                 if (oldItem.isAddIcon() != newItem.isAddIcon()) {
                     return false;
                     return false;
                 }
                 }
-                return oldItem.getId() == newItem.getId();
-            }
-
-            @Override
-            public boolean areContentsTheSame(@NonNull VoiceListBean oldItem, @NonNull VoiceListBean newItem) {
                 if (!Objects.equals(oldItem.getName(), newItem.getName())) {
                 if (!Objects.equals(oldItem.getName(), newItem.getName())) {
                     return false;
                     return false;
                 }
                 }
@@ -88,7 +91,7 @@ public class VoiceAIListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
         }
         }
     }
     }
 
 
-    public void submit(List<VoiceListBean> itemBeanList) {
+    public void submit(@NonNull List<VoiceListBean> itemBeanList) {
         VoiceListBean voiceListBean = new VoiceListBean();
         VoiceListBean voiceListBean = new VoiceListBean();
         voiceListBean.setViewType(TYPE_FOOT);
         voiceListBean.setViewType(TYPE_FOOT);
         itemBeanList.add(voiceListBean);
         itemBeanList.add(voiceListBean);
@@ -133,14 +136,7 @@ public class VoiceAIListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
                 if (actionHandler != null) actionHandler.addClick();
                 if (actionHandler != null) actionHandler.addClick();
             });
             });
             binding.setChoiceClick(v -> {
             binding.setChoiceClick(v -> {
-                VoiceListBean viewBean = binding.getBean();
-                if (checkBean != null) {
-                    checkBean.setVoicePlayState(VoiceListBean.VoicePlayState.INITIAL);
-                    checkBean.setCheck(false);
-                }
-                viewBean.setCheck(true);
-                checkBean = viewBean;
-                if (actionHandler != null) actionHandler.choiceItemClick(viewBean);
+                if (actionHandler != null) actionHandler.voiceItemClick(binding.getBean());
             });
             });
         }
         }
 
 
@@ -150,11 +146,10 @@ public class VoiceAIListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
         }
         }
     }
     }
 
 
-
     public interface ActionHandler {
     public interface ActionHandler {
         void addClick();
         void addClick();
 
 
-        void choiceItemClick(VoiceListBean voiceListBean);
+        void voiceItemClick(VoiceListBean voiceListBean);
     }
     }
 
 
 }
 }

+ 68 - 58
app/src/main/java/com/atmob/voiceai/module/voiceai/VoiceAIViewModel.java

@@ -4,6 +4,7 @@ import android.text.TextUtils;
 
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorInt;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.Transformations;
 import androidx.lifecycle.Transformations;
 
 
@@ -22,6 +23,7 @@ import com.atmob.voiceai.data.api.response.VoiceTypeResponse;
 import com.atmob.voiceai.data.repositories.MemberRepository;
 import com.atmob.voiceai.data.repositories.MemberRepository;
 import com.atmob.voiceai.data.repositories.VoiceAIRepository;
 import com.atmob.voiceai.data.repositories.VoiceAIRepository;
 import com.atmob.voiceai.helper.ErrorHelper;
 import com.atmob.voiceai.helper.ErrorHelper;
+import com.atmob.voiceai.module.generating.VoiceGeneratingActivity;
 import com.atmob.voiceai.module.subscription.SubscriptionPageActivity;
 import com.atmob.voiceai.module.subscription.SubscriptionPageActivity;
 import com.atmob.voiceai.utils.BoxingUtil;
 import com.atmob.voiceai.utils.BoxingUtil;
 import com.atmob.voiceai.utils.SpannableUtil;
 import com.atmob.voiceai.utils.SpannableUtil;
@@ -29,6 +31,7 @@ import com.atmob.voiceai.utils.ToastUtil;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
+import java.util.Objects;
 
 
 import javax.inject.Inject;
 import javax.inject.Inject;
 
 
@@ -42,7 +45,7 @@ public class VoiceAIViewModel extends BaseViewModel {
 
 
     private final String LAST_GENERATE_TEXT = "last_generate_text";
     private final String LAST_GENERATE_TEXT = "last_generate_text";
 
 
-    private final LiveData<CharSequence> voiceProbationTxt;
+    private final MediatorLiveData<CharSequence> voiceProbationTxt = new MediatorLiveData<>();
     private final MutableLiveData<String> voicePrintTxt = new MutableLiveData<>();
     private final MutableLiveData<String> voicePrintTxt = new MutableLiveData<>();
     private final LiveData<String> voicePrintLimitTxt;
     private final LiveData<String> voicePrintLimitTxt;
     private final MemberRepository memberRepository;
     private final MemberRepository memberRepository;
@@ -51,17 +54,16 @@ public class VoiceAIViewModel extends BaseViewModel {
     private final int freeTxtColor;
     private final int freeTxtColor;
     private final VoiceAIRepository voiceAIRepository;
     private final VoiceAIRepository voiceAIRepository;
 
 
-    private CloneVoiceListBean cloneBean;
-    private VoiceListBean addVoiceBean;
 
 
     private final MutableLiveData<List<TypeListBean>> voiceTypeList = new MutableLiveData<>();
     private final MutableLiveData<List<TypeListBean>> voiceTypeList = new MutableLiveData<>();
     private final MutableLiveData<List<VoiceListBean>> voiceDetailList = new MutableLiveData<>();
     private final MutableLiveData<List<VoiceListBean>> voiceDetailList = new MutableLiveData<>();
 
 
-    private final MutableLiveData<VoiceListBean> choiceVoiceBean = new MutableLiveData<>();
+    private VoiceListBean choiceVoiceBean;
+    private final SingleLiveEvent<VoiceListBean> playExampleVoice = new SingleLiveEvent<>();
 
 
     private final SingleLiveEvent<?> showGenerateAd = new SingleLiveEvent<>();
     private final SingleLiveEvent<?> showGenerateAd = new SingleLiveEvent<>();
 
 
-    private int currentTypeId = -1;
+    private String currentTypeId;
     private Disposable voiceListDisposable;
     private Disposable voiceListDisposable;
 
 
     @Inject
     @Inject
@@ -76,18 +78,29 @@ public class VoiceAIViewModel extends BaseViewModel {
             return length + "/200";
             return length + "/200";
         });
         });
         freeTxtColor = ContextUtil.getApplication().getResources().getColor(R.color.voice_free_crux_txt_color);
         freeTxtColor = ContextUtil.getApplication().getResources().getColor(R.color.voice_free_crux_txt_color);
-        voiceProbationTxt = Transformations.map(adFreeGenerateNumber, number -> {
-            if (BoxingUtil.boxing(number) <= 0) {
-                return null;
+
+        voiceProbationTxt.addSource(adFreeGenerateNumber, number -> {
+            if (BoxingUtil.boxing(memberRepository.getIsMember().getValue())) {
+                voiceProbationTxt.setValue("");
+                return;
             }
             }
-            String allTxt = ContextUtil.getContext().getString(R.string.voice_ai_get_free_number, number);
-            return SpannableUtil.getSpannableStringBuilder(allTxt, String.valueOf(number), freeTxtColor, false);
+            setFreeGenerateText(BoxingUtil.boxing(number));
         });
         });
         voicePrintTxt.setValue(KVUtils.getDefault().getString(LAST_GENERATE_TEXT, ""));
         voicePrintTxt.setValue(KVUtils.getDefault().getString(LAST_GENERATE_TEXT, ""));
-
+        voiceProbationTxt.addSource(memberRepository.getIsMember(), isMember -> {
+            if (BoxingUtil.boxing(isMember)) {
+                voiceProbationTxt.setValue("");
+                return;
+            }
+            setFreeGenerateText(BoxingUtil.boxing(adFreeGenerateNumber.getValue()));
+        });
         iniVoiceAI();
         iniVoiceAI();
     }
     }
 
 
+    public LiveData<VoiceListBean> getPlayExampleVoice() {
+        return playExampleVoice;
+    }
+
     public LiveData<?> getShowGenerateAd() {
     public LiveData<?> getShowGenerateAd() {
         return showGenerateAd;
         return showGenerateAd;
     }
     }
@@ -125,17 +138,27 @@ public class VoiceAIViewModel extends BaseViewModel {
         voicePrintTxt.setValue("");
         voicePrintTxt.setValue("");
     }
     }
 
 
+    private void setFreeGenerateText(int number) {
+        voiceProbationTxt.setValue(SpannableUtil.getSpannableStringBuilder(
+                ContextUtil.getContext().getString(R.string.voice_ai_get_free_number, number),
+                String.valueOf(number), freeTxtColor, false));
+    }
+
     public void onGenerateClick() {
     public void onGenerateClick() {
-        VoiceListBean bean = choiceVoiceBean.getValue();
-        if (bean == null) {
+        String content = voicePrintTxt.getValue();
+        if (TextUtils.isEmpty(content)) {
+            ToastUtil.show(R.string.voice_ai_print_hint, ToastUtil.LENGTH_SHORT);
+            return;
+        }
+        if (choiceVoiceBean == null) {
             ToastUtil.show(R.string.voice_ai_no_choice, ToastUtil.LENGTH_SHORT);
             ToastUtil.show(R.string.voice_ai_no_choice, ToastUtil.LENGTH_SHORT);
             return;
             return;
         }
         }
         if (BoxingUtil.boxing(isMember().getValue())) {
         if (BoxingUtil.boxing(isMember().getValue())) {
-            requestGenerateVoice(bean);
+            requestGenerateVoice();
             return;
             return;
         }
         }
-        if (bean.isHasPro()) {
+        if (choiceVoiceBean.isHasPro()) {
             SubscriptionPageActivity.start(ActivityUtil.getTopActivity());
             SubscriptionPageActivity.start(ActivityUtil.getTopActivity());
             return;
             return;
         }
         }
@@ -146,8 +169,9 @@ public class VoiceAIViewModel extends BaseViewModel {
         SubscriptionPageActivity.start(ActivityUtil.getTopActivity());
         SubscriptionPageActivity.start(ActivityUtil.getTopActivity());
     }
     }
 
 
-    private void requestGenerateVoice(VoiceListBean bean) {
-
+    private void requestGenerateVoice() {
+        voiceAIRepository.requestTextToSpeech(choiceVoiceBean, voicePrintTxt.getValue());
+        VoiceGeneratingActivity.start(ActivityUtil.getTopActivity());
     }
     }
 
 
 
 
@@ -166,18 +190,6 @@ public class VoiceAIViewModel extends BaseViewModel {
             @Override
             @Override
             public void onSuccess(@NonNull VoiceInfoResponse voiceInfoResponse) {
             public void onSuccess(@NonNull VoiceInfoResponse voiceInfoResponse) {
                 adFreeGenerateNumber.setValue(voiceInfoResponse.getVoiceTimes());
                 adFreeGenerateNumber.setValue(voiceInfoResponse.getVoiceTimes());
-                List<CloneVoiceListBean> cloneVoiceList = voiceInfoResponse.getCloneVoiceList();
-                if (cloneVoiceList != null && !cloneVoiceList.isEmpty()) {
-                    cloneBean = cloneVoiceList.get(0);
-                    cloneBean.setCloneVoiceAvatar(voiceInfoResponse.getCloneVoiceAvatar());
-                    cloneBean.setCloneVoiceName(voiceInfoResponse.getCloneVoiceName());
-                    if (addVoiceBean != null) {
-                        addVoiceBean.setId(cloneBean.getId());
-                        addVoiceBean.setName(cloneBean.getCloneVoiceName());
-                        addVoiceBean.setAvatarUrl(cloneBean.getCloneVoiceAvatar());
-                        addVoiceBean.setVoiceUrl(cloneBean.getVoiceUrl());
-                    }
-                }
             }
             }
 
 
             @Override
             @Override
@@ -211,8 +223,8 @@ public class VoiceAIViewModel extends BaseViewModel {
         KVUtils.getDefault().putString(LAST_GENERATE_TEXT, generateTxt);
         KVUtils.getDefault().putString(LAST_GENERATE_TEXT, generateTxt);
     }
     }
 
 
-    public void refreshVoiceList(int id) {
-        if (id == this.currentTypeId) {
+    public void refreshVoiceList(String id) {
+        if (Objects.equals(this.currentTypeId, id)) {
             return;
             return;
         }
         }
         this.currentTypeId = id;
         this.currentTypeId = id;
@@ -229,27 +241,16 @@ public class VoiceAIViewModel extends BaseViewModel {
             @Override
             @Override
             public void onSuccess(@NonNull VoiceListResponse voiceListResponse) {
             public void onSuccess(@NonNull VoiceListResponse voiceListResponse) {
                 List<VoiceListBean> voiceList = voiceListResponse.getVoiceList();
                 List<VoiceListBean> voiceList = voiceListResponse.getVoiceList();
-                if (voiceList == null) {
-                    voiceList = new ArrayList<>();
-                }
-                if (cloneBean == null) {
-                    VoiceListBean cloneVoiceListBean = new VoiceListBean();
-                    cloneVoiceListBean.setAddIcon(true);
-                    cloneVoiceListBean.setId(-1);
-                    cloneVoiceListBean.setName(ContextUtil.getContext().getString(R.string.add_voice));
-                    addVoiceBean = cloneVoiceListBean;
-                    voiceList.add(0, cloneVoiceListBean);
-                } else {
-                    VoiceListBean cloneVoiceBean = getVoiceBeans(cloneBean);
-                    voiceList.add(0, cloneVoiceBean);
+                if (voiceList != null) {
+                    for (VoiceListBean voiceListBean : voiceList) {
+                        if (voiceListBean.isAddIcon()) {
+                            continue;
+                        }
+                        setChoiceVoiceBean(voiceListBean, false);
+                        break;
+                    }
                 }
                 }
                 voiceDetailList.setValue(voiceList);
                 voiceDetailList.setValue(voiceList);
-                VoiceListBean choiceBean = choiceVoiceBean.getValue();
-                if (choiceBean != null) {
-                    choiceBean.setVoicePlayState(VoiceListBean.VoicePlayState.INITIAL);
-                    choiceBean.setCheck(false);
-                }
-                choiceVoiceBean.setValue(null);
             }
             }
 
 
             @Override
             @Override
@@ -268,24 +269,33 @@ public class VoiceAIViewModel extends BaseViewModel {
         return voiceListBean;
         return voiceListBean;
     }
     }
 
 
-    public void setChoiceVoiceBean(VoiceListBean voiceListBean) {
-        choiceVoiceBean.setValue(voiceListBean);
+    private void setChoiceVoiceBean(VoiceListBean voiceListBean, boolean playVoice) {
+        if (this.choiceVoiceBean != null) {
+            this.choiceVoiceBean.setCheck(false);
+            this.choiceVoiceBean.setVoicePlayState(VoiceListBean.VoicePlayState.INITIAL);
+        }
+        this.choiceVoiceBean = voiceListBean;
+        this.choiceVoiceBean.setCheck(true);
+        if (playVoice) {
+            playExampleVoice.setValue(choiceVoiceBean);
+        }
+    }
 
 
+    public void voiceItemClick(VoiceListBean voiceListBean) {
+        setChoiceVoiceBean(voiceListBean, true);
     }
     }
 
 
     public void seeAdGenerate() {
     public void seeAdGenerate() {
-        VoiceListBean bean = choiceVoiceBean.getValue();
-        if (bean == null) {
+        if (choiceVoiceBean == null) {
             ToastUtil.show(R.string.voice_ai_no_choice, ToastUtil.LENGTH_SHORT);
             ToastUtil.show(R.string.voice_ai_no_choice, ToastUtil.LENGTH_SHORT);
             return;
             return;
         }
         }
-        requestGenerateVoice(bean);
+        requestGenerateVoice();
     }
     }
 
 
     public void setVoicePlayEnd() {
     public void setVoicePlayEnd() {
-        VoiceListBean bean = choiceVoiceBean.getValue();
-        if (bean != null) {
-            bean.setVoicePlayState(VoiceListBean.VoicePlayState.INITIAL);
+        if (choiceVoiceBean != null) {
+            choiceVoiceBean.setVoicePlayState(VoiceListBean.VoicePlayState.INITIAL);
         }
         }
     }
     }
 }
 }

BIN
app/src/main/res/drawable-xxhdpi/bg_generating_background1.webp


BIN
app/src/main/res/drawable-xxhdpi/bg_generating_background2.webp


+ 1 - 1
app/src/main/res/drawable/bg_voice_ai_list_check.xml

@@ -3,5 +3,5 @@
     <stroke
     <stroke
         android:width="2dp"
         android:width="2dp"
         android:color="@color/colorPrimaryVariant" />
         android:color="@color/colorPrimaryVariant" />
-    <corners android:radius="12dp" />
+    <corners android:radius="10dp" />
 </shape>
 </shape>

+ 5 - 0
app/src/main/res/drawable/bg_voice_playing.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="10dp" />
+    <solid android:color="@color/black60" />
+</shape>

+ 125 - 0
app/src/main/res/layout/activity_voice_generating.xml

@@ -0,0 +1,125 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+        <variable
+            name="voiceGeneratingViewModel"
+            type="com.atmob.voiceai.module.generating.VoiceGeneratingViewModel" />
+
+        <import type="com.atmob.common.ui.SizeUtil" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/colorPrimary">
+
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:src="@drawable/bg_generating_background1"
+            app:layout_constraintDimensionRatio="1080:1722"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:src="@drawable/bg_generating_background2"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintDimensionRatio="1080:1515"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+        <Space
+            android:id="@+id/space_status_bar"
+            android:layout_width="match_parent"
+            android:layout_height="@{SizeUtil.getStatusBarHeight(), default=@dimen/app_status_bar_height}"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <Space
+            android:id="@+id/space1"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintDimensionRatio="360:74"
+            app:layout_constraintTop_toBottomOf="@+id/space_status_bar" />
+
+        <ImageView
+            android:id="@+id/iv_avatar"
+            imageUrl="@{voiceGeneratingViewModel.cloneBean.avatarUrl}"
+            radius="@{100}"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="23dp"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/space1"
+            app:layout_constraintWidth_percent="0.2"
+            tools:src="@drawable/bg_voice_ai_tab_selected" />
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:textColor="@color/white"
+            android:textSize="20sp"
+            app:layout_constraintLeft_toLeftOf="@+id/iv_avatar"
+            app:layout_constraintTop_toBottomOf="@+id/iv_avatar"
+            tools:text="Taylor Swift" />
+
+        <Space
+            android:id="@+id/space2"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintDimensionRatio="360:32"
+            app:layout_constraintTop_toBottomOf="@id/tv_name" />
+
+        <TextView
+            android:id="@+id/tv_generating_hint"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voice_generating_hint"
+            android:textColor="@color/white"
+            android:textSize="30sp"
+            android:textStyle="bold"
+            app:layout_constraintLeft_toLeftOf="@id/iv_avatar"
+            app:layout_constraintTop_toBottomOf="@+id/space2" />
+
+        <TextView
+            android:id="@+id/tv_generating_hint2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:text="@string/voice_generating_hint2"
+            android:textColor="@color/white80"
+            android:textSize="14sp"
+            app:layout_constraintLeft_toLeftOf="@id/iv_avatar"
+            app:layout_constraintTop_toBottomOf="@+id/tv_generating_hint" />
+
+
+        <com.tencent.qgame.animplayer.AnimView
+            app:layout_constraintVertical_bias="0.184390243902439"
+            app:layout_constraintTop_toBottomOf="@+id/tv_generating_hint2"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:id="@+id/anim_view"
+            app:layout_constraintDimensionRatio="360:200.5"
+            android:layout_width="match_parent"
+            android:layout_height="0dp" />
+
+        <TextView
+            android:text="@{voiceGeneratingViewModel.generatingTime}"
+            app:layout_constraintVertical_bias="0.5274825174825175"
+            android:textSize="14sp"
+            android:textColor="@color/white"
+            app:layout_constraintTop_toBottomOf="@+id/anim_view"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:gravity="center"
+            tools:text="Generating ~ 15 sec"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 6 - 0
app/src/main/res/layout/activity_voice_result.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 22 - 4
app/src/main/res/layout/item_voice_ai_list.xml

@@ -55,20 +55,38 @@
             app:layout_constraintTop_toTopOf="parent" />
             app:layout_constraintTop_toTopOf="parent" />
 
 
         <View
         <View
+            android:id="@+id/v_voice_playing"
             isGone="@{bean.voicePlayState == VoicePlayState.INITIAL}"
             isGone="@{bean.voicePlayState == VoicePlayState.INITIAL}"
-            android:background="@color/colorPrimaryVariant"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:background="@drawable/bg_voice_playing"
             app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
             app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
             app:layout_constraintEnd_toEndOf="@+id/iv_avatar"
             app:layout_constraintEnd_toEndOf="@+id/iv_avatar"
             app:layout_constraintStart_toStartOf="@+id/iv_avatar"
             app:layout_constraintStart_toStartOf="@+id/iv_avatar"
-            app:layout_constraintTop_toTopOf="@+id/iv_avatar"
+            app:layout_constraintTop_toTopOf="@+id/iv_avatar" />
+
+
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/svga_voice_loading"
+            isGone="@{bean.voicePlayState == VoicePlayState.INITIAL}"
             android:layout_width="0dp"
             android:layout_width="0dp"
-            android:layout_height="0dp" />
+            android:layout_height="0dp"
+            android:background="@color/transparent"
+            app:lottie_autoPlay="true"
+            app:lottie_loop="true"
+            app:lottie_fileName="anim/voice_ai_example_loading.json"
+            app:layout_constraintBottom_toBottomOf="@+id/v_voice_playing"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintEnd_toEndOf="@+id/v_voice_playing"
+            app:layout_constraintStart_toStartOf="@+id/v_voice_playing"
+            app:layout_constraintTop_toTopOf="@+id/v_voice_playing"
+            app:layout_constraintWidth_percent="0.5128205128205128" />
 
 
         <View
         <View
-            android:background="@drawable/bg_voice_ai_list_check"
             isGone="@{!bean.check}"
             isGone="@{!bean.check}"
             android:layout_width="0dp"
             android:layout_width="0dp"
             android:layout_height="0dp"
             android:layout_height="0dp"
+            android:background="@drawable/bg_voice_ai_list_check"
             app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
             app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
             app:layout_constraintEnd_toEndOf="@+id/iv_avatar"
             app:layout_constraintEnd_toEndOf="@+id/iv_avatar"
             app:layout_constraintStart_toStartOf="@+id/iv_avatar"
             app:layout_constraintStart_toStartOf="@+id/iv_avatar"

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

@@ -17,4 +17,9 @@
     <string name="voice_new">New</string>
     <string name="voice_new">New</string>
     <string name="voice_ai_no_choice">Please choose</string>
     <string name="voice_ai_no_choice">Please choose</string>
     <string name="voice_play_error">Play Error</string>
     <string name="voice_play_error">Play Error</string>
+    <string name="voice_generating_hint">PLEASE\nDO NOT CLOSE\nTHE APPLICATION</string>
+    <string name="voice_generating_hint2">WHILE GENERATING VOICE</string>
+    <string name="generating_time">Generating ~ %d sec</string>
+    <string name="generating_time_countdown_over">It\'ll be ready soon</string>
+    <string name="generate_error">Generation timed out, please try again</string>
 </resources>
 </resources>

+ 2 - 1
build.gradle

@@ -26,7 +26,8 @@ buildscript {
         lottie_version = "6.0.0"
         lottie_version = "6.0.0"
         recyclerview_version = "1.3.0"
         recyclerview_version = "1.3.0"
         constraintlayout_version = "2.1.4"
         constraintlayout_version = "2.1.4"
-        stringfog_verstion = "4.0.1"
+        stringfog_version = "4.0.1"
+        media3_version = "1.3.1"
     }
     }
 
 
     dependencies {
     dependencies {