添加应用指纹校验功能
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package cc.winboll.studio.libappbase.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.utils.APPUtils;
|
||||
import cc.winboll.studio.libappbase.utils.SignGetUtils;
|
||||
|
||||
/**
|
||||
* @Describe 签名显示+正版校验对话框
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/20 21:20:00
|
||||
* @LastEditTime 2026/01/21 11:00:00
|
||||
*/
|
||||
public class SignGetDialog extends Dialog {
|
||||
private static final String TAG = "SignGetDialog";
|
||||
private EditText etSignFingerprint;
|
||||
private TextView tvAuthResult;
|
||||
private Context mContext;
|
||||
|
||||
public SignGetDialog(Context context) {
|
||||
super(context, R.style.DialogStyle); // 适配默认对话框样式
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_sign_get); // 绑定xml布局
|
||||
setCancelable(true); // 点击外部可关闭
|
||||
initView();
|
||||
initSignAndCheck(); // 获取签名+正版校验
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
etSignFingerprint = findViewById(R.id.et_sign_fingerprint);
|
||||
tvAuthResult = findViewById(R.id.tv_auth_result);
|
||||
// 输入框只读,方便复制
|
||||
etSignFingerprint.setEnabled(false);
|
||||
}
|
||||
|
||||
// 核心:获取签名+调用APPUtils校验
|
||||
private void initSignAndCheck() {
|
||||
// 1. 获取当前应用签名
|
||||
String sign = getCurrentSign();
|
||||
if (sign == null) {
|
||||
etSignFingerprint.setText("签名获取失败");
|
||||
} else {
|
||||
// 签名字符串转0/1 bit数组(每2个bit加空格,每16位换行,下一行无前置空格)
|
||||
String bitArrayStr = convertSignToBitArrayWithWrap(sign);
|
||||
etSignFingerprint.setText(bitArrayStr);
|
||||
}
|
||||
LogUtils.d(TAG, "当前应用签名:" + sign);
|
||||
|
||||
// 2. 正版校验+显示结果
|
||||
APPUtils.checkAppValid(mContext);
|
||||
boolean isOfficial = isSignValid();
|
||||
if (isOfficial) {
|
||||
ToastUtils.show("这是正版的WinBoLL应用,请放心使用。");
|
||||
tvAuthResult.setTextColor(Color.BLUE);
|
||||
tvAuthResult.setText("这是正版的WinBoLL应用,请放心使用。");
|
||||
} else {
|
||||
ToastUtils.show("您使用的可能不是正版的 WinBoLL 应用。");
|
||||
tvAuthResult.setTextColor(Color.RED);
|
||||
tvAuthResult.setText("您使用的可能不是正版的 WinBoLL 应用。");
|
||||
}
|
||||
}
|
||||
|
||||
// 核心修改:签名字符串转0/1 bit数组(每2个bit加空格,每16位换行,下一行无前置空格)
|
||||
private String convertSignToBitArrayWithWrap(String signStr) {
|
||||
StringBuilder bitBuilder = new StringBuilder();
|
||||
// 1. 字符转8位bit
|
||||
for (char c : signStr.toCharArray()) {
|
||||
String bit8 = String.format("%8s", Integer.toBinaryString(c)).replace(' ', '0');
|
||||
bitBuilder.append(bit8);
|
||||
}
|
||||
String fullBitStr = bitBuilder.toString();
|
||||
|
||||
// 2. 按16位分组,组内每2个bit加空格(避免换行后带空格)
|
||||
StringBuilder finalBuilder = new StringBuilder();
|
||||
int groupSize = 16; // 每组16个bit
|
||||
for (int i = 0; i < fullBitStr.length(); i += groupSize) {
|
||||
// 截取16位bit为一组
|
||||
int end = Math.min(i + groupSize, fullBitStr.length());
|
||||
String group = fullBitStr.substring(i, end);
|
||||
|
||||
// 组内每2个bit加空格
|
||||
StringBuilder groupWithSpace = new StringBuilder();
|
||||
for (int j = 0; j < group.length(); j++) {
|
||||
groupWithSpace.append(group.charAt(j));
|
||||
if ((j + 1) % 2 == 0 && j != group.length() - 1) {
|
||||
groupWithSpace.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// 添加组到最终结果,每组后换行(最后一组不换行)
|
||||
finalBuilder.append(groupWithSpace);
|
||||
if (end < fullBitStr.length()) {
|
||||
finalBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
return finalBuilder.toString();
|
||||
}
|
||||
|
||||
// 获取签名(复用SignGetUtils逻辑,避免重复代码)
|
||||
private String getCurrentSign() {
|
||||
try {
|
||||
return SignGetUtils.getSignStr(mContext); // 复用工具类逻辑
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "获取签名失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 校验签名是否合法(匹配APPUtils目标签名)
|
||||
private boolean isSignValid() {
|
||||
String currentSign = getCurrentSign();
|
||||
String targetSign = APPUtils.TARGET_SIGN_FINGERPRINT; // 取APPUtils目标签名
|
||||
return currentSign != null && targetSign != null && currentSign.equals(targetSign);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package cc.winboll.studio.libappbase.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Base64;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/20 19:17
|
||||
* @Describe APPUtils 应用包名、签名校验工具类
|
||||
*/
|
||||
public class APPUtils {
|
||||
|
||||
public static final String TAG = "APPUtils";
|
||||
// 目标应用签名指纹(BASE64格式,自行替换为你的合法指纹)
|
||||
//public static final String TARGET_SIGN_FINGERPRINT = "你的应用签名SHA1指纹BASE64值";
|
||||
public static final String TARGET_SIGN_FINGERPRINT = "bMArVdXE4ZZo42vS9e/kXE63MkE=";
|
||||
// 目标应用包名(自行替换为你的应用包名)
|
||||
private static final String TARGET_PACKAGE_NAME = "cc.winboll.studio.你的应用包名";
|
||||
|
||||
/**
|
||||
* 检查应用包名+签名指纹合法性,不匹配打日志,匹配无操作
|
||||
* @param context 上下文
|
||||
*/
|
||||
public static void checkAppValid(Context context) {
|
||||
if (context == null) {
|
||||
LogUtils.w(TAG, "checkAppValid: context为空,跳过校验");
|
||||
return;
|
||||
}
|
||||
// 1. 校验包名
|
||||
String currentPkg = context.getPackageName();
|
||||
LogUtils.d(TAG, "checkAppValid: 当前应用包名=" + currentPkg + ",目标包名=" + TARGET_PACKAGE_NAME);
|
||||
if (!TARGET_PACKAGE_NAME.equals(currentPkg)) {
|
||||
LogUtils.e(TAG, "checkAppValid: 应用包名不匹配,非法环境");
|
||||
return;
|
||||
}
|
||||
// 2. 校验签名指纹
|
||||
String currentSign = getAppSignFingerprint(context);
|
||||
LogUtils.d(TAG, "checkAppValid: 当前应用签名指纹=" + currentSign + ",目标指纹=" + TARGET_SIGN_FINGERPRINT);
|
||||
if (currentSign == null || !TARGET_SIGN_FINGERPRINT.equals(currentSign)) {
|
||||
LogUtils.e(TAG, "checkAppValid: 应用签名指纹不匹配,非法环境");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前应用签名SHA1指纹(BASE64编码,适配Java7)
|
||||
* @param context 上下文
|
||||
* @return 签名指纹字符串,失败返回null
|
||||
*/
|
||||
private static String getAppSignFingerprint(Context context) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
Signature[] signatures = pkgInfo.signatures;
|
||||
if (signatures == null || signatures.length == 0) {
|
||||
LogUtils.w(TAG, "getAppSignFingerprint: 未获取到应用签名");
|
||||
return null;
|
||||
}
|
||||
// SHA1摘要 + BASE64编码,和目标指纹格式统一
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
md.update(signatures[0].toByteArray());
|
||||
return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.e(TAG, "getAppSignFingerprint: 包名未找到", e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LogUtils.e(TAG, "getAppSignFingerprint: 不支持SHA1算法", e);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "getAppSignFingerprint: 获取签名异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package cc.winboll.studio.libappbase.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Base64;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/20 19:50
|
||||
* @Describe 获取应用签名指纹(SHA1+Base64,直接复制用)
|
||||
*/
|
||||
public class SignGetUtils {
|
||||
private static final String TAG = "SignGetUtils";
|
||||
|
||||
/**
|
||||
* 一键获取当前应用签名指纹(直接调用,看日志复制结果)
|
||||
*/
|
||||
public static void getCurrentAppSign(Context context) {
|
||||
if (context == null) {
|
||||
LogUtils.e(TAG, "context不能为空");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
Signature[] signatures = pkgInfo.signatures;
|
||||
if (signatures == null || signatures.length == 0) {
|
||||
LogUtils.e(TAG, "未获取到应用签名");
|
||||
return;
|
||||
}
|
||||
// 和APPUtils校验格式完全一致(SHA1+Base64 NO_WRAP)
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
md.update(signatures[0].toByteArray());
|
||||
String signBase64 = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||
|
||||
// 关键日志:复制【】里的内容到APPUtils的TARGET_SIGN_FINGERPRINT
|
||||
LogUtils.d(TAG, "当前应用包名:" + context.getPackageName());
|
||||
LogUtils.d(TAG, "当前应用签名指纹(直接复制):【" + signBase64 + "】");
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.e(TAG, "获取签名失败:包名不存在", e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LogUtils.e(TAG, "获取签名失败:不支持SHA1", e);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "获取签名失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:直接返回签名字符串,供对话框调用
|
||||
public static String getSignStr(Context context) {
|
||||
if (context == null) return null;
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
Signature[] signatures = pkgInfo.signatures;
|
||||
if (signatures == null || signatures.length == 0) return null;
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
md.update(signatures[0].toByteArray());
|
||||
return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "获取签名字符串失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,22 @@ import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.dialogs.SignGetDialog;
|
||||
import cc.winboll.studio.libappbase.models.APPInfo;
|
||||
|
||||
/**
|
||||
* @Describe AboutView 原生实现关于页面,无第三方依赖,适配API30,抽象通用功能控件(邮件/网页跳转)
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/11 12:23:00
|
||||
* @LastEditTime 2026/01/12 01:05:30
|
||||
* @LastEditTime 2026/01/20 20:45:00
|
||||
*/
|
||||
public class AboutView extends LinearLayout {
|
||||
// 全局常量区(标识、回调标识)
|
||||
@@ -41,7 +42,6 @@ public class AboutView extends LinearLayout {
|
||||
private static final int PADDING_MID = 16;
|
||||
private static final int PADDING_SMALL = 8;
|
||||
private static final int ICON_SIZE = 48;
|
||||
private static final int LINE_HEIGHT = 1;
|
||||
private static final int ITEM_ICON_SIZE = 24;
|
||||
|
||||
// 成员属性区(按 核心依赖→业务配置→视图相关 归类排序,注释清晰)
|
||||
@@ -66,12 +66,19 @@ public class AboutView extends LinearLayout {
|
||||
private EditText metDevUserName; // 调试用户名输入框
|
||||
private EditText metDevUserPassword; // 调试密码输入框
|
||||
|
||||
// 视图绑定
|
||||
private ImageView ivAppIcon;
|
||||
private TextView tvAppNameVersion;
|
||||
private TextView tvAppDesc;
|
||||
private LinearLayout llFunctionContainer;
|
||||
|
||||
// 构造方法区(按 参数从少到多 排序,适配 代码创建+XML引用 场景)
|
||||
public AboutView(Context context) {
|
||||
super(context);
|
||||
LogUtils.d(TAG, "AboutView(Context) 构造方法调用,代码创建视图场景");
|
||||
this.mContext = context;
|
||||
initDefaultParams();
|
||||
initViewFromXml();
|
||||
}
|
||||
|
||||
public AboutView(Context context, APPInfo appInfo) {
|
||||
@@ -79,6 +86,7 @@ public class AboutView extends LinearLayout {
|
||||
LogUtils.d(TAG, "AboutView(Context,APPInfo) 构造调用,入参APPInfo:" + (appInfo == null ? "null" : appInfo.getAppName()));
|
||||
this.mContext = context;
|
||||
this.mAPPInfo = appInfo;
|
||||
initViewFromXml();
|
||||
initAll();
|
||||
}
|
||||
|
||||
@@ -87,6 +95,7 @@ public class AboutView extends LinearLayout {
|
||||
LogUtils.d(TAG, "AboutView(Context,AttributeSet) 构造调用,XML布局引用场景");
|
||||
this.mContext = context;
|
||||
initDefaultParams();
|
||||
initViewFromXml();
|
||||
}
|
||||
|
||||
public AboutView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
@@ -94,8 +103,45 @@ public class AboutView extends LinearLayout {
|
||||
LogUtils.d(TAG, "AboutView(Context,AttributeSet,int) 构造调用,XML布局+样式配置,defStyleAttr:" + defStyleAttr);
|
||||
this.mContext = context;
|
||||
initDefaultParams();
|
||||
initViewFromXml();
|
||||
}
|
||||
|
||||
// 核心:加载xml布局并绑定视图
|
||||
// private void initViewFromXml() {
|
||||
// View.inflate(mContext, R.layout.layout_about_view, this);
|
||||
// ivAppIcon = findViewById(R.id.iv_app_icon);
|
||||
// tvAppNameVersion = findViewById(R.id.tv_app_name_version);
|
||||
// tvAppDesc = findViewById(R.id.tv_app_desc);
|
||||
// llFunctionContainer = findViewById(R.id.ll_function_container);
|
||||
// LogUtils.d(TAG, "initViewFromXml 布局加载+视图绑定完成");
|
||||
// }
|
||||
// 1. 新增视图绑定属性(加在原有视图属性后面)
|
||||
private ImageButton ibSigngetdialog;
|
||||
|
||||
// 2. 完善initViewFromXml方法,新增按钮绑定
|
||||
private void initViewFromXml() {
|
||||
View.inflate(mContext, R.layout.layout_about_view, this);
|
||||
ivAppIcon = findViewById(R.id.iv_app_icon);
|
||||
tvAppNameVersion = findViewById(R.id.tv_app_name_version);
|
||||
tvAppDesc = findViewById(R.id.tv_app_desc);
|
||||
llFunctionContainer = findViewById(R.id.ll_function_container);
|
||||
ibSigngetdialog = findViewById(R.id.ib_signgetdialog); // 新增按钮绑定
|
||||
setBtnClickListener(); // 新增绑定点击事件
|
||||
LogUtils.d(TAG, "initViewFromXml 布局加载+视图绑定完成");
|
||||
}
|
||||
|
||||
// 3. 新增按钮点击事件方法(放在initViewFromXml下面即可)
|
||||
private void setBtnClickListener() {
|
||||
ibSigngetdialog.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "签名获取按钮点击,弹出SignGetDialog");
|
||||
new SignGetDialog(mContext).show(); // 弹出对话框
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 对外公开方法区(供外部调用,职责单一,注释明确)
|
||||
/**
|
||||
* 一站式初始化所有关于页逻辑,包含参数、信息、视图全流程初始化
|
||||
@@ -106,10 +152,6 @@ public class AboutView extends LinearLayout {
|
||||
LogUtils.w(TAG, "initAll() 初始化终止:APPInfo 为 null,无法获取应用核心信息");
|
||||
return;
|
||||
}
|
||||
// 基础布局配置
|
||||
setOrientation(VERTICAL);
|
||||
setPadding(dp2px(PADDING_MID), dp2px(PADDING_LARGE), dp2px(PADDING_MID), dp2px(PADDING_LARGE));
|
||||
setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
|
||||
// 按初始化流程执行,有序无冗余
|
||||
initDefaultParams();
|
||||
@@ -129,7 +171,7 @@ public class AboutView extends LinearLayout {
|
||||
public void setAPPInfoAndInit(APPInfo appInfo) {
|
||||
LogUtils.d(TAG, "setAPPInfoAndInit() 调用,传入新APPInfo:" + (appInfo == null ? "null" : appInfo.getAppName()));
|
||||
this.mAPPInfo = appInfo;
|
||||
removeAllViews();
|
||||
llFunctionContainer.removeAllViews();
|
||||
initAll();
|
||||
LogUtils.d(TAG, "setAPPInfoAndInit() 应用信息重置+页面重构完成");
|
||||
}
|
||||
@@ -141,7 +183,7 @@ public class AboutView extends LinearLayout {
|
||||
public void setAPPInfo(APPInfo appInfo) {
|
||||
LogUtils.d(TAG, "setAPPInfo() 调用,传入APPInfo:" + (appInfo == null ? "null" : appInfo.getAppName()));
|
||||
this.mAPPInfo = appInfo;
|
||||
removeAllViews();
|
||||
llFunctionContainer.removeAllViews();
|
||||
initAll();
|
||||
}
|
||||
|
||||
@@ -196,7 +238,7 @@ public class AboutView extends LinearLayout {
|
||||
LogUtils.d(TAG, "initAppVersionInfo() 获取版本号失败,默认赋值unknown", e);
|
||||
mszAppVersionName = "unknown";
|
||||
}
|
||||
mszCurrentAppPackageName = String.format("%s_%s.apk", mszAppAPKName, mszAppVersionName);
|
||||
mszCurrentAppPackageName = String.format("%s_%s.apk", mszAppVersionName, mszAppVersionName);
|
||||
LogUtils.d(TAG, "initAppVersionInfo() 完成,版本号:" + mszAppVersionName + ",当前APK名:" + mszCurrentAppPackageName);
|
||||
}
|
||||
|
||||
@@ -245,77 +287,37 @@ public class AboutView extends LinearLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心视图组装:按 图标→应用信息→分割线→通用功能控件 顺序构建页面
|
||||
* 核心视图组装:赋值基础信息+添加功能项
|
||||
*/
|
||||
private void initAboutPageView() {
|
||||
LogUtils.d(TAG, "initAboutPageView() 开始组装关于页视图");
|
||||
addAppIcon();
|
||||
addAppInfoDesc();
|
||||
addLineSeparator();
|
||||
// 基础信息赋值
|
||||
ivAppIcon.setImageResource(mnAppIcon);
|
||||
tvAppNameVersion.setText(String.format("%s %s", mszAppName, mszAppVersionName));
|
||||
if (mszAppDescription.isEmpty()) {
|
||||
tvAppDesc.setVisibility(GONE);
|
||||
} else {
|
||||
tvAppDesc.setVisibility(VISIBLE);
|
||||
tvAppDesc.setText(mszAppDescription);
|
||||
}
|
||||
|
||||
// 通用功能控件:网页跳转类+邮件类,复用抽象控件
|
||||
addView(new WebJumpFunctionItemView(mContext, "WinBoLL 主页", WINBOLL_OFFICIAL_HOME, R.drawable.ic_winboll));
|
||||
addView(new EmailFunctionItemView(mContext, "联系邮箱", "WinBoLLStudio<studio@winboll.cc>", R.drawable.ic_winboll));
|
||||
addFunctionView(new WebJumpFunctionItemView(mContext, "WinBoLL 主页", WINBOLL_OFFICIAL_HOME, R.drawable.ic_winboll));
|
||||
addFunctionView(new EmailFunctionItemView(mContext, "联系邮箱", "WinBoLLStudio<studio@winboll.cc>", R.drawable.ic_winboll));
|
||||
if (!mszHomePage.isEmpty()) {
|
||||
addView(new WebJumpFunctionItemView(mContext, "应用APK下载地址", mszHomePage, R.drawable.ic_winboll));
|
||||
addFunctionView(new WebJumpFunctionItemView(mContext, "应用APK下载地址", mszHomePage, R.drawable.ic_winboll));
|
||||
}
|
||||
if (!mszGitea.isEmpty()) {
|
||||
addView(new WebJumpFunctionItemView(mContext, "应用Git源码地址", mszGitea, R.drawable.ic_winboll));
|
||||
addFunctionView(new WebJumpFunctionItemView(mContext, "应用Git源码地址", mszGitea, R.drawable.ic_winboll));
|
||||
}
|
||||
LogUtils.d(TAG, "initAboutPageView() 视图组装完成,功能项加载完毕");
|
||||
}
|
||||
|
||||
// 视图构建辅助方法区(基础视图组件)
|
||||
/**
|
||||
* 添加应用图标组件,居中展示
|
||||
*/
|
||||
private void addAppIcon() {
|
||||
ImageView ivIcon = new ImageView(mContext);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dp2px(ICON_SIZE), dp2px(ICON_SIZE));
|
||||
params.bottomMargin = dp2px(PADDING_MID);
|
||||
ivIcon.setLayoutParams(params);
|
||||
ivIcon.setImageResource(mnAppIcon);
|
||||
ivIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
addView(ivIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加应用名称+版本号+描述信息组件,垂直居中展示
|
||||
*/
|
||||
private void addAppInfoDesc() {
|
||||
LinearLayout llDesc = new LinearLayout(mContext);
|
||||
llDesc.setOrientation(VERTICAL);
|
||||
llDesc.setGravity(Gravity.CENTER);
|
||||
llDesc.setPadding(0, 0, 0, dp2px(PADDING_MID));
|
||||
|
||||
TextView tvAppName = new TextView(mContext);
|
||||
tvAppName.setText(String.format("%s %s", mszAppName, mszAppVersionName));
|
||||
tvAppName.setTextSize(18);
|
||||
tvAppName.setTextColor(mContext.getResources().getColor(R.color.gray_900));
|
||||
llDesc.addView(tvAppName);
|
||||
|
||||
if (!mszAppDescription.isEmpty()) {
|
||||
TextView tvDesc = new TextView(mContext);
|
||||
tvDesc.setText(mszAppDescription);
|
||||
tvDesc.setTextSize(14);
|
||||
tvDesc.setTextColor(mContext.getResources().getColor(R.color.gray_500));
|
||||
tvDesc.setPadding(0, dp2px(PADDING_SMALL), 0, 0);
|
||||
llDesc.addView(tvDesc);
|
||||
}
|
||||
addView(llDesc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加视图分割线,区分不同功能模块
|
||||
*/
|
||||
private void addLineSeparator() {
|
||||
View line = new View(mContext);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dp2px(LINE_HEIGHT));
|
||||
// 添加功能项到容器
|
||||
private void addFunctionView(View view) {
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
params.topMargin = dp2px(PADDING_SMALL);
|
||||
params.bottomMargin = dp2px(PADDING_MID);
|
||||
line.setLayoutParams(params);
|
||||
line.setBackgroundColor(mContext.getResources().getColor(R.color.gray_200));
|
||||
addView(line);
|
||||
llFunctionContainer.addView(view, params);
|
||||
}
|
||||
|
||||
// 工具方法区(通用工具+业务工具,静态优先,便于复用)
|
||||
|
||||
Reference in New Issue
Block a user