Compare commits

...

2 Commits

Author SHA1 Message Date
2896b6401b <appbase>APK 15.15.5 release Publish. 2026-01-20 20:54:24 +08:00
1aa270482e 添加应用指纹校验功能 2026-01-20 20:52:49 +08:00
10 changed files with 492 additions and 76 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jan 13 03:23:34 HKT 2026
stageCount=5
#Tue Jan 20 20:54:24 HKT 2026
stageCount=6
libraryProject=libappbase
baseVersion=15.15
publishVersion=15.15.4
publishVersion=15.15.5
buildCount=0
baseBetaVersion=15.15.5
baseBetaVersion=15.15.6

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jan 13 03:23:17 HKT 2026
stageCount=5
#Tue Jan 20 20:54:24 HKT 2026
stageCount=6
libraryProject=libappbase
baseVersion=15.15
publishVersion=15.15.4
publishVersion=15.15.5
buildCount=0
baseBetaVersion=15.15.5
baseBetaVersion=15.15.6

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
// 工具方法区(通用工具+业务工具,静态优先,便于复用)

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M7,14C5.9,14 5,13.1 5,12S5.9,10 7,10 9,10.9 9,12 8.1,14 7,14M12.6,10C11.8,7.7 9.6,6 7,6C3.7,6 1,8.7 1,12S3.7,18 7,18C9.6,18 11.8,16.3 12.6,14H16V18H20V14H23V10H12.6Z"/>
</vector>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center"
android:background="#FFDCDCDC">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="应用指纹校验"
android:textSize="16sp"
android:textColor="@color/gray_900"
android:textStyle="bold"
android:layout_marginBottom="12dp"/>
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et_sign_fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/edit_text"
android:textSize="12sp"
android:gravity="top"
android:hint="签名获取中..."
android:singleLine="false"
android:scrollHorizontally="false"
android:scrollbars="vertical"
android:overScrollMode="always"
android:typeface="monospace"
android:paddingLeft="10dp"
android:paddingRight="10dp"/>
</ScrollView>
<TextView
android:id="@+id/tv_auth_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="11sp"
android:gravity="center"
android:textColor="@color/gray_900"/>
</LinearLayout>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingLeft="16dp"
android:paddingTop="32dp"
android:paddingRight="16dp"
android:paddingBottom="32dp">
<ImageView
android:id="@+id/iv_app_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="16dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/tv_app_name_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@color/gray_900"/>
<TextView
android:id="@+id/tv_app_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:textSize="14sp"
android:textColor="@color/gray_500"/>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:background="@color/gray_200"/>
<LinearLayout
android:id="@+id/ll_function_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_key"
android:id="@+id/ib_signgetdialog"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -11,5 +11,10 @@
<item name="colorText">#FF00B322</item>
<item name="colorTextBackgound">#FF000000</item>
</style>
<style name="DialogStyle" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>