直接查看 《 Android实现动态切换环境配置4.0版本》
前言
在上一篇幅《Android在APP开发阶段,实现动态的环境配置切换》 我们实现了可以动态切换环境配置的功能,现在扩展了它的功能,添加了手动输入的URL地址,保存到历史记录列表中,可以从列表选择使用,这样可以在手动输入地址的时候更方便、快捷一些,并且添加了一些可以动态修改自定义View的参数设置功能。
先上图
apk 安装包地址:https://download.csdn.net/download/Silence1515/21059203
项目工程地址:https://download.csdn.net/download/Silence1515/21059232
历史记录功能
在手动输入URL地址的部分添加了历史记录功能,可以配置常用的地址,如果有手动输入的地址且保存使用,则会缓存在本地,下次中以查看到。历史记录数据,每次会将最近输入的地址放在前面,且地址不会重复,配置的常用地址不会再另外缓存到本地。
主要看一下修改业务的类:
UI 是的一些测试代码:
package com.windfallsheng;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.bean.EnvConfigWrapper;
import com.windfallsheng.envconfig.dialog.EnvConfigCheckedCompleteListener;
import com.windfallsheng.envconfig.envconfigexample.R;
import com.windfallsheng.envconfig.widget.EnvConfigShadowLayout;
import com.windfallsheng.lib.util.LogUtils;
public class MainActivity extends AppCompatActivity implements EnvConfigCheckedCompleteListener {
private boolean isDisable;
private boolean isSingleClick;
private boolean isExplicitDisplay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout linearLayout = findViewById(R.id.layout_disable);
LinearLayout linearLayoutTest = findViewById(R.id.layout_test_click);
LinearLayout layoutSet = findViewById(R.id.layout_set);
LinearLayout layoutClick = findViewById(R.id.layout_click);
linearLayout.setOnClickListener(v -> {
Toast.makeText(MainActivity.this, "点击事件响应", Toast.LENGTH_SHORT).show();
});
linearLayoutTest.setOnClickListener(v -> {
Toast.makeText(MainActivity.this, "Test点击事件响应", Toast.LENGTH_SHORT).show();
});
EnvConfigShadowLayout envConfigShadowLayout = findViewById(R.id.shadow_layout);
layoutSet.setOnClickListener(v -> {
isDisable = !isDisable;
envConfigShadowLayout.setDisabled(isDisable);
});
EnvConfigShadowLayout shadowLayout = findViewById(R.id.shadow_layout_click);
layoutClick.setOnClickListener(v -> {
Toast.makeText(MainActivity.this, "属性设置--点击事件响应", Toast.LENGTH_SHORT).show();
isExplicitDisplay = !isExplicitDisplay;
shadowLayout.setExplicitDisplay(isExplicitDisplay);
//
// isSingleClick = !isSingleClick;
// shadowLayout.setSingleClick(isSingleClick);
});
}
@Override
public void onEnvConfigCheckedComplete(EnvConfigWrapper envConfigWrapper) {
// 根据返回结果,处理实际项目中的业务;
LogUtils.dl("MainActivity", "method:onConfigCompleteClicked#envConfigWrapper=" + envConfigWrapper);
// 修改环境配置的方法可以根据需求放在 EnvConfigDialogFragment 中调用也可以;
EnvConfigHelper.getInstance()
.updateEnvServerUrl(envConfigWrapper);
}
}
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="wrap_content"
android:scrollbars="none"
tools:context="com.windfallsheng.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:orientation="vertical">
<!-- 第一行内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:id="@+id/layout_verify_coupon"
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp"
app:is_explicit_display="true"
app:is_single_click="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="用户可见--单次点击触发"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:id="@+id/layout_one_move_accomplish"
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="用户不可见--连续多次点击触发"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
</LinearLayout>
<!-- 第二行内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp"
app:is_explicit_display="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="用户可见--连续多次点击触发"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp"
app:is_single_click="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="用户不可见--单次点击触发"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
</LinearLayout>
<!-- 第三行内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_disable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp"
app:is_disabled="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="disabled禁用状态"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_test_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:layout_width="78dp"
android:layout_height="78dp"
app:is_explicit_display="true"
app:limit_click_count="5" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="连续点击次数上限设置"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
</LinearLayout>
<!-- 第三行内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:id="@+id/shadow_layout"
android:layout_width="78dp"
android:layout_height="78dp"
app:is_explicit_display="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="测试set赋值"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="156dp"
android:layout_weight="1"
android:background="@drawable/selector_common_bg_8dp_white_gray"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.windfallsheng.envconfig.widget.EnvConfigShadowLayout
android:id="@+id/shadow_layout_click"
android:layout_width="78dp"
android:layout_height="78dp"
app:is_explicit_display="true"
app:is_single_click="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="是否单次点击/显式可见设置"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
<Space
android:layout_width="16dp"
android:layout_height="100dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
UrlHistoryBean 实体类:
package com.windfallsheng.envconfig.bean;
import androidx.annotation.NonNull;
import java.io.Serializable;
/**
* @Author: lzsheng
* @date: 2021/8/13 14:17
* @description:
*/
public class UrlHistoryBean implements Serializable ,Cloneable{
private String type;
private String urlStr;
public UrlHistoryBean(String type, String urlStr) {
this.type = type;
this.urlStr = urlStr;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrlStr() {
return urlStr;
}
public void setUrlStr(String urlStr) {
this.urlStr = urlStr;
}
@NonNull
@Override
protected Object clone() {
UrlHistoryBean urlHistoryBean = null;
try {
urlHistoryBean = (UrlHistoryBean) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return urlHistoryBean;
}
@Override
public String toString() {
return "UrlHistoryBean{" +
"type='" + type + '\'' +
", urlStr='" + urlStr + '\'' +
'}';
}
}
UrlDetailsInputLayout 自定义的URL输入类中的业务变动:
package com.windfallsheng.envconfig.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Editable;
import android.text.Selection;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.URLUtil;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.ServerUrlDetailsBean;
import com.windfallsheng.envconfig.bean.UrlHistoryBean;
import com.windfallsheng.envconfig.dialog.EnvConfigDialogFragment;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @Author: lzsheng
* @date: 2021/8/6 17:15
* @description:
*/
public class UrlDetailsInputLayout extends RelativeLayout {
private final int INPUT_MAX_LENGTH = 80;
private Context mContext;
private View mRootView;
private LinearLayout mLayoutContent;
private TextView mTvTypeName;
private TextView mTvShowUrlHistory;
private EditText mEtInput;
private boolean isChecked;
private CheckBox mCbSelect;
private String mTypeId;
private String mTypeName;
private String mUrlInput;
private UrlHistoryBean mUrlHistoryBean = new UrlHistoryBean(EnvConfigDialogFragment.URL_HISTORY_TYPE_INPUT, "");
private OnShowUrlHistoryListener mOnShowUrlHistoryListener;
public UrlDetailsInputLayout(Context context) {
super(context);
init(context, null, 0);
}
public UrlDetailsInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public UrlDetailsInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
this.mContext = context;
mRootView = LayoutInflater.from(context).inflate(R.layout.env_config_layout_url_details_input, null);
mLayoutContent = mRootView.findViewById(R.id.layout_content);
mTvTypeName = mRootView.findViewById(R.id.tv_type_name);
mTvShowUrlHistory = mRootView.findViewById(R.id.tv_show_url_history);
mEtInput = mRootView.findViewById(R.id.et_input);
mCbSelect = mRootView.findViewById(R.id.cb_selecte);
addView(mRootView);
mRootView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.url_details_input_layout);
mTypeId = typedArray.getString(R.styleable.url_details_input_layout_type_id);
mTypeName = typedArray.getString(R.styleable.url_details_input_layout_type_name);
mUrlInput = typedArray.getString(R.styleable.url_details_input_layout_url_input);
isChecked = typedArray.getBoolean(R.styleable.url_details_input_layout_is_checked, false);
int bgColor = typedArray.getColor(R.styleable.url_details_input_layout_item_bg, ContextCompat.getColor(mContext, R.color.white));
typedArray.recycle();
mLayoutContent.setBackgroundColor(bgColor);
mTvTypeName.setText(mTypeName);
if (!TextUtils.isEmpty(mUrlInput)) {
mEtInput.setText(mUrlInput);
}
mEtInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String str = s.toString().trim();
Editable editable = mEtInput.getText();
if (str.length() > INPUT_MAX_LENGTH) {
Toast.makeText(mContext, "超过80个字符的输入限制", Toast.LENGTH_SHORT).show();
int selEndIndex = Selection.getSelectionEnd(editable);
//截取新字符串
String newStr = str.substring(0, INPUT_MAX_LENGTH);
mEtInput.setText(newStr);
editable = mEtInput.getText();
//新字符串的长度
int newLen = editable.length();
//旧光标位置超过字符串长度
if (selEndIndex > newLen) {
selEndIndex = editable.length();
}
//设置新光标所在的位置
Selection.setSelection(editable, selEndIndex);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
mCbSelect.setChecked(isChecked);
mLayoutContent.setOnClickListener(v -> {
isChecked = !isChecked;
mCbSelect.setChecked(isChecked);
});
mTvShowUrlHistory.setOnClickListener(v -> {
if (mOnShowUrlHistoryListener != null) {
mOnShowUrlHistoryListener.onShowUrlHistory(v);
}
});
}
public void setShowHistoryViewId(int id) {
mTvShowUrlHistory.setId(id);
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
if (mCbSelect != null) {
mCbSelect.setChecked(this.isChecked);
}
}
public String getTypeName() {
return mTypeName;
}
public void setOnShowUrlHistoryListener(OnShowUrlHistoryListener onShowUrlHistoryListener) {
this.mOnShowUrlHistoryListener = onShowUrlHistoryListener;
}
/**
*
*/
public void setUrlInput(UrlHistoryBean urlHistoryBean) {
if (urlHistoryBean != null) {
mUrlHistoryBean = urlHistoryBean;
String urlStr = urlHistoryBean.getUrlStr();
if (!TextUtils.isEmpty(urlStr)) {
mEtInput.setText(urlStr);
}
}
}
/**
* 获取数据 ServerUrlDetailsBean 实例,如果网址输入无效则返回 null;
*/
public ServerUrlDetailsBean getUrlDetailBean() {
String urlInput = mEtInput.getText().toString().trim();
if (!TextUtils.isEmpty(urlInput) && isUrlValid(urlInput)) {
if (mUrlHistoryBean != null) {
String urlStr = mUrlHistoryBean.getUrlStr();
if (TextUtils.equals(urlInput, urlStr)) {
// 如果url地址相同,暂不处理;
} else {
// 如果url地址不同,需要修改类型,并且重新赋值;
mUrlHistoryBean.setUrlStr(urlInput);
mUrlHistoryBean.setType(EnvConfigDialogFragment.URL_HISTORY_TYPE_INPUT);
}
}
return new ServerUrlDetailsBean(mTypeId, mUrlHistoryBean, mTypeName, mCbSelect.isChecked());
}
return null;
}
/**
* 重置所有数据及UI
*/
public void resetData() {
mCbSelect.setChecked(false);
isChecked = false;
mEtInput.setText("");
mUrlInput = "";
}
/**
* 网址是否有效
*/
private boolean isUrlValid(String urlString) {
try {
URL url = new URL(urlString);
return URLUtil.isValidUrl(urlString) && Patterns.WEB_URL.matcher(urlString).matches();
} catch (MalformedURLException e) {
}
return false;
}
public interface OnShowUrlHistoryListener {
void onShowUrlHistory(View v);
}
}
UrlDetailsInputLayout 类修改了 UI 的样式和交互逻辑,以及处理手动输入URL的逻辑。
相应的 xml布局调整:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/tv_type_name"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:gravity="center_vertical"
android:textColor="@color/green_008c8c"
android:textSize="15sp"
tools:text="H5服务器地址" />
<TextView
android:id="@+id/tv_show_url_history"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:background="@drawable/layout_item_bg"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="历史记录"
android:textColor="@color/green_008c8c"
android:textSize="15sp" />
</RelativeLayout>
<EditText
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:hint="请输入地址(最长可输入80个字符)"
android:textColorHint="@color/gray_c3c3c3"
android:textColor="@color/app_text_color"
android:textSize="15sp" />
</LinearLayout>
<CheckBox
android:id="@+id/cb_selecte"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false" />
</LinearLayout>
EnvConfigDialogFragment 类中配置的 json 格式略微做了些调整,还有新增的 配置 json。另外,与历史记录相关的业务做了调整;
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.text.TextUtils;
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 android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.windfallsheng.envconfig.EnvConfigHelper;
import com.windfallsheng.envconfig.EnvServerUrlConsts;
import com.windfallsheng.envconfig.R;
import com.windfallsheng.envconfig.bean.AllServerUrlDetailsBean;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.bean.EnvConfigBean;
import com.windfallsheng.envconfig.bean.EnvConfigWrapper;
import com.windfallsheng.envconfig.bean.ServerUrlDetailsBean;
import com.windfallsheng.envconfig.bean.UrlHistoryBean;
import com.windfallsheng.envconfig.widget.UrlDetailsInputLayout;
import com.windfallsheng.lib.Consts;
import com.windfallsheng.lib.util.DisplayHelper;
import com.windfallsheng.lib.util.LogUtils;
import com.windfallsheng.lib.util.ResUtils;
import com.windfallsheng.lib.util.SharedPrefsManager;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: lzsheng
* @CreateDate: 2020/12/16 14:54
* @LastModifiedTime: 2020/12/16 14:54
* @Description:
*/
public class EnvConfigDialogFragment extends BaseDialogFragment implements UrlDetailsInputLayout.OnShowUrlHistoryListener {
public static final int VIEW_TYPE_SERVER_URL_DETAILS = 1;
public static final int VIEW_TYPE_AD = 2;
public static final int VIEW_TYPE_WEB = 3;
public static final int VIEW_TYPE_FIRST_INSTALL = 4;
public static final int VIEW_TYPE_LOG_DEBUG = 5;
/**
* 默认的URL
*/
public static final String URL_HISTORY_TYPE_DEFAULT = "default";
/**
* 手动输入的URL
*/
public static final String URL_HISTORY_TYPE_INPUT = "input";
/**
* URL的分类标题
*/
public static final String URL_HISTORY_TYPE_TITLE = "url_history_type_title";
/**
*
*/
private static final String ENV_CONFIG_WRAPPER_DEFAULT = "env_config_wrapper_default";
private final boolean CHECKED = true;
private final boolean UNCHECKED = false;
private final boolean REQUIRED = true;
private final boolean UNREQUIRED = false;
private final boolean GROUP = true;
private final boolean NO_GROUP = false;
private final String GROUP_ID_SERVER_URL_DETAILS = "server_url_details";
private final String GROUP_ID_SKIP_AD_PAGE = "skip_ad_page";
private final String GROUP_ID_FIRST_INSTALL = "first_install";
private final String GROUP_ID_LOG_DEBUG = "log_debug";
private final String TAG = "EnvConfigDialogFragment";
/**
* 根据需要修改配置数据的json串;
*/
private final String configJsonAllServerDetails = "[" +
"{" +
"\"configId\":\"dev_test_server_url_details\"," +
"\"groupId\":\"" + GROUP_ID_SERVER_URL_DETAILS + "\"," +
"\"modeOfEnv\":\"" + EnvConfigHelper.EnvMode.DEV_TEST_SERVER.getMode() + "\"," +
"\"labelName\":\"" + EnvConfigHelper.EnvMode.DEV_TEST_SERVER.getDesc() + "\"," +
"\"isChecked\":" + CHECKED + "," +
"\"isRequired\":" + REQUIRED + "," +
"\"isGroup\":" + GROUP + "," +
"\"viewType\":" + VIEW_TYPE_SERVER_URL_DETAILS + "," +
"\"apiServerUrlDetails\":" +
"{" +
"\"typeId\":\"api\"," +
"\"typeName\":\"API服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestServer.API + "\"" +
"}" +
"}," +
"\"imageServerUrlDetails\":" +
"{" +
"\"typeId\":\"image\"," +
"\"typeName\":\"image服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestServer.IMAGE + "\"" +
"}" +
"}," +
"\"h5ServerUrlDetails\":" +
"{" +
"\"typeId\":\"h5\"," +
"\"typeName\":\"H5服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestServer.H5 + "\"" +
"}" +
"}" +
"}," +
"{" +
"\"configId\":\"dev_test_local_server_url_details\"," +
"\"groupId\":\"" + GROUP_ID_SERVER_URL_DETAILS + "\"," +
"\"labelName\":\"" + EnvConfigHelper.EnvMode.DEV_TEST_LOCAL.getDesc() + "\"," +
"\"modeOfEnv\":\"" + EnvConfigHelper.EnvMode.DEV_TEST_LOCAL.getMode() + "\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + REQUIRED + "," +
"\"isGroup\":" + GROUP + "," +
"\"viewType\":" + VIEW_TYPE_SERVER_URL_DETAILS + "," +
"\"apiServerUrlDetails\":" +
"{" +
"\"typeId\":\"api\"," +
"\"typeName\":\"API服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestLocalServer.API + "\"" +
"}" +
"}," +
"\"imageServerUrlDetails\":" +
"{" +
"\"typeId\":\"image\"," +
"\"typeName\":\"image服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestLocalServer.IMAGE + "\"" +
"}" +
"}," +
"\"h5ServerUrlDetails\":" +
"{" +
"\"typeId\":\"h5\"," +
"\"typeName\":\"H5服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.TestLocalServer.H5 + "\"" +
"}" +
"}" +
"}," +
"{" +
"\"configId\":\"dev_pilot_server_url_details\"," +
"\"groupId\":\"" + GROUP_ID_SERVER_URL_DETAILS + "\"," +
"\"labelName\":\"" + EnvConfigHelper.EnvMode.DEV_PILOT_RUN.getDesc() + "\"," +
"\"modeOfEnv\":\"" + EnvConfigHelper.EnvMode.DEV_PILOT_RUN.getMode() + "\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + REQUIRED + "," +
"\"isGroup\":" + GROUP + "," +
"\"viewType\":" + VIEW_TYPE_SERVER_URL_DETAILS + "," +
"\"apiServerUrlDetails\":" +
"{" +
"\"typeId\":\"api\"," +
"\"typeName\":\"API服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.PilotRunServer.API + "\"" +
"}" +
"}," +
"\"imageServerUrlDetails\":" +
"{" +
"\"typeId\":\"image\"," +
"\"typeName\":\"image服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.PilotRunServer.IMAGE + "\"" +
"}" +
"}," +
"\"h5ServerUrlDetails\":" +
"{" +
"\"typeId\":\"h5\"," +
"\"typeName\":\"H5服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.PilotRunServer.H5 + "\"" +
"}" +
"}" +
"}," +
"{" +
"\"configId\":\"dev_product_server_url_details\"," +
"\"groupId\":\"" + GROUP_ID_SERVER_URL_DETAILS + "\"," +
"\"modeOfEnv\":\"" + EnvConfigHelper.EnvMode.DEV_PRODUCT.getMode() + "\"," +
"\"labelName\":\"" + EnvConfigHelper.EnvMode.DEV_PRODUCT.getDesc() + "\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + REQUIRED + "," +
"\"isGroup\":" + GROUP + "," +
"\"viewType\":" + VIEW_TYPE_SERVER_URL_DETAILS + "," +
"\"apiServerUrlDetails\":" +
"{" +
"\"typeId\":\"api\"," +
"\"typeName\":\"API服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.ProductServer.API + "\"" +
"}" +
"}," +
"\"imageServerUrlDetails\":" +
"{" +
"\"typeId\":\"image\"," +
"\"typeName\":\"image服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.ProductServer.IMAGE + "\"" +
"}" +
"}," +
"\"h5ServerUrlDetails\":" +
"{" +
"\"typeId\":\"h5\"," +
"\"typeName\":\"H5服务器地址\"," +
"\"urlHistoryBean\":" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":\"" + EnvServerUrlConsts.ProductServer.H5 + "\"" +
"}" +
"}" +
"}" +
"]";
/**
* 根据需要修改配置数据的json串;
*/
private final String configJsonSimple = "[" +
"{" +
"\"configId\":\"first_install\"," +
"\"groupId\":\"" + GROUP_ID_FIRST_INSTALL + "\"," +
"\"labelName\":\"是否为首次安装启动(选中代表是)\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + UNREQUIRED + "," +
"\"isGroup\":" + NO_GROUP + "," +
"\"viewType\":" + VIEW_TYPE_FIRST_INSTALL +
"}," +
"{" +
"\"configId\":\"log_debug\"," +
"\"groupId\":\"" + GROUP_ID_LOG_DEBUG + "\"," +
"\"labelName\":\"是否关闭Log日志(选中代表关闭)\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + UNREQUIRED + "," +
"\"isGroup\":" + NO_GROUP + "," +
"\"viewType\":" + VIEW_TYPE_LOG_DEBUG +
"}," +
"{" +
"\"configId\":\"skip_ad_page\"," +
"\"groupId\":\"" + GROUP_ID_SKIP_AD_PAGE + "\"," +
"\"labelName\":\"是否跳过广告页(选中代表是)\"," +
"\"isChecked\":" + UNCHECKED + "," +
"\"isRequired\":" + UNREQUIRED + "," +
"\"isGroup\":" + NO_GROUP + "," +
"\"viewType\":" + VIEW_TYPE_AD +
"}" +
"]";
private String urlHistoryDefaultJson = "[" +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":" + "\"https://history.url.temp.aa.com.cn:998\"" +
"}," +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":" + "\"https://history.url.temp.bb.com.cn:998\"" +
"}," +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":" + "\"https://history.url.temp.cc.com.cn:998\"" +
"}," +
"{" +
"\"type\":\"" + URL_HISTORY_TYPE_DEFAULT + "\"," +
"\"urlStr\":" + "\"https://history.url.temp.dd.com.cn:998\"" +
"}" +
"]";
private View rootView;
private RecyclerView mRecyclerView;
private TextView tvCheckDetails;
private TextView tvEnvDetails;
private TextView tvFinish;
private TextView tvResetAllConfig;
private EnvConfigAdapter mEnvConfigAdapter;
private List<EnvConfigBean> mEnvConfigList;
/**
* 手动输入URL的UI部分;
*/
private UrlDetailsInputLayout mUrlInputLayoutApi;
private UrlDetailsInputLayout mUrlInputLayoutImage;
private UrlDetailsInputLayout mUrlInputLayoutH5;
/**
* 传入的配置项或者上次缓存的配置项;
*/
private EnvConfigWrapper mEnvConfigWrapperDefault;
private EnvConfigCheckedCompleteListener mEnvConfigCheckedCompleteListener;
public static EnvConfigDialogFragment newInstance() {
return newInstance(null);
}
public static EnvConfigDialogFragment newInstance(EnvConfigWrapper envConfigWrapper) {
EnvConfigDialogFragment fragment = new EnvConfigDialogFragment();
Bundle args = new Bundle();
args.putSerializable(ENV_CONFIG_WRAPPER_DEFAULT, envConfigWrapper);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
if (getArguments() != null) {
mEnvConfigWrapperDefault = (EnvConfigWrapper) getArguments().getSerializable(ENV_CONFIG_WRAPPER_DEFAULT);
}
// 如果入参没有配置项,则查看有没有本地缓存;
if (mEnvConfigWrapperDefault == null) {
// 暂时使用 SharedPreferences 作为本地缓存,根据实际需要选择处理方式;
mEnvConfigWrapperDefault = EnvConfigHelper.getInstance().initEnvConfigWrapper();
}
LogUtils.dl(TAG, "method:onAttach#mEnvConfigWrapperDefault=" + mEnvConfigWrapperDefault);
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);
mRecyclerView = rootView.findViewById(R.id.recyclerview_config);
tvCheckDetails = rootView.findViewById(R.id.tv_check_details);
tvEnvDetails = rootView.findViewById(R.id.tv_env_config_details);
tvFinish = rootView.findViewById(R.id.tv_finish);
tvResetAllConfig = rootView.findViewById(R.id.tv_reset_config);
mUrlInputLayoutApi = rootView.findViewById(R.id.url_details_layout_api);
mUrlInputLayoutImage = rootView.findViewById(R.id.url_details_layout_image);
// 设置ID;
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);
// 调整布局的宽高
ViewGroup.LayoutParams layoutParams = rootView.getLayoutParams();
layoutParams.height = (int) (DisplayHelper.getScreenHeight(mContext) * 0.8);
layoutParams.width = (int) (DisplayHelper.getScreenWidth(mContext) * 0.8);
rootView.setLayoutParams(layoutParams);
FrameLayout parent = (FrameLayout) rootView.getParent();
parent.setBackgroundResource(R.color.transparent);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setNestedScrollingEnabled(false);
mEnvConfigList = initListDataFromJson();
// 如果最近一次的选中数据,则修改默认选中项;
initEnvConfigListDataAndViews(mEnvConfigList);
mEnvConfigAdapter = new EnvConfigAdapter();
mEnvConfigAdapter.addAll(mEnvConfigList);
mRecyclerView.setAdapter(mEnvConfigAdapter);
// 行点击事件
mEnvConfigAdapter.setOnRecyclerViewItemClick((EnvConfigAdapter.OnRecyclerViewItemClick<EnvConfigBean>)
(childView, position, envConfigBean) -> {
updateListData(position, envConfigBean);
mEnvConfigAdapter.notifyDataSetChanged();
});
// 重置配置项事件;
tvResetAllConfig.setOnClickListener(v -> {
handleResetAllConfig();
});
tvCheckDetails.setOnClickListener(v -> {
showAllConfigDetails();
});
mUrlInputLayoutApi.setOnShowUrlHistoryListener(EnvConfigDialogFragment.this);
mUrlInputLayoutImage.setOnShowUrlHistoryListener(EnvConfigDialogFragment.this);
mUrlInputLayoutH5.setOnShowUrlHistoryListener(EnvConfigDialogFragment.this);
tvFinish.setOnClickListener(v -> handlerFinishClicked());
}
@Override
public void onShowUrlHistory(View v) {
List<UrlHistoryBean> urlList = initUrlHistoryListFromJson();
new SingleSelectionDialog.Builder(mContext)
.setTitle(R.string.title_select_url)
.setUrlHistoryList(urlList)
.setOnUrlSelecteComplete((dialog, childView, position, urlHistoryBean) -> {
int id = v.getId();
if (id == R.id.url_detail_input_api_id) {
mUrlInputLayoutApi.setUrlInput(urlHistoryBean);
mUrlInputLayoutApi.setChecked(true);
} else if (id == R.id.url_detail_input_image_id) {
mUrlInputLayoutImage.setUrlInput(urlHistoryBean);
mUrlInputLayoutImage.setChecked(true);
} else if (id == R.id.url_detail_input_h5_id) {
mUrlInputLayoutH5.setUrlInput(urlHistoryBean);
mUrlInputLayoutH5.setChecked(true);
}
dialog.dismiss();
})
.setOnCloseClickedListener(dialog -> {
dialog.dismiss();
})
.create()
.show();
}
private List<EnvConfigBean> initListDataFromJson() {
Gson gson = new Gson();
List<EnvConfigBean> list = new ArrayList<>();
// 可以选择从 default_config_server_url_details.json 中读取配置;
List<AllServerUrlDetailsBean> serverList = gson.fromJson(configJsonAllServerDetails, new TypeToken<List<AllServerUrlDetailsBean>>() {
}.getType());
list.addAll(serverList);
List<EnvConfigBean> cofigList = gson.fromJson(configJsonSimple, new TypeToken<List<EnvConfigBean>>() {
}.getType());
list.addAll(cofigList);
return list;
}
private List<UrlHistoryBean> initUrlHistoryListFromJson() {
Gson gson = new Gson();
List<UrlHistoryBean> list = new ArrayList<>();
// 可以选择从 default_config_server_url_details.json 中读取配置;
List<UrlHistoryBean> serverList = null;
try {
serverList = gson.fromJson(urlHistoryDefaultJson, new TypeToken<List<UrlHistoryBean>>() {
}.getType());
} catch (JsonSyntaxException e) {
LogUtils.el(TAG, "method:initUrlHistoryListFromJson#e=" + e.getMessage());
}
list.add(new UrlHistoryBean(URL_HISTORY_TYPE_TITLE, "常用地址:"));
if (serverList != null && serverList.size() > 0) {
list.addAll(serverList);
}
list.add(new UrlHistoryBean(URL_HISTORY_TYPE_TITLE, "最近输入地址:"));
// 本地缓存;
SharedPrefsManager sharedPrefsManager = SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP);
String json = sharedPrefsManager
.getString(Consts.EnvConfig.URL_INPUT_HISTORY_JSON);
if (!TextUtils.isEmpty(json)) {
List<UrlHistoryBean> inputUrlList = null;
try {
inputUrlList = gson.fromJson(json, new TypeToken<List<UrlHistoryBean>>() {
}.getType());
} catch (Exception e) {
LogUtils.el(TAG, e.getMessage());
sharedPrefsManager.removeKey(Consts.EnvConfig.URL_INPUT_HISTORY_JSON);
}
if (inputUrlList != null && inputUrlList.size() > 0) {
list.addAll(inputUrlList);
}
}
return list;
}
private List<EnvConfigBean> getListDataFromJson() {
List<EnvConfigBean> list = null;
try {
String jsonStr = ResUtils.getJsonStr(mContext, "default_config_server_url_details.json");
Gson gson = new Gson();
list = gson.fromJson(jsonStr, new TypeToken<List<EnvConfigBean>>() {
}.getType());
} catch (Exception e) {
LogUtils.el(TAG, e.getMessage());
}
return list;
}
private void showAllConfigDetails() {
EnvConfigWrapper envConfigWrapper = buildEnvConfigWrapper();
// String modeValueBuild = envConfigWrapper.getModeOfBuild();
AllServerUrlDetailsBean allServerUrlDetailsOfCheckedEnv = envConfigWrapper.getAllServerUrlDetailsOfCheckedEnv();
StringBuilder builder = new StringBuilder();
//
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean != null) {
builder
.append("★★★BuildConfig部分★★★").append("\n\n")
.append("★BuildConfig#APPLICATION_ID=").append(appInfoBean.getApplicationId()).append("★\n\n")
.append("★BuildConfig#VERSION_CODE=").append(appInfoBean.getVersionCode()).append("★\n\n")
.append("★BuildConfig#VERSION_NAME=").append(appInfoBean.getVersionName()).append("★\n\n")
.append("★BuildConfig#BUILD_TYPE=").append(appInfoBean.getBuildType()).append("★\n\n")
.append("★BuildConfig#FLAVOR=").append(appInfoBean.getFlavor()).append("★\n\n")
.append("★BuildConfig#DEBUG=").append(appInfoBean.isDebug()).append("★\n\n")
.append("★BuildConfig#LOG_DEBUG=").append(appInfoBean.isLogDebug()).append("★\n\n")
.append("★打包模式modeOfBuild=").append(appInfoBean.getModeOfBuild()).append("★\n\n");
} else {
LogUtils.el(TAG, "method:showAllConfigDetails#EnvConfigHelper#mAppInfoBean not initialize.");
}
// 实际的环境情况:
EnvConfigHelper.EnvMode envModeInUse = EnvConfigHelper.getInstance().getEnvModeInUse();
String envDesc = null;
if (envModeInUse != null) {
envDesc = envModeInUse.getDesc();
}
builder
.append("★★★当前使用的地址★★★").append("\n\n")
.append("★当前使用的环境:").append(envDesc).append("=").append(EnvConfigHelper.getInstance().getModeValueInUse()).append("★\n\n")
.append("★api服务器地址=").append(EnvServerUrlConsts.TargetServer.API).append("★\n\n")
.append("★image服务器地址=").append(EnvServerUrlConsts.TargetServer.IMAGE).append("★\n\n")
.append("★H5服务器地址=").append(EnvServerUrlConsts.TargetServer.H5).append("★\n\n");
//
builder
// .append("BuildConfig部分:").append("\n\n")
// .append("BuildConfig#APPLICATION_ID=").append(BuildConfig.APPLICATION_ID).append("\n\n")
// .append("BuildConfig#VERSION_CODE=").append(BuildConfig.VERSION_CODE).append("\n\n")
// .append("BuildConfig#VERSION_NAME=").append(BuildConfig.VERSION_NAME).append("\n\n")
// .append("BuildConfig#BUILD_TYPE=").append(BuildConfig.BUILD_TYPE).append("\n\n")
// .append("BuildConfig#FLAVOR=").append(BuildConfig.FLAVOR).append("\n\n")
// .append("BuildConfig#DEBUG=").append(BuildConfig.DEBUG).append("\n\n")
// .append("BuildConfig#LOG_DEBUG=").append(BuildConfig.LOG_DEBUG).append("\n\n")
.append("★★★修改配置后部分★★★").append("\n\n");
if (allServerUrlDetailsOfCheckedEnv != null) {
builder
.append("★当前选中的环境:").append(allServerUrlDetailsOfCheckedEnv.getLabelName()).append("=")
.append(allServerUrlDetailsOfCheckedEnv.getModeOfEnv()).append("=")
.append(allServerUrlDetailsOfCheckedEnv.getChecked()).append("★\n\n");
ServerUrlDetailsBean apiDetailsSelected = envConfigWrapper.getApiServerUrlDetailsInput();
// 手动输入且被选中的URL的优先级高于环境配置内的URL地址,优先使用手动输入的URL
if (apiDetailsSelected == null || !apiDetailsSelected.isChecked()) {
apiDetailsSelected = allServerUrlDetailsOfCheckedEnv.getApiServerUrlDetails();
}
if (apiDetailsSelected != null) {
UrlHistoryBean urlHistoryBean = apiDetailsSelected.getUrlHistoryBean();
String urlStr = null;
if (urlHistoryBean != null) {
urlStr = urlHistoryBean.getUrlStr();
}
builder
.append("★").append(apiDetailsSelected.getTypeName()).append("=").append(urlStr).append("★\n\n");
}
ServerUrlDetailsBean imageDetailsSelected = envConfigWrapper.getImageServerUrlDetailsInput();
if (imageDetailsSelected == null || !imageDetailsSelected.isChecked()) {
imageDetailsSelected = allServerUrlDetailsOfCheckedEnv.getImageServerUrlDetails();
}
if (imageDetailsSelected != null) {
UrlHistoryBean urlHistoryBean = imageDetailsSelected.getUrlHistoryBean();
String urlStr = null;
if (urlHistoryBean != null) {
urlStr = urlHistoryBean.getUrlStr();
}
builder
.append("★").append(imageDetailsSelected.getTypeName()).append("=").append(urlStr).append("★\n\n");
}
ServerUrlDetailsBean h5DetailsSelected = envConfigWrapper.getH5ServerUrlDetailsInput();
if (h5DetailsSelected == null || !h5DetailsSelected.isChecked()) {
h5DetailsSelected = allServerUrlDetailsOfCheckedEnv.getH5ServerUrlDetails();
}
if (h5DetailsSelected != null) {
UrlHistoryBean urlHistoryBean = h5DetailsSelected.getUrlHistoryBean();
String urlStr = null;
if (urlHistoryBean != null) {
urlStr = urlHistoryBean.getUrlStr();
}
builder
.append("★").append(h5DetailsSelected.getTypeName()).append("=").append(urlStr).append("★\n\n");
}
}
EnvConfigBean firstInstall = envConfigWrapper.getFirstInstall();
if (firstInstall != null) {
builder
.append("★").append(firstInstall.getLabelName()).append("=").append(firstInstall.getChecked()).append("★\n\n");
}
EnvConfigBean logDebug = envConfigWrapper.getLogDebug();
if (logDebug != null) {
builder
.append("★").append(logDebug.getLabelName()).append("=").append(logDebug.getChecked()).append("★\n\n");
}
EnvConfigBean skipAdPage = envConfigWrapper.getSkipAdPage();
if (skipAdPage != null) {
builder
.append("★").append(skipAdPage.getLabelName()).append("=").append(skipAdPage.getChecked()).append("★\n\n");
}
String allConfigDetailsStr = builder.toString();
LogUtils.dl(TAG, "method:showAllConfigDetails#allConfigDetailsStr=" + allConfigDetailsStr);
tvEnvDetails.setText(allConfigDetailsStr);
}
/**
* 重置所有配置项及UI效果;
*/
private void handleResetAllConfig() {
SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP)
.removeKey(Consts.EnvConfig.ENV_CONFIG_WRAPPER_JSON);
mEnvConfigWrapperDefault = null;
mEnvConfigList = initListDataFromJson();
//
initEnvConfigListDataAndViews(mEnvConfigList);
mEnvConfigAdapter.addNewAll(mEnvConfigList);
mEnvConfigAdapter.notifyDataSetChanged();
if (mUrlInputLayoutApi != null) {
mUrlInputLayoutApi.resetData();
}
if (mUrlInputLayoutImage != null) {
mUrlInputLayoutImage.resetData();
}
if (mUrlInputLayoutH5 != null) {
mUrlInputLayoutH5.resetData();
}
tvCheckDetails.performClick();
}
/**
* 如果最近一次的选中数据有值,则修改默认选中项,如果没有则按默认项处理;
*
* @param envConfigBeanList
*/
private void initEnvConfigListDataAndViews(List<EnvConfigBean> envConfigBeanList) {
if (mEnvConfigWrapperDefault == null) {
// 当前逻辑分支和json中的默认配置相同,可以不必再执行;
// 没有设定默认的配置时,根据构建环境进行默认处理;
handleInitEnvConfigListDataAndViews(envConfigBeanList);
} else {
// 有默认配置项时
handleInitEnvConfigListDataWithDefault(envConfigBeanList);
}
}
private void handleInitEnvConfigListDataWithDefault(List<EnvConfigBean> envConfigBeanList) {
AllServerUrlDetailsBean allServerUrlDetailsOfCheckedEnv = mEnvConfigWrapperDefault.getAllServerUrlDetailsOfCheckedEnv();
EnvConfigBean envConfigFirstInstall = mEnvConfigWrapperDefault.getFirstInstall();
EnvConfigBean envConfigLogDebug = mEnvConfigWrapperDefault.getLogDebug();
EnvConfigBean envConfigSkipAdPage = mEnvConfigWrapperDefault.getSkipAdPage();
String modeValueLast = allServerUrlDetailsOfCheckedEnv.getModeOfEnv();
for (EnvConfigBean envConfigBean : envConfigBeanList) {
String groupId = envConfigBean.getGroupId();
switch (groupId) {
case GROUP_ID_SERVER_URL_DETAILS:
if (envConfigBean instanceof AllServerUrlDetailsBean) {
AllServerUrlDetailsBean allServerUrlDetailsBean = (AllServerUrlDetailsBean) envConfigBean;
String modeValue = allServerUrlDetailsBean.getModeOfEnv();
if (TextUtils.equals(modeValueLast, modeValue)) {
envConfigBean.setChecked(CHECKED);
} else {
envConfigBean.setChecked(UNCHECKED);
}
} else {
LogUtils.el(TAG, "method:initEnvConfigListDataAndViews#envConfigBean not a instance of AllServerUrlDetailsBean.");
}
break;
case GROUP_ID_FIRST_INSTALL:
if (envConfigFirstInstall != null) {
envConfigBean.setChecked(envConfigBean.getChecked());
}
break;
case GROUP_ID_LOG_DEBUG:
if (envConfigLogDebug != null) {
envConfigBean.setChecked(envConfigLogDebug.getChecked());
}
break;
case GROUP_ID_SKIP_AD_PAGE:
if (envConfigSkipAdPage != null) {
envConfigBean.setChecked(envConfigSkipAdPage.getChecked());
}
break;
default:
LogUtils.el(TAG, "method:initEnvConfigListData#未处理当前逻辑分支");
break;
}
}
ServerUrlDetailsBean apiDetailsInput = mEnvConfigWrapperDefault.getApiServerUrlDetailsInput();
if (apiDetailsInput != null) {
mUrlInputLayoutApi.setChecked(apiDetailsInput.isChecked());
mUrlInputLayoutApi.setUrlInput(apiDetailsInput.getUrlHistoryBean());
}
ServerUrlDetailsBean imageDetailsInput = mEnvConfigWrapperDefault.getImageServerUrlDetailsInput();
if (imageDetailsInput != null) {
mUrlInputLayoutImage.setChecked(imageDetailsInput.isChecked());
mUrlInputLayoutImage.setUrlInput(imageDetailsInput.getUrlHistoryBean());
}
ServerUrlDetailsBean h5DetailsInput = mEnvConfigWrapperDefault.getH5ServerUrlDetailsInput();
if (h5DetailsInput != null) {
mUrlInputLayoutH5.setChecked(h5DetailsInput.isChecked());
mUrlInputLayoutH5.setUrlInput(h5DetailsInput.getUrlHistoryBean());
}
}
private void handleInitEnvConfigListDataAndViews(List<EnvConfigBean> envConfigBeanList) {
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean == null) {
Toast.makeText(mContext, "appInfoBean = null", Toast.LENGTH_LONG).show();
return;
}
String modeOfBuild = appInfoBean.getModeOfBuild();
if (TextUtils.isEmpty(modeOfBuild)) {
Toast.makeText(mContext, "modeOfBuild 为空", Toast.LENGTH_LONG).show();
return;
}
// String modeBuild = BuildConfig.MODE;
for (EnvConfigBean envConfigBean : envConfigBeanList) {
String groupId = envConfigBean.getGroupId();
switch (groupId) {
case GROUP_ID_SERVER_URL_DETAILS:
if (envConfigBean instanceof AllServerUrlDetailsBean) {
handServerUrlDetalsDefault(modeOfBuild, envConfigBean);
} else {
LogUtils.el(TAG, "method:initEnvConfigListDataAndViews#envConfigBean not a instance of AllServerUrlDetailsBean.");
}
break;
case GROUP_ID_FIRST_INSTALL:
case GROUP_ID_LOG_DEBUG:
case GROUP_ID_SKIP_AD_PAGE:
// 暂时按默认状态处理;
// String modePilotRun = EnvConfigHelper.EnvMode.DEV_PILOT_RUN.getMode();
// if (TextUtils.equals(modeBuild, modePilotRun) ||
// TextUtils.equals(modeBuild, modeDevPilotRun)) {
// envConfigBean.setChecked(CHECKED);
// } else {
// envConfigBean.setChecked(UNCHECKED);
// }
break;
default:
LogUtils.el(TAG, "method:initEnvConfigListData#未处理当前逻辑分支");
break;
}
}
}
private void handServerUrlDetalsDefault(String modeBuild, EnvConfigBean envConfigBean) {
AllServerUrlDetailsBean allServerUrlDetailsBean = (AllServerUrlDetailsBean) envConfigBean;
String modeValue = allServerUrlDetailsBean.getModeOfEnv();
if (TextUtils.equals(modeBuild, EnvConfigHelper.EnvMode.DEVELOPMENT.getMode())) {
// 开发环境默认使用 DEV_TEST_SERVER;
String modeDevServer = EnvConfigHelper.EnvMode.DEV_TEST_SERVER.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
envConfigBean.setChecked(CHECKED);
} else {
envConfigBean.setChecked(UNCHECKED);
}
} else if (TextUtils.equals(modeBuild, EnvConfigHelper.EnvMode.PILOT_RUN.getMode())) {
// 试运行环境默认使用 DEV_PILOT_RUN;
String modeDevServer = EnvConfigHelper.EnvMode.DEV_PILOT_RUN.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
envConfigBean.setChecked(CHECKED);
} else {
envConfigBean.setChecked(UNCHECKED);
}
} else if (TextUtils.equals(modeBuild, EnvConfigHelper.EnvMode.PRODUCT.getMode())) {
// 生产环境默认使用 DEV_PRODUCT;
String modeDevServer = EnvConfigHelper.EnvMode.DEV_PRODUCT.getMode();
if (TextUtils.equals(modeValue, modeDevServer)) {
envConfigBean.setChecked(CHECKED);
} else {
envConfigBean.setChecked(UNCHECKED);
}
}
}
/**
* 根据点击事件发生所在position,及数据类型,修改了数据集合中相关数据的状态;
*
* @param position
*/
private void updateListData(int position, EnvConfigBean evnConfigBean) {
LogUtils.d(TAG, "method:updateListData#evnConfigBean=" + evnConfigBean);
boolean isGroup = evnConfigBean.isGroup();
if (isGroup) {
// 多个选项为一组时的业务处理
handleGroupSingleChecked(position, evnConfigBean);
} else {
boolean isChecked = evnConfigBean.isChecked();
// 单个选项为一组时的业务处理
// switch (groupId) {
// case GROUP_ID_SKIP_AD_PAGE:
// case GROUP_ID_FIRST_INSTALL:
// case GROUP_ID_LOG_DEBUG:
evnConfigBean.setChecked(!isChecked);
// break;
// default:
// LogUtils.el(TAG, "method:updateListData#未处理当前逻辑分支");
// break;
// }
}
}
/**
* 处理一组内容选中的逻辑,单选逻辑;
* 如果是必选项,则只有一个可被选中,否则可以没有选中项;
*
* @param position
* @param evnConfigBean
*/
private void handleGroupSingleChecked(int position, EnvConfigBean evnConfigBean) {
String groupIdChecked = evnConfigBean.getGroupId();
boolean isRequired = evnConfigBean.isRequired();
boolean isChecked = evnConfigBean.isChecked();
for (int i = 0; i < mEnvConfigList.size(); i++) {
EnvConfigBean item = mEnvConfigList.get(i);
String groupIdItem = item.getGroupId();
if (!TextUtils.equals(groupIdChecked, groupIdItem)) {
continue;
}
// 保证同一组内的选项有且只有一个可被选中;
if (position == i) {
if (isRequired) {
if (isChecked) {
LogUtils.wl(TAG, "method:handleGroupSingleChecked#isRequired=" + isRequired
+ ", isChecked=" + isChecked);
} else {
// 保证同一组内的选项至少有一个选项被选中;
evnConfigBean.setChecked(!isChecked);
}
} else {
evnConfigBean.setChecked(!isChecked);
}
} else {
item.setChecked(UNCHECKED);
}
}
}
/**
* 处理点击完成的业务;
*/
private void handlerFinishClicked() {
EnvConfigWrapper envConfigWrapper = buildEnvConfigWrapper();
// 暂时使用 SharedPreferences 作为本地缓存,根据实际需要选择处理方式;
Gson gson = new Gson();
String envConfigWrapperJson = gson.toJson(envConfigWrapper);
SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP)
.putString(Consts.EnvConfig.ENV_CONFIG_WRAPPER_JSON, envConfigWrapperJson)
.commit();
// 修改环境配置的方法可以根据需求放在 EnvConfigDialogFragment 的此处调用;
// 也可以在页面的 mEnvConfigCheckedCompleteListener 回调时调用;
// EnvConfigHelper.getInstance()
// .updateEnvServerUrl(envConfigWrapper.getAllServerUrlDetailsOfCheckedEnv().getModeOfEnv(), envConfigWrapper);
// 接口回调;
if (mEnvConfigCheckedCompleteListener == null) {
LogUtils.el(TAG, "method:handlerFinishClicked#mConfigCompleteListener = " + mEnvConfigCheckedCompleteListener);
} else {
mEnvConfigCheckedCompleteListener.onEnvConfigCheckedComplete(envConfigWrapper);
}
dismiss();
}
/**
* 构建返回结果,将选中的数据内容封装在对象内;
*
* @return
*/
private EnvConfigWrapper buildEnvConfigWrapper() {
EnvConfigWrapper envConfigWrapper = new EnvConfigWrapper();
for (int i = 0; i < mEnvConfigList.size(); i++) {
EnvConfigBean item = mEnvConfigList.get(i);
String groupIdItem = item.getGroupId();
switch (groupIdItem) {
// 封装当前选项的数据
case GROUP_ID_SKIP_AD_PAGE:
envConfigWrapper.setSkipAdPage((EnvConfigBean) item.clone());
break;
// 封装当前选项的数据
case GROUP_ID_FIRST_INSTALL:
envConfigWrapper.setFirstInstall((EnvConfigBean) item.clone());
break;
// 封装当前选项的数据
case GROUP_ID_LOG_DEBUG:
envConfigWrapper.setLogDebug((EnvConfigBean) item.clone());
break;
// 封装当前选项的数据
case GROUP_ID_SERVER_URL_DETAILS:
if (!item.getChecked()) {
continue;
}
if (item instanceof AllServerUrlDetailsBean) {
AllServerUrlDetailsBean serverUrlDetailsBean = (AllServerUrlDetailsBean) item;
envConfigWrapper.setAllServerUrlDetailsOfCheckedEnv((AllServerUrlDetailsBean) serverUrlDetailsBean.clone());
} else {
LogUtils.el(TAG, "method:handlerFinishClicked#envConfigBean not a instance of AllServerUrlDetailsBean.");
}
break;
default:
LogUtils.el(TAG, "method:handlerFinishClicked#未处理当前逻辑分支");
break;
}
}
UrlHistoryBean urlInputApi = null;
UrlHistoryBean urlInputImage = null;
UrlHistoryBean urlInputH5 = null;
if (mUrlInputLayoutApi != null && mUrlInputLayoutApi.isChecked()) {
ServerUrlDetailsBean urlDetailApi = mUrlInputLayoutApi.getUrlDetailBean();
if (urlDetailApi == null) {
Toast.makeText(mContext, mUrlInputLayoutApi.getTypeName() + "输入的URL地址无效", Toast.LENGTH_SHORT).show();
} else {
envConfigWrapper.setApiServerUrlDetailsInput(urlDetailApi);
urlInputApi = urlDetailApi.getUrlHistoryBean();
}
}
if (mUrlInputLayoutImage != null && mUrlInputLayoutImage.isChecked()) {
ServerUrlDetailsBean urlDetailImage = mUrlInputLayoutImage.getUrlDetailBean();
if (urlDetailImage == null) {
Toast.makeText(mContext, mUrlInputLayoutImage.getTypeName() + "输入的URL地址无效", Toast.LENGTH_SHORT).show();
} else {
envConfigWrapper.setImageServerUrlDetailsInput(urlDetailImage);
urlInputImage = urlDetailImage.getUrlHistoryBean();
}
}
if (mUrlInputLayoutH5 != null && mUrlInputLayoutH5.isChecked()) {
ServerUrlDetailsBean urlDetailH5 = mUrlInputLayoutH5.getUrlDetailBean();
if (urlDetailH5 == null) {
Toast.makeText(mContext, mUrlInputLayoutH5.getTypeName() + "输入的URL地址无效", Toast.LENGTH_SHORT).show();
} else {
envConfigWrapper.setH5ServerUrlDetailsInput(urlDetailH5);
urlInputH5 = urlDetailH5.getUrlHistoryBean();
}
}
if (urlInputApi != null || urlInputImage != null || urlInputH5 != null) {
List<UrlHistoryBean> urllList = new ArrayList<>();
addUrlInput(urlInputApi, urllList);
addUrlInput(urlInputImage, urllList);
addUrlInput(urlInputH5, urllList);
EnvConfigHelper.getInstance().saveUrlInputHistory(urllList);
}
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean != null) {
envConfigWrapper.setModeOfBuild(appInfoBean.getModeOfBuild());
} else {
LogUtils.el(TAG, "method:handlerFinishClicked#EnvConfigHelper#mAppInfoBean not initialize.");
}
return envConfigWrapper;
}
private void addUrlInput(UrlHistoryBean urlHistoryBean, List<UrlHistoryBean> urllList) {
if (urlHistoryBean != null && !TextUtils.isEmpty(urlHistoryBean.getUrlStr())) {
urllList.add(urlHistoryBean);
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
}
EnvConfigHelper 类的 saveUrlInputHistory() 缓存方法的业务重新实现,以实现历史列表的保存,及不重复等功能;
package com.windfallsheng.envconfig;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.StringDef;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.windfallsheng.envconfig.bean.AllServerUrlDetailsBean;
import com.windfallsheng.envconfig.bean.AppInfoBean;
import com.windfallsheng.envconfig.bean.EnvConfigWrapper;
import com.windfallsheng.envconfig.bean.ServerUrlDetailsBean;
import com.windfallsheng.envconfig.bean.UrlHistoryBean;
import com.windfallsheng.envconfig.dialog.EnvConfigDialogFragment;
import com.windfallsheng.lib.Consts;
import com.windfallsheng.lib.util.LogUtils;
import com.windfallsheng.lib.util.SharedPrefsManager;
import java.util.ArrayList;
import java.util.List;
/**
* @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 String mModeValueInUse;
/**
* 当前使用的 环境枚举定义;
*/
private EnvMode mEnvModeInUse;
private AppInfoBean mAppInfoBean;
private EnvConfigHelper() {
}
public static EnvConfigHelper getInstance() {
return SingletonHolder.INSTANCE;
}
public EnvConfigHelper initContext(Context context) {
mContext = context;
return this;
}
public String getModeValueInUse() {
return mModeValueInUse;
}
public EnvMode getEnvModeInUse() {
return mEnvModeInUse;
}
public AppInfoBean getAppInfoBean() {
return mAppInfoBean;
}
public void setAppInfoBean(AppInfoBean appInfoBean) {
mAppInfoBean = appInfoBean;
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param modeValue
*/
public void updateEnvServerUrl(@MODE String modeValue, EnvConfigWrapper envConfigWrapper) {
// 修改当前模式为已修改的模式;
this.mModeValueInUse = modeValue;
this.mEnvModeInUse = getEnvModeByMode(modeValue);
// 判断是否有手动输入的地址项;
String urlApiStr = "";
String urlImageStr = "";
String urlH5Str = "";
if (envConfigWrapper != null) {
ServerUrlDetailsBean apiDetailsInput = envConfigWrapper.getApiServerUrlDetailsInput();
if (apiDetailsInput != null) {
UrlHistoryBean urlHistoryBean = apiDetailsInput.getUrlHistoryBean();
if (urlHistoryBean != null) {
urlApiStr = urlHistoryBean.getUrlStr();
}
}
ServerUrlDetailsBean imageDetailsInput = envConfigWrapper.getImageServerUrlDetailsInput();
if (imageDetailsInput != null) {
UrlHistoryBean urlHistoryBean = imageDetailsInput.getUrlHistoryBean();
if (urlHistoryBean != null) {
urlImageStr = urlHistoryBean.getUrlStr();
}
}
ServerUrlDetailsBean h5DetailsInput = envConfigWrapper.getH5ServerUrlDetailsInput();
if (h5DetailsInput != null) {
UrlHistoryBean urlHistoryBean = h5DetailsInput.getUrlHistoryBean();
if (urlHistoryBean != null) {
urlH5Str = urlHistoryBean.getUrlStr();
}
}
}
// 手动输入的地址项优先赋值;
if (!TextUtils.isEmpty(urlApiStr)) {
EnvServerUrlConsts.TargetServer.API = urlApiStr;
}
if (!TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = urlImageStr;
}
if (!TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = urlH5Str;
}
switch (modeValue) {
case Consts.EnvMode.DEV_TEST_LOCAL:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.TestLocalServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.TestLocalServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.TestLocalServer.H5;
}
break;
case Consts.EnvMode.DEVELOPMENT:
case Consts.EnvMode.DEV_TEST_SERVER:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.TestServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.TestServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.TestServer.H5;
}
break;
case Consts.EnvMode.PILOT_RUN:
case Consts.EnvMode.DEV_PILOT_RUN:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.PilotRunServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.PilotRunServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.PilotRunServer.H5;
}
break;
case Consts.EnvMode.PRODUCT:
case Consts.EnvMode.DEV_PRODUCT:
// 手动输入的地址项优先赋值,若没有输入地址,则使用默认配置地址;
if (TextUtils.isEmpty(urlApiStr)) {
EnvServerUrlConsts.TargetServer.API = EnvServerUrlConsts.ProductServer.API;
}
if (TextUtils.isEmpty(urlImageStr)) {
EnvServerUrlConsts.TargetServer.IMAGE = EnvServerUrlConsts.ProductServer.IMAGE;
}
if (TextUtils.isEmpty(urlH5Str)) {
EnvServerUrlConsts.TargetServer.H5 = EnvServerUrlConsts.ProductServer.H5;
}
break;
default:
LogUtils.el(TAG, "method:updateEnvServerUrl#未处理当前逻辑分支");
break;
}
LogUtils.dl("updateEnvServerUrl★★\n★★modeValue=" + modeValue +
"\n★★EnvServerUrlConsts.TargetServer.API_SERVER=" + EnvServerUrlConsts.TargetServer.API +
",\n★★EnvServerUrlConsts.TargetServer.IMAGE_SERVER=" + EnvServerUrlConsts.TargetServer.IMAGE +
",\n★★EnvServerUrlConsts.TargetServer.H5_SERVER=" + EnvServerUrlConsts.TargetServer.H5);
}
private EnvMode getEnvModeByMode(String modeSelected) {
switch (modeSelected) {
case Consts.EnvMode.DEV_TEST_LOCAL:
return EnvMode.DEV_TEST_LOCAL;
case Consts.EnvMode.DEVELOPMENT:
return EnvMode.DEVELOPMENT;
case Consts.EnvMode.DEV_TEST_SERVER:
return EnvMode.DEV_TEST_SERVER;
case Consts.EnvMode.PILOT_RUN:
return EnvMode.PILOT_RUN;
case Consts.EnvMode.DEV_PILOT_RUN:
return EnvMode.DEV_PILOT_RUN;
case Consts.EnvMode.PRODUCT:
return EnvMode.PRODUCT;
case Consts.EnvMode.DEV_PRODUCT:
return EnvMode.DEV_PRODUCT;
default:
LogUtils.el(TAG, "method:updateEnvServerUrl#未处理当前逻辑分支");
break;
}
return null;
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param envConfigWrapper
*/
public void updateEnvServerUrl(EnvConfigWrapper envConfigWrapper) {
if (envConfigWrapper == null) {
LogUtils.el(TAG, "method:updateEnvServerUrl#return#envConfigWrapper == null");
return;
}
AllServerUrlDetailsBean allServerUrlDetailsOfCheckedEnv = envConfigWrapper.getAllServerUrlDetailsOfCheckedEnv();
if (allServerUrlDetailsOfCheckedEnv == null) {
LogUtils.el(TAG, "method:updateEnvServerUrl#return#allServerUrlDetailsOfCheckedEnv == null");
return;
}
String modeValue = allServerUrlDetailsOfCheckedEnv.getModeOfEnv();
updateEnvServerUrl(modeValue, envConfigWrapper);
}
/**
* 根据选择的环境类型,调整环境配置;
*
* @param modeValue
*/
public void updateEnvServerUrl(@MODE String modeValue) {
updateEnvServerUrl(modeValue, null);
}
/**
* 在应用启动时最先初始化配置信息;
*/
public void initDefaultEnv() {
String modeValue = null;
LogUtils.dl(TAG, "method:initDefaultEnv#mAppInfoBean=" + mAppInfoBean);
if (mAppInfoBean == null) {
LogUtils.el(TAG, "method:initDefaultEnv#return#mAppInfoBean == null");
return;
}
modeValue = mAppInfoBean.getModeOfBuild();
LogUtils.dl(TAG, "method:initDefaultEnv#modeValue=" + modeValue);
if (TextUtils.isEmpty(modeValue)) {
LogUtils.el(TAG, "method:initDefaultEnv#return#modeValue == " + modeValue);
return;
}
// 如果打包构建的环境是生产和试运行,则以打包环境为准
if (TextUtils.equals(modeValue, Consts.EnvMode.PILOT_RUN) ||
TextUtils.equals(modeValue, Consts.EnvMode.PRODUCT)) {
updateEnvServerUrl(modeValue);
} else if (TextUtils.equals(modeValue, Consts.EnvMode.DEVELOPMENT)) {
// 如果是开发环境,先检查是否有上次缓存的配置数据;
EnvConfigWrapper envConfigWrapper = initEnvConfigWrapper();
LogUtils.dl(TAG, "method:initDefaultEnv#envConfigWrapper=" + envConfigWrapper);
if (envConfigWrapper == null) {
// 如果没有缓存数据,则以构建环境为准;
modeValue = mAppInfoBean.getModeOfBuild();
updateEnvServerUrl(modeValue);
} else {
// 如果上次缓存的有数据,则以缓存数据为准;
updateEnvServerUrl(envConfigWrapper);
}
}
}
public EnvConfigWrapper initEnvConfigWrapper() {
if (mContext == null) {
LogUtils.el(TAG, "method:initEnvConfigWrapper#return null#mContext == null");
return null;
}
// 暂时使用 SharedPreferences 作为本地缓存,根据实际需要选择处理方式;
String envConfigWrapperJson = SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP)
.getString(Consts.EnvConfig.ENV_CONFIG_WRAPPER_JSON, "");
EnvConfigWrapper envConfigWrapper = null;
if (!TextUtils.isEmpty(envConfigWrapperJson)) {
Gson gson = new Gson();
try {
envConfigWrapper = gson.fromJson(envConfigWrapperJson, EnvConfigWrapper.class);
} catch (JsonSyntaxException e) {
LogUtils.el(TAG, "method:initEnvConfigWrapper#e=" + e.getMessage());
SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP)
.removeKey(Consts.EnvConfig.ENV_CONFIG_WRAPPER_JSON);
}
}
return envConfigWrapper;
}
public void saveUrlInputHistory(List<UrlHistoryBean> urlStrInputList) {
if (urlStrInputList == null || urlStrInputList.size() == 0) {
return;
}
// 默认类型的 URL不需要再缓存,可以移除;
int size = urlStrInputList.size();
for (int i = 0; i < size; i++) {
UrlHistoryBean urlInputBean = urlStrInputList.get(i);
String typeInput = urlInputBean.getType();
// 如果输入的是默认的地址,则从输入列表中移除,这样能保证默认的地址不在新增的历史记录里;
if (TextUtils.equals(typeInput, EnvConfigDialogFragment.URL_HISTORY_TYPE_DEFAULT)) {
urlStrInputList.remove(urlInputBean);
i--;
size--;
}
}
int sizeNew = urlStrInputList.size();
// 移除内部URL字段相同的重复元素;
if (sizeNew > 1) {
for (int i = 0; i < sizeNew; i++) {
// if (sizeNew <= 1) {
// break;
// }
UrlHistoryBean urlInputBean = urlStrInputList.get(i);
String url = urlInputBean.getUrlStr();
for (int j = i + 1; j < sizeNew; j++) {
UrlHistoryBean next = urlStrInputList.get(j);
String urlNext = next.getUrlStr();
// 移除内部URL字段相同的重复元素;
if (TextUtils.equals(url, urlNext)) {
urlStrInputList.remove(next);
j--;
sizeNew--;
}
}
}
}
Gson gson = new Gson();
// 查看是否有缓存;
SharedPrefsManager sharedPrefsManager = SharedPrefsManager.getInstance()
.init(mContext, Consts.EnvConfig.ENV_CONFIG_SP);
String json = sharedPrefsManager
.getString(Consts.EnvConfig.URL_INPUT_HISTORY_JSON);
List<UrlHistoryBean> historyList = null;
if (!TextUtils.isEmpty(json)) {
try {
historyList = gson.fromJson(json, new TypeToken<List<UrlHistoryBean>>() {
}.getType());
} catch (JsonSyntaxException e) {
LogUtils.el(TAG, "method:initEnvConfigWrapper#e=" + e.getMessage());
sharedPrefsManager
.removeKey(Consts.EnvConfig.URL_INPUT_HISTORY_JSON);
}
if (historyList != null) {
// 限制缓存数量;
for (; ; ) {
if (historyList.size() > 50) {
historyList.remove(historyList.size() - 1);
} else {
break;
}
}
// 移除新输入的地址和缓存中重复元素
int sizeInput = urlStrInputList.size();
for (int i = 0; i < sizeInput; i++) {
int sizeHistory = historyList.size();
UrlHistoryBean urlInputBean = urlStrInputList.get(i);
String urlInput = urlInputBean.getUrlStr();
String typeInput = urlInputBean.getType();
// 如果输入的是默认的地址,则从输入列表中移除,这样能保证默认的地址不在新增的历史记录里;
if (TextUtils.equals(typeInput, EnvConfigDialogFragment.URL_HISTORY_TYPE_DEFAULT)) {
urlStrInputList.remove(urlInputBean);
i--;
sizeInput--;
}
// 如果输入的地址和历史中的记录重复时;
for (int j = 0; j < sizeHistory; j++) {
UrlHistoryBean item = historyList.get(j);
String urlHistory = item.getUrlStr();
if (TextUtils.equals(urlInput, urlHistory)) {
// 移除原历史列表中的相同地址和记录;
historyList.remove(item);
j--;
sizeHistory--;
}
}
}
}
}
if (historyList == null) {
historyList = new ArrayList<>();
}
historyList.addAll(0, urlStrInputList);
if (historyList.size() > 0) {
String newJson = gson.toJson(historyList);
sharedPrefsManager
.putString(Consts.EnvConfig.URL_INPUT_HISTORY_JSON, newJson)
.commit();
}
}
/**
* 自定义的环境模式,其属性值如果在productFlavors中存在则要和它保持一致;
*/
public enum EnvMode {
/**
* 开发版本下的服务器测试环境
*/
DEV_TEST_SERVER(Consts.EnvMode.DEV_TEST_SERVER, "开发测试环境(测试服务器)"),
/**
* 开发版本下的本地环境
*/
DEV_TEST_LOCAL(Consts.EnvMode.DEV_TEST_LOCAL, "开发本地测试环境(本地地址)"),
/**
* 开发版本下的试运行环境
*/
DEV_PILOT_RUN(Consts.EnvMode.DEV_PILOT_RUN, "开发试运行环境"),
/**
* 开发版本下的生产环境
*/
DEV_PRODUCT(Consts.EnvMode.DEV_PRODUCT, "开发生产环境"),
/**
* 开发版本
*/
DEVELOPMENT(Consts.EnvMode.DEVELOPMENT, "打包开发环境"),
/**
* 试运行版本
*/
PILOT_RUN(Consts.EnvMode.PILOT_RUN, "打包试运行环境"),
/**
* 生产版本
*/
PRODUCT(Consts.EnvMode.PRODUCT, "打包生产环境");
/**
* 定义的环境;
*/
private String mMode;
/**
* 对应环境的描述;
*/
private String mDesc;
EnvMode(String mode, String desc) {
this.mMode = mode;
this.mDesc = desc;
}
public String getMode() {
return mMode;
}
public String getDesc() {
return mDesc;
}
}
@StringDef({Consts.EnvMode.DEV_TEST_LOCAL, Consts.EnvMode.DEV_TEST_SERVER, Consts.EnvMode.DEV_PILOT_RUN, Consts.EnvMode.DEV_PRODUCT, Consts.EnvMode.DEVELOPMENT, Consts.EnvMode.PILOT_RUN, Consts.EnvMode.PRODUCT})
public @interface MODE {
}
private static class SingletonHolder {
private static final EnvConfigHelper INSTANCE = new EnvConfigHelper();
}
}
EnvConfigShadowLayout 部分修改的逻辑:
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.EnvConfigDialogFragment;
import com.windfallsheng.lib.util.LogUtils;
/**
* @Author: lzsheng
* @date: 2021/8/6 17:15
* @description:
*/
public class EnvConfigShadowLayout extends RelativeLayout {
/**
* 连续点击次数累计到触发弹框的上限值默认值;
*/
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;
/**
* 是否是单次点击;
*/
private boolean isSingleClick;
/**
* 是否是显式的展示,即用户在UI上是否是可见的;
*/
private boolean isExplicitDisplay;
/**
* 记录连续点击时上次点击的时间;
*/
private long mLastClickTimeMillis;
/**
* 记录连续点击的次数统计;
*/
private int mClickCount;
/**
* 是否禁用;
*/
private boolean isDisabled;
/**
* 连续点击次数累计到触发弹框的上限值;
*/
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) {
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);
mLimitClickCount = typedArray.getInt(R.styleable.env_config_shadow_layout_limit_click_count, LIMIT_CLICK_COUNT_DEFAULT);
typedArray.recycle();
handleTimeInterval();
handleLimitClickCount();
if (isDisabled) {
LogUtils.dl("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("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"));
}
}
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 < MIN_TIME_INTERVAL_DEFAULT) {
mClickCount++;
} else {
resetCounter();
}
} else {
// 首次点击;
mClickCount++;
}
LogUtils.dl("initClickEvent#onClick#mClickCount=" + mClickCount + ", mLimitClickCount=" + mLimitClickCount);
if (mClickCount >= mLimitClickCount) {
// 显示弹框
showEnvConfigDialog();
//
resetCounter();
} else {
mLastClickTimeMillis = currentTimeMillis;
}
}
});
}
private void showEnvConfigDialog() {
AppInfoBean appInfoBean = EnvConfigHelper.getInstance().getAppInfoBean();
if (appInfoBean == null) {
Toast.makeText(mContext, "appInfoBean = null", Toast.LENGTH_LONG).show();
return;
}
String modeOfBuild = appInfoBean.getModeOfBuild();
if (TextUtils.isEmpty(modeOfBuild)) {
Toast.makeText(mContext, "modeOfBuild 为空", Toast.LENGTH_LONG).show();
return;
}
// String modeBuild = BuildConfig.MODE;
// 构建的包是生产和试运行环境时,暂时不支持修改地址;这个业务功能可以根据需求调整是否限制;
if (TextUtils.equals(modeOfBuild, EnvConfigHelper.EnvMode.PILOT_RUN.getMode()) ||
TextUtils.equals(modeOfBuild, EnvConfigHelper.EnvMode.PRODUCT.getMode())) {
LogUtils.wl("method:showEnvConfigDialog#return#modeOfBuild=" + modeOfBuild);
Toast.makeText(mContext, modeOfBuild + "环境暂不支持修改环境配置", Toast.LENGTH_LONG).show();
return;
}
EnvConfigDialogFragment envConfigDialogFragment = EnvConfigDialogFragment.newInstance();
if (mContext instanceof FragmentActivity) {
FragmentActivity activity = (FragmentActivity) mContext;
envConfigDialogFragment.show(activity.getSupportFragmentManager(), "envConfigDialogFragment");
} else {
Toast.makeText(mContext, "Context not instance of FragmentActivity.", Toast.LENGTH_LONG).show();
LogUtils.wl("method:showEnvConfigDialog#Context not instance of FragmentActivity.");
}
}
private void resetCounter() {
mClickCount = 0;
mLastClickTimeMillis = 0;
}
}
attrs.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="ResourceName">
<declare-styleable name="env_config_shadow_layout">
<!-- 是否显式的展示,即用户在UI上是否可见 -->
<attr name="is_explicit_display" format="boolean" />
<!-- 是否是音单次点击 -->
<attr name="is_single_click" format="boolean" />
<!-- 是否禁用 -->
<attr name="is_disabled" format="boolean" />
<!-- 连续点击次数累计到触发弹框的上限值 -->
<attr name="limit_click_count" format="integer" />
<!-- 连续点击次数的间隔时间 -->
<attr name="min_time_interval" format="integer" />
</declare-styleable>
<declare-styleable name="url_details_input_layout">
<attr name="type_id" format="string" />
<attr name="type_name" format="string" />
<attr name="url_input" format="string" />
<attr name="item_bg" format="reference|color" />
<attr name="is_checked" format="boolean" />
</declare-styleable>
</resources>
总结
在原来的业务大框架下,新增了手动输入的URL的历史记录的功能,基本是满足大部分使用场景的,实际应用还要立足于自身项目,做一些调整。