浏览代码

增加启屏页流程

zk 2 年之前
父节点
当前提交
179dc5fd80

+ 3 - 1
app/src/main/java/com/atmob/voiceai/data/repositories/AccountRepository.java

@@ -74,7 +74,9 @@ public class AccountRepository {
                     if (memberInfo != null) {
                         memberRepository.setIsMember(memberInfo.isIsMember());
                         refreshMemberHandler.removeCallbacksAndMessages(null);
-                        refreshMemberHandler.postDelayed(this::refreshUserData, memberInfo.getExpireTime() - memberInfo.getTimestamp());
+                        if (memberInfo.isIsMember()) {
+                            refreshMemberHandler.postDelayed(this::refreshUserData, memberInfo.getExpireTime() - memberInfo.getTimestamp());
+                        }
                     }
                 });
     }

+ 0 - 11
app/src/main/java/com/atmob/voiceai/module/clonevoice/CloneVoiceFragment.java

@@ -1,17 +1,12 @@
 package com.atmob.voiceai.module.clonevoice;
 
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.text.method.ScrollingMovementMethod;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.widget.SeekBar;
-
 import androidx.activity.result.ActivityResult;
 import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.NonNull;
@@ -21,25 +16,19 @@ import androidx.media3.common.MediaItem;
 import androidx.media3.common.PlaybackException;
 import androidx.media3.common.Player;
 import androidx.media3.exoplayer.ExoPlayer;
-
 import com.atmob.app.lib.base.BaseFragment;
 import com.atmob.common.logging.AtmobLog;
-import com.atmob.common.runtime.ActivityUtil;
 import com.atmob.common.ui.SizeUtil;
 import com.atmob.voiceai.R;
 import com.atmob.voiceai.databinding.FragmentCloneVoiceBinding;
 import com.atmob.voiceai.dialog.CloneDeleteSureDialog;
 import com.atmob.voiceai.dialog.RecordingInstructionsDialog;
-import com.atmob.voiceai.module.cloning.VoiceCloningActivity;
 import com.atmob.voiceai.utils.ActivityForResultUtil;
 import com.atmob.voiceai.utils.AudioRecorder;
-import com.atmob.voiceai.utils.FileUtils;
 import com.atmob.voiceai.utils.ToastUtil;
 import com.atmob.voiceai.widget.AudioProgressBar;
 import com.gyf.immersionbar.ImmersionBar;
-
 import java.io.IOException;
-
 import dagger.hilt.android.AndroidEntryPoint;
 
 @AndroidEntryPoint

+ 28 - 1
app/src/main/java/com/atmob/voiceai/module/main/MainActivity.java

@@ -4,14 +4,17 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.view.KeyEvent;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 
 import com.atmob.app.lib.base.BaseActivity;
+import com.atmob.voiceai.R;
 import com.atmob.voiceai.databinding.ActivityMainBinding;
 import com.atmob.voiceai.databinding.ItemMainTabLayoutBinding;
+import com.atmob.voiceai.utils.ToastUtil;
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayoutMediator;
 import com.gyf.immersionbar.ImmersionBar;
@@ -22,7 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint;
 @AndroidEntryPoint
 public class MainActivity extends BaseActivity<ActivityMainBinding> {
 
-
+    private long mExitTime;
     private MainViewModel mainViewModel;
     private static final String TO_TAB_FRAGMENT = "to_tab_fragment";
     private TabLayoutMediator tabLayoutMediator;
@@ -136,4 +139,28 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> {
     protected boolean shouldImmersion() {
         return true;
     }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
+            exit();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    public void exit() {
+        if ((System.currentTimeMillis() - mExitTime) > 2000) {
+            ToastUtil.show(R.string.exit_application_text, ToastUtil.LENGTH_LONG);
+            mExitTime = System.currentTimeMillis();
+        } else {
+            try {
+                Intent intent = new Intent();
+                intent.setAction(Intent.ACTION_MAIN);
+                intent.addCategory(Intent.CATEGORY_HOME);
+                startActivity(intent);
+            } catch (Exception ignore) {
+            }
+        }
+    }
 }

+ 2 - 7
app/src/main/java/com/atmob/voiceai/module/setting/SettingViewModel.java

@@ -56,16 +56,11 @@ public class SettingViewModel extends BaseViewModel {
     }
 
     public void onPrivacyPolicyClick() {
-        startWeb(Constants.PRIVACY_POLICY);
+        CommonUtils.startWeb(Constants.PRIVACY_POLICY);
     }
 
     public void onTermsOfServiceClick() {
-        startWeb(Constants.USER_AGREEMENT);
+        CommonUtils.startWeb(Constants.USER_AGREEMENT);
     }
 
-    private void startWeb(String url) {
-        Uri uri = Uri.parse(url);
-        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        ActivityUtil.getTopActivity().startActivity(intent);
-    }
 }

+ 147 - 0
app/src/main/java/com/atmob/voiceai/module/splash/SplashActivity.java

@@ -1,16 +1,32 @@
 package com.atmob.voiceai.module.splash;
 
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.atmob.app.lib.base.BaseActivity;
+import com.atmob.common.ui.SizeUtil;
+import com.atmob.voiceai.R;
+import com.atmob.voiceai.data.consts.Constants;
 import com.atmob.voiceai.databinding.ActivitySplashBinding;
 import com.atmob.voiceai.module.main.MainActivity;
+import com.atmob.voiceai.utils.CommonUtils;
+import com.atmob.voiceai.utils.SpannableUtil;
+
+import dagger.hilt.android.AndroidEntryPoint;
 
 @SuppressLint("CustomSplashScreen")
+@AndroidEntryPoint
 public class SplashActivity extends BaseActivity<ActivitySplashBinding> {
+
+
+    private SplashViewModel splashViewModel;
+
     @Override
     protected boolean shouldImmersion() {
         return true;
@@ -20,6 +36,137 @@ public class SplashActivity extends BaseActivity<ActivitySplashBinding> {
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        initView();
+        initObserver();
+    }
+
+    private void initView() {
+
+    }
+
+    private void initObserver() {
+        splashViewModel.getShowMainEvent().observe(this, o -> showMain());
+        splashViewModel.getCurrentGuide().observe(this, current -> {
+            if (current == null) {
+                return;
+            }
+            if (SplashViewModel.GUIDE_PAGE_1 == current) {
+                startGuideEnter(binding.ivGuideHeader1, binding.tvGuideHeader1, binding.ivGuide1, 400);
+            } else if (SplashViewModel.GUIDE_PAGE_2 == current) {
+                startGuideLeave(binding.ivGuideHeader1, binding.tvGuideHeader1, binding.ivGuide1);
+                new Handler().postDelayed(() -> {
+                    startGuideEnter(binding.ivGuideHeader2, binding.tvGuideHeader2, binding.ivGuide2, 0);
+                }, 600);
+            } else if (SplashViewModel.GUIDE_PAGE_3 == current) {
+                startGuideLeave(binding.ivGuideHeader2, binding.tvGuideHeader2, binding.ivGuide2);
+                new Handler().postDelayed(() -> {
+                    binding.tvNextStep.animate().alpha(0).setDuration(200).start();
+                }, 400);
+                showHeadGuideView();
+                showEndGuide();
+            } else if (SplashViewModel.GUIDE_PAGE_END == current) {
+                closeGuide();
+            }
+        });
+    }
+
+    private void showHeadGuideView() {
+        int[] resIds = {
+
+        };
+        SplashGuideAdapter splashGuideAdapter = new SplashGuideAdapter(resIds);
+        binding.rvGuide.setAdapter(splashGuideAdapter);
+        binding.rvGuide.setLayoutManager();
+    }
+
+    private void closeGuide() {
+        goMain();
+    }
+
+    private void showEndGuide() {
+        binding.tvPracticalFunny.setVisibility(View.VISIBLE);
+        binding.tvPractical.setVisibility(View.VISIBLE);
+        binding.tvGuideDone.setVisibility(View.VISIBLE);
+        binding.tvPrivacyPolicy.setVisibility(View.VISIBLE);
+        String allTxt = getString(R.string.splash_privacy_all_txt);
+        String targetTxt1 = getString(R.string.splash_privacy_policy);
+        String targetTxt2 = getString(R.string.splash_terms_of_service);
+        SpannableUtil.getAgreementSpannableStringBuilder(binding.tvPrivacyPolicy, allTxt, new String[]{targetTxt1, targetTxt2}, getResources().getColor(R.color.colorPrimaryVariant), false,
+                v -> {
+                    CommonUtils.startWeb(Constants.PRIVACY_POLICY);
+                },
+                v -> {
+                    CommonUtils.startWeb(Constants.USER_AGREEMENT);
+                });
+        ValueAnimator alphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+        alphaAnimator.setStartDelay(400);
+        alphaAnimator.setDuration(800);
+        alphaAnimator.addUpdateListener(animation -> {
+            float alpha = (float) animation.getAnimatedValue();
+            binding.tvPracticalFunny.setAlpha(alpha);
+            binding.tvPractical.setAlpha(alpha);
+            binding.tvGuideDone.setAlpha(alpha);
+            binding.tvPrivacyPolicy.setAlpha(alpha);
+            binding.rvGuide.setAlpha(alpha);
+            binding.vGuideMask.setAlpha(alpha);
+        });
+        alphaAnimator.start();
+    }
+
+
+    private void startGuideEnter(@NonNull View headerView1, @NonNull View headerView2, @NonNull View contentView, int contentDelay) {
+        ValueAnimator alphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+        alphaAnimator.setDuration(800);
+        alphaAnimator.addUpdateListener(animation -> {
+            float alpha = (float) animation.getAnimatedValue();
+            headerView1.setAlpha(alpha);
+            headerView2.setAlpha(alpha);
+        });
+        alphaAnimator.start();
+
+        ValueAnimator contentAnimator = ValueAnimator.ofFloat(SizeUtil.getScreenWidth(), 0f);
+        contentAnimator.setStartDelay(contentDelay);
+        contentAnimator.setDuration(800);
+        contentAnimator.addUpdateListener(animation -> {
+            float value = (float) animation.getAnimatedValue();
+            contentView.setX(value);
+        });
+        contentAnimator.start();
+    }
+
+
+    private void startGuideLeave(@NonNull View headerView1, @NonNull View headerView2, @NonNull View contentView) {
+        ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1f, 0f);
+        alphaAnimator.setDuration(600);
+        alphaAnimator.addUpdateListener(animation -> {
+            float alpha = (float) animation.getAnimatedValue();
+            headerView1.setAlpha(alpha);
+            headerView2.setAlpha(alpha);
+        });
+        alphaAnimator.start();
+
+        ValueAnimator contentAnimator = ValueAnimator.ofFloat(0f, SizeUtil.getScreenWidth());
+        contentAnimator.setDuration(800);
+        contentAnimator.addUpdateListener(animation -> {
+            float value = (float) animation.getAnimatedValue();
+            contentView.setX(-value);
+        });
+        contentAnimator.start();
+    }
+
+    @Override
+    protected void initViewModel() {
+        super.initViewModel();
+        splashViewModel = getViewModelProvider().get(SplashViewModel.class);
+        binding.setSplashViewModel(splashViewModel);
+    }
+
+
+    private void showMain() {
+        new Handler().postDelayed(this::goMain, 2000);
+    }
+
+    private void goMain() {
         MainActivity.start(this);
         finish();
     }

+ 49 - 0
app/src/main/java/com/atmob/voiceai/module/splash/SplashGuideAdapter.java

@@ -0,0 +1,49 @@
+package com.atmob.voiceai.module.splash;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.atmob.voiceai.databinding.ItemSplashGuideBinding;
+
+public class SplashGuideAdapter extends RecyclerView.Adapter<SplashGuideAdapter.ViewHolder> {
+
+    private final int[] guideImages;
+
+    public SplashGuideAdapter(int[] guideImages) {
+        this.guideImages = guideImages;
+    }
+
+    @NonNull
+    @Override
+    public SplashGuideAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        ItemSplashGuideBinding binding = ItemSplashGuideBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
+        return new SplashGuideAdapter.ViewHolder(ItemSplashGuideBinding.inflate(binding));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull SplashGuideAdapter.ViewHolder holder, int position) {
+        holder.bind(guideImages[position]);
+    }
+
+    @Override
+    public int getItemCount() {
+        return guideImages.length;
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+
+        private ItemSplashGuideBinding binding;
+
+        public ViewHolder(@NonNull ItemSplashGuideBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+        }
+
+        public void bind(int resId) {
+            binding.setResId(resId);
+        }
+    }
+}

+ 67 - 0
app/src/main/java/com/atmob/voiceai/module/splash/SplashViewModel.java

@@ -0,0 +1,67 @@
+package com.atmob.voiceai.module.splash;
+
+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.data.KVUtils;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.lifecycle.HiltViewModel;
+
+@HiltViewModel
+public class SplashViewModel extends BaseViewModel {
+
+    public static final int GUIDE_PAGE_1 = 1;
+    public static final int GUIDE_PAGE_2 = 2;
+    public static final int GUIDE_PAGE_3 = 3;
+    public static final int GUIDE_PAGE_END = 4;
+
+    private static final String IS_FIRST_OPEN = "is_first_open";
+    private final MutableLiveData<Boolean> showGuide = new MutableLiveData<>();
+    private final SingleLiveEvent<?> showMainEvent = new SingleLiveEvent<>();
+    private final MutableLiveData<Integer> currentGuide = new MutableLiveData<>();
+
+    @Inject
+    public SplashViewModel() {
+        boolean isFirst = KVUtils.getDefault().getBoolean(IS_FIRST_OPEN, true);
+        if (isFirst) {
+            showGuide.setValue(true);
+            currentGuide.setValue(GUIDE_PAGE_3);
+        } else {
+            showMainEvent.call();
+        }
+    }
+
+    public LiveData<Integer> getCurrentGuide() {
+        return currentGuide;
+    }
+
+    public LiveData<?> getShowMainEvent() {
+        return showMainEvent;
+    }
+
+    public LiveData<Boolean> getShowGuide() {
+        return showGuide;
+    }
+
+
+    private void firstStartMain() {
+//        KVUtils.getDefault().putBoolean(IS_FIRST_OPEN, false);
+    }
+
+    public void onContinueClick(int index) {
+        if (index == GUIDE_PAGE_1) {
+            currentGuide.setValue(GUIDE_PAGE_2);
+        } else if (index == GUIDE_PAGE_2) {
+            currentGuide.setValue(GUIDE_PAGE_3);
+        }
+    }
+
+    public void onGuideDoneClick() {
+        firstStartMain();
+        currentGuide.setValue(GUIDE_PAGE_END);
+    }
+}

+ 8 - 0
app/src/main/java/com/atmob/voiceai/utils/CommonUtils.java

@@ -5,6 +5,8 @@ import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 
+import com.atmob.common.runtime.ActivityUtil;
+
 public class CommonUtils {
 
     /**
@@ -26,4 +28,10 @@ public class CommonUtils {
         emailIntent.putExtra(Intent.EXTRA_TEXT, content);
         context.startActivity(Intent.createChooser(emailIntent, "Choose Email Client"));
     }
+
+    public static void startWeb(String url) {
+        Uri uri = Uri.parse(url);
+        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+        ActivityUtil.getTopActivity().startActivity(intent);
+    }
 }

+ 70 - 0
app/src/main/java/com/atmob/voiceai/widget/GradientBorderView.java

@@ -0,0 +1,70 @@
+package com.atmob.voiceai.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.atmob.voiceai.R;
+
+public class GradientBorderView extends View {
+
+
+    private int startColor;
+    private int endColor;
+    private int strokeWidth;
+    private Paint bgPaint;
+    private Paint centerPaint;
+    RectF bgRectF;
+    RectF centerRectF;
+    private float radius;
+
+    public GradientBorderView(Context context) {
+        super(context);
+        init();
+    }
+
+    public GradientBorderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public GradientBorderView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        startColor = 0xff000000;
+        endColor = 0xff000000;
+        strokeWidth = 12;
+        centerRectF = new RectF();
+        bgRectF = new RectF();
+
+        bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        bgPaint.setColor(getContext().getResources().getColor(R.color.colorPrimaryVariant));
+        bgPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
+
+        centerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        centerPaint.setColor(getContext().getResources().getColor(R.color.voice_record_0_20));
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        bgRectF.set(0, 0, w, h);
+        centerRectF.set(strokeWidth, strokeWidth, w - strokeWidth, h - strokeWidth);
+        radius = h / 2f;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        canvas.drawRoundRect(bgRectF, radius, radius, bgPaint);
+        canvas.drawRoundRect(centerRectF, radius, radius, centerPaint);
+    }
+}

+ 26 - 0
app/src/main/java/com/atmob/voiceai/widget/HollowStrokeTextView.java

@@ -0,0 +1,26 @@
+package com.atmob.voiceai.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class HollowStrokeTextView extends androidx.appcompat.widget.AppCompatTextView {
+
+
+    public HollowStrokeTextView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public HollowStrokeTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public HollowStrokeTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+    }
+
+
+}

二进制
app/src/main/res/drawable-xxhdpi/splash_guide_1.webp


二进制
app/src/main/res/drawable-xxhdpi/splash_guide_2.webp


二进制
app/src/main/res/drawable-xxhdpi/splash_guide_header_1.webp


二进制
app/src/main/res/drawable-xxhdpi/splash_guide_header_2.webp


+ 23 - 0
app/src/main/res/drawable/bg_splash_continue.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="24dp" />
+            <gradient
+                android:endColor="#0594D7"
+                android:startColor="#12F45D" />
+        </shape>
+    </item>
+
+    <item
+        android:bottom="2dp"
+        android:left="2dp"
+        android:right="2dp"
+        android:top="2dp">
+        <shape android:shape="rectangle">
+            <corners android:radius="24dp" />
+            <solid android:color="@color/colorPrimary" />
+        </shape>
+    </item>
+</layer-list>

+ 9 - 0
app/src/main/res/drawable/rv_guide_mask.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:angle="-90"
+        android:endColor="#18191A"
+        android:centerX="0.7"
+        android:centerColor="#18191A"
+        android:startColor="#0018191A" />
+</shape>

+ 241 - 5
app/src/main/res/layout/activity_splash.xml

@@ -1,7 +1,243 @@
 <?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:background="@drawable/bg_theme_splash"
-    android:layout_height="match_parent">
+<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">
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+    <data>
+
+        <variable
+            name="splashViewModel"
+            type="com.atmob.voiceai.module.splash.SplashViewModel" />
+
+        <import type="com.atmob.common.ui.SizeUtil" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/bg_theme_splash">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/guide_layout"
+            isGone="@{!splashViewModel.showGuide}"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <Space
+                android:id="@+id/space_status"
+                android:layout_width="match_parent"
+                android:layout_height="@{SizeUtil.getStatusBarHeight(), default=@dimen/app_status_bar_height}"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@color/colorPrimary" />
+
+            <Space
+                android:id="@+id/space1"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:46"
+                app:layout_constraintTop_toBottomOf="@+id/space_status" />
+
+            <ImageView
+                android:id="@+id/iv_guide_header1"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:alpha="0"
+                android:src="@drawable/splash_guide_header_1"
+                app:layout_constraintDimensionRatio="232:33"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/space1"
+                app:layout_constraintWidth_percent="0.6444444444444444" />
+
+
+            <TextView
+                android:id="@+id/tv_guide_header1"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:alpha="0"
+                android:text="@string/splash_generate_header_txt_1"
+                android:textColor="@color/white"
+                android:textSize="14sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/iv_guide_header1"
+                app:layout_constraintWidth_percent="0.7777777777777778" />
+
+            <Space
+                android:id="@+id/space2"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:27"
+                app:layout_constraintTop_toBottomOf="@+id/tv_guide_header1" />
+
+            <ImageView
+                android:id="@+id/iv_guide1"
+                x="@{SizeUtil.getScreenWidth()}"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:src="@drawable/splash_guide_1"
+                app:layout_constraintDimensionRatio="360:320"
+                app:layout_constraintTop_toBottomOf="@+id/space2"
+                tools:visibility="gone" />
+
+
+            <ImageView
+                android:id="@+id/iv_guide_header2"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:alpha="0"
+                android:src="@drawable/splash_guide_header_2"
+                app:layout_constraintDimensionRatio="214:33"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/space1"
+                app:layout_constraintWidth_percent="0.5944444444444444" />
+
+            <TextView
+                android:id="@+id/tv_guide_header2"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:alpha="0"
+                android:text="@string/splash_generate_header_txt_2"
+                android:textColor="@color/white"
+                android:textSize="14sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/iv_guide_header1"
+                app:layout_constraintWidth_percent="0.7777777777777778" />
+
+
+            <Space
+                android:id="@+id/space3"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:27"
+                app:layout_constraintTop_toBottomOf="@+id/tv_guide_header2" />
+
+            <ImageView
+                android:id="@+id/iv_guide2"
+                x="@{SizeUtil.getScreenWidth()}"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:src="@drawable/splash_guide_2"
+                app:layout_constraintDimensionRatio="360:320"
+                app:layout_constraintTop_toBottomOf="@+id/space3"
+                tools:visibility="gone" />
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/rv_guide"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:alpha="0" />
+
+            <View
+                android:id="@+id/v_guide_mask"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:alpha="0"
+                android:background="@drawable/rv_guide_mask" />
+
+            <TextView
+                android:id="@+id/tv_guide_done"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:alpha="0"
+                android:background="@drawable/bg_voice_ai_btn"
+                android:gravity="center"
+                android:onClick="@{()-> splashViewModel.onGuideDoneClick()}"
+                android:text="@string/splash_guide_done"
+                android:textColor="@color/colorPrimary"
+                android:textSize="17sp"
+                android:textStyle="bold"
+                android:visibility="gone"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintDimensionRatio="240:48"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/space_status"
+                app:layout_constraintVertical_bias="0.8732394366197183"
+                app:layout_constraintWidth_percent="0.6666666666666667" />
+
+            <TextView
+                android:id="@+id/tv_privacy_policy"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:alpha="0"
+                android:gravity="center"
+                android:textColor="@color/white80"
+                android:textSize="12sp"
+                android:visibility="gone"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/tv_guide_done"
+                app:layout_constraintVertical_bias="0.6363636363636364"
+                app:layout_constraintWidth_percent="0.7611111111111111"
+                tools:text="After clicking the button, you agree to the app’s 
+privacy policy and Terms of service" />
+
+            <Space
+                android:id="@+id/space4"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintBottom_toTopOf="@+id/tv_guide_done"
+                app:layout_constraintDimensionRatio="360:48" />
+
+            <TextView
+                android:id="@+id/tv_practical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:alpha="0"
+                android:gravity="center"
+                android:text="@string/splash_practical_txt"
+                android:textColor="@color/white"
+                android:textSize="32sp"
+                android:textStyle="bold"
+                android:visibility="gone"
+                app:layout_constraintBottom_toTopOf="@+id/space4" />
+
+            <TextView
+                android:id="@+id/tv_practical_funny"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dp"
+                android:alpha="0"
+                android:text="@string/splash_practical_funny_txt"
+                android:textColor="@color/white50"
+                android:textSize="17sp"
+                android:visibility="gone"
+                app:layout_constraintBottom_toTopOf="@+id/tv_practical"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent" />
+
+
+            <TextView
+                android:id="@+id/tv_next_step"
+                isGone="@{!splashViewModel.showGuide}"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:background="@drawable/bg_splash_continue"
+                android:gravity="center"
+                android:onClick="@{()-> splashViewModel.onContinueClick(splashViewModel.currentGuide)}"
+                android:text="@string/splash_continue"
+                android:textColor="@color/white"
+                android:textSize="17sp"
+                android:textStyle="bold"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintDimensionRatio="248:48"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/space_status"
+                app:layout_constraintVertical_bias="0.9154929577464789"
+                app:layout_constraintWidth_percent="0.6888888888888889" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 27 - 0
app/src/main/res/layout/item_splash_guide.xml

@@ -0,0 +1,27 @@
+<?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="resId"
+            type="Integer" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:id="@+id/iv_guide"
+            imageRes="@{resId}"
+            radius="@{12}"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:src="@drawable/icon_default_avatar" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

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

@@ -75,4 +75,14 @@
     <string name="record_maybe_best">Can be cloned, but results may not be accurate</string>
     <string name="record_best">Can be cloned! he results are more accurate</string>
     <string name="cloning_state">Cloning</string>
+    <string name="splash_continue">Continue</string>
+    <string name="exit_application_text">Press again to exit the program.</string>
+    <string name="splash_generate_header_txt_1">just type whatever you want,select voice and tap generate. It is so simple</string>
+    <string name="splash_generate_header_txt_2">just type whatever you want,select voice and tap generate. It is so simple of your voice</string>
+    <string name="splash_guide_done">Start Having Fun</string>
+    <string name="splash_practical_txt">Practical And \nFun AI Voice App</string>
+    <string name="splash_practical_funny_txt">Funny Voice AI</string>
+    <string name="splash_privacy_all_txt"><![CDATA[After clicking the button, you agree to the app’s \nprivacy policy and Terms of service]]></string>
+    <string name="splash_privacy_policy">privacy policy</string>
+    <string name="splash_terms_of_service">Terms of service</string>
 </resources>