Selaa lähdekoodia

增加客服聊天界面

zk 2 vuotta sitten
vanhempi
commit
c0d454d76d

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

@@ -94,6 +94,11 @@
         <activity
             android:name=".module.member.ServiceDescriptionActivity"
             android:screenOrientation="portrait" />
+        <activity
+            android:name=".module.customerservice.CustomerServiceActivity"
+            android:launchMode="singleInstance"
+            android:taskAffinity="${applicationId}.customer_service"
+            android:screenOrientation="portrait" />
 
 
     </application>

+ 9 - 0
app/src/main/java/com/datarecovery/master/data/api/AtmobApi.java

@@ -3,6 +3,7 @@ package com.datarecovery.master.data.api;
 
 import com.atmob.app.lib.base.BaseResponse;
 import com.datarecovery.master.data.api.request.BaseRequest;
+import com.datarecovery.master.data.api.request.CustomerUrlRequest;
 import com.datarecovery.master.data.api.request.FindOrderRequest;
 import com.datarecovery.master.data.api.request.LoginRequest;
 import com.datarecovery.master.data.api.request.MemberDetailRequest;
@@ -11,6 +12,7 @@ import com.datarecovery.master.data.api.request.OrderPageRequest;
 import com.datarecovery.master.data.api.request.OrderRequest;
 import com.datarecovery.master.data.api.request.PaymentStatusRequest;
 import com.datarecovery.master.data.api.request.SendCodeRequest;
+import com.datarecovery.master.data.api.response.CustomerUrlResponse;
 import com.datarecovery.master.data.api.response.FindOrderResponse;
 import com.datarecovery.master.data.api.response.FuncAuthsResponse;
 import com.datarecovery.master.data.api.response.LoginResponse;
@@ -19,6 +21,7 @@ import com.datarecovery.master.data.api.response.MemberPayResponse;
 import com.datarecovery.master.data.api.response.OrderPageResponse;
 import com.datarecovery.master.data.api.response.PaymentStatusResponse;
 import com.datarecovery.master.data.api.response.UserCaseResponse;
+import com.datarecovery.master.data.api.response.UserInfoResponse;
 
 import atmob.reactivex.rxjava3.core.Single;
 import atmob.retrofit2.http.Body;
@@ -59,4 +62,10 @@ public interface AtmobApi {
 
     @POST("/project/recover/v1/user/case/list")
     Single<BaseResponse<UserCaseResponse>> userCaseList(@Body BaseRequest request);
+
+    @POST("/project/customer/v1/customer/getUrl")
+    Single<BaseResponse<CustomerUrlResponse>> getCustomerUrl(@Body CustomerUrlRequest request);
+
+    @POST("/project/recover/v1/user/info")
+    Single<BaseResponse<UserInfoResponse>> getUserInfo(@Body BaseRequest request);
 }

+ 17 - 0
app/src/main/java/com/datarecovery/master/data/api/request/CustomerUrlRequest.java

@@ -0,0 +1,17 @@
+package com.datarecovery.master.data.api.request;
+
+import com.google.gson.annotations.SerializedName;
+
+public class CustomerUrlRequest extends BaseRequest {
+
+    @SerializedName("userId")
+    private String userId;
+
+    @SerializedName("phone")
+    private String phone;
+
+    public CustomerUrlRequest(String userId, String phone) {
+        this.userId = userId;
+        this.phone = phone;
+    }
+}

+ 13 - 0
app/src/main/java/com/datarecovery/master/data/api/response/CustomerUrlResponse.java

@@ -0,0 +1,13 @@
+package com.datarecovery.master.data.api.response;
+
+import com.google.gson.annotations.SerializedName;
+
+public class CustomerUrlResponse {
+
+    @SerializedName("customerUrl")
+    private String customerUrl;
+
+    public String getCustomerUrl() {
+        return customerUrl;
+    }
+}

+ 13 - 0
app/src/main/java/com/datarecovery/master/data/api/response/UserInfoResponse.java

@@ -0,0 +1,13 @@
+package com.datarecovery.master.data.api.response;
+
+import com.google.gson.annotations.SerializedName;
+
+public class UserInfoResponse {
+
+    @SerializedName("ssid")
+    private String userId;
+
+    public String getUserId() {
+        return userId;
+    }
+}

+ 2 - 1
app/src/main/java/com/datarecovery/master/data/consts/Constants.java

@@ -5,7 +5,8 @@ import com.datarecovery.master.BuildConfig;
 public class Constants {
 
 
-    private static final String Atmob_Server_Base_URL_LOCAL = "http://192.168.10.68:8880";
+    //    private static final String Atmob_Server_Base_URL_LOCAL = "http://192.168.10.68:8880";
+    private static final String Atmob_Server_Base_URL_LOCAL = "http://192.168.10.171:8880";
     public static final String Atmob_Server_Base_URL_REMOTE = BuildConfig.HOST;
     public static final String Atmob_Server_Base_URL = BuildConfig.isLocalNetwork ? Atmob_Server_Base_URL_LOCAL : Atmob_Server_Base_URL_REMOTE;
 

+ 44 - 0
app/src/main/java/com/datarecovery/master/data/repositories/AccountRepository.java

@@ -14,6 +14,7 @@ import com.datarecovery.master.data.api.request.BaseRequest;
 import com.datarecovery.master.data.api.request.LoginRequest;
 import com.datarecovery.master.data.api.request.SendCodeRequest;
 import com.datarecovery.master.data.api.response.LoginResponse;
+import com.datarecovery.master.data.api.response.UserInfoResponse;
 import com.datarecovery.master.data.consts.ErrorCode;
 import com.datarecovery.master.utils.BoxingUtil;
 import com.datarecovery.master.utils.RxHttpHandler;
@@ -34,6 +35,7 @@ public class AccountRepository {
 
     private static final String TAG = "AccountRepository";
     private static final String KEY_LOGIN_PHONE_NUM = "key_account_repo_login_phone_num";
+    private static final String KEY_LOGIN_USER_ID = "key_account_repo_login_user_id";
     private static final String KEY_LOGIN_TOKEN = "key_account_repo_login_token";
     private final MutableLiveData<String> loginPhoneNum = new MutableLiveData<>();
     private final LiveData<Boolean> isLogin = Transformations.map(loginPhoneNum, phoneNum -> !TextUtils.isEmpty(phoneNum));
@@ -51,6 +53,8 @@ public class AccountRepository {
         token = KVUtils.getDefault().getString(KEY_LOGIN_TOKEN, null);
     }
 
+    private boolean requestUserInfoDisabled = false;
+
     @Inject
     public AccountRepository(AtmobApi atmobApi, DeviceFuncRepository deviceFuncRepository, PayRepository payRepository) {
         this.atmobApi = atmobApi;
@@ -76,6 +80,8 @@ public class AccountRepository {
         payRepository.refreshOrderPageList();
         KVUtils.getDefault().putString(KEY_LOGIN_PHONE_NUM, phoneNum);
         KVUtils.getDefault().putString(KEY_LOGIN_TOKEN, token);
+        //查找并存储userId
+        refreshUserInfo();
     }
 
 
@@ -93,6 +99,36 @@ public class AccountRepository {
                 });
     }
 
+
+    private void refreshUserInfo() {
+        if (requestUserInfoDisabled) {
+            return;
+        }
+        getUserInfo().subscribe(new SingleObserver<UserInfoResponse>() {
+            @Override
+            public void onSubscribe(@NonNull Disposable d) {
+                requestUserInfoDisabled = true;
+            }
+
+            @Override
+            public void onSuccess(@NonNull UserInfoResponse userInfoResponse) {
+                requestUserInfoDisabled = false;
+                KVUtils.getDefault().putString(KEY_LOGIN_USER_ID, userInfoResponse.getUserId());
+            }
+
+            @Override
+            public void onError(@NonNull Throwable e) {
+                requestUserInfoDisabled = false;
+            }
+        });
+    }
+
+    private Single<UserInfoResponse> getUserInfo() {
+        return atmobApi.getUserInfo(new BaseRequest())
+                .compose(RxHttpHandler.handle(true))
+                .compose(RxJavaUtil.SingleSchedule.io2Main());
+    }
+
     public Single<LoginResponse> login(String phoneNum, String verificationCode) {
         if (errorCodeTimes >= 5) {
             return Single.error(new LoginTooOftenException());
@@ -144,6 +180,7 @@ public class AccountRepository {
         payRepository.refreshOrderPageList();
         KVUtils.getDefault().putString(KEY_LOGIN_PHONE_NUM, "");
         KVUtils.getDefault().putString(KEY_LOGIN_TOKEN, "");
+        KVUtils.getDefault().putString(KEY_LOGIN_USER_ID, "");
         if (BoxingUtil.boxing(isLogin.getValue())) {
             loginPhoneNum.postValue(null);
             memberStatusInfo.postValue(null);
@@ -156,6 +193,13 @@ public class AccountRepository {
                 .compose(RxJavaUtil.SingleSchedule.io2Main());
     }
 
+    public static String getUserId() {
+        return KVUtils.getDefault().getString(KEY_LOGIN_USER_ID, "");
+    }
+
+    public static String getKeyLoginPhoneNum() {
+        return KVUtils.getDefault().getString(KEY_LOGIN_PHONE_NUM, "");
+    }
 
     public static class RequestCodeTooOftenException extends Exception {
     }

+ 16 - 1
app/src/main/java/com/datarecovery/master/data/repositories/ConfigRepository.java

@@ -6,10 +6,14 @@ import android.text.TextUtils;
 import com.atmob.common.logging.AtmobLog;
 import com.atmob.user.AtmobUser;
 import com.datarecovery.master.BuildConfig;
+import com.datarecovery.master.data.api.AtmobApi;
+import com.datarecovery.master.data.api.request.CustomerUrlRequest;
+import com.datarecovery.master.data.api.response.CustomerUrlResponse;
 import com.datarecovery.master.data.consts.ChannelId;
 import com.datarecovery.master.module.member.MemberType;
 import com.datarecovery.master.sdk.gravity.GravityHelper;
 import com.datarecovery.master.utils.BoxingUtil;
+import com.datarecovery.master.utils.RxHttpHandler;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -19,6 +23,9 @@ import java.util.Objects;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import atmob.reactivex.rxjava3.core.Single;
+import atmob.rxjava.utils.RxJavaUtil;
+
 @Singleton
 public class ConfigRepository {
 
@@ -32,12 +39,20 @@ public class ConfigRepository {
     };
 
     private static final List<String> trialMemberList = new ArrayList<>(Arrays.asList(trialMember));
+    private final AtmobApi atmobApi;
 
     @Inject
-    public ConfigRepository() {
+    public ConfigRepository(AtmobApi atmobApi) {
+        this.atmobApi = atmobApi;
         getTrialMembershipStatus();
     }
 
+    public Single<CustomerUrlResponse> getCustomerUrl(String userId, String phone) {
+        return atmobApi.getCustomerUrl(new CustomerUrlRequest(userId, phone))
+                .compose(RxHttpHandler.handle(true))
+                .compose(RxJavaUtil.SingleSchedule.io2Main());
+    }
+
 
     private void getTrialMembershipStatus() {
         //如果是商店包,都开启试用会员

+ 183 - 0
app/src/main/java/com/datarecovery/master/module/customerservice/CustomerServiceActivity.java

@@ -0,0 +1,183 @@
+package com.datarecovery.master.module.customerservice;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+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.atmob.app.lib.base.BaseActivity;
+import com.atmob.common.runtime.ActivityUtil;
+import com.datarecovery.master.databinding.ActivityCustomerServiceBinding;
+import com.datarecovery.master.dialog.CommonLoadingDialog;
+import com.datarecovery.master.module.main.MainActivity;
+import com.datarecovery.master.utils.ActivityUtilHelper;
+import com.datarecovery.master.utils.AndroidBug5497Workaround;
+import com.datarecovery.master.utils.BoxingUtil;
+import com.gyf.immersionbar.ImmersionBar;
+
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+
+@AndroidEntryPoint
+public class CustomerServiceActivity extends BaseActivity<ActivityCustomerServiceBinding> {
+
+    private CustomerServiceViewModel customerServiceViewModel;
+
+    private CommonLoadingDialog loadingDialog;
+
+    public static void start(Context context) {
+        Intent intent = new Intent(context, CustomerServiceActivity.class);
+        if (!(context instanceof Activity)) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected void configImmersion(@NonNull ImmersionBar immersionBar) {
+        immersionBar.statusBarDarkFont(true);
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initView();
+        initObserver();
+        AndroidBug5497Workaround.assistActivity(this);
+    }
+
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+
+    @Override
+    protected void initViewModel() {
+        customerServiceViewModel = getViewModelProvider().get(CustomerServiceViewModel.class);
+        binding.setCustomerViewModel(customerServiceViewModel);
+    }
+
+    private void initView() {
+        addTopStatusBarHeight(binding.browserHeader);
+        initWebView();
+    }
+
+    private void initObserver() {
+        customerServiceViewModel.getCustomerUrl().observe(this, this::loadCustomerUrl);
+        customerServiceViewModel.getShowLoading().observe(this, this::showLoadingDialog);
+        customerServiceViewModel.getOnFinishEvent().observe(this, o -> onBackPressed());
+    }
+
+    @Override
+    public void onBackPressed() {
+        Activity activity = null;
+        if (ActivityUtil.getActivityCount() >= 2 && (activity = ActivityUtilHelper.getActivity(ActivityUtilHelper.getActivityCount() - 2)) != null && !(activity instanceof MainActivity)) {
+            startActivity(new Intent(this, activity.getClass()));
+        } else {
+            MainActivity.start(this);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        customerServiceViewModel.checkCustomerUrl();
+    }
+
+    public void showLoadingDialog(Boolean show) {
+        if (BoxingUtil.boxing(show)) {
+            if (loadingDialog == null) {
+                loadingDialog = new CommonLoadingDialog(this);
+            }
+            loadingDialog.show();
+        } else {
+            if (loadingDialog != null) {
+                loadingDialog.dismiss();
+            }
+        }
+    }
+
+    @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 {
+                    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(intent);
+                    return true;
+                }
+            }
+        });
+        binding.browserWebView.setWebChromeClient(new WebChromeClient() {
+            @Override
+            public void onReceivedTitle(WebView view, String title) {
+                super.onReceivedTitle(view, title);
+                customerServiceViewModel.setWebTitle(title);
+            }
+        });
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+    }
+
+    private void loadCustomerUrl(String 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://");
+    }
+}

+ 91 - 0
app/src/main/java/com/datarecovery/master/module/customerservice/CustomerServiceViewModel.java

@@ -0,0 +1,91 @@
+package com.datarecovery.master.module.customerservice;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.atmob.app.lib.base.BaseViewModel;
+import com.atmob.app.lib.livedata.SingleLiveEvent;
+import com.datarecovery.master.R;
+import com.datarecovery.master.data.api.response.CustomerUrlResponse;
+import com.datarecovery.master.data.repositories.AccountRepository;
+import com.datarecovery.master.data.repositories.ConfigRepository;
+import com.datarecovery.master.utils.ToastUtil;
+
+import javax.inject.Inject;
+
+import atmob.reactivex.rxjava3.annotations.NonNull;
+import atmob.reactivex.rxjava3.core.SingleObserver;
+import atmob.reactivex.rxjava3.disposables.Disposable;
+import dagger.hilt.android.lifecycle.HiltViewModel;
+
+@HiltViewModel
+public class CustomerServiceViewModel extends BaseViewModel {
+
+
+    private final ConfigRepository configRepository;
+    private final MutableLiveData<String> customerUrl = new MutableLiveData<>();
+    private final MutableLiveData<Boolean> showLoading = new MutableLiveData<>();
+    private final SingleLiveEvent<?> onFinishEvent = new SingleLiveEvent<>();
+
+    private final MutableLiveData<String> webTitle = new MutableLiveData<>();
+
+    @Inject
+    public CustomerServiceViewModel(ConfigRepository configRepository) {
+        this.configRepository = configRepository;
+    }
+
+    public LiveData<String> getCustomerUrl() {
+        return customerUrl;
+    }
+
+    public LiveData<Boolean> getShowLoading() {
+        return showLoading;
+    }
+
+    public LiveData<?> getOnFinishEvent() {
+        return onFinishEvent;
+    }
+
+    public LiveData<String> getWebTitle() {
+        return webTitle;
+    }
+
+    public void onBackClick() {
+        onFinishEvent.call();
+    }
+
+    public void setWebTitle(String webTitle) {
+        this.webTitle.setValue(webTitle);
+    }
+
+
+    public void refreshCustomerUrl() {
+        configRepository.getCustomerUrl(AccountRepository.getUserId(), AccountRepository.getKeyLoginPhoneNum())
+                .subscribe(new SingleObserver<CustomerUrlResponse>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+                        addDisposable(d);
+                        showLoading.setValue(true);
+                    }
+
+                    @Override
+                    public void onSuccess(@NonNull CustomerUrlResponse customerUrlResponse) {
+                        showLoading.setValue(false);
+                        customerUrl.setValue(customerUrlResponse.getCustomerUrl());
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        showLoading.setValue(false);
+                        ToastUtil.show(R.string.net_error, ToastUtil.LENGTH_SHORT);
+                    }
+                });
+    }
+
+    public void checkCustomerUrl() {
+        if (customerUrl.getValue() == null) {
+            refreshCustomerUrl();
+        }
+    }
+}

+ 4 - 3
app/src/main/java/com/datarecovery/master/module/mine/MineViewModel.java

@@ -15,6 +15,7 @@ import com.datarecovery.master.data.repositories.AccountRepository;
 import com.datarecovery.master.data.repositories.DeviceFuncRepository;
 import com.datarecovery.master.handler.EventHelper;
 import com.datarecovery.master.module.about.AboutActivity;
+import com.datarecovery.master.module.customerservice.CustomerServiceActivity;
 import com.datarecovery.master.module.feedback.UserFeedbackActivity;
 import com.datarecovery.master.module.homepage.HomePageViewModel;
 import com.datarecovery.master.module.login.LoginActivity;
@@ -23,9 +24,7 @@ import com.datarecovery.master.module.member.MemberType;
 import com.datarecovery.master.module.wxrecover.WeChatRecoverActivity;
 import com.datarecovery.master.sdk.wechat.WechatHelper;
 import com.datarecovery.master.utils.BoxingUtil;
-
 import javax.inject.Inject;
-
 import dagger.hilt.android.lifecycle.HiltViewModel;
 
 
@@ -35,6 +34,7 @@ public class MineViewModel extends BaseViewModel {
     private final SingleLiveEvent<?> showExitDialog = new SingleLiveEvent<>();
     private final SingleLiveEvent<?> showLogoutDialog = new SingleLiveEvent<>();
     private final LiveData<String> loginTips;
+
     private final AccountRepository accountRepository;
     private final DeviceFuncRepository deviceFuncRepository;
 
@@ -54,6 +54,7 @@ public class MineViewModel extends BaseViewModel {
         });
     }
 
+
     public LiveData<?> getShowExitDialog() {
         return showExitDialog;
     }
@@ -125,7 +126,7 @@ public class MineViewModel extends BaseViewModel {
     }
 
     public void onAppealClick() {
-        WechatHelper.launchCustomerService();
+        CustomerServiceActivity.start(ActivityUtil.getTopActivity());
         EventHelper.report(EventId.hf1001115);
     }
 }

+ 17 - 0
app/src/main/java/com/datarecovery/master/utils/ActivityUtilHelper.java

@@ -0,0 +1,17 @@
+package com.datarecovery.master.utils;
+
+import android.app.Activity;
+
+import com.atmob.common.runtime.ActivityUtil;
+
+
+public class ActivityUtilHelper {
+
+    public static Activity getActivity(int index) {
+        return null;
+    }
+
+    public static int getActivityCount() {
+        return ActivityUtil.getActivityCount();
+    }
+}

+ 76 - 0
app/src/main/java/com/datarecovery/master/utils/AndroidBug5497Workaround.java

@@ -0,0 +1,76 @@
+package com.datarecovery.master.utils;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+
+public class AndroidBug5497Workaround {
+    public static void assistActivity(Activity activity) {
+        new AndroidBug5497Workaround(activity);
+    }
+
+    private View mChildOfContent;
+    private int usableHeightPrevious;
+    private FrameLayout.LayoutParams frameLayoutParams;
+    private int contentHeight;
+    private boolean isfirst = true;
+    private Activity activity;
+    private int statusBarHeight;
+
+    private AndroidBug5497Workaround(Activity activity) {
+        //获取状态栏的高度
+        int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
+        statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
+        this.activity = activity;
+        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
+        mChildOfContent = content.getChildAt(0);
+        //界面出现变动都会调用这个监听事件
+        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+            public void onGlobalLayout() {
+                if (isfirst) {
+                    contentHeight = mChildOfContent.getHeight();//兼容华为等机型
+                    isfirst = false;
+                }
+                possiblyResizeChildOfContent();
+            }
+        });
+        frameLayoutParams = (FrameLayout.LayoutParams)
+                mChildOfContent.getLayoutParams();
+    }
+
+    //重新调整跟布局的高度
+    private void possiblyResizeChildOfContent() {
+        int usableHeightNow = computeUsableHeight();
+        //当前可见高度和上一次可见高度不一致 布局变动
+        if (usableHeightNow != usableHeightPrevious) {
+            //int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型
+            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
+            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
+            if (heightDifference > (usableHeightSansKeyboard / 4)) {
+                // keyboard probably just became visible
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                    //frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
+                    frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
+                } else {
+                    frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
+                }
+            } else {
+                frameLayoutParams.height = contentHeight;
+            }
+            mChildOfContent.requestLayout();
+            usableHeightPrevious = usableHeightNow;
+        }
+    }
+
+    /**
+     * 计算mChildOfContent可见高度 ** @return
+     */
+    private int computeUsableHeight() {
+        Rect r = new Rect();
+        mChildOfContent.getWindowVisibleDisplayFrame(r);
+        return (r.bottom - r.top);
+    }
+}

+ 39 - 0
app/src/main/res/layout/activity_customer_service.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+
+    <data>
+
+        <variable
+            name="customerViewModel"
+            type="com.datarecovery.master.module.customerservice.CustomerServiceViewModel" />
+    </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"
+            style="@style/tool_bar_style"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="parent"
+            app:navigationIcon="@drawable/icon_back"
+            app:navigationOnClickListener="@{()-> customerViewModel.onBackClick()}">
+
+            <TextView
+                style="@style/Tool_Bar_Title_Txt"
+                android:text="@{customerViewModel.webTitle}"
+                tools:text="this is a test title" />
+        </androidx.appcompat.widget.Toolbar>
+
+        <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/browser_header" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 1 - 1
app/src/main/res/layout/fragment_mine.xml

@@ -201,7 +201,7 @@
                         android:onClick="@{()->mineViewModel.onCustomerServiceClick()}" />
 
                     <include
-                        isGone="@{!mineViewModel.isLogin || AtmobUser.getAtmobTgPlatformId() == ChannelId.BD}"
+                        isGone="@{AtmobUser.getAtmobTgPlatformId() == ChannelId.BD}"
                         layout="@layout/layout_item_settings"
                         settingsIcon="@{@drawable/icon_small_appeal}"
                         settingsName="@{@string/mine_appeal}"