فهرست منبع

[new]新增android端独立启屏页以及隐私政策流程

zk 1 سال پیش
والد
کامیت
d2eb509942
35فایلهای تغییر یافته به همراه1364 افزوده شده و 24 حذف شده
  1. 18 0
      android/app/build.gradle
  2. 24 15
      android/app/src/main/AndroidManifest.xml
  3. 0 6
      android/app/src/main/java/com/atmob/elec_asst/MainActivity.java
  4. 14 0
      android/app/src/main/java/com/atmob/elec_asst/MyApplication.java
  5. 139 0
      android/app/src/main/java/com/atmob/elec_asst/activity/BrowserActivity.java
  6. 31 0
      android/app/src/main/java/com/atmob/elec_asst/activity/MainActivity.java
  7. 72 0
      android/app/src/main/java/com/atmob/elec_asst/activity/SplashActivity.java
  8. 131 0
      android/app/src/main/java/com/atmob/elec_asst/base/BaseActivity.java
  9. 320 0
      android/app/src/main/java/com/atmob/elec_asst/base/BaseDialog.java
  10. 76 0
      android/app/src/main/java/com/atmob/elec_asst/base/DialogQueue.java
  11. 7 0
      android/app/src/main/java/com/atmob/elec_asst/base/IFragmentBackController.java
  12. 15 0
      android/app/src/main/java/com/atmob/elec_asst/constants/Constants.java
  13. 59 0
      android/app/src/main/java/com/atmob/elec_asst/dialog/AgreementDialog.java
  14. 70 0
      android/app/src/main/java/com/atmob/elec_asst/utils/SharedPreferencesUtil.java
  15. 55 0
      android/app/src/main/java/com/atmob/elec_asst/utils/SizeUtil.java
  16. 50 0
      android/app/src/main/java/com/atmob/elec_asst/utils/SpannableUtil.java
  17. 7 0
      android/app/src/main/res/anim/anim_alpha_in.xml
  18. 7 0
      android/app/src/main/res/anim/anim_alpha_out.xml
  19. BIN
      android/app/src/main/res/drawable-xxhdpi/bg_splash.webp
  20. BIN
      android/app/src/main/res/drawable-xxhdpi/icon_back.webp
  21. BIN
      android/app/src/main/res/drawable-xxhdpi/icon_splash_logo.webp
  22. BIN
      android/app/src/main/res/drawable-xxhdpi/icon_splash_title.webp
  23. 5 0
      android/app/src/main/res/drawable/bg_agreement_agree.xml
  24. 5 0
      android/app/src/main/res/drawable/bg_agreement_dialog.xml
  25. 5 0
      android/app/src/main/res/drawable/bg_agreement_disagree.xml
  26. 31 0
      android/app/src/main/res/drawable/bg_theme_splash.xml
  27. 36 0
      android/app/src/main/res/layout/activity_browser.xml
  28. 8 0
      android/app/src/main/res/layout/activity_splash.xml
  29. 128 0
      android/app/src/main/res/layout/dialog_agreement.xml
  30. 8 0
      android/app/src/main/res/values/strings.xml
  31. 17 0
      android/app/src/main/res/values/styles.xml
  32. 18 0
      android/app/src/main/res/values/themes.xml
  33. 4 0
      android/build.gradle
  34. 4 2
      lib/main.dart
  35. 0 1
      lib/module/splash/controller.dart

+ 18 - 0
android/app/build.gradle

@@ -87,6 +87,24 @@ android {
             signingConfig signingConfigs.ting
         }
     }
+
+    buildFeatures {
+        viewBinding true
+        dataBinding true
+    }
+
+}
+
+dependencies {
+
+    //AppCompat
+    implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompat_version"
+
+    //ConstraintLayout
+    implementation "androidx.constraintlayout:constraintlayout:$rootProject.ext.constraintlayout_version"
+
+    //immersionbar
+    implementation "com.geyifeng.immersionbar:immersionbar:$rootProject.ext.immersionbar_version"
 }
 
 flutter {

+ 24 - 15
android/app/src/main/AndroidManifest.xml

@@ -4,34 +4,43 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <!--    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
 
     <application
-        android:name="${applicationName}"
+        android:name=".MyApplication"
+        android:allowBackup="false"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
+        android:theme="@style/Theme.ElecAsst"
         android:networkSecurityConfig="@xml/network_security_config">
+
         <activity
-            android:name=".MainActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:theme="@style/Theme.Splash"
+            android:name=".activity.SplashActivity"
             android:exported="true"
+            android:screenOrientation="portrait">
+
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".activity.MainActivity"
+            android:exported="false"
             android:hardwareAccelerated="true"
             android:launchMode="singleTop"
-            android:taskAffinity=""
-            android:theme="@style/LaunchTheme"
-            android:windowSoftInputMode="adjustResize">
-            <!-- Specifies an Android theme to apply to this Activity as soon as
-                 the Android process has started. This theme is visible to the user
-                 while the Flutter UI initializes. After that, this theme continues
-                 to determine the Window background behind the Flutter UI. -->
+            android:windowSoftInputMode="adjustResize"
+            android:screenOrientation="portrait">
             <meta-data
                 android:name="io.flutter.embedding.android.NormalTheme"
                 android:resource="@style/NormalTheme" />
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
         </activity>
+
+        <activity
+            android:name=".activity.BrowserActivity"
+            android:screenOrientation="portrait" />
         <!-- Don't delete the meta-data below.
              This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
         <meta-data

+ 0 - 6
android/app/src/main/java/com/atmob/elec_asst/MainActivity.java

@@ -1,6 +0,0 @@
-package com.atmob.elec_asst;
-
-import io.flutter.embedding.android.FlutterActivity;
-
-public class MainActivity extends FlutterActivity {
-}

+ 14 - 0
android/app/src/main/java/com/atmob/elec_asst/MyApplication.java

@@ -0,0 +1,14 @@
+package com.atmob.elec_asst;
+
+import android.app.Application;
+
+import com.atmob.elec_asst.utils.SharedPreferencesUtil;
+
+public class MyApplication extends Application {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        SharedPreferencesUtil.init(this);
+    }
+}

+ 139 - 0
android/app/src/main/java/com/atmob/elec_asst/activity/BrowserActivity.java

@@ -0,0 +1,139 @@
+package com.atmob.elec_asst.activity;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.gyf.immersionbar.ImmersionBar;
+import com.atmob.elec_asst.base.BaseActivity;
+import com.atmob.elec_asst.databinding.ActivityBrowserBinding;
+
+
+public class BrowserActivity extends BaseActivity<ActivityBrowserBinding> {
+
+    private static final String KEY_URL = "key_browser_url";
+
+    public static void start(Context context, String url) {
+        Intent intent = new Intent(context, BrowserActivity.class);
+        intent.putExtra(KEY_URL, url);
+        context.startActivity(intent);
+    }
+
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initView();
+        checkIntent(getIntent());
+    }
+
+
+
+    private void initView() {
+        addTopStatusBarHeight(binding.browserHeader);
+        initWebView();
+        binding.browserHeader.setNavigationOnClickListener(view -> {
+            finish();
+        });
+    }
+
+
+    @SuppressLint("SetJavaScriptEnabled")
+    private void initWebView() {
+        WebSettings settings = binding.browserWebView.getSettings();
+        settings.setUseWideViewPort(true);
+        settings.setLoadWithOverviewMode(true);
+        settings.setJavaScriptEnabled(true);
+        settings.setDomStorageEnabled(true);
+        settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
+        settings.setLoadsImagesAutomatically(true);
+        settings.setMediaPlaybackRequiresUserGesture(true);
+        settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+        settings.setAllowFileAccess(true);
+        settings.setJavaScriptCanOpenWindowsAutomatically(true);
+        settings.setDefaultTextEncodingName("utf-8");
+        settings.setAllowContentAccess(true);
+        settings.setAllowFileAccessFromFileURLs(true);
+
+        binding.browserWebView.setWebViewClient(new WebViewClient() {
+            @Override
+            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+                String url = request.getUrl().toString();
+                if (url.startsWith("http://") || url.startsWith("https://")) {
+                    binding.browserWebView.loadUrl(url);
+                    return false;
+                } else {
+                    try {
+                        Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        startActivity(intent);
+                        return true;
+                    } catch (Exception e) {
+                        return false;
+                    }
+                }
+            }
+        });
+        binding.browserWebView.setWebChromeClient(new WebChromeClient() {
+            @Override
+            public void onReceivedTitle(WebView view, String title) {
+                super.onReceivedTitle(view, title);
+                binding.browserHeader.setTitle(title);
+            }
+        });
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        checkIntent(intent);
+    }
+
+    private void checkIntent(Intent intent) {
+        String url = intent.getStringExtra(KEY_URL);
+        if (TextUtils.isEmpty(url)) {
+            return;
+        }
+        if (isUrl(url)) {
+            binding.browserWebView.loadUrl(url);
+        } else {
+            try {
+                Uri uri = Uri.parse(url);
+                startActivity(new Intent(Intent.ACTION_VIEW, uri));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            finish();
+        }
+    }
+
+    public boolean isUrl(String str) {
+        if (TextUtils.isEmpty(str)) {
+            return false;
+        }
+        String upperCaseStr = str.toUpperCase();
+        return upperCaseStr.startsWith("HTTP://") || upperCaseStr.startsWith("HTTPS://");
+    }
+
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+
+    @Override
+    protected void configImmersion(@NonNull ImmersionBar immersionBar) {
+        immersionBar.statusBarDarkFont(true);
+    }
+
+}

+ 31 - 0
android/app/src/main/java/com/atmob/elec_asst/activity/MainActivity.java

@@ -0,0 +1,31 @@
+package com.atmob.elec_asst.activity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.view.KeyEvent;
+
+import io.flutter.embedding.android.FlutterActivity;
+
+public class MainActivity extends FlutterActivity {
+
+
+    public static void start(Context context) {
+        Intent intent = new Intent(context, MainActivity.class);
+        if (!(context instanceof Activity)) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        context.startActivity(intent);
+    }
+
+
+    public void exit() {
+        try {
+            Intent intent = new Intent();
+            intent.setAction(Intent.ACTION_MAIN);
+            intent.addCategory(Intent.CATEGORY_HOME);
+            startActivity(intent);
+        } catch (Exception ignore) {
+        }
+    }
+}

+ 72 - 0
android/app/src/main/java/com/atmob/elec_asst/activity/SplashActivity.java

@@ -0,0 +1,72 @@
+package com.atmob.elec_asst.activity;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.elec_asst.base.BaseActivity;
+import com.atmob.elec_asst.constants.Constants;
+import com.atmob.elec_asst.databinding.ActivitySplashBinding;
+import com.atmob.elec_asst.dialog.AgreementDialog;
+import com.atmob.elec_asst.utils.SharedPreferencesUtil;
+
+
+public class SplashActivity extends BaseActivity<ActivitySplashBinding> {
+
+
+    AgreementDialog agreementDialog;
+
+
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        checkPolicy();
+    }
+
+    private void checkPolicy() {
+        if (SharedPreferencesUtil.getBoolean(Constants.isPolicyGranted,false)) {
+            nextStep();
+        } else {
+            showAgreementDialog();
+        }
+    }
+
+    private void showAgreementDialog() {
+        if (agreementDialog == null) {
+            agreementDialog = new AgreementDialog(this);
+            agreementDialog.setAgreementAction(new AgreementDialog.AgreementAction() {
+                @Override
+                public void onAgree() {
+                    SharedPreferencesUtil.putBoolean(Constants.isPolicyGranted,true);
+                    nextStep();
+                }
+
+                @Override
+                public void onDisagree() {
+                    finishAfterTransition();
+                }
+            });
+        }
+        agreementDialog.show();
+    }
+
+    private void nextStep(){
+        showMain();
+    }
+
+    private void showMain() {
+        new Handler().postDelayed(this::goMain, 2000);
+    }
+
+    private void goMain() {
+        MainActivity.start(this);
+        finishAfterTransition();
+    }
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+}

+ 131 - 0
android/app/src/main/java/com/atmob/elec_asst/base/BaseActivity.java

@@ -0,0 +1,131 @@
+package com.atmob.elec_asst.base;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.databinding.ViewDataBinding;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewbinding.ViewBinding;
+import com.gyf.immersionbar.ImmersionBar;
+import com.atmob.elec_asst.R;
+import com.atmob.elec_asst.utils.SizeUtil;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.util.List;
+
+public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
+
+    protected T binding;
+
+    private ViewModelProvider viewModelProvider;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initBinding();
+        initImmersion();
+        initViewModelProvider();
+        initViewModel();
+    }
+
+    private void initBinding() {
+        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
+        if (type == null) {
+            return;
+        }
+        Class<? extends ViewBinding> bindingClass = (Class<? extends ViewBinding>) type.getActualTypeArguments()[0];
+        try {
+            Method inflate = bindingClass.getMethod("inflate", LayoutInflater.class);
+            binding = (T) inflate.invoke(null, getLayoutInflater());
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+        ViewDataBinding viewDataBinding = binding instanceof ViewDataBinding ? ((ViewDataBinding) binding) : null;
+        if (viewDataBinding != null) {
+            viewDataBinding.setLifecycleOwner(this);
+        }
+        if (binding != null) {
+            setContentView(binding.getRoot());
+        }
+    }
+
+    private void initImmersion() {
+        if (shouldImmersion()) {
+            ImmersionBar immersionBar = ImmersionBar.with(this);
+            configImmersion(immersionBar);
+            immersionBar.init();
+        }
+    }
+
+    protected abstract boolean shouldImmersion();
+
+    protected void configImmersion(@NonNull ImmersionBar immersionBar) {
+
+    }
+
+    private void initViewModelProvider() {
+            viewModelProvider = new ViewModelProvider(this);
+    }
+
+    protected final ViewModelProvider getViewModelProvider() {
+        return viewModelProvider;
+    }
+
+    protected void initViewModel() {
+
+    }
+
+    protected void addTopStatusBarHeight(ViewGroup target) {
+        ViewGroup.MarginLayoutParams marginLayoutParams
+                = (ViewGroup.MarginLayoutParams) target.getLayoutParams();
+        marginLayoutParams.topMargin = SizeUtil.getStatusBarHeight(this);
+        target.setLayoutParams(marginLayoutParams);
+    }
+
+    @Override
+    public void onBackPressed() {
+        FragmentManager supportFragmentManager = getSupportFragmentManager();
+        List<Fragment> fragments = supportFragmentManager.getFragments();
+        for (Fragment fragment : fragments) {
+            IFragmentBackController iFragmentBackController = fragment instanceof IFragmentBackController ? ((IFragmentBackController) fragment) : null;
+            if (iFragmentBackController != null) {
+                if (!iFragmentBackController.canBack()) {
+                    onFragmentBackRefused();
+                    return;
+                }
+            }
+        }
+        super.onBackPressed();
+        overridePendingTransition(R.anim.anim_alpha_in, R.anim.anim_alpha_out);
+    }
+
+    protected void onFragmentBackRefused() {
+
+    }
+
+    @Override
+    public void startActivity(Intent intent) {
+        super.startActivity(intent);
+        overridePendingTransition(R.anim.anim_alpha_in, R.anim.anim_alpha_out);
+    }
+
+    @Override
+    public void startActivity(Intent intent, @Nullable Bundle options) {
+        super.startActivity(intent, options);
+        overridePendingTransition(R.anim.anim_alpha_in, R.anim.anim_alpha_out);
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+        overridePendingTransition(R.anim.anim_alpha_in, R.anim.anim_alpha_out);
+    }
+}

+ 320 - 0
android/app/src/main/java/com/atmob/elec_asst/base/BaseDialog.java

@@ -0,0 +1,320 @@
+package com.atmob.elec_asst.base;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.databinding.ViewDataBinding;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.viewbinding.ViewBinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+
+public class BaseDialog<T extends ViewBinding> extends Dialog implements Comparable<BaseDialog<T>>,
+        LifecycleOwner, LifecycleEventObserver {
+
+    protected T binding;
+
+    private Handler handler;
+
+    private int gravity;
+
+    private int margin;
+
+    private boolean isUseQueue = true;
+
+    private LifecycleRegistry lifecycleRegistry;
+
+    private Lifecycle ownerActivityLifecycle;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            HIGH_PRIORITY,
+            DEFAULT_PRIORITY,
+            LOW_PRIORITY
+    })
+    @interface Priority {
+
+    }
+
+    static final int HIGH_PRIORITY = 100;
+
+    static final int DEFAULT_PRIORITY = 50;
+    static final int LOW_PRIORITY = 0;
+    private OnDismissListener onDismissListener;
+
+    private OnShowListener onShowListener;
+    private boolean widthFullScreen;
+    private boolean heightFullScreen;
+
+    public BaseDialog(@NonNull Context context) {
+        super(context);
+        init(context);
+    }
+
+    public BaseDialog(@NonNull Context context, int themeResId) {
+        super(context, themeResId);
+        init(context);
+    }
+
+    protected BaseDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
+        super(context, cancelable, cancelListener);
+        init(context);
+    }
+
+    private void init(Context context) {
+        handler = new Handler(Looper.getMainLooper());
+
+        lifecycleRegistry = new LifecycleRegistry(this);
+
+        initBinding();
+
+        initDialogParams();
+
+        initDialogOwner(context);
+
+        initListener();
+
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+    }
+
+    private void initDialogOwner(Context context) {
+        Activity activity = context instanceof Activity ? ((Activity) context) : null;
+        if (activity != null) {
+            setOwnerActivity(activity);
+        }
+        AppCompatActivity appCompatActivity = activity instanceof AppCompatActivity ? ((AppCompatActivity) activity) : null;
+        if (appCompatActivity != null) {
+            ownerActivityLifecycle = appCompatActivity.getLifecycle();
+            ownerActivityLifecycle.addObserver(this);
+        }
+    }
+
+    private void initListener() {
+        super.setOnDismissListener(dialog -> {
+            if (lifecycleRegistry.getCurrentState() != Lifecycle.State.DESTROYED) {
+                lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+            }
+            if (onDismissListener != null) {
+                onDismissListener.onDismiss(dialog);
+            }
+            onDismiss();
+            if (useQueue()) {
+                DialogQueue.nextDialog();
+            }
+        });
+        super.setOnShowListener(dialog -> {
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+            if (onShowListener != null) {
+                onShowListener.onShow(dialog);
+            }
+            onShow();
+        });
+    }
+
+    private void initDialogParams() {
+        Class<? extends BaseDialog> aClass = this.getClass();
+        FullScreen annotation = aClass.getAnnotation(FullScreen.class);
+        if (annotation != null) {
+            widthFullScreen = annotation.width();
+            heightFullScreen = annotation.height();
+            gravity = annotation.gravity();
+        }
+    }
+
+    private void initBinding() {
+        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
+        if (type == null) {
+            return;
+        }
+        Class<? extends ViewBinding> bindingClass = (Class<? extends ViewBinding>) type.getActualTypeArguments()[0];
+        try {
+            Method inflate = bindingClass.getMethod("inflate", LayoutInflater.class);
+            binding = (T) inflate.invoke(null, getLayoutInflater());
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+        ViewDataBinding viewDataBinding = binding instanceof ViewDataBinding ? ((ViewDataBinding) binding) : null;
+        if (viewDataBinding != null) {
+            viewDataBinding.setLifecycleOwner(this);
+        }
+        if (binding != null) {
+            setContentView(binding.getRoot());
+        }
+    }
+
+    public void setMargin(int margin) {
+        this.margin = margin;
+    }
+
+    protected void onShow() {
+    }
+
+    protected void onDismiss() {
+    }
+
+    @Override
+    public void setOnDismissListener(@Nullable OnDismissListener listener) {
+        onDismissListener = listener;
+    }
+
+    @Override
+    public void setOnShowListener(@Nullable OnShowListener listener) {
+        onShowListener = listener;
+    }
+
+    @Override
+    @CallSuper
+    public void show() {
+        if (useQueue()) {
+            DialogQueue.requestShow(this);
+        } else {
+            showInternal();
+        }
+    }
+
+    void showInternal() {
+        if (couldShow()) {
+            super.show();
+            Window window = getWindow();
+            WindowManager.LayoutParams attributes = window.getAttributes();
+            attributes.height = heightFullScreen ? WindowManager.LayoutParams.MATCH_PARENT : WindowManager.LayoutParams.WRAP_CONTENT;
+            attributes.width = widthFullScreen ? WindowManager.LayoutParams.MATCH_PARENT : WindowManager.LayoutParams.WRAP_CONTENT;
+            attributes.gravity = gravity;
+            //todo 暂时只处理TOP的margin,bottom待添加
+            if (gravity == Gravity.TOP) {
+                attributes.y = margin;
+            }
+
+            window.setAttributes(attributes);
+        } else {
+            if (useQueue()) {
+                DialogQueue.nextDialog();
+            }
+        }
+    }
+
+    private boolean couldShow() {
+        if (isShowing()) {
+            return false;
+        }
+        if (getOwnerActivity() != null) {
+            return !getOwnerActivity().isDestroyed() && !getOwnerActivity().isFinishing();
+        }
+        Context context = getContext();
+        if (context instanceof Activity) {
+            Activity activity = (Activity) context;
+            return !activity.isDestroyed() && !activity.isFinishing();
+        }
+        return true;
+    }
+
+    @Override
+    @CallSuper
+    public void dismiss() {
+        handler.post(() -> {
+            if (isShowing() && isDecorViewAttach()) {
+                super.dismiss();
+            }
+        });
+    }
+
+    private boolean isDecorViewAttach() {
+        Window window = getWindow();
+        if (window == null) {
+            return false;
+        }
+        View decorView = window.getDecorView();
+        if (decorView == null) {
+            return false;
+        }
+        return decorView.isAttachedToWindow();
+    }
+
+    @Override
+    public int compareTo(BaseDialog other) {
+        if (other == null) {
+            return 1;
+        }
+        if (getPriority() == other.getPriority()) {
+            return 0;
+        }
+        return getPriority() < other.getPriority() ? 1 : -1;
+    }
+
+    @Priority
+    protected int getPriority() {
+        return DEFAULT_PRIORITY;
+    }
+
+    protected boolean useQueue() {
+        return isUseQueue;
+    }
+
+    public void setUseQueue(boolean isUseQueue) {
+        this.isUseQueue = isUseQueue;
+    }
+
+
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return lifecycleRegistry;
+    }
+
+    @Override
+    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
+        if (event == Lifecycle.Event.ON_DESTROY) {
+            if (ownerActivityLifecycle != null) {
+                ownerActivityLifecycle.removeObserver(this);
+            }
+            dismiss();
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+            return;
+        }
+        if (!isShowing()) {
+            return;
+        }
+        if (event == Lifecycle.Event.ON_RESUME) {
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+        } else if (event == Lifecycle.Event.ON_PAUSE) {
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
+        } else if (event == Lifecycle.Event.ON_START) {
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        } else if (event == Lifecycle.Event.ON_STOP) {
+            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        }
+    }
+
+    @Target(ElementType.TYPE)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface FullScreen {
+        boolean width() default true;
+
+        boolean height() default true;
+
+        int gravity() default Gravity.CENTER;
+    }
+
+}

+ 76 - 0
android/app/src/main/java/com/atmob/elec_asst/base/DialogQueue.java

@@ -0,0 +1,76 @@
+package com.atmob.elec_asst.base;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+//todo 优化
+public class DialogQueue {
+
+    private static final String TAG = DialogQueue.class.getSimpleName();
+
+    private static final Queue<BaseDialog> dialogQueue = new PriorityQueue<>();
+
+    private static final List<OnQueueStatusChangeCallback> callbacks = new ArrayList<>();
+
+    private DialogQueue() {
+
+    }
+
+    public static void requestShow(BaseDialog dialog) {
+        dialogQueue.add(dialog);
+        notifyDialogAdd();
+        if (dialogQueue.size() == 1) {
+            dialog.showInternal();
+        }
+    }
+
+    public static void nextDialog() {
+        BaseDialog currentDialog = dialogQueue.poll();
+        BaseDialog nextDialog = dialogQueue.peek();
+        if (nextDialog != null) {
+            nextDialog.showInternal();
+        } else {
+            notifyQueueEmpty();
+        }
+    }
+
+    public static boolean hasDialogShowing() {
+        return !dialogQueue.isEmpty();
+    }
+
+    private static void notifyDialogAdd() {
+        for (OnQueueStatusChangeCallback callback : callbacks) {
+            if (callback != null) {
+                callback.onDialogAdded();
+            }
+        }
+    }
+
+    private static void notifyQueueEmpty() {
+        for (OnQueueStatusChangeCallback callback : callbacks) {
+            if (callback != null) {
+                callback.onQueueEmpty();
+            }
+        }
+    }
+
+    public static void registerQueueStatusCallback(OnQueueStatusChangeCallback callback) {
+        if (callback == null) {
+            return;
+        }
+        callbacks.add(callback);
+    }
+
+    public static void unregisterQueueStatusCallback(OnQueueStatusChangeCallback callback) {
+        callbacks.remove(callback);
+    }
+
+    public interface OnQueueStatusChangeCallback {
+        void onQueueEmpty();
+
+        void onDialogAdded();
+    }
+}

+ 7 - 0
android/app/src/main/java/com/atmob/elec_asst/base/IFragmentBackController.java

@@ -0,0 +1,7 @@
+package com.atmob.elec_asst.base;
+
+public interface IFragmentBackController {
+    default boolean canBack() {
+        return true;
+    }
+}

+ 15 - 0
android/app/src/main/java/com/atmob/elec_asst/constants/Constants.java

@@ -0,0 +1,15 @@
+package com.atmob.elec_asst.constants;
+
+
+
+public class Constants {
+
+
+    public static final String PRIVACY_POLICY = "https://cdn.v8dashen.com/static/xt-xm-privacy.html";
+    public static final String USER_AGREEMENT = "https://cdn.v8dashen.com/static/xt-xm-clause.html";
+
+
+
+    public static final String isPolicyGranted = "is_policy_granted";
+
+}

+ 59 - 0
android/app/src/main/java/com/atmob/elec_asst/dialog/AgreementDialog.java

@@ -0,0 +1,59 @@
+package com.atmob.elec_asst.dialog;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.annotation.NonNull;
+
+import com.atmob.elec_asst.R;
+import com.atmob.elec_asst.activity.BrowserActivity;
+import com.atmob.elec_asst.base.BaseDialog;
+import com.atmob.elec_asst.constants.Constants;
+import com.atmob.elec_asst.databinding.DialogAgreementBinding;
+import com.atmob.elec_asst.utils.SpannableUtil;
+
+@BaseDialog.FullScreen(height = false)
+public class AgreementDialog extends BaseDialog<DialogAgreementBinding> {
+
+    private AgreementAction agreementAction;
+
+
+    public AgreementDialog(@NonNull Context context) {
+        super(context, R.style.Theme_Common_Dialog);
+        setCancelable(false);
+        binding.tvDisAgree.setOnClickListener(v -> {
+            if (agreementAction != null) {
+                agreementAction.onDisagree();
+            }
+            dismiss();
+        });
+        binding.tvAgree.setOnClickListener(v -> {
+            if (agreementAction != null) {
+                agreementAction.onAgree();
+            }
+            dismiss();
+        });
+        SpannableUtil.getAgreementSpannableStringBuilder(binding.tvContent, context.getString(R.string.dialog_agreement_complete_txt),
+                new String[]{
+                        context.getString(R.string.dialog_agreement_privacy),
+                        context.getString(R.string.dialog_agreement_user_agreement)
+                }, Color.parseColor("#5E8BFF"), false, v -> {
+                    BrowserActivity.start(context, Constants.PRIVACY_POLICY);
+                }, v -> {
+                    BrowserActivity.start(context, Constants.USER_AGREEMENT);
+                });
+    }
+
+
+    public AgreementDialog setAgreementAction(AgreementAction agreementAction) {
+        this.agreementAction = agreementAction;
+        return this;
+    }
+
+    public interface AgreementAction {
+        void onAgree();
+
+        void onDisagree();
+    }
+
+}

+ 70 - 0
android/app/src/main/java/com/atmob/elec_asst/utils/SharedPreferencesUtil.java

@@ -0,0 +1,70 @@
+package com.atmob.elec_asst.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class SharedPreferencesUtil {
+
+    private static final String PREF_NAME = "app_preferences";
+    private static SharedPreferences sharedPreferences;
+
+    // 初始化 SharedPreferences
+    public static void init(Context context) {
+        if (sharedPreferences == null) {
+            sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+        }
+    }
+
+    // 保存字符串
+    public static void putString(String key, String value) {
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putString(key, value);
+        editor.apply();
+    }
+
+    // 获取字符串
+    public static String getString(String key, String defaultValue) {
+        return sharedPreferences.getString(key, defaultValue);
+    }
+
+    // 保存整型
+    public static void putInt(String key, int value) {
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putInt(key, value);
+        editor.apply();
+    }
+
+    // 获取整型
+    public static int getInt(String key, int defaultValue) {
+        return sharedPreferences.getInt(key, defaultValue);
+    }
+
+    // 保存布尔值
+    public static void putBoolean(String key, boolean value) {
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putBoolean(key, value);
+        editor.apply();
+    }
+
+    // 获取布尔值
+    public static boolean getBoolean(String key, boolean defaultValue) {
+        return sharedPreferences.getBoolean(key, defaultValue);
+    }
+
+    // 清除所有数据
+    public static void clear() {
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.clear();
+        editor.apply();
+    }
+
+    // 移除特定的键
+    public static void remove(String key) {
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.remove(key);
+        editor.apply();
+    }
+}
+
+
+

+ 55 - 0
android/app/src/main/java/com/atmob/elec_asst/utils/SizeUtil.java

@@ -0,0 +1,55 @@
+package com.atmob.elec_asst.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.WindowManager;
+
+public class SizeUtil {
+
+    private static final DisplayMetrics outMetrics = new DisplayMetrics();
+
+    private SizeUtil() {
+
+    }
+
+    public static float dp2px(Context context,float dip) {
+        Resources resources = context.getResources();
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, resources.getDisplayMetrics());
+    }
+
+    public static float sp2px(Context context,float sp) {
+        Resources resources = context.getResources();
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, resources.getDisplayMetrics());
+    }
+
+    public static float px2dp(Context context,float pixel) {
+        Resources resources = context.getResources();
+        DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+        return pixel / displayMetrics.density;
+    }
+
+    public static int getScreenWidth(Context context) {
+        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        windowManager.getDefaultDisplay().getRealMetrics(outMetrics);
+        return outMetrics.widthPixels;
+    }
+
+    public static int getScreenHeight(Context context) {
+        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        windowManager.getDefaultDisplay().getRealMetrics(outMetrics);
+        return outMetrics.heightPixels;
+    }
+
+    public static int getStatusBarHeight(Context context) {
+        int height = 0;
+        @SuppressLint({"InternalInsetResource", "DiscouragedApi"})
+        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
+        if (resourceId > 0) {
+            height = context.getResources().getDimensionPixelSize(resourceId);
+        }
+        return height;
+    }
+}

+ 50 - 0
android/app/src/main/java/com/atmob/elec_asst/utils/SpannableUtil.java

@@ -0,0 +1,50 @@
+package com.atmob.elec_asst.utils;
+
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.view.View;
+import android.widget.TextView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+
+
+public class SpannableUtil {
+
+
+    public static void getAgreementSpannableStringBuilder(TextView targetView, String allTxt, String[] targetTxt, @ColorInt int color, boolean isLine, View.OnClickListener... clickListener) {
+        if (clickListener.length > 0)
+            targetView.setMovementMethod(LinkMovementMethod.getInstance());
+        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(allTxt);
+        int[] targetTextStart = new int[targetTxt.length];
+        for (int i = 0; i < targetTxt.length; i++) {
+            targetTextStart[i] = allTxt.indexOf(targetTxt[i]);
+        }
+
+        for (int i = 0; i < targetTxt.length; i++) {
+            int finalI = i;
+            ClickableSpan targetSpan = new ClickableSpan() {
+                @Override
+                public void onClick(@NonNull View widget) {
+                    if (finalI < clickListener.length && clickListener[finalI] != null) {
+                        clickListener[finalI].onClick(widget);
+                    }
+                }
+
+                @Override
+                public void updateDrawState(@NonNull TextPaint ds) {
+                    ds.setColor(color);
+                    ds.setUnderlineText(isLine);
+                }
+            };
+            if (targetTextStart[i] < 0) {
+                continue;
+            }
+            spannableStringBuilder.setSpan(targetSpan, targetTextStart[i],
+                    targetTextStart[i] + targetTxt[i].length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        targetView.setText(spannableStringBuilder);
+    }
+}

+ 7 - 0
android/app/src/main/res/anim/anim_alpha_in.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="200">
+    <alpha
+        android:fromAlpha="0"
+        android:toAlpha="1" />
+</set>

+ 7 - 0
android/app/src/main/res/anim/anim_alpha_out.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="250">
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="0" />
+</set>

BIN
android/app/src/main/res/drawable-xxhdpi/bg_splash.webp


BIN
android/app/src/main/res/drawable-xxhdpi/icon_back.webp


BIN
android/app/src/main/res/drawable-xxhdpi/icon_splash_logo.webp


BIN
android/app/src/main/res/drawable-xxhdpi/icon_splash_title.webp


+ 5 - 0
android/app/src/main/res/drawable/bg_agreement_agree.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="8dp" />
+    <solid android:color="#6177F2" />
+</shape>

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

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

+ 5 - 0
android/app/src/main/res/drawable/bg_agreement_disagree.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="8dp" />
+    <solid android:color="#F0F0F0" />
+</shape>

+ 31 - 0
android/app/src/main/res/drawable/bg_theme_splash.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <size
+                android:width="1080px"
+                android:height="1920px" />
+            <solid android:color="#fff" />
+        </shape>
+    </item>
+
+    <item>
+        <bitmap android:src="@drawable/bg_splash" />
+    </item>
+
+    <item android:top="532.8px">
+        <bitmap
+            android:gravity="top"
+            android:src="@drawable/icon_splash_title" />
+
+    </item>
+
+    <item android:bottom="96px">
+        <bitmap
+            android:gravity="bottom"
+            android:src="@drawable/icon_splash_logo" />
+
+
+    </item>
+
+</layer-list>

+ 36 - 0
android/app/src/main/res/layout/activity_browser.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+
+    <data>
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout 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"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/browser_header"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="parent"
+            app:navigationIcon="@drawable/icon_back"
+            tools:title="this is a test title" />
+
+        <View
+            android:id="@+id/divider_line"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="#EEEEEE"
+            app:layout_constraintTop_toBottomOf="@+id/browser_header" />
+
+        <WebView
+            android:id="@+id/browser_web_view"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/divider_line" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 8 - 0
android/app/src/main/res/layout/activity_splash.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+
+</LinearLayout>

+ 128 - 0
android/app/src/main/res/layout/dialog_agreement.xml

@@ -0,0 +1,128 @@
+<?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>
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        tools:background="@color/black70">
+
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <View
+                android:id="@+id/v_bg"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:background="@drawable/bg_agreement_dialog"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintWidth_percent="0.7777777777777778" />
+
+            <Space
+                android:id="@+id/space_1"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:24"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/tv_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/dialog_agreement_title"
+                android:textColor="#25262A"
+                android:textSize="15sp"
+                android:textStyle="bold"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/space_1" />
+
+            <Space
+                android:id="@+id/space_2"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:12"
+                app:layout_constraintTop_toBottomOf="@+id/tv_title" />
+
+
+            <TextView
+                android:id="@+id/tv_content"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:textColor="#5F5F61"
+                android:textSize="14sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/space_2"
+                app:layout_constraintWidth_percent="0.6888888888888889"
+                tools:text="为了更好地为您服务,我们可能向系统申请一些必要权限,用于基本服务和功能。我们非常重视您的隐私和个
+人信息,请使用之前请仔细阅读《隐私政策》和《用户使用协议》。同意后,我们将继续为您服务。" />
+
+            <Space
+                android:id="@+id/space_3"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:34"
+                app:layout_constraintTop_toBottomOf="@+id/tv_content" />
+
+            <TextView
+                android:id="@+id/tv_dis_agree"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:background="@drawable/bg_agreement_disagree"
+                android:gravity="center"
+                android:text="@string/dialog_agreement_disagree"
+                android:textColor="#5F5F61"
+                android:textSize="14sp"
+                android:textStyle="bold"
+                app:layout_constraintDimensionRatio="120:36"
+                app:layout_constraintHorizontal_chainStyle="packed"
+                app:layout_constraintLeft_toLeftOf="@+id/v_bg"
+                app:layout_constraintRight_toLeftOf="@+id/tv_agree"
+                app:layout_constraintTop_toBottomOf="@+id/space_3"
+                app:layout_constraintWidth_percent="0.3333333333333333" />
+
+            <TextView
+                android:id="@+id/tv_agree"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_marginStart="8dp"
+                android:background="@drawable/bg_agreement_agree"
+                android:gravity="center"
+                android:text="@string/dialog_agreement_agree"
+                android:textColor="#fff"
+                android:textSize="14sp"
+                android:textStyle="bold"
+                app:layout_constraintDimensionRatio="120:36"
+                app:layout_constraintLeft_toRightOf="@+id/tv_dis_agree"
+                app:layout_constraintRight_toRightOf="@+id/v_bg"
+                app:layout_constraintTop_toTopOf="@+id/tv_dis_agree"
+                app:layout_constraintWidth_percent="0.3333333333333333" />
+
+            <Space
+                android:id="@+id/space_4"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                app:layout_constraintDimensionRatio="360:24"
+                app:layout_constraintTop_toBottomOf="@+id/tv_dis_agree" />
+
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

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

@@ -1,3 +1,11 @@
 <resources>
     <string name="app_name">小听</string>
+
+    <string name="dialog_agreement_title">隐私政策及权限说明</string>
+    <string name="dialog_agreement_complete_txt">为了更好地为您服务,我们可能向系统申请一些必要权限,用于基本服务和功能。我们非常重视您的隐私和个
+人信息,请使用之前请仔细阅读《隐私政策》和《用户使用协议》。同意后,我们将继续为您服务。</string>
+    <string name="dialog_agreement_privacy">《隐私政策》</string>
+    <string name="dialog_agreement_user_agreement">《用户使用协议》</string>
+    <string name="dialog_agreement_disagree">不同意</string>
+    <string name="dialog_agreement_agree">同意并继续</string>
 </resources>

+ 17 - 0
android/app/src/main/res/values/styles.xml

@@ -15,4 +15,21 @@
     <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
         <item name="android:windowBackground">?android:colorBackground</item>
     </style>
+
+
+    <style name="Theme.Common.Dialog" parent="Theme.AppCompat.Light.Dialog">
+
+        <!-- Customize your theme here. -->
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimAmount">0.7</item>
+
+        <item name="android:windowAnimationStyle">@style/Animation.CommonDialog</item>
+    </style>
+
+
+    <style name="Animation.CommonDialog" parent="Animation.AppCompat.Dialog">
+        <item name="android:windowEnterAnimation">@anim/anim_alpha_in</item>
+        <item name="android:windowExitAnimation">@anim/anim_alpha_out</item>
+    </style>
 </resources>

+ 18 - 0
android/app/src/main/res/values/themes.xml

@@ -0,0 +1,18 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+
+    <style name="Theme.ElecAsst" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Primary brand color. -->
+
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <!-- Customize your theme here. -->
+    </style>
+
+    <style name="Theme.Splash" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="android:windowBackground">@drawable/bg_theme_splash</item>
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+    </style>
+
+
+</resources>

+ 4 - 0
android/build.gradle

@@ -4,6 +4,10 @@ allprojects {
         applicationId = "com.xingmeng.xiaoting"
         minSdkVersion = 23
         targetSdkVersion = 32
+
+        appcompat_version = "1.6.1"
+        constraintlayout_version = "2.1.4"
+        immersionbar_version = "3.2.2"
     }
     repositories {
         google()

+ 4 - 2
lib/main.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:electronic_assistant/resource/colors.gen.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
 import 'package:electronic_assistant/resource/string_source.dart';
@@ -17,7 +19,7 @@ void main() async {
   //全局配置smartDialog
   smartConfig();
   //获取包信息
-  appInfoUtil.init();
+  await appInfoUtil.init();
   //mmkv
   await KVUtil.init();
 
@@ -57,7 +59,7 @@ class MyApp extends StatelessWidget {
       child: GetMaterialApp(
         onGenerateTitle: (_) => StringName.appName.tr,
         getPages: AppPage.pages,
-        initialRoute: RoutePath.splash,
+        initialRoute: Platform.isAndroid ? RoutePath.mainTab : RoutePath.splash,
         initialBinding: AppBinding(),
         theme: ThemeData(
           useMaterial3: true,

+ 0 - 1
lib/module/splash/controller.dart

@@ -10,7 +10,6 @@ import 'package:electronic_assistant/utils/mmkv_util.dart';
 import 'package:electronic_assistant/widget/alert_dialog.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/gestures.dart';
-import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
 
 class SplashController extends BaseController {