文章目录
- 前言
- 先上图
- 启动初始化环境配置
- 环境配置入口
- 环境配置初始化
- 静态URL配置
- 手输URL配置
- 开关配置
- 快捷测试入口
- 环境配置主页面
- 其它
- 总结
前言
在上一篇幅《Android实现动态切换环境配置2.0版本》我们实现了可以动态切换环境配置的功能,但是一些不同类型的配置参数没有进行区别对待,造成实现逻辑比较耦合,当前3.0版本从页面结构和代码实现上进行了拆分,方便后续不同的参数配置业务功能的修改和扩展。
先上图
启动初始化环境配置
初始化环境配置的方式:
EnvConfigHelper.getInstance()
.initDefaultEnvConfig()
在应用启动时,尽可能早的初始化环境配置,可以将当前环境下的各URL尽快赋值。
package com.windfallsheng;
import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Build;
import android.util.Log;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.envconfigexample.BuildConfig;
import com.windfallsheng.lib.envconfig.util.EnvUtils;
import com.windfallsheng.lib.envconfig.util.LogUtils;
/**
* @Author: lzsheng
* @date: 2021/11/3 13:02
* @description:
*/
public class MyApplication extends Application {
private static Application mApplication = null;
public static Application getInstance() {
return mApplication;
}
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
BaseApplicationHelper.getInstance().initApplicationContext(MyApplication.this);
// 日志开关;
@SuppressLint("WrongConstant") boolean isProductRelease = EnvUtils.isProductRelease(BuildConfig.MODE, BuildConfig.BUILD_TYPE);
Log.i("MyApplication", "onCreate#isProductRelease=" + isProductRelease);
LogUtils.initDebug(!isProductRelease);
//
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
String device = Build.DEVICE;
String release = Build.VERSION.RELEASE;
LogUtils.dl("MyApplication", "method:onCreate#manufacturer=" + manufacturer +
", model=" + model + ", device=" + device + ", release=" + release);
// 如果配置模块是单独放在一个 Module 中,则不能得到主工程的 BuildConfig,
// 这里可以使用 AppInfoBean,作为传递到配置模块内供使用,所以这里必须要先赋值;
// 这种情况下,这个赋值逻辑可以放在应用注册的 Application 的初始化阶段;
// 如果配置模块是放在主工程内,则可以在内部直接调用 BuildConfig 中的值;
AppInfoBean appInfoBean = new AppInfoBean(BuildConfig.APPLICATION_ID, BuildConfig.BUILD_TYPE,
BuildConfig.FLAVOR, BuildConfig.VERSION_CODE, BuildConfig.VERSION_NAME,
BuildConfig.LOG_DEBUG, BuildConfig.DEBUG, BuildConfig.MODE, Build.MANUFACTURER,
Build.MODEL, Build.DEVICE, Build.VERSION.RELEASE);
LogUtils.dl("MyApplication", "method:onCreate#appInfoBean=" + appInfoBean);
EnvConfigHelper.getInstance()
.initContext(mApplication)
.setAppInfoBean(appInfoBean);
// 初始化环境配置参数;
EnvConfigHelper.getInstance()
.initDefaultEnvConfig();
// 打印下默认环境地址;
if (!isProductRelease) {
EnvConfigHelper.getInstance()
.printEnvInfo();
}
}
}
环境配置入口
通过自定义UI的事件进行触发,进入环境配置入口页面。并且可以对触发条件,通过UI属性进行设置,是否单次点击、是否连续多次点击、点击次数上限,点击时间间隔、是否仅支持查看配置、用户是否可见、是否禁用等等。
package com.windfallsheng.envconfig.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.FragmentActivity;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.dialog.BaseDialogFragment;
import com.windfallsheng.envconfig.dialog.EnvConfigDialogFragment;
import com.windfallsheng.envconfig.dialog.EnvConfigViewDetailsDialogFragment;
import com.windfallsheng.lib.envconfig.EnvMode;
import com.windfallsheng.lib.envconfig.util.LogUtils;
/**
* @Author: lzsheng
* @date: 2021/8/6 17:15
* @description:
*/
public class EnvConfigShadowLayout extends RelativeLayout {
private final String TAG = "EnvConfigShadowLayout";
/**
* 连续点击次数累计到触发弹框的上限值默认值;
*/
private final int LIMIT_CLICK_COUNT_DEFAULT = 14;
/**
* 连续点击次数的间隔时间默认值;
*/
private final int MIN_TIME_INTERVAL_DEFAULT = 300;
/**
* 连续点击次数的间隔时间
*/
private int mMinTimeInterval;
private Context mContext;
private View mRootView;
private TextView mTvClickIt;
/**
* 是否是单次点击触发弹出配置弹框,false是需要连续多次点击;
*/
private boolean isSingleClick;
/**
* 是否是显式的展示,即用户在UI上是否是可见的;
*/
private boolean isExplicitDisplay;
/**
* 记录连续点击时上次点击的时间;
*/
private long mLastClickTimeMillis;
/**
* 记录连续点击的次数统计;
*/
private int mClickCount;
/**
* 是否禁用;
*/
private boolean isDisabled;
/**
* 是否支持修改环境配置,如果不支持则只能查看;
*/
private boolean isSupportedUpdateEnv;
/**
* 连续点击次数累计到触发弹框的上限值;
*/
private int mLimitClickCount;
public EnvConfigShadowLayout(Context context) {
super(context);
init(context, null, 0);
}
public EnvConfigShadowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public EnvConfigShadowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public void setSingleClick(boolean isSingleClick) {
this.isSingleClick = isSingleClick;
}
public void setDisabled(boolean isDisabled) {
this.isDisabled = isDisabled;
if (isDisabled) {
isExplicitDisplay = true;
mTvClickIt.setOnClickListener(null);
} else {
isExplicitDisplay = false;
initClickEvent();
}
initViewDisplayStatus(isExplicitDisplay);
}
public void setExplicitDisplay(boolean isExplicitDisplay) {
this.isExplicitDisplay = isExplicitDisplay;
initViewDisplayStatus(isExplicitDisplay);
}
public void setLimitClickCount(int limitClickCount) {
mLimitClickCount = limitClickCount;
handleLimitClickCount();
}
public void setMinTimeInterval(int minTimeInterval) {
mMinTimeInterval = minTimeInterval;
handleTimeInterval();
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
this.mContext = context;
mRootView = LayoutInflater.from(context).inflate(R.layout.env_config_layout_env_config_shadow, null);
mTvClickIt = mRootView.findViewById(R.id.tv_click_it);
// mRootView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(mRootView);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.env_config_shadow_layout);
// 用户是否可见
isExplicitDisplay = typedArray.getBoolean(R.styleable.env_config_shadow_layout_is_explicit_display, false);
isSingleClick = typedArray.getBoolean(R.styleable.env_config_shadow_layout_is_single_click, false);
mMinTimeInterval = typedArray.getInt(R.styleable.env_config_shadow_layout_min_time_interval, MIN_TIME_INTERVAL_DEFAULT);
isDisabled = typedArray.getBoolean(R.styleable.env_config_shadow_layout_is_disabled, false);
isSupportedUpdateEnv = typedArray.getBoolean(R.styleable.env_config_shadow_layout_is_supported_update_env, true);
mLimitClickCount = typedArray.getInt(R.styleable.env_config_shadow_layout_limit_click_count, LIMIT_CLICK_COUNT_DEFAULT);
typedArray.recycle();
handleTimeInterval();
handleLimitClickCount();
if (isDisabled) {
LogUtils.dl(TAG, "init#return#isDisabled=" + isDisabled);
return;
}
initViewDisplayStatus(isExplicitDisplay);
initClickEvent();
}
/**
* 设置数据的上下限;
*/
private void handleLimitClickCount() {
if (mLimitClickCount < 2) {
mLimitClickCount = 2;
} else if (mMinTimeInterval > 1000) {
mLimitClickCount = 1000;
}
}
/**
* 设置数据的上下限;
*/
private void handleTimeInterval() {
if (mMinTimeInterval < 100) {
mMinTimeInterval = 100;
} else if (mMinTimeInterval > 1000) {
mMinTimeInterval = 1000;
}
}
private void initViewDisplayStatus(boolean isExplicitDisplay) {
LogUtils.dl(TAG, "initViewDisplayStatus#isExplicitDisplay" + isExplicitDisplay);
if (isExplicitDisplay) {
// 用户是否可见
mTvClickIt.setTextColor(Color.parseColor("#FA8166"));
mRootView.setBackgroundColor(Color.parseColor("#F2F2F2"));
} else {
mTvClickIt.setTextColor(Color.parseColor("#00000000"));
mRootView.setBackgroundColor(Color.parseColor("#00000000"));
}
// 界面刷新;
postInvalidate();
}
private void initClickEvent() {
mTvClickIt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.dl("initClickEvent#onClick#mTvClickIt");
if (isSingleClick) {
// 显示环境配置弹框
showEnvConfigDialog();
return;
}
long currentTimeMillis = System.currentTimeMillis();
if (mLastClickTimeMillis > 0) {
long timeInterval = currentTimeMillis - mLastClickTimeMillis;
if (timeInterval < mMinTimeInterval) {
mClickCount++;
} else {
resetCounter();
}
} else {
// 首次点击;
mClickCount++;
}
LogUtils.dl(TAG, "initClickEvent#onClick#mClickCount=" + mClickCount + ", mLimitClickCount=" + mLimitClickCount);
if (mClickCount >= mLimitClickCount) {
// 显示环境配置弹框
showEnvConfigDialog();
//
resetCounter();
} else {
mLastClickTimeMillis = currentTimeMillis;
}
}
});
}
/**
* 弹出选择环境配置的各个页面;
* <p>
* 同时,这里做了限制,打包构建时的生产和试运行环境弹出的页面和开发环境是不同的;
*/
private void showEnvConfigDialog() {
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean == null) {
Toast.makeText(mContext, "appInfoBean = null", Toast.LENGTH_LONG).show();
return;
}
String modeOfBuild = appInfoBean.getModeValueOfBuild();
if (TextUtils.isEmpty(modeOfBuild)) {
Toast.makeText(mContext, "modeOfBuild 为空", Toast.LENGTH_LONG).show();
return;
}
// String modeBuild = BuildConfig.MODE;
// 构建的包是生产和试运行环境时,暂时不支持修改地址;这个业务功能可以根据需求调整是否限制;
if (TextUtils.equals(modeOfBuild, EnvMode.PILOT_RUN.getMode()) ||
TextUtils.equals(modeOfBuild, EnvMode.PRODUCT.getMode())) {
LogUtils.wl(TAG, "method:showEnvConfigDialog#return#modeOfBuild=" + modeOfBuild);
// Toast.makeText(mContext, modeOfBuild + "环境暂不支持修改环境配置", Toast.LENGTH_LONG).show();
EnvConfigViewDetailsDialogFragment envConfigViewDetailsDialogFragment = EnvConfigViewDetailsDialogFragment.newInstance();
showDialogFragment(envConfigViewDetailsDialogFragment, "envConfigViewDetailsDialogFragment");
return;
}
// 根据是否支持修改环境配置状态判断弹框页面的内容;
if (isSupportedUpdateEnv) {
EnvConfigDialogFragment envConfigDialogFragment = EnvConfigDialogFragment.newInstance();
showDialogFragment(envConfigDialogFragment, "envConfigDialogFragment");
} else {
EnvConfigViewDetailsDialogFragment envConfigViewDetailsDialogFragment = EnvConfigViewDetailsDialogFragment.newInstance();
showDialogFragment(envConfigViewDetailsDialogFragment, "envConfigViewDetailsDialogFragment");
}
}
private void showDialogFragment(BaseDialogFragment targetDialogFragment, String tag) {
if (mContext instanceof FragmentActivity) {
FragmentActivity activity = (FragmentActivity) mContext;
targetDialogFragment.show(activity.getSupportFragmentManager(), tag);
} else {
Toast.makeText(mContext, "Context not instance of FragmentActivity.", Toast.LENGTH_LONG).show();
LogUtils.wl(TAG, "method:showEnvConfigDialog#Context not instance of FragmentActivity.");
}
}
private void resetCounter() {
mClickCount = 0;
mLastClickTimeMillis = 0;
}
}
环境配置初始化
用户初始化默认选择的环境配置参数,同时可以根据选择的环境修改相应的数据;
package com.windfallsheng.envconfig;
import android.content.Context;
import android.text.TextUtils;
import com.windfallsheng.envconfig.bean.AllServersUrlConfigBean;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.bean.EnvConfigBean;
import com.windfallsheng.envconfig.bean.EnvConfigWrapper;
import com.windfallsheng.envconfig.bean.MockUserInfoWrapper;
import com.windfallsheng.envconfig.bean.UrlDetailsBean;
import com.windfallsheng.lib.envconfig.Consts;
import com.windfallsheng.lib.envconfig.EnvMode;
import com.windfallsheng.lib.envconfig.EnvServerUrlConsts;
import com.windfallsheng.lib.envconfig.MODE;
import com.windfallsheng.lib.envconfig.util.CollectionUtils;
import com.windfallsheng.lib.envconfig.util.LogUtils;
import com.windfallsheng.lib.xxpermissions.Permission;
import com.windfallsheng.lib.xxpermissions.XXPermissions;
import java.util.Map;
/**
* @Author: lzsheng
* @CreateDate: 2020/12/17 15:22
* @LastModifiedTime: 2020/12/17 15:22
* @Description: 单例模式,以方便全局随时可以获取设置的参数;
*/
public class EnvConfigHelper {
public static String TAG = "EnvConfigHelper";
private Context mContext;
/**
* 当前使用的环境的枚举定义;
*/
private EnvMode mEnvModeInUse;
/**
* 根据环境配置中查看是否跳过登录;
*/
private boolean hasSkipLogin;
/**
* 根据环境配置中查看是否跳过广告页;
*/
private boolean hasSkipAdPage;
/**
* 根据环境配置中查看是否使用模拟用户;
*/
private boolean hasUseMockUser;
private boolean startService1;
private boolean disableService1;
private boolean ignoreService1;
private boolean forceService1;
/**
* 根据环境配置中查看模拟用户;
*/
private MockUserInfoWrapper mMockUserInfoWrapper;
/**
* 主项目中注入的应用信息;
*/
private AppInfoBean mAppInfoBean;
private EnvConfigHelper() {
}
public static EnvConfigHelper getInstance() {
return SingletonHolder.INSTANCE;
}
public EnvConfigHelper initContext(Context context) {
mContext = context;
return this;
}
/**
* 获取当前打包构建的环境模式;
*
* @return
*/
public String getModeOfBuild() {
if (mAppInfoBean != null) {
return mAppInfoBean.getFlavor();
}
return "";
}
public String getModeValueInUse() {
if (mEnvModeInUse != null) {
return mEnvModeInUse.getMode();
}
return "";
}
public EnvMode getEnvModeInUse() {
return mEnvModeInUse;
}
public AppInfoBean getAppInfoBean() {
return mAppInfoBean;
}
public void setAppInfoBean(AppInfoBean appInfoBean) {
mAppInfoBean = appInfoBean;
}
public MockUserInfoWrapper getMockUserInfoWrapper() {
return mMockUserInfoWrapper;
}
public void setMockUserInfoWrapper(MockUserInfoWrapper mockUserInfoWrapper) {
mMockUserInfoWrapper = mockUserInfoWrapper;
}
/**
* 打印当前使用的环境的配置信息详情;
*/
public void printEnvInfo() {
String envDesc = null;
if (mEnvModeInUse != null) {
envDesc = mEnvModeInUse.getDesc();
}
LogUtils.dl(TAG, "printEnvInfo★★" +
"\n★★当前使用中的环境=" + envDesc + "=modeValue=" + mEnvModeInUse.getMode() + "★★" +
",\n★★打包模式modeOfBuild/FLAVOR=" + getModeOfBuild() + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.API=" + EnvServerUrlConsts.TargetServer.API + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.IMAGE=" + EnvServerUrlConsts.TargetServer.IMAGE + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.H5=" + EnvServerUrlConsts.TargetServer.H5 + "★★" +
",\n★★hasSkipLogin=" + hasSkipLogin + "★★" +
",\n★★hasSkipAdPage=" + hasSkipAdPage + "★★" +
",\n★★startService1=" + startService1 + "★★" +
",\n★★disableService1=" + disableService1 + "★★" +
",\n★★hasUseMockUser=" + hasUseMockUser + "★★" +
",\n★★ignoreService1=" + ignoreService1 + "★★" +
",\n★★forceService1=" + forceService1 + "★★" +
",\n★★mMockUserInfoWrapper=" + mMockUserInfoWrapper + "★★");
}
/**
* 在应用启动时最先初始化配置信息;
* <p>
* 如果有缓存的配置数据,则从缓存数据读取;
*/
public void initDefaultEnvConfig() {
LogUtils.dl(TAG, "method:initDefaultEnvConfig#mAppInfoBean=" + mAppInfoBean);
if (mAppInfoBean == null) {
LogUtils.el(TAG, "method:initDefaultEnvConfig#return#mAppInfoBean == null");
return;
}
String modeValueOfBuild = mAppInfoBean.getModeValueOfBuild();
LogUtils.dl(TAG, "method:initDefaultEnvConfig#modeValueOfBuild=" + modeValueOfBuild);
if (TextUtils.isEmpty(modeValueOfBuild)) {
LogUtils.el(TAG, "method:initDefaultEnvConfig#return#modeValueOfBuild=" + modeValueOfBuild);
return;
}
// 如果打包构建的环境是生产和试运行,则以打包环境为准
if (TextUtils.equals(modeValueOfBuild, Consts.EnvMode.PILOT_RUN) ||
TextUtils.equals(modeValueOfBuild, Consts.EnvMode.PRODUCT)) {
updateEnvServerUrlBySelectedMode(modeValueOfBuild);
} else if (TextUtils.equals(modeValueOfBuild, Consts.EnvMode.DEVELOPMENT)) {
// 如果是开发环境,先检查权限,没有权限则直接按打包环境状态修改;
if (!XXPermissions.isGrantedPermission(mContext, Permission.WRITE_EXTERNAL_STORAGE,
Permission.READ_EXTERNAL_STORAGE)) {
updateEnvServerUrlBySelectedMode(modeValueOfBuild);
LogUtils.dl(TAG, "method:initDefaultEnvConfig#return#XXPermissions#isGrantedPermission = false");
return;
}
// 如果是开发环境,先检查是否有上次缓存的配置数据;
EnvConfigWrapper envConfigWrapper = EnvConfigUtils.initEnvConfigWrapperInUse(mContext);
LogUtils.dl(TAG, "method:initDefaultEnvConfig#envConfigWrapper=" + envConfigWrapper);
if (envConfigWrapper == null || envConfigWrapper.isEmpty()) {
// 如果没有缓存数据,则以构建环境为准;
modeValueOfBuild = mAppInfoBean.getModeValueOfBuild();
updateEnvServerUrlBySelectedMode(modeValueOfBuild);
} else {
// 如果上次缓存的有数据,则以缓存数据为准;
updateEnvServerUrlBySelectedMode(envConfigWrapper);
}
}
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param modeValue
*/
public void updateEnvServerUrlBySelectedMode(@MODE String modeValue, EnvConfigWrapper envConfigWrapper) {
// 修改当前使用的环境模式为已修改的模式;
this.mEnvModeInUse = getEnvModeByModeValue(modeValue);
// 是否有手动输入的地址项;
String urlInputApiStr = "";
String urlImageStr = "";
String urlH5Str = "";
if (envConfigWrapper != null) {
// 处理一些开关项配置;
initSwitchStatus(envConfigWrapper.getSwitchConfigMap());
/**
* 判断是否有手动输入的地址项;
* 初始化手动输入的URL地址;
*/
final Map<String, UrlDetailsBean> serverUrlDetailsSelectedMap = envConfigWrapper.getUrlConfigDetailsInputSelectedMap();
if (CollectionUtils.isNotEmptyMap(serverUrlDetailsSelectedMap)) {
urlInputApiStr = EnvConfigUtils.initSelectedUrlStrByTypeId(EnvConfigConsts.UrlInputType.TYPE_ID_API, serverUrlDetailsSelectedMap);
urlImageStr = EnvConfigUtils.initSelectedUrlStrByTypeId(EnvConfigConsts.UrlInputType.TYPE_ID_IMAGE, serverUrlDetailsSelectedMap);
urlH5Str = EnvConfigUtils.initSelectedUrlStrByTypeId(EnvConfigConsts.UrlInputType.TYPE_ID_H5, serverUrlDetailsSelectedMap);
// 手动输入的地址项优先赋值;
if (!TextUtils.isEmpty(urlInputApiStr)) {
if (!urlInputApiStr.endsWith("/")) {
// retrofit的baseUrl必须以“/”结尾,否则抛异常
urlInputApiStr += "/";
}
EnvServerUrlConsts.TargetServer.API = urlInputApiStr;
}
if (!TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = urlImageStr;
}
if (!TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = urlH5Str;
}
}
}
// 根据环境选择,进行各个URL地址的赋值操作,及部分环境默认的配置开关的状态处理;
switch (modeValue) {
case Consts.EnvMode.DEV_TEST_LOCAL:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlInputApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestLocalServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestLocalServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestLocalServer.H5;
}
break;
case Consts.EnvMode.DEVELOPMENT:
case Consts.EnvMode.DEV_TEST_SERVER_1:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlInputApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_1.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_1.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_1.H5;
}
break;
case Consts.EnvMode.DEV_TEST_SERVER_2:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlInputApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_2.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_2.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTP + EnvServerUrlConsts.TestServer_2.H5;
}
break;
case Consts.EnvMode.DEV_PILOT_RUN:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlInputApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.H5;
}
break;
case Consts.EnvMode.DEV_PRODUCT:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlInputApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.H5;
}
break;
case Consts.EnvMode.PILOT_RUN:
handleEnvOfPilotRun();
break;
case Consts.EnvMode.PRODUCT:
handleEnvOfProduct();
break;
default:
LogUtils.el(TAG, "method:updateEnvServerUrlBySelectedMode#未处理当前配置环境的逻辑分支");
break;
}
//
String envDesc = null;
if (mEnvModeInUse != null) {
envDesc = mEnvModeInUse.getDesc();
}
LogUtils.dl(TAG, "updateEnvServerUrlBySelectedMode★★" +
"\n★★当前使用中的环境=" + envDesc + "=modeValue=" + modeValue + "★★" +
",\n★★打包模式modeOfBuild/FLAVOR=" + getModeOfBuild() + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.API=" + EnvServerUrlConsts.TargetServer.API + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.IMAGE=" + EnvServerUrlConsts.TargetServer.IMAGE + "★★" +
",\n★★EnvServerUrlConsts.TargetServer.H5=" + EnvServerUrlConsts.TargetServer.H5 + "★★" +
",\n★★hasSkipLogin=" + hasSkipLogin + "★★" +
",\n★★hasSkipAdPage=" + hasSkipAdPage + "★★" +
",\n★★startService1=" + startService1 + "★★" +
",\n★★disableService1=" + disableService1 + "★★" +
",\n★★hasUseMockUser=" + hasUseMockUser + "★★" +
",\n★★ignoreService1=" + ignoreService1 + "★★" +
",\n★★forceService1=" + forceService1 + "★★" +
",\n★★mMockUserInfoWrapper=" + mMockUserInfoWrapper + "★★");
}
/**
* 处理试运行环境的配置信息;
*/
private void handleEnvOfPilotRun() {
hasSkipLogin = false;
hasSkipAdPage = false;
startService1 = false;
disableService1 = false;
hasUseMockUser = false;
ignoreService1 = false;
// 试运行环境需要开启
forceService1 = true;
//
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.API;
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.IMAGE;
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.PilotRunServer.H5;
}
/**
* 处理试生产环境的配置信息;
*/
private void handleEnvOfProduct() {
hasSkipLogin = false;
hasSkipAdPage = false;
startService1 = false;
disableService1 = false;
hasUseMockUser = false;
ignoreService1 = false;
// 生产环境需要开启
forceService1 = true;
//
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.API;
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.IMAGE;
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.Protocol.HTTPS + EnvServerUrlConsts.ProductServer.H5;
}
private EnvMode getEnvModeByModeValue(String modeSelected) {
switch (modeSelected) {
case Consts.EnvMode.DEV_TEST_LOCAL:
return EnvMode.DEV_TEST_LOCAL;
case Consts.EnvMode.DEV_TEST_SERVER_1:
return EnvMode.DEV_TEST_SERVER_1;
case Consts.EnvMode.DEV_TEST_SERVER_2:
return EnvMode.DEV_TEST_SERVER_2;
case Consts.EnvMode.DEV_PILOT_RUN:
return EnvMode.DEV_PILOT_RUN;
case Consts.EnvMode.DEV_PRODUCT:
return EnvMode.DEV_PRODUCT;
case Consts.EnvMode.DEVELOPMENT:
return EnvMode.DEVELOPMENT;
case Consts.EnvMode.PILOT_RUN:
return EnvMode.PILOT_RUN;
case Consts.EnvMode.PRODUCT:
return EnvMode.PRODUCT;
default:
LogUtils.el(TAG, "method:getEnvModeByModeValue#未处理当前逻辑分支");
break;
}
return null;
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param envConfigWrapper
*/
public void updateEnvServerUrlBySelectedMode(EnvConfigWrapper envConfigWrapper) {
if (envConfigWrapper == null) {
LogUtils.el(TAG, "method:updateEnvServerUrl#return#envConfigWrapper == null");
return;
}
AllServersUrlConfigBean allServersUrlConfigBean = envConfigWrapper.getAllServerUrlDetails();
if (allServersUrlConfigBean == null) {
LogUtils.el(TAG, "method:updateEnvServerUrl#return#allServersUrlConfigBean == null");
return;
}
String modeValue = allServersUrlConfigBean.getModeOfEnv();
updateEnvServerUrlBySelectedMode(modeValue, envConfigWrapper);
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param modeValue
*/
public void updateEnvServerUrlBySelectedMode(@MODE String modeValue) {
updateEnvServerUrlBySelectedMode(modeValue, null);
}
/**
* 根据环境配置中查看是否跳过登录、开启禁止代理等开关项的配置;
*
* @param switchConfigMap
*/
public void initSwitchStatus(Map<String, EnvConfigBean> switchConfigMap) {
if (CollectionUtils.isEmptyMap(switchConfigMap)) {
return;
}
hasSkipLogin = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.SKIP_LOGIN, switchConfigMap);
hasSkipAdPage = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.SKIP_AD_PAGE, switchConfigMap);
hasUseMockUser = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.USE_MOCK_USER, switchConfigMap);
startService1 = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.START_SERVICE_1, switchConfigMap);
disableService1 = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.DISABLE_SERVICE_1, switchConfigMap);
ignoreService1 = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.IGNORE_SERVICE_1, switchConfigMap);
forceService1 = initSwitchStatusByConfigId(EnvConfigConsts.ConfigId.FORCE_SERVICE_1, switchConfigMap);
}
/**
* 默认返回false;
*
* @param configId
* @param switchConfigMap
* @return
*/
private boolean initSwitchStatusByConfigId(String configId, Map<String, EnvConfigBean> switchConfigMap) {
if (!switchConfigMap.containsKey(configId)) {
return false;
}
final EnvConfigBean envConfigBean = switchConfigMap.get(configId);
if (envConfigBean == null) {
return false;
}
return envConfigBean.isChecked();
}
/**
* 根据环境配置中查看是否跳过登录;
*/
public boolean hasSkipLogin() {
return hasSkipLogin;
}
/**
* 根据环境配置中查看是否使用模拟用户;
*/
public boolean hasUseMockUser() {
return hasUseMockUser;
}
public boolean hasSkipAdPage() {
return hasSkipAdPage;
}
public boolean isStartService1() {
return startService1;
}
public boolean isDisableService1() {
return disableService1;
}
public boolean isIgnoreService1() {
return ignoreService1;
}
public boolean isForceService1() {
return forceService1;
}
private static class SingletonHolder {
private static final EnvConfigHelper INSTANCE = new EnvConfigHelper();
}
}
静态URL配置
解析 json 配置中的各个环境的地址,根据本地缓存的相应数据的状态,再修改相应数据的内容,展示在UI列表上,同时处理选择环境的事件逻辑,及页面数据的重置。
package com.windfallsheng.envconfig.fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.windfallsheng.envconfig.EnvConfigConsts;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.EnvConfigUtils;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.adapter.AllServerUrlDetailsConfigAdapter;
import com.windfallsheng.envconfig.bean.AllServersUrlConfigBean;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.lib.ToastUtils;
import com.windfallsheng.lib.envconfig.EnvMode;
import com.windfallsheng.lib.envconfig.util.CollectionUtils;
import com.windfallsheng.lib.envconfig.util.LogUtils;
import java.util.List;
/**
* @Author: lzsheng
* @date: 2021/10/26 15:32
* @description: URL配置相关
*/
public class ServerUrlConfigListFragment extends Fragment {
private final String TAG = "ServerUrlConfigListFragment";
private Context mContext;
private View rootView;
private RecyclerView mRecyclerView;
private AllServerUrlDetailsConfigAdapter mAllServerUrlDetailsConfigAdapter;
private List<AllServersUrlConfigBean> mEnvConfigList;
private AllServersUrlConfigBean mAllServerUrlDetailsSelected;
private ServerUrlConfigListFragment() {
}
public static ServerUrlConfigListFragment newInstance() {
ServerUrlConfigListFragment fragment = new ServerUrlConfigListFragment();
return fragment;
}
@Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_all_server_url_details_config, container, false);
mRecyclerView = rootView.findViewById(R.id.recyclerview_config);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setNestedScrollingEnabled(false);
// 初始化列表数据;
initListData();
mAllServerUrlDetailsConfigAdapter = new AllServerUrlDetailsConfigAdapter();
mAllServerUrlDetailsConfigAdapter.addAll(mEnvConfigList);
mRecyclerView.setAdapter(mAllServerUrlDetailsConfigAdapter);
// 行点击事件
mAllServerUrlDetailsConfigAdapter.setOnRecyclerViewItemClick((AllServerUrlDetailsConfigAdapter.OnRecyclerViewItemClick<AllServersUrlConfigBean>)
(childView, position, allServersUrlConfigBean) -> {
refreshListData(position, allServersUrlConfigBean);
mAllServerUrlDetailsConfigAdapter.notifyDataSetChanged();
});
// 在相关的数据初始化完成后,获取选中项;
mAllServerUrlDetailsSelected = getConfigSelected();
}
private void initListData() {
mEnvConfigList = EnvConfigUtils.getAllServersUrlConfigFromJson();
// 本地缓存数据;
AllServersUrlConfigBean allServerUrlDetailsCache = EnvConfigUtils.getAllServersUrlConfigFromCache(mContext);
// 如果最近一次的选中数据,则修改默认选中项;
initEnvConfigListDataAndViews(mEnvConfigList, allServerUrlDetailsCache);
}
public void resetListAndViews() {
mEnvConfigList = EnvConfigUtils.getAllServersUrlConfigFromJson();
mAllServerUrlDetailsConfigAdapter.addNewAll(mEnvConfigList);
mAllServerUrlDetailsConfigAdapter.notifyDataSetChanged();
// 在相关的数据初始化完成后,获取选中项;
mAllServerUrlDetailsSelected = getConfigSelected();
}
/**
* 返回这个页面的配置信息,将选中的配置项返回;
*
* @return
*/
public AllServersUrlConfigBean getAllServerUrlDetailsSelected() {
if (mAllServerUrlDetailsSelected == null) {
ToastUtils.showToast("URL配置项未选中");
return null;
} else {
// 将数据对象的copy返回给外界使用,不能和当前页面持有的对象产生关联影响;
return (AllServersUrlConfigBean) mAllServerUrlDetailsSelected.clone();
}
}
/**
* 根据点击事件发生所在position,及数据类型,修改了数据集合中相关数据的状态;
*
* @param position
*/
private void refreshListData(int position, AllServersUrlConfigBean allServersUrlConfigBean) {
LogUtils.d(TAG, "method:updateListData#allServersUrlConfigBean=" + allServersUrlConfigBean);
final List<AllServersUrlConfigBean> envConfigList = mAllServerUrlDetailsConfigAdapter.getEnvConfigList();
for (int i = 0; i < envConfigList.size(); i++) {
AllServersUrlConfigBean item = envConfigList.get(i);
final String groupId = item.getGroupId();
if (TextUtils.equals(groupId, EnvConfigConsts.GroupId.SERVER_URL_DETAILS)) {
// 处理各服务器地址选项的选择事件;
if (position == i) {
// 被点击的行的处理业务;
boolean isChecked = item.isChecked();
if (isChecked) {
// 如果当前项在点击前就是选中状态时,要根据是否是必有选项进行处理;
final boolean isRequired = allServersUrlConfigBean.isRequired();
if (isRequired) {
// 必须有选择项时,不修改选中状态;
} else {
item.setChecked(!isChecked);
}
mAllServerUrlDetailsSelected = item;
} else {
// 如果当前项在点击前就是未选中状态时;
item.setChecked(!isChecked);
mAllServerUrlDetailsSelected = item;
}
} else {
// 未被点击的行都应该是false;
item.setChecked(EnvConfigConsts.Bool.UNCHECKED);
}
}
}
}
/**
* 如果最近一次的选中数据有值,则修改默认选中项,如果没有则按默认项处理;
*
* @param allServersUrlConfigList
*/
private void initEnvConfigListDataAndViews(List<AllServersUrlConfigBean> allServersUrlConfigList, AllServersUrlConfigBean allServerUrlDetailsCache) {
if (allServerUrlDetailsCache == null) {
// 没有本地缓存数据时,设置默认选项;
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean == null) {
Toast.makeText(mContext, "appInfoBean = null", Toast.LENGTH_LONG).show();
return;
}
String modeOfBuild = appInfoBean.getModeValueOfBuild();
if (TextUtils.isEmpty(modeOfBuild)) {
Toast.makeText(mContext, "modeOfBuild 为空", Toast.LENGTH_LONG).show();
return;
}
for (AllServersUrlConfigBean serversUrlConfigBean : allServersUrlConfigList) {
handServerUrlDetalsDefault(modeOfBuild, serversUrlConfigBean);
}
return;
}
// 有本地缓存时,将本地缓存数据修改到列表中;
final String modeOfEnvCache = allServerUrlDetailsCache.getModeOfEnv();
if (CollectionUtils.isNotEmpty(allServersUrlConfigList)) {
for (int i = 0; i < allServersUrlConfigList.size(); i++) {
final AllServersUrlConfigBean allServersUrlConfigBean = allServersUrlConfigList.get(i);
final String modeOfEnv = allServersUrlConfigBean.getModeOfEnv();
if (TextUtils.equals(modeOfEnv, modeOfEnvCache)) {
allServersUrlConfigBean.setChecked(allServerUrlDetailsCache.isChecked());
} else {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.UNCHECKED);
}
}
}
}
/**
* 获取选中数据;
*/
private AllServersUrlConfigBean getConfigSelected() {
if (CollectionUtils.isNotEmpty(mEnvConfigList)) {
for (int i = 0; i < mEnvConfigList.size(); i++) {
final AllServersUrlConfigBean allServersUrlConfigBean = mEnvConfigList.get(i);
if (allServersUrlConfigBean.isChecked()) {
return allServersUrlConfigBean;
}
}
}
return null;
}
private void handServerUrlDetalsDefault(String modeBuild, AllServersUrlConfigBean allServersUrlConfigBean) {
String modeValue = allServersUrlConfigBean.getModeOfEnv();
if (TextUtils.equals(modeBuild, EnvMode.DEVELOPMENT.getMode())) {
// 开发环境默认使用 DEV_TEST_SERVER;
String modeDevServer = EnvMode.DEV_TEST_SERVER_1.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.CHECKED);
} else {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.UNCHECKED);
}
} else if (TextUtils.equals(modeBuild, EnvMode.PILOT_RUN.getMode())) {
// 试运行环境默认使用 DEV_PILOT_RUN;
String modeDevServer = EnvMode.DEV_PILOT_RUN.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.CHECKED);
} else {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.UNCHECKED);
}
} else if (TextUtils.equals(modeBuild, EnvMode.PRODUCT.getMode())) {
// 生产环境默认使用 DEV_PRODUCT;
String modeDevServer = EnvMode.DEV_PRODUCT.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.CHECKED);
} else {
allServersUrlConfigBean.setChecked(EnvConfigConsts.Bool.UNCHECKED);
}
}
}
}
手输URL配置
在静态的URL配置之外,还支持手动输入URL,如果手动输入的URL地址被选择,则会优先使用手动输入的地址。另外在配置被应用后,可以保存输入的地址到历史记录中,方便后期再次使用,URL的历史记录保存数量有上限,并且将最新输入内容保存在最上面,同时地址不会重复保存:
package com.windfallsheng.envconfig.fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.gson.Gson;
import com.windfallsheng.envconfig.EnvConfigConsts;
import com.windfallsheng.envconfig.EnvConfigUtils;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.UrlDetailsBean;
import com.windfallsheng.envconfig.bean.UrlHistoryBean;
import com.windfallsheng.envconfig.dialog.SingleSelectionDialog;
import com.windfallsheng.envconfig.widget.UrlDetailsInputLayout;
import com.windfallsheng.lib.ToastUtils;
import com.windfallsheng.lib.envconfig.util.CollectionUtils;
import com.windfallsheng.lib.envconfig.util.LogUtils;
import com.windfallsheng.lib.envconfig.util.SharedPrefsManager;
import com.windfallsheng.lib.xxpermissions.Permission;
import com.windfallsheng.lib.xxpermissions.XXPermissions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author: lzsheng
* @date: 2021/10/26 15:32
* @description: 手动输入的URL配置相关
*/
public class ServerUrlManualInputFragment extends Fragment implements
UrlDetailsInputLayout.OnShowUrlHistoryListener, UrlDetailsInputLayout.OnCheckBoxClickListener {
// private static final String ARG_PARAM1 = "param1";
// private static final String ARG_PARAM2 = "param2";
private final String TAG = "ServerUrlManualInputFragment";
/**
* 可缓存的最大历史url数量;
*/
private final int MAX_COUNT_OF_HISTORY_URL = 50;
private Context mContext;
private View rootView;
/**
* key为{@link UrlDetailsBean#typeId};
*/
// private Map<String, UrlDetailsBean> mUrlConfigDetailsInputSelectedMap;
/**
*
*/
private UrlDetailsBean mApiUrlDetailsBean;
private UrlDetailsBean mImageUrlDetailsBean;
private UrlDetailsBean mH5UrlDetailsBean;
/**
* 手动输入URL的UI部分;
*/
private UrlDetailsInputLayout mUrlInputLayoutApi;
private UrlDetailsInputLayout mUrlInputLayoutImage;
private UrlDetailsInputLayout mUrlInputLayoutH5;
private ServerUrlManualInputFragment() {
// Required empty public constructor
}
// public static UrlConfigFragment newInstance(String param1, String param2) {
public static ServerUrlManualInputFragment newInstance() {
ServerUrlManualInputFragment fragment = new ServerUrlManualInputFragment();
// Bundle args = new Bundle();
// args.putString(ARG_PARAM1, param1);
// args.putString(ARG_PARAM2, param2);
// fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_server_url_details_input, container, false);
// 设置ID;
mUrlInputLayoutApi = rootView.findViewById(R.id.url_details_layout_api);
mUrlInputLayoutImage = rootView.findViewById(R.id.url_details_layout_image);
mUrlInputLayoutH5 = rootView.findViewById(R.id.url_details_layout_h5);
mUrlInputLayoutApi.setShowHistoryViewId(R.id.url_detail_input_api_id);
mUrlInputLayoutImage.setShowHistoryViewId(R.id.url_detail_input_image_id);
mUrlInputLayoutH5.setShowHistoryViewId(R.id.url_detail_input_h5_id);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 设置事件回调;
mUrlInputLayoutApi.setOnShowUrlHistoryListener(ServerUrlManualInputFragment.this);
mUrlInputLayoutApi.setOnCheckBoxClickListener(ServerUrlManualInputFragment.this);
mUrlInputLayoutImage.setOnShowUrlHistoryListener(ServerUrlManualInputFragment.this);
mUrlInputLayoutImage.setOnCheckBoxClickListener(ServerUrlManualInputFragment.this);
mUrlInputLayoutH5.setOnShowUrlHistoryListener(ServerUrlManualInputFragment.this);
mUrlInputLayoutH5.setOnCheckBoxClickListener(ServerUrlManualInputFragment.this);
// 如果最近一次的选中数据,则修改默认选中项;
initData();
LogUtils.dl(TAG, "method:onActivityCreated#mApiUrlDetailsBean=" + mApiUrlDetailsBean +
", mWsUrlDetailsBean=" + mImageUrlDetailsBean);
// 根据数据状态修改UI;
mUrlInputLayoutApi.refreshViewsByUrlDetails(mApiUrlDetailsBean);
mUrlInputLayoutImage.refreshViewsByUrlDetails(mImageUrlDetailsBean);
mUrlInputLayoutH5.refreshViewsByUrlDetails(mH5UrlDetailsBean);
}
/**
* 将所有的数据全部返回;
*
* @return
*/
public Map<String, UrlDetailsBean> getServerUrlDetailsInput() {
Map<String, UrlDetailsBean> map = new HashMap<>();
// 输入的地址内容可能会发生改变,需要重新获取内部的值;
mApiUrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutApi.getUrlHistoryBean());
mImageUrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutImage.getUrlHistoryBean());
mH5UrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutH5.getUrlHistoryBean());
putUrlConfigInputMap(map, (UrlDetailsBean) mApiUrlDetailsBean.clone());
putUrlConfigInputMap(map, (UrlDetailsBean) mImageUrlDetailsBean.clone());
putUrlConfigInputMap(map, (UrlDetailsBean) mH5UrlDetailsBean.clone());
return map;
}
public UrlDetailsBean getApiUrlDetailsBean() {
// 输入的地址内容可能会发生改变,需要重新获取内部的值;
mApiUrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutApi.getUrlHistoryBean());
return mApiUrlDetailsBean;
}
public UrlDetailsBean getImageUrlDetailsBean() {
// 输入的地址内容可能会发生改变,需要重新获取内部的值;
mImageUrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutImage.getUrlHistoryBean());
return mImageUrlDetailsBean;
}
public UrlDetailsBean getH5UrlDetailsBean() {
mH5UrlDetailsBean.setUrlHistoryBean(mUrlInputLayoutH5.getUrlHistoryBean());
return mH5UrlDetailsBean;
}
private void putUrlConfigInputMap(Map<String, UrlDetailsBean> map, UrlDetailsBean urlDetailsBean) {
if (map == null || urlDetailsBean == null) {
return;
}
// if (urlDetailsBean.isChecked()) {
final String typeIdApi = urlDetailsBean.getTypeId();
if (!TextUtils.isEmpty(typeIdApi)) {
map.put(typeIdApi, (UrlDetailsBean) urlDetailsBean.clone());
}
// }
}
/**
* 重置所有数据;
*/
public void resetDataAndViews() {
Map<String, UrlDetailsBean> urlConfigInputMap = EnvConfigUtils.getUrlConfigInputMapFromJson();
mApiUrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_API);
mImageUrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_IMAGE);
mH5UrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_H5);
mUrlInputLayoutApi.refreshViewsByUrlDetails(mApiUrlDetailsBean);
mUrlInputLayoutImage.refreshViewsByUrlDetails(mImageUrlDetailsBean);
mUrlInputLayoutH5.refreshViewsByUrlDetails(mH5UrlDetailsBean);
}
/**
* 如果最近一次的选中数据有值,则修改默认选中项,如果没有则按默认项处理;
*/
private void initData() {
Map<String, UrlDetailsBean> urlConfigInputMap = EnvConfigUtils.getUrlConfigInputMapFromJson();
Map<String, UrlDetailsBean> urlConfigInputCacheMap = EnvConfigUtils.getUrlConfigDetailsOfInputFromCache(mContext);
if (CollectionUtils.isEmptyMap(urlConfigInputMap)) {
return;
}
//初始化json中的配置;
mApiUrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_API);
mImageUrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_IMAGE);
mH5UrlDetailsBean = urlConfigInputMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_H5);
if (CollectionUtils.isEmptyMap(urlConfigInputCacheMap)) {
// 如果没有缓存数据就不再处理;
return;
}
// 根据本地缓存的数据,修改状态;
UrlDetailsBean apiUrlDetailsCache = urlConfigInputCacheMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_API);
UrlDetailsBean imageUrlDetailsCache = urlConfigInputCacheMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_IMAGE);
UrlDetailsBean h5UrlDetailsCache = urlConfigInputCacheMap.get(EnvConfigConsts.UrlInputType.TYPE_ID_H5);
//
initDataWithCache(apiUrlDetailsCache, mApiUrlDetailsBean);
//
initDataWithCache(imageUrlDetailsCache, mImageUrlDetailsBean);
//
initDataWithCache(h5UrlDetailsCache, mH5UrlDetailsBean);
}
private void initDataWithCache(UrlDetailsBean urlDetailsBeanCache, UrlDetailsBean urlDetailsBean) {
if (urlDetailsBeanCache != null) {
urlDetailsBean.setChecked(urlDetailsBeanCache.isChecked());
urlDetailsBean.setUrlHistoryBean(urlDetailsBeanCache.getUrlHistoryBean());
}
}
@Override
public void onShowUrlHistory(View view, UrlHistoryBean urlHistory) {
List<UrlHistoryBean> urlList = EnvConfigUtils.initUrlHistoryList(mContext);
int id = view.getId();
new SingleSelectionDialog.Builder(mContext)
.setTitle(R.string.title_select_url)
.setUrlHistoryList(urlList)
.setOnUrlSelecteComplete((dialog, childView, position, urlHistoryBean) -> {
if (id == R.id.url_detail_input_api_id) {
refreshItemDateAndViews(urlHistoryBean, mUrlInputLayoutApi, mApiUrlDetailsBean);
} else if (id == R.id.url_detail_input_image_id) {
refreshItemDateAndViews(urlHistoryBean, mUrlInputLayoutImage, mImageUrlDetailsBean);
} else if (id == R.id.url_detail_input_h5_id) {
refreshItemDateAndViews(urlHistoryBean, mUrlInputLayoutH5, mH5UrlDetailsBean);
}
dialog.dismiss();
})
.setOnCloseClickedListener(dialog -> dialog.dismiss())
.create()
.show();
}
private void refreshItemDateAndViews(UrlHistoryBean urlHistoryBean, UrlDetailsInputLayout urlInputLayout, UrlDetailsBean urlDetailsBean) {
// 刷新UI;
urlInputLayout.refreshViewsByUrlDetails(urlHistoryBean.getUrlStr());
urlInputLayout.setUrlInput(urlHistoryBean.getUrlStr());
urlInputLayout.setChecked(EnvConfigConsts.Bool.CHECKED);
//数据状态修改
urlDetailsBean.setChecked(EnvConfigConsts.Bool.CHECKED);
urlDetailsBean.setUrlHistoryBean(urlHistoryBean);
}
@Override
public void OnCheckBoxClick(View view, boolean isChecked, String input) {
if (isChecked) {
if (TextUtils.isEmpty(input)) {
ToastUtils.showToast("请输入URL地址");
}
}
int id = view.getId();
if (id == R.id.url_detail_input_api_id) {
refreshDataOnChecked(isChecked, mApiUrlDetailsBean, mUrlInputLayoutApi);
} else if (id == R.id.url_detail_input_image_id) {
refreshDataOnChecked(isChecked, mImageUrlDetailsBean, mUrlInputLayoutImage);
} else if (id == R.id.url_detail_input_h5_id) {
refreshDataOnChecked(isChecked, mH5UrlDetailsBean, mUrlInputLayoutImage);
}
}
private void refreshDataOnChecked(boolean isChecked, UrlDetailsBean urlDetailsBean, UrlDetailsInputLayout urlInputLayout) {
urlDetailsBean.setChecked(isChecked);
urlDetailsBean.setUrlHistoryBean(urlInputLayout.getUrlHistoryBean());
}
/**
* 将手动输入的URL地址缓存到本地,同时注意重复数据问题等;
*/
public void saveUrlInputHistory() {
if (!XXPermissions.isGrantedPermission(mContext, Permission.WRITE_EXTERNAL_STORAGE,
Permission.READ_EXTERNAL_STORAGE)) {
return;
}
// 查找当前页面数据中有没有手动输入地址的内容,有的话放入集合中,如果没有则不必要再执行保存数据的操作;
List<UrlHistoryBean> newInputUrlHistoryList = initCurrentInputUrlHistoryList();
if (CollectionUtils.isEmpty(newInputUrlHistoryList)) {
return;
}
//移除目标集合中有重复URL地址的项;
removeDuplicateUrls(newInputUrlHistoryList);
// 初始化现有的所有历史数据,默认配置和缓存内容;
List<UrlHistoryBean> urlHistoryList = EnvConfigUtils.initUrlHistoryList(mContext);
// 新数据和缓存中的所有数据进行对比处理URL地址重复的情况;
if (urlHistoryList != null) {
// 移除新输入的地址,如果它与缓存中元素重复;
int sizeInput = newInputUrlHistoryList.size();
for (int i = 0; i < sizeInput; i++) {
int sizeHistory = urlHistoryList.size();
UrlHistoryBean urlInputBean = newInputUrlHistoryList.get(i);
String urlInput = urlInputBean.getUrlStr();
// // 如果输入的地址和历史中的记录重复时;
for (int j = 0; j < sizeHistory; j++) {
UrlHistoryBean item = urlHistoryList.get(j);
String urlHistory = item.getUrlStr();
if (TextUtils.equals(urlInput, urlHistory)) {
// 移除原历史列表中的相同地址和记录;
urlHistoryList.remove(item);
j--;
sizeHistory--;
}
}
}
} else {
urlHistoryList = new ArrayList<>();
}
// 只保留手动输入的地址内容,其它的全部从列表中移除;
if (urlHistoryList.size() > 0) {
int size = urlHistoryList.size();
for (int i = 0; i < size; i++) {
UrlHistoryBean urlInputBean = urlHistoryList.get(i);
String typeInput = urlInputBean.getType();
// 如果输入的不是手动输入的地址,则从输入列表中移除,这样能保证默认的地址不在新增的历史记录里;
if (!TextUtils.equals(typeInput, EnvConfigConsts.UrlHistoryType.INPUT)) {
urlHistoryList.remove(urlInputBean);
i--;
size--;
}
}
}
urlHistoryList.addAll(0, newInputUrlHistoryList);
// 限制缓存数量,将超出缓存上限的数据移除;
limitCacheCount(urlHistoryList);
// 将手动输入的URL数据缓存;
if (urlHistoryList.size() > 0) {
final Gson gson = new Gson();
String newJson = gson.toJson(urlHistoryList);
SharedPrefsManager sharedPrefsManager = SharedPrefsManager.getInstance()
.init(mContext, EnvConfigConsts.EnvConfig.ENV_CONFIG_SP);
sharedPrefsManager
.putString(EnvConfigConsts.EnvConfig.URL_INPUT_HISTORY_JSON, newJson)
.commit();
}
}
/**
* 限制缓存数量,将超出缓存上限的数据移除;
*
* @param urlHistoryListCache
*/
private void limitCacheCount(List<UrlHistoryBean> urlHistoryListCache) {
if (urlHistoryListCache.size() <= MAX_COUNT_OF_HISTORY_URL) {
return;
}
for (; ; ) {
if (urlHistoryListCache.size() > MAX_COUNT_OF_HISTORY_URL) {
int index = urlHistoryListCache.size() - 1;
if (index > 0) {
urlHistoryListCache.remove(index);
}
} else {
break;
}
}
}
/**
* 移除目标集合中有重复URL地址的项;
*
* @param urlHistoryBeanList
*/
private void removeDuplicateUrls(List<UrlHistoryBean> urlHistoryBeanList) {
int size = urlHistoryBeanList.size();
// 移除内部URL字段相同的重复元素;
if (size > 1) {
for (int i = 0; i < size; i++) {
// if (size <= 1) {
// break;
// }
UrlHistoryBean urlInputBean = urlHistoryBeanList.get(i);
String url = urlInputBean.getUrlStr();
for (int j = i + 1; j < size; j++) {
UrlHistoryBean next = urlHistoryBeanList.get(j);
String urlNext = next.getUrlStr();
// 移除内部URL字段相同的重复元素;
if (TextUtils.equals(url, urlNext)) {
urlHistoryBeanList.remove(next);
j--;
size--;
}
}
}
}
}
/**
* 查找当前页面数据中有手动输入地址的内容,有的话放入集合中;
* 默认配置的地址项不放在集合中;
*
* @return
*/
private List<UrlHistoryBean> initCurrentInputUrlHistoryList() {
List<UrlHistoryBean> newUrlHistoryList = new ArrayList<>();
//
addCurrentInputUrlHistory(newUrlHistoryList, mApiUrlDetailsBean);
//
addCurrentInputUrlHistory(newUrlHistoryList, mImageUrlDetailsBean);
//
addCurrentInputUrlHistory(newUrlHistoryList, mH5UrlDetailsBean);
return newUrlHistoryList;
}
private void addCurrentInputUrlHistory(List<UrlHistoryBean> newUrlHistoryList, UrlDetailsBean urlDetailsBean) {
if (newUrlHistoryList == null || urlDetailsBean == null) {
return;
}
if (!urlDetailsBean.isChecked()) {
return;
}
final UrlHistoryBean urlHistoryBean = urlDetailsBean.getUrlHistoryBean();
if (urlHistoryBean == null) {
return;
}
final String urlStr = urlHistoryBean.getUrlStr();
if (TextUtils.isEmpty(urlStr)) {
return;
}
final String type = urlHistoryBean.getType();
if (TextUtils.equals(type, EnvConfigConsts.UrlHistoryType.INPUT)) {
newUrlHistoryList.add(urlHistoryBean);
}
}
}
开关配置
开关配置项的各个开关相互独立,处理起来比较简单,基本是都是布尔值来控制相应的业务逻辑。这些开关参数设置后,可以在项目中具体的去应用,比如让应用启动后就直接跳转广告页,直接去测试其它功能,节省了开发测试的时间。
package com.windfallsheng.envconfig.fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.windfallsheng.envconfig.EnvConfigConsts;
import com.windfallsheng.envconfig.EnvConfigUtils;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.adapter.SwitchConfigAdapter;
import com.windfallsheng.envconfig.bean.EnvConfigBean;
import com.windfallsheng.envconfig.bean.MockUserInfoWrapper;
import com.windfallsheng.envconfig.dialog.UserInfoEditDialog;
import com.windfallsheng.lib.ToastUtils;
import com.windfallsheng.lib.envconfig.util.CollectionUtils;
import com.windfallsheng.lib.envconfig.util.LogUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author: lzsheng
* @date: 2021/10/26 15:45
* @description: 一些开关配置项的处理;
*/
public class SwitchConfigFragment extends Fragment {
private final String TAG = "SwitchConfigFragment";
private Context mContext;
private View rootView;
private RecyclerView mRecyclerView;
private SwitchConfigAdapter mSwitchConfigAdapter;
private List<EnvConfigBean> mSwitchConfigList;
/**
* 被选择的Mock用户;
*/
private MockUserInfoWrapper mMockUserWrapperSelected;
private SwitchConfigFragment() {
}
public static SwitchConfigFragment newInstance() {
SwitchConfigFragment fragment = new SwitchConfigFragment();
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_switch_config, container, false);
mRecyclerView = rootView.findViewById(R.id.recyclerview_config);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mMockUserWrapperSelected = EnvConfigUtils.getMockUserSelected(mContext);
//
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
// 如果本地有缓存的数据,则根据缓存数据的状态,列表中可能的选中项;
mSwitchConfigList = EnvConfigUtils.initSwitchConfigListWithCache(mContext);
LogUtils.dl(TAG, "method:onActivityCreated#mSwitchConfigList=" + mSwitchConfigList);
mSwitchConfigAdapter = new SwitchConfigAdapter();
mSwitchConfigAdapter.addAll(mSwitchConfigList);
mRecyclerView.setAdapter(mSwitchConfigAdapter);
// 行点击事件
mSwitchConfigAdapter.setOnRecyclerViewItemClick((SwitchConfigAdapter.OnRecyclerViewItemClick<EnvConfigBean>)
(childView, position, envConfigBean) -> {
handleRecyclerViewItemClick(position, envConfigBean);
});
}
/**
* 返回这个页面的配置信息,将选中的配置项返回;
*
* @return
*/
public Map<String, EnvConfigBean> buildSwitchConfigMap() {
if (CollectionUtils.isEmpty(mSwitchConfigList)) {
return null;
}
Map<String, EnvConfigBean> switchConfigMap = new HashMap<>();
for (int i = 0; i < mSwitchConfigList.size(); i++) {
final EnvConfigBean envConfigBean = mSwitchConfigList.get(i);
final String configId = envConfigBean.getConfigId();
if (TextUtils.isEmpty(configId)) {
ToastUtils.showToast("configId为空");
continue;
}
// 将选中的项放在集合当中;
if (envConfigBean.isChecked()) {
// 将数据对象的copy返回给外界使用,不能和当前页面持有的对象产生关联影响;
switchConfigMap.put(configId, (EnvConfigBean) envConfigBean.clone());
}
// if (TextUtils.equals(configId, CONFIG_ID_USE_MOCK_USER)){
// if (envConfigBean.isChecked()){
// mUserInfoWrapperSelected = EnvConfigUtils.getMockUserSelected(mContext);
// }
// }
}
return switchConfigMap;
}
public MockUserInfoWrapper getMockUserWrapperSelected() {
if (mMockUserWrapperSelected != null) {
// 将数据对象的copy返回给外界使用,不能和当前页面持有的对象产生关联影响;
mMockUserWrapperSelected.clone();
}
return mMockUserWrapperSelected;
}
public void resetListAndViews() {
mSwitchConfigList = EnvConfigUtils.getSwitchConfigFromJson();
mSwitchConfigAdapter.addNewAll(mSwitchConfigList);
mSwitchConfigAdapter.notifyDataSetChanged();
//
mMockUserWrapperSelected = EnvConfigUtils.getMockUserSelected(mContext);
}
private void handleRecyclerViewItemClick(int position, EnvConfigBean envConfigBean) {
// 单个选项为一组时的业务处理
boolean isChecked = envConfigBean.isChecked();
envConfigBean.setChecked(!isChecked);
mSwitchConfigAdapter.notifyItemChanged(position);
// 如果开启了使用mock用户的功能;
if (envConfigBean.isChecked()) {
final String configId = envConfigBean.getConfigId();
if (TextUtils.equals(configId, EnvConfigConsts.ConfigId.USE_MOCK_USER)) {
showUserInfoEditDialog();
}
}
}
private void showUserInfoEditDialog() {
List<MockUserInfoWrapper> mockUserInfoWrapperList = EnvConfigUtils.initMockUserWrapperList(mContext);
new UserInfoEditDialog.Builder(mContext)
.setTitle(R.string.title_select_mock_user)
.setMockUserInfoWrapperList(mockUserInfoWrapperList)
.setOnSaveAllUsersCompleteListener((dialog, userInfoWrapper) -> {
mMockUserWrapperSelected = userInfoWrapper;
dialog.dismiss();
})
.setOnCloseClickedListener(dialog -> {
dialog.dismiss();
})
.create()
.show();
}
}
快捷测试入口
根据自己业务的需要,可以在这里设置一些快捷的功能测试入口;
package com.windfallsheng.envconfig.fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.windfallsheng.envconfig.EnvConfigUtils;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.MockUserInfoWrapper;
import com.windfallsheng.envconfig.dialog.UserInfoEditDialog;
import com.windfallsheng.lib.ToastUtils;
import java.util.List;
/**
* @Author: lzsheng
* @date: 2021/10/26 17:03
* @description: 一些功能的测试入口;
*/
public class TestAccessFragment extends Fragment {
private final String TAG = "TestAccessFragment";
private View rootView;
private Context mContext;
private TextView tvTest1;
private TextView tvTest2;
private TextView tvTest3;
private TextView tvTest5;
private TextView tvTest4;
/**
* 语音播报
*/
private TextView tvTest;
/**
* 语音播报,输入金额;
*/
private EditText etContent;
private TextView tvMockLoginUser;
public static TestAccessFragment newInstance() {
TestAccessFragment fragment = new TestAccessFragment();
return fragment;
}
@Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_test_access, container, false);
tvTest = rootView.findViewById(R.id.tv_test);
etContent = rootView.findViewById(R.id.et_content);
tvTest1 = rootView.findViewById(R.id.tv_test_1);
tvTest5 = rootView.findViewById(R.id.tv_test_5);
tvTest4 = rootView.findViewById(R.id.tv_test_4);
tvMockLoginUser = rootView.findViewById(R.id.tv_mock_login_user);
tvTest2 = rootView.findViewById(R.id.tv_test_2);
tvTest3 = rootView.findViewById(R.id.tv_test_3);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
tvMockLoginUser.setOnClickListener(v -> {
showUserInfoEditDialog();
});
tvTest.setOnClickListener(v -> {
final String trim = etContent.getText().toString().trim();
if (TextUtils.isEmpty(trim)) {
ToastUtils.showToast("输入内容为空");
} else {
ToastUtils.showToast(trim);
}
});
tvTest1.setOnClickListener(v -> {
ToastUtils.showToast(tvTest1.getText().toString());
});
tvTest2.setOnClickListener(v -> {
ToastUtils.showToast(tvTest2.getText().toString());
});
tvTest3.setOnClickListener(v -> {
ToastUtils.showToast(tvTest3.getText().toString());
});
tvTest4.setOnClickListener(v -> {
ToastUtils.showToast(tvTest4.getText().toString());
});
tvTest5.setOnClickListener(v -> {
ToastUtils.showToast(tvTest5.getText().toString());
});
}
private void showUserInfoEditDialog() {
List<MockUserInfoWrapper> userInfoWrapperList = EnvConfigUtils.initMockUserWrapperList(mContext);
new UserInfoEditDialog.
Builder(mContext)
.setTitle(R.string.title_select_mock_user)
.setMockUserInfoWrapperList(userInfoWrapperList)
.setOnSaveAllUsersCompleteListener((dialog, userInfoWrapper) -> dialog.dismiss())
.setOnCloseClickedListener(dialog -> dialog.dismiss())
.create()
.show();
}
}
环境配置主页面
主页面展示了各个配置项的fragment,同时负担处理,完成配置、应用配置、恢复默认设置、及查看配置详情的交互业务。
package com.windfallsheng.envconfig.dialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.flyco.tablayout.SlidingTabLayout;
import com.google.gson.Gson;
import com.windfallsheng.envconfig.EnvConfigConsts;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.AllServersUrlConfigBean;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.bean.EnvConfigBean;
import com.windfallsheng.envconfig.bean.EnvConfigWrapper;
import com.windfallsheng.envconfig.bean.MockUserInfoWrapper;
import com.windfallsheng.envconfig.bean.UrlDetailsBean;
import com.windfallsheng.envconfig.fragment.ServerUrlConfigListFragment;
import com.windfallsheng.envconfig.fragment.ServerUrlManualInputFragment;
import com.windfallsheng.envconfig.fragment.SwitchConfigFragment;
import com.windfallsheng.envconfig.fragment.TestAccessFragment;
import com.windfallsheng.lib.ToastUtils;
import com.windfallsheng.lib.envconfig.util.CollectionUtils;
import com.windfallsheng.lib.envconfig.util.DisplayHelper;
import com.windfallsheng.lib.envconfig.util.LogUtils;
import com.windfallsheng.lib.envconfig.util.SharedPrefsManager;
import com.windfallsheng.lib.xxpermissions.Permission;
import com.windfallsheng.lib.xxpermissions.XXPermissions;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Author: lzsheng
* @CreateDate: 2020/12/16 14:54
* @Description:
*/
public class EnvConfigDialogFragment extends BaseDialogFragment {
private final String TAG = "EnvConfigDialogFragment";
private View rootView;
/**
* 分类下的各页面;
*/
private ServerUrlConfigListFragment mAllServerUrlDetailsConfigFragment;
private ServerUrlManualInputFragment mServerUrlManualInputFragment;
private SwitchConfigFragment mSwitchConfigFragment;
private TestAccessFragment mTestAccessFragment;
private TextView tvClose;
private TextView tvCheckDetails;
private TextView tvApply;
private TextView tvFinish;
private TextView tvResetAllConfig;
/**
* 选择完成后的回调;
*/
private EnvConfigCheckedCompleteListener mEnvConfigCheckedCompleteListener;
/**
*
*/
private SlidingTabLayout mTabLayout;
private ViewPager mViewPager;
private List<Fragment> mFragments = new ArrayList<>();
private String[] mTabTitles = new String[]{"URL配置", "手输URL", "开关配置", "测试入口"};
public static EnvConfigDialogFragment newInstance() {
EnvConfigDialogFragment envConfigDialogFragment = new EnvConfigDialogFragment();
return envConfigDialogFragment;
}
@Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
if (context instanceof EnvConfigCheckedCompleteListener) {
try {
mEnvConfigCheckedCompleteListener = (EnvConfigCheckedCompleteListener) context;
} catch (ClassCastException e) {
LogUtils.el(TAG, "method:onAttach#exception=" + e.getMessage());
}
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
rootView = inflater.inflate(R.layout.env_config_layout_fragment_dialog, container, false);
tvClose = rootView.findViewById(R.id.tv_close);
tvCheckDetails = rootView.findViewById(R.id.tv_check_details);
mTabLayout = rootView.findViewById(R.id.tab_layout);
mViewPager = rootView.findViewById(R.id.viewpager);
tvApply = rootView.findViewById(R.id.tv_apply);
tvFinish = rootView.findViewById(R.id.tv_finish);
tvResetAllConfig = rootView.findViewById(R.id.tv_reset_config);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 调整布局的宽高
ViewGroup.LayoutParams layoutParams = rootView.getLayoutParams();
// layoutParams.height = (int) (DisplayHelper.getScreenHeight(mContext) * 0.8);
// layoutParams.width = (int) (DisplayHelper.getScreenWidth(mContext) * 0.8);
layoutParams.height = (int) (DisplayHelper.getScreenHeight(mContext) * 1);
layoutParams.width = (int) (DisplayHelper.getScreenWidth(mContext) * 0.95);
rootView.setLayoutParams(layoutParams);
FrameLayout parent = (FrameLayout) rootView.getParent();
parent.setBackgroundResource(R.color.transparent);
// viewpager及页面数据相关;
mFragments = new ArrayList<>();
mAllServerUrlDetailsConfigFragment = ServerUrlConfigListFragment.newInstance();
mServerUrlManualInputFragment = ServerUrlManualInputFragment.newInstance();
mSwitchConfigFragment = SwitchConfigFragment.newInstance();
mTestAccessFragment = TestAccessFragment.newInstance();
mFragments.add(mAllServerUrlDetailsConfigFragment);
mFragments.add(mServerUrlManualInputFragment);
mFragments.add(mSwitchConfigFragment);
mFragments.add(mTestAccessFragment);
mViewPager.setOffscreenPageLimit(mFragments.size());
//设置viewpager的adapter
mViewPager.setAdapter(new FragmentAdapter(getChildFragmentManager()));
//TabLayout与ViewPager的绑定
mTabLayout.setViewPager(mViewPager);
tvClose.setOnClickListener(v -> dismiss());
// 重置配置项事件;
tvResetAllConfig.setOnClickListener(v -> handleResetAllConfig());
tvCheckDetails.setOnClickListener(v -> showEnvConfigDetailsDialog());
// 应用配置事件;
tvApply.setOnClickListener(v -> {
handlerApplyClicked();
ToastUtils.showToast("应用成功");
});
tvFinish.setOnClickListener(v -> handlerFinishClicked());
}
private void showEnvConfigDetailsDialog() {
final EnvConfigWrapper envConfigWrapper = buildEnvConfigWrapperFromFragments();
new ShowEnvConfigDetailsDialog.Builder(mContext)
.setTitle(R.string.title_configuration_details)
.setEnvConfigWrapper(envConfigWrapper)
.create()
.show();
}
/**
* 重置所有配置项及UI效果;
*/
private void handleResetAllConfig() {
// 移除缓存数据;
if (XXPermissions.isGrantedPermission(mContext, Permission.WRITE_EXTERNAL_STORAGE,
Permission.READ_EXTERNAL_STORAGE)) {
final SharedPrefsManager sharedPrefsManager = SharedPrefsManager.getInstance()
.init(mContext, EnvConfigConsts.EnvConfig.ENV_CONFIG_SP);
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.ALL_SERVER_URL_DETAILS);
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.SERVER_URL_DETAILS_INPUT);
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.SWITCH_CONFIG_MAP_JSON);
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.MOCK_USER_CONFIG_JSON);
}
// UI及页面数据重置;
mAllServerUrlDetailsConfigFragment.resetListAndViews();
mServerUrlManualInputFragment.resetDataAndViews();
mSwitchConfigFragment.resetListAndViews();
// 重置页面及数据后,需要应用当前的设置;
handlerApplyClicked();
}
/**
* 处理点击完成的业务;
*/
private void handlerApplyClicked() {
// 封装数据;
final EnvConfigWrapper envConfigWrapper = buildEnvConfigWrapperFromFragments();
if (envConfigWrapper.isEmpty()) {
ToastUtils.showToast("配置的结果对象内容为空");
return;
}
// 刷新环境配置;
EnvConfigHelper.getInstance()
.updateEnvServerUrlBySelectedMode(envConfigWrapper);
// 保存配置信息到本地;
if (XXPermissions.isGrantedPermission(mContext, Permission.WRITE_EXTERNAL_STORAGE,
Permission.READ_EXTERNAL_STORAGE)) {
// 缓存手动输入的URL历史数据;
mServerUrlManualInputFragment.saveUrlInputHistory();
// 缓存其它配置数据;
Gson gson = new Gson();
//
final AllServersUrlConfigBean allServerUrlDetails = envConfigWrapper.getAllServerUrlDetails();
// 暂时使用 SharedPreferences 作为本地缓存,根据实际需要选择处理方式;
final SharedPrefsManager sharedPrefsManager = SharedPrefsManager.getInstance();
sharedPrefsManager.init(mContext, EnvConfigConsts.EnvConfig.ENV_CONFIG_SP);
if (allServerUrlDetails != null) {
String allServerUrlJson = gson.toJson(allServerUrlDetails);
sharedPrefsManager
.putString(EnvConfigConsts.EnvConfig.ALL_SERVER_URL_DETAILS, allServerUrlJson)
.commit();
} else {
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.ALL_SERVER_URL_DETAILS);
}
// 手动输入的URL;
final Map<String, UrlDetailsBean> serverUrlDetailsSelectedMap = envConfigWrapper.getUrlConfigDetailsInputSelectedMap();
if (CollectionUtils.isNotEmptyMap(serverUrlDetailsSelectedMap)) {
String serverUrlInputJson = gson.toJson(serverUrlDetailsSelectedMap);
sharedPrefsManager
.putString(EnvConfigConsts.EnvConfig.SERVER_URL_DETAILS_INPUT, serverUrlInputJson)
.commit();
} else {
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.SERVER_URL_DETAILS_INPUT);
}
// 开关配置项;
final Map<String, EnvConfigBean> switchConfigMap = envConfigWrapper.getSwitchConfigMap();
if (CollectionUtils.isNotEmptyMap(switchConfigMap)) {
String switchConfigJson = gson.toJson(switchConfigMap);
sharedPrefsManager
.putString(EnvConfigConsts.EnvConfig.SWITCH_CONFIG_MAP_JSON, switchConfigJson)
.commit();
} else {
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.SWITCH_CONFIG_MAP_JSON);
}
// Mock用户;
final MockUserInfoWrapper mockUserInfoWrapper = envConfigWrapper.getMockUserInfoWrapper();
if (mockUserInfoWrapper != null) {
String mockUserInfoWrapperJson = gson.toJson(mockUserInfoWrapper);
sharedPrefsManager
.putString(EnvConfigConsts.EnvConfig.MOCK_USER_CONFIG_JSON, mockUserInfoWrapperJson)
.commit();
} else {
sharedPrefsManager.removeKey(EnvConfigConsts.EnvConfig.MOCK_USER_CONFIG_JSON);
}
}
// 修改环境配置的方法可以根据需求放在 EnvConfigDialogFragment 的此处调用;
// 也可以在页面的 mEnvConfigCheckedCompleteListener 回调时调用;
// EnvConfigHelper.getInstance()
// .updateEnvServerUrl(envConfigWrapper.getAllServerUrlDetailsOfCheckedEnv().getModeOfEnv(), envConfigWrapper);
// 接口回调;
if (mEnvConfigCheckedCompleteListener == null) {
LogUtils.el(TAG, "method:handlerFinishClicked#mConfigCompleteListener=" + mEnvConfigCheckedCompleteListener);
} else {
mEnvConfigCheckedCompleteListener.onEnvConfigCheckedComplete(envConfigWrapper);
}
}
/**
* 处理点击完成的业务;
*/
private void handlerFinishClicked() {
handlerApplyClicked();
dismiss();
}
/**
* 构建返回结果,将选中的数据内容封装在对象内;
*
* @return
*/
private EnvConfigWrapper buildEnvConfigWrapperFromFragments() {
// 获取各个页面的相关数据;
final AllServersUrlConfigBean allServerUrlDetailsSelected = mAllServerUrlDetailsConfigFragment.getAllServerUrlDetailsSelected();
final Map<String, EnvConfigBean> switchConfigMap = mSwitchConfigFragment.buildSwitchConfigMap();
final MockUserInfoWrapper mockUserWrapperSelected = mSwitchConfigFragment.getMockUserWrapperSelected();
final Map<String, UrlDetailsBean> serverUrlDetailsInputSelected = mServerUrlManualInputFragment.getServerUrlDetailsInput();
// 封装数据;
final EnvConfigWrapper envConfigWrapper = new EnvConfigWrapper();
if (allServerUrlDetailsSelected != null) {
envConfigWrapper.setAllServerUrlDetails(allServerUrlDetailsSelected);
}
if (CollectionUtils.isNotEmptyMap(serverUrlDetailsInputSelected)) {
envConfigWrapper.setUrlConfigDetailsInputSelectedMap(serverUrlDetailsInputSelected);
}
if (CollectionUtils.isNotEmptyMap(switchConfigMap)) {
envConfigWrapper.setSwitchConfigMap(switchConfigMap);
}
if (mockUserWrapperSelected != null) {
envConfigWrapper.setMockUserInfoWrapper(mockUserWrapperSelected);
}
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean != null) {
envConfigWrapper.setModeOfBuild(appInfoBean.getModeValueOfBuild());
} else {
LogUtils.el(TAG, "method:buildEnvConfigWrapperFromFragments#EnvConfigHelper#mAppInfoBean not initialize.");
}
return envConfigWrapper;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
public class FragmentAdapter extends FragmentPagerAdapter {
public FragmentAdapter(FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@Override
public int getCount() {
return mTabTitles.length;
}
@NonNull
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mTabTitles[position];
}
}
}
其它
其它的一些功能,包括展示配置详情、手动输入的URL的自定义UI处理、手动输入的URL的历史记录功能,及使用mock用户的功能,本属于额外的功能,不再做说明。
总结
绝大部分的配置参数在修改和扩展时,只需要修改json串的内容就可以,即时是需要添加一些开关配置项,会需要改动一部分的代码,但相对来说,改动范围是可控的,也是比较方便的,基础的逻辑框架不需要更改。目前的参数配置基本是满足大部分使用场景的,实际应用还要立足于自身项目,做一些调整。