Compare commits
3 Commits
aes-v15.10
...
471ca23585
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
471ca23585 | ||
|
|
56be1767bb | ||
|
|
b66d53da1b |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -87,15 +87,19 @@ lint/tmp/
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
# 忽略 Lint 输出文件
|
||||
# Custom
|
||||
.androidide
|
||||
lint-results.xml
|
||||
lint-results.html
|
||||
|
||||
## 忽略 AndroidIDE 临时文件夹
|
||||
.androidide
|
||||
winboll.properties
|
||||
local.properties
|
||||
|
||||
## 忽略模块应用编译配置
|
||||
/settings.gradle
|
||||
/gradle.properties
|
||||
/winboll.properties
|
||||
/local.properties
|
||||
|
||||
## 忽略 srv 纠结问题
|
||||
/srv/
|
||||
|
||||
## 忽略 winboll-x 文件夹
|
||||
/winboll-x/
|
||||
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
// versionName 更新后需要手动设置
|
||||
// 项目模块目录的 build.gradle 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "15.10"
|
||||
versionName "15.9"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Sep 28 20:11:37 HKT 2025
|
||||
stageCount=1
|
||||
#Sun Aug 31 23:40:17 HKT 2025
|
||||
stageCount=4
|
||||
libraryProject=libaes
|
||||
baseVersion=15.10
|
||||
publishVersion=15.10.0
|
||||
baseVersion=15.9
|
||||
publishVersion=15.9.3
|
||||
buildCount=0
|
||||
baseBetaVersion=15.10.1
|
||||
baseBetaVersion=15.9.4
|
||||
|
||||
@@ -15,8 +15,7 @@ import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.libaes.winboll.APPInfo;
|
||||
import cc.winboll.studio.libaes.winboll.AboutView;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity {
|
||||
|
||||
@@ -24,7 +23,7 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
|
||||
|
||||
Context mContext;
|
||||
Toolbar mToolbar;
|
||||
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
@@ -34,7 +33,7 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -65,13 +64,13 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
|
||||
);
|
||||
layout.addView(aboutView, params);
|
||||
|
||||
WinBoLLActivityManager.getInstance().add(this);
|
||||
GlobalApplication.getWinBoLLActivityManager().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
WinBoLLActivityManager.getInstance().registeRemove(this);
|
||||
GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
|
||||
}
|
||||
|
||||
public AboutView CreateAboutView() {
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.view.Gravity;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import com.hjq.toast.style.WhiteToastStyle;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
|
||||
|
||||
public class App extends GlobalApplication {
|
||||
@@ -19,7 +18,6 @@ public class App extends GlobalApplication {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
WinBoLLActivityManager.init(this);
|
||||
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
|
||||
@@ -15,7 +15,7 @@ import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.aes.R;
|
||||
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
|
||||
import cc.winboll.studio.libaes.models.DrawerMenuBean;
|
||||
import cc.winboll.studio.libaes.beans.DrawerMenuBean;
|
||||
import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog;
|
||||
import cc.winboll.studio.libaes.dialogs.StoragePathDialog;
|
||||
import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity;
|
||||
@@ -25,10 +25,10 @@ import cc.winboll.studio.libaes.unittests.TestAToolbarActivity;
|
||||
import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity;
|
||||
import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import com.a4455jkjh.colorpicker.ColorPickerDialog;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.util.ArrayList;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
|
||||
public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Fri Sep 26 05:36:14 HKT 2025
|
||||
stageCount=9
|
||||
#Mon Sep 22 06:01:08 HKT 2025
|
||||
stageCount=8
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.10
|
||||
publishVersion=15.10.8
|
||||
publishVersion=15.10.7
|
||||
buildCount=0
|
||||
baseBetaVersion=15.10.9
|
||||
baseBetaVersion=15.10.8
|
||||
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
// versionName 更新后需要手动设置
|
||||
// 项目模块目录的 build.gradle 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "15.8"
|
||||
versionName "15.9"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Mon Sep 01 07:56:33 HKT 2025
|
||||
stageCount=7
|
||||
#Sun Sep 21 22:28:28 GMT 2025
|
||||
stageCount=0
|
||||
libraryProject=libapputils
|
||||
baseVersion=15.8
|
||||
publishVersion=15.8.6
|
||||
buildCount=0
|
||||
baseBetaVersion=15.8.7
|
||||
baseVersion=15.9
|
||||
publishVersion=15.9.0
|
||||
buildCount=2
|
||||
baseBetaVersion=15.9.1
|
||||
|
||||
@@ -21,8 +21,8 @@ android {
|
||||
|
||||
dependencies {
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api 'cc.winboll.studio:libapputils:15.10.0'
|
||||
api 'cc.winboll.studio:libappbase:15.10.9'
|
||||
api 'cc.winboll.studio:libapputils:15.8.5'
|
||||
api 'cc.winboll.studio:libappbase:15.9.5'
|
||||
|
||||
// 吐司类库
|
||||
api 'com.github.getActivity:ToastUtils:10.5'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Sep 28 20:11:37 HKT 2025
|
||||
stageCount=1
|
||||
#Sun Aug 31 05:00:43 CST 2025
|
||||
stageCount=4
|
||||
libraryProject=libaes
|
||||
baseVersion=15.10
|
||||
publishVersion=15.10.0
|
||||
baseVersion=15.9
|
||||
publishVersion=15.9.3
|
||||
buildCount=0
|
||||
baseBetaVersion=15.10.1
|
||||
baseBetaVersion=15.9.4
|
||||
|
||||
@@ -23,15 +23,17 @@ import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import cc.winboll.studio.libaes.DrawerMenuDataAdapter;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.models.AESThemeBean;
|
||||
import cc.winboll.studio.libaes.models.DrawerMenuBean;
|
||||
import cc.winboll.studio.libaes.beans.AESThemeBean;
|
||||
import cc.winboll.studio.libaes.beans.DrawerMenuBean;
|
||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
|
||||
import cc.winboll.studio.libaes.views.ADrawerMenuListView;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import com.baoyz.widget.PullRefreshLayout;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class DrawerFragmentActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
||||
public abstract class DrawerFragmentActivity extends AppCompatActivity implements IWinBoLLActivity,AdapterView.OnItemClickListener {
|
||||
|
||||
public static final String TAG = "DrawerFragmentActivity";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libaes.models;
|
||||
package cc.winboll.studio.libaes.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libaes.models;
|
||||
package cc.winboll.studio.libaes.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libaes.models;
|
||||
package cc.winboll.studio.libaes.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
@@ -1,60 +0,0 @@
|
||||
package cc.winboll.studio.libaes.dialogs;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/28 17:40:47
|
||||
* @Date 2024/08/12 14:46:25
|
||||
* @Describe 询问用户确定与否的选择框
|
||||
*/
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
public class YesNoAlertDialog {
|
||||
|
||||
public static final String TAG = "YesNoAlertDialog";
|
||||
|
||||
public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) {
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
|
||||
context);
|
||||
|
||||
// set title
|
||||
alertDialogBuilder.setTitle(szTitle);
|
||||
|
||||
// set dialog message
|
||||
alertDialogBuilder
|
||||
.setMessage(szMessage)
|
||||
.setCancelable(true)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener(){
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
listener.onNo();
|
||||
}
|
||||
})
|
||||
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// if this button is clicked, close
|
||||
// current activity
|
||||
listener.onYes();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("NO", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// if this button is clicked, just close
|
||||
// the dialog box and do nothing
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
// create alert dialog
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
|
||||
// show it
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public interface OnDialogResultListener {
|
||||
abstract void onYes();
|
||||
abstract void onNo();
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package cc.winboll.studio.libaes.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 10:16
|
||||
* @Describe WinBoLLModel
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import cc.winboll.studio.libappbase.APPModel;
|
||||
|
||||
public class WinBoLLModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "WinBoLLModel";
|
||||
|
||||
String appPackageName;
|
||||
String appMainServiveName;
|
||||
|
||||
public WinBoLLModel() {
|
||||
this.appPackageName = "";
|
||||
this.appMainServiveName = "";
|
||||
}
|
||||
|
||||
public WinBoLLModel(boolean isDebuging, String appPackageName, String appMainServiveName) {
|
||||
this.appPackageName = appPackageName;
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public WinBoLLModel(String appPackageName, String appMainServiveName) {
|
||||
this.appPackageName = appPackageName;
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public void setAppPackageName(String appPackageName) {
|
||||
this.appPackageName = appPackageName;
|
||||
}
|
||||
|
||||
public String getAppPackageName() {
|
||||
return appPackageName;
|
||||
}
|
||||
|
||||
public void setAppMainServiveName(String appMainServiveName) {
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public String getAppMainServiveName() {
|
||||
return appMainServiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return APPModel.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("appPackageName").value(getAppPackageName());
|
||||
jsonWriter.name("appMainServiveName").value(getAppMainServiveName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("appPackageName")) {
|
||||
setAppPackageName(jsonReader.nextString());
|
||||
} else if (name.equals("appMainServiveName")) {
|
||||
setAppMainServiveName(jsonReader.nextString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
|
||||
public class TestASupportToolbarActivity extends AppCompatActivity implements IWinBoLLActivity {
|
||||
|
||||
|
||||
@@ -10,11 +10,22 @@ import android.os.Bundle;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
|
||||
public class TestAToolbarActivity extends Activity {
|
||||
public class TestAToolbarActivity extends Activity implements IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "TestAToolbarActivity";
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
AESThemeUtil.applyAppTheme(this);
|
||||
|
||||
@@ -14,10 +14,10 @@ import android.widget.Toast;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
|
||||
import cc.winboll.studio.libaes.models.DrawerMenuBean;
|
||||
import cc.winboll.studio.libaes.beans.DrawerMenuBean;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import java.util.ArrayList;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
|
||||
public class TestDrawerFragmentActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import android.view.MenuItem;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
|
||||
import cc.winboll.studio.libaes.models.AESThemeBean;
|
||||
import cc.winboll.studio.libaes.beans.AESThemeBean;
|
||||
|
||||
public class AESThemeUtil {
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import cc.winboll.studio.libaes.R;
|
||||
import cc.winboll.studio.libaes.utils.AppVersionUtils;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
||||
import cc.winboll.studio.libapputils.utils.PrefUtils;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.io.IOException;
|
||||
@@ -32,7 +33,6 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import mehdi.sakout.aboutpage.BuildConfig;
|
||||
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
|
||||
|
||||
public class AboutView extends LinearLayout {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class AboutView extends LinearLayout {
|
||||
|
||||
public static final int MSG_APPUPDATE_CHECKED = 0;
|
||||
|
||||
static Context _mContext;
|
||||
Context mContext;
|
||||
APPInfo mAPPInfo;
|
||||
|
||||
WinBoLLServiceStatusView mWinBoLLServiceStatusView;
|
||||
@@ -64,7 +64,7 @@ public class AboutView extends LinearLayout {
|
||||
|
||||
public AboutView(Context context, APPInfo appInfo) {
|
||||
super(context);
|
||||
_mContext = context;
|
||||
mContext = context;
|
||||
|
||||
setAPPInfo(appInfo);
|
||||
initView(context);
|
||||
@@ -72,7 +72,7 @@ public class AboutView extends LinearLayout {
|
||||
|
||||
public AboutView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
_mContext = context;
|
||||
mContext = context;
|
||||
|
||||
initView(context, attrs);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class AboutView extends LinearLayout {
|
||||
mszWinBoLLServerHost = GlobalApplication.isDebuging() ? "https://yun-preivew.winboll.cc": "https://yun.winboll.cc";
|
||||
|
||||
try {
|
||||
mszAppVersionName = _mContext.getPackageManager().getPackageInfo(_mContext.getPackageName(), 0).versionName;
|
||||
mszAppVersionName = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
@@ -125,13 +125,13 @@ public class AboutView extends LinearLayout {
|
||||
|
||||
|
||||
if (GlobalApplication.isDebuging()) {
|
||||
LayoutInflater inflater = LayoutInflater.from(_mContext);
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View addedView = inflater.inflate(R.layout.view_about_dev, this, false);
|
||||
LinearLayout llMain = addedView.findViewById(R.id.viewaboutdevLinearLayout1);
|
||||
metDevUserName = addedView.findViewById(R.id.viewaboutdevEditText1);
|
||||
metDevUserPassword = addedView.findViewById(R.id.viewaboutdevEditText2);
|
||||
metDevUserName.setText(PrefUtils.getString(_mContext, "metDevUserName", ""));
|
||||
metDevUserPassword.setText(PrefUtils.getString(_mContext, "metDevUserPassword", ""));
|
||||
metDevUserName.setText(PrefUtils.getString(mContext, "metDevUserName", ""));
|
||||
metDevUserPassword.setText(PrefUtils.getString(mContext, "metDevUserPassword", ""));
|
||||
//mDevelopHostConnectionStatusView = new DevelopHostConnectionStatusView(context);
|
||||
mWinBoLLServiceStatusView = addedView.findViewById(R.id.viewaboutdevWinBoLLServiceStatusView1);
|
||||
mWinBoLLServiceStatusView.setServerHost(mszWinBoLLServerHost);
|
||||
@@ -140,7 +140,7 @@ public class AboutView extends LinearLayout {
|
||||
llMain.addView(createAboutPage());
|
||||
addView(addedView);
|
||||
} else {
|
||||
LayoutInflater inflater = LayoutInflater.from(_mContext);
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View addedView = inflater.inflate(R.layout.view_about_www, this, false);
|
||||
LinearLayout llMain = addedView.findViewById(R.id.viewaboutwwwLinearLayout1);
|
||||
//mDevelopHostConnectionStatusView = new DevelopHostConnectionStatusView(context);
|
||||
@@ -162,9 +162,9 @@ public class AboutView extends LinearLayout {
|
||||
String szReleaseAppVersionName = "";
|
||||
try {
|
||||
//LogUtils.d(TAG, String.format("mContext.getPackageName() %s", mContext.getPackageName()));
|
||||
String szSubBetaSuffix = subBetaSuffix(_mContext.getPackageName());
|
||||
String szSubBetaSuffix = subBetaSuffix(mContext.getPackageName());
|
||||
//LogUtils.d(TAG, String.format("szSubBetaSuffix : %s", szSubBetaSuffix));
|
||||
szReleaseAppVersionName = _mContext.getPackageManager().getPackageInfo(szSubBetaSuffix, 0).versionName;
|
||||
szReleaseAppVersionName = mContext.getPackageManager().getPackageInfo(szSubBetaSuffix, 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public class AboutView extends LinearLayout {
|
||||
String szMsg = "Current app is :\n[ " + mszCurrentAppPackageName
|
||||
+ " ]\nThe last app is :\n[ " + mszNewestAppPackageName
|
||||
+ " ]\nIs download the last app?";
|
||||
YesNoAlertDialog.show(_mContext, "Application Update Prompt", szMsg, mIsDownlaodUpdateListener);
|
||||
YesNoAlertDialog.show(mContext, "Application Update Prompt", szMsg, mIsDownlaodUpdateListener);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -216,22 +216,22 @@ public class AboutView extends LinearLayout {
|
||||
protected View createAboutPage() {
|
||||
// 定义 GitWeb 按钮
|
||||
//
|
||||
Element elementGitWeb = new Element(_mContext.getString(R.string.gitea_home), R.drawable.ic_winboll);
|
||||
Element elementGitWeb = new Element(mContext.getString(R.string.gitea_home), R.drawable.ic_winboll);
|
||||
elementGitWeb.setOnClickListener(mGitWebOnClickListener);
|
||||
// 定义检查更新按钮
|
||||
//
|
||||
Element elementAppUpdate = new Element(_mContext.getString(R.string.app_update), R.drawable.ic_winboll);
|
||||
Element elementAppUpdate = new Element(mContext.getString(R.string.app_update), R.drawable.ic_winboll);
|
||||
elementAppUpdate.setOnClickListener(mAppUpdateOnClickListener);
|
||||
|
||||
String szAppInfo = "";
|
||||
try {
|
||||
szAppInfo = mszAppName + " "
|
||||
+ _mContext.getPackageManager().getPackageInfo(_mContext.getPackageName(), 0).versionName
|
||||
+ mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName
|
||||
+ "\n" + mszAppDescription;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
AboutPage aboutPage = new AboutPage(_mContext);
|
||||
AboutPage aboutPage = new AboutPage(mContext);
|
||||
aboutPage.setDescription(szAppInfo)
|
||||
//.isRTL(false)
|
||||
//.setCustomFont(String) // or Typeface
|
||||
@@ -256,10 +256,10 @@ public class AboutView extends LinearLayout {
|
||||
//
|
||||
Element elementAppMode;
|
||||
if (GlobalApplication.isDebuging()) {
|
||||
elementAppMode = new Element(_mContext.getString(R.string.app_normal), R.drawable.ic_winboll);
|
||||
elementAppMode = new Element(mContext.getString(R.string.app_normal), R.drawable.ic_winboll);
|
||||
elementAppMode.setOnClickListener(mAppNormalOnClickListener);
|
||||
} else {
|
||||
elementAppMode = new Element(_mContext.getString(R.string.app_debug), R.drawable.ic_winboll);
|
||||
elementAppMode = new Element(mContext.getString(R.string.app_debug), R.drawable.ic_winboll);
|
||||
elementAppMode.setOnClickListener(mAppDebugOnClickListener);
|
||||
}
|
||||
aboutPage.addItem(elementAppMode);
|
||||
@@ -272,7 +272,7 @@ public class AboutView extends LinearLayout {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
//ToastUtils.show("mAppDebugOnClickListener");
|
||||
setApp2DebugMode(_mContext);
|
||||
setApp2DebugMode(mContext);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@ public class AboutView extends LinearLayout {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
//ToastUtils.show("mAppNormalOnClickListener");
|
||||
setApp2NormalMode(_mContext);
|
||||
setApp2NormalMode(mContext);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -290,9 +290,9 @@ public class AboutView extends LinearLayout {
|
||||
//intent.setAction(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
GlobalApplication.setIsDebuging(true);
|
||||
GlobalApplication.saveDebugStatus(_mContext);
|
||||
GlobalApplication.saveDebugStatus();
|
||||
|
||||
WinBoLLActivityManager.getInstance().finishAll();
|
||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -302,9 +302,9 @@ public class AboutView extends LinearLayout {
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
GlobalApplication.setIsDebuging(false);
|
||||
GlobalApplication.saveDebugStatus(_mContext);
|
||||
GlobalApplication.saveDebugStatus();
|
||||
|
||||
WinBoLLActivityManager.getInstance().finishAll();
|
||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@ public class AboutView extends LinearLayout {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(mszGitea));
|
||||
_mContext.startActivity(browserIntent);
|
||||
mContext.startActivity(browserIntent);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -329,8 +329,8 @@ public class AboutView extends LinearLayout {
|
||||
String credential = "";
|
||||
if (GlobalApplication.isDebuging()) {
|
||||
credential = Credentials.basic(metDevUserName.getText().toString(), metDevUserPassword.getText().toString());
|
||||
PrefUtils.saveString(_mContext, "metDevUserName", metDevUserName.getText().toString());
|
||||
PrefUtils.saveString(_mContext, "metDevUserPassword", metDevUserPassword.getText().toString());
|
||||
PrefUtils.saveString(mContext, "metDevUserName", metDevUserName.getText().toString());
|
||||
PrefUtils.saveString(mContext, "metDevUserPassword", metDevUserPassword.getText().toString());
|
||||
} else {
|
||||
String username = "WinBoLL";
|
||||
String password = "WinBoLLPowerByZhanGSKen";
|
||||
@@ -387,7 +387,7 @@ public class AboutView extends LinearLayout {
|
||||
public void onYes() {
|
||||
String szUrl = mszWinBoLLServerHost + "/studio/download.php?appname=" + mszAppAPKFolderName + "&apkname=" + mszNewestAppPackageName;
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(szUrl));
|
||||
_mContext.startActivity(browserIntent);
|
||||
mContext.startActivity(browserIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,9 +11,9 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.libaes.models.WinBoLLClientServiceBean;
|
||||
import cc.winboll.studio.libaes.beans.WinBoLLClientServiceBean;
|
||||
import cc.winboll.studio.libaes.winboll.AssistantService;
|
||||
import cc.winboll.studio.libapputils.utils.ServiceUtils;
|
||||
import cc.winboll.studio.libappbase.utils.ServiceUtils;
|
||||
|
||||
public class AssistantService extends Service {
|
||||
|
||||
@@ -71,7 +71,7 @@ public class AssistantService extends Service {
|
||||
// 唤醒和绑定主进程
|
||||
//
|
||||
void wakeupAndBindMain() {
|
||||
if (ServiceUtils.isServiceAlive(getApplicationContext(), WinBoLLClientService.class.getName()) == false) {
|
||||
if (ServiceUtils.isServiceRunning(getApplicationContext(), WinBoLLClientService.class.getName()) == false) {
|
||||
startForegroundService(new Intent(AssistantService.this, WinBoLLClientService.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 09:34
|
||||
* @Describe WinBoLL 窗口操作接口
|
||||
*/
|
||||
public abstract interface IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "IWinBoLLActivity";
|
||||
|
||||
public static final String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND";
|
||||
|
||||
public Activity getActivity();
|
||||
public String getTag();
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/25 04:29:19
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
|
||||
|
||||
public static final String TAG = "MyActivityLifecycleCallbacks";
|
||||
|
||||
public String mInfo = "";
|
||||
|
||||
public MyActivityLifecycleCallbacks() {
|
||||
|
||||
}
|
||||
|
||||
void createActivityeInfo(Activity activity) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Intent receivedIntent = activity.getIntent();
|
||||
sb.append("\nCallingActivity : \n");
|
||||
if (activity.getCallingActivity() != null) {
|
||||
sb.append(activity.getCallingActivity().getPackageName());
|
||||
}
|
||||
sb.append("\nReceived Intent Package : \n");
|
||||
sb.append(receivedIntent.getPackage());
|
||||
|
||||
Bundle extras = receivedIntent.getExtras();
|
||||
if (extras != null) {
|
||||
for (String key : extras.keySet()) {
|
||||
sb.append("\nIntentInfo");
|
||||
sb.append("\n键: ");
|
||||
sb.append(key);
|
||||
sb.append(", 值: ");
|
||||
sb.append(extras.get(key));
|
||||
//Log.d("IntentInfo", "键: " + key + ", 值: " + extras.get(key));
|
||||
}
|
||||
}
|
||||
mInfo = sb.toString();
|
||||
//Log.d("IntentInfo", "发送Intent的应用包名: " + senderPackage);
|
||||
}
|
||||
|
||||
public void showActivityeInfo() {
|
||||
//ToastUtils.show("ActivityeInfo : " + mInfo);
|
||||
LogUtils.d(TAG, "ActivityeInfo : " + mInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
// 在这里可以做一些初始化相关的操作,例如记录Activity的创建时间等
|
||||
//System.out.println(activity.getLocalClassName() + " was created");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was created");
|
||||
createActivityeInfo(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
//System.out.println(activity.getLocalClassName() + " was started");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was started");
|
||||
//createActivityeInfo(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
//System.out.println(activity.getLocalClassName() + " was resumed");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was resumed");
|
||||
//createActivityeInfo(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
//System.out.println(activity.getLocalClassName() + " was paused");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was paused");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
//System.out.println(activity.getLocalClassName() + " was stopped");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was stopped");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
// 可以在这里添加保存状态的自定义逻辑
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
//System.out.println(activity.getLocalClassName() + " was destroyed");
|
||||
LogUtils.d(TAG, activity.getLocalClassName() + " was destroyed");
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 10:13
|
||||
* @Describe WinBoLL 系列应用通用管理类
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libaes.models.WinBoLLModel;
|
||||
|
||||
public class WinBoLL {
|
||||
|
||||
public static final String TAG = "WinBoLL";
|
||||
|
||||
public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND";
|
||||
public static final String EXTRA_WINBOLLMODEL = "EXTRA_WINBOLLMODEL";
|
||||
|
||||
public static void bindToAPPBase(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBase(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
public static void bindToAPPBaseBeta(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBaseBeta(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase.beta";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
static void startBind(Context context, String toPackage, String appMainService) {
|
||||
Intent intent = new Intent(ACTION_BIND);
|
||||
intent.putExtra(EXTRA_WINBOLLMODEL, (new WinBoLLModel(toPackage, appMainService)).toString());
|
||||
intent.setPackage(toPackage);
|
||||
LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService));
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 10:02
|
||||
* @Describe 应用活动窗口管理器
|
||||
* 参考 :
|
||||
* android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label
|
||||
* https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import cc.winboll.studio.libappbase.LogActivity;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
public class WinBoLLActivityManager {
|
||||
|
||||
public static final String TAG = "WinBoLLActivityManager";
|
||||
|
||||
public static final String EXTRA_TAG = "EXTRA_TAG";
|
||||
|
||||
|
||||
public enum WinBoLLUI_TYPE { Aplication, Service }
|
||||
|
||||
GlobalApplication mGlobalApplication;
|
||||
volatile static WinBoLLActivityManager _mIWinBoLLActivityManager;
|
||||
Map<String, IWinBoLLActivity> mActivityListMap;
|
||||
|
||||
volatile static WinBoLLUI_TYPE _WinBoLLUI_TYPE = WinBoLLUI_TYPE.Service;
|
||||
public static void setWinBoLLUI_TYPE(WinBoLLUI_TYPE winBoLLUI_TYPE) {
|
||||
_WinBoLLUI_TYPE = winBoLLUI_TYPE;
|
||||
}
|
||||
|
||||
public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() {
|
||||
return _WinBoLLUI_TYPE;
|
||||
}
|
||||
|
||||
WinBoLLActivityManager(GlobalApplication application) {
|
||||
mGlobalApplication = application;
|
||||
mActivityListMap = new HashMap<String, IWinBoLLActivity>();
|
||||
}
|
||||
|
||||
public static WinBoLLActivityManager getInstance() {
|
||||
return _mIWinBoLLActivityManager;
|
||||
}
|
||||
|
||||
public static synchronized void init(GlobalApplication application) {
|
||||
if (_mIWinBoLLActivityManager == null) {
|
||||
_mIWinBoLLActivityManager = new WinBoLLActivityManager(application);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把Activity添加到管理中
|
||||
*/
|
||||
public <T extends IWinBoLLActivity> void add(T activity) {
|
||||
if (isActivityActive(activity.getTag())) {
|
||||
LogUtils.d(TAG, String.format("add(...) %s is active.", activity.getTag()));
|
||||
} else {
|
||||
mActivityListMap.put(activity.getTag(), activity);
|
||||
LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", activity.getTag(), mActivityListMap.size()));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// activity: 为 null 时,
|
||||
// intent.putExtra 函数 "tag" 参数为 tag
|
||||
// activity: 不为 null 时,
|
||||
// intent.putExtra 函数 "tag" 参数为 activity.getTag()
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Class<T> clazz) {
|
||||
// 如果窗口已存在就重启窗口
|
||||
if (!resumeActivity(clazz)) {
|
||||
// 新建一个任务窗口
|
||||
Intent intent = new Intent(context, clazz);
|
||||
//打开多任务窗口 flags
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
//intent.putExtra("tag", tag);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Intent intent, Class<T> clazz) {
|
||||
// 如果窗口已存在就重启窗口
|
||||
if (!resumeActivity(clazz)) {
|
||||
// 新建一个任务窗口
|
||||
//Intent intent = new Intent(context, clazz);
|
||||
//打开多任务窗口 flags
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
//intent.putExtra("tag", tag);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends IWinBoLLActivity> void startLogActivity(Context context) {
|
||||
// 如果窗口已存在就重启窗口
|
||||
//if (!resumeActivity(LogActivity.class)) {
|
||||
// 新建一个任务窗口
|
||||
Intent intent = new Intent(context, LogActivity.class);
|
||||
//打开多任务窗口 flags
|
||||
// Define the bounds.
|
||||
// Rect bounds = new Rect(0, 0, 800, 200);
|
||||
// // Set the bounds as an activity option.
|
||||
// ActivityOptions options = ActivityOptions.makeBasic();
|
||||
// options.setLaunchBounds(bounds);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
|
||||
//intent.putExtra(EXTRA_TAG, tag);
|
||||
|
||||
//context.startActivity(intent, options.toBundle());
|
||||
context.startActivity(intent);
|
||||
//}
|
||||
}
|
||||
|
||||
//
|
||||
// 判断 tag 绑定的 Activity 是否已经创建
|
||||
//
|
||||
public boolean isActivityActive(String tag) {
|
||||
return mActivityListMap.get(tag) != null;
|
||||
}
|
||||
|
||||
Activity getActivityByTag(String tag) {
|
||||
return (mActivityListMap.get(tag) == null) ?null: mActivityListMap.get(tag).getActivity();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
|
||||
//
|
||||
public <T extends IWinBoLLActivity> boolean resumeActivity(Class<T> clazz) {
|
||||
try {
|
||||
Activity activity = getActivityByTag(clazz.newInstance().getTag());
|
||||
if (activity != null) {
|
||||
return resumeActivity(activity);
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
|
||||
//
|
||||
public <T extends IWinBoLLActivity> boolean resumeActivity(String tag) {
|
||||
Activity activity = getActivityByTag(tag);
|
||||
if (activity != null) {
|
||||
return resumeActivity(activity);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
|
||||
//
|
||||
public <T extends IWinBoLLActivity> boolean resumeActivity(Activity activity) {
|
||||
ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
//返回启动它的根任务(home 或者 MainActivity)
|
||||
//Intent intent = new Intent(mContext, activity.getClass());
|
||||
//TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
|
||||
//stackBuilder.addNextIntentWithParentStack(intent);
|
||||
//stackBuilder.startActivities();
|
||||
am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
|
||||
//ToastUtils.show("resumeActivity");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 结束所有 Activity
|
||||
*/
|
||||
public void finishAll() {
|
||||
try {
|
||||
//ToastUtils.show(String.format("finishAll() size : %d", _mIWinBoLLActivityList.size()));
|
||||
for (int i = mActivityListMap.size() - 1; i > -1; i--) {
|
||||
IWinBoLLActivity iWinBoLLActivity = mActivityListMap.get(i);
|
||||
ToastUtils.show("finishAll() activity");
|
||||
if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) {
|
||||
//ToastUtils.show("activity != null ...");
|
||||
if (getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Service) {
|
||||
// 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。
|
||||
iWinBoLLActivity.getActivity().finishAndRemoveTask();
|
||||
//ToastUtils.show("finishAll() activity.finishAndRemoveTask();");
|
||||
} else if (getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Aplication) {
|
||||
// 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。
|
||||
iWinBoLLActivity.getActivity().finish();
|
||||
//ToastUtils.show("finishAll() activity.finish();");
|
||||
} else {
|
||||
ToastUtils.show("WinBollApplication.WinBollUI_TYPE error.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束指定Activity
|
||||
*/
|
||||
public <T extends IWinBoLLActivity> void finish(T iWinBoLLActivity) {
|
||||
try {
|
||||
if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) {
|
||||
//根据tag 移除 MyActivity
|
||||
//String tag= activity.getTag();
|
||||
//_mIWinBoLLActivityList.remove(tag);
|
||||
//ToastUtils.show("remove");
|
||||
//ToastUtils.show("_mIWinBoLLActivityArrayMap.size() " + Integer.toString(_mIWinBoLLActivityArrayMap.size()));
|
||||
|
||||
// 窗口回调规则:
|
||||
// [] 当前窗口位置 >> 调度出的窗口位置
|
||||
// ★:[0] 1 2 3 4 >> 1
|
||||
// ★:0 1 [2] 3 4 >> 1
|
||||
// ★:0 1 2 [3] 4 >> 2
|
||||
// ★:0 1 2 3 [4] >> 3
|
||||
// ★:[0] >> 直接关闭当前窗口
|
||||
Activity preActivity = getPreActivity(iWinBoLLActivity);
|
||||
iWinBoLLActivity.getActivity().finish();
|
||||
if (preActivity != null) {
|
||||
resumeActivity(preActivity);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
Activity getPreActivity(IWinBoLLActivity iWinBoLLActivity) {
|
||||
try {
|
||||
boolean bingo = false;
|
||||
IWinBoLLActivity preIWinBoLLActivity = null;
|
||||
for (Map.Entry<String, IWinBoLLActivity> entity : mActivityListMap.entrySet()) {
|
||||
if (entity.getKey().equals(iWinBoLLActivity.getTag())) {
|
||||
bingo = true;
|
||||
LogUtils.d(TAG, "bingo");
|
||||
break;
|
||||
}
|
||||
preIWinBoLLActivity = entity.getValue();
|
||||
}
|
||||
|
||||
if (bingo) {
|
||||
return preIWinBoLLActivity.getActivity();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends IWinBoLLActivity> boolean registeRemove(T iWinBoLLActivity) {
|
||||
IWinBoLLActivity iWinBoLLActivityTest = mActivityListMap.get(iWinBoLLActivity.getTag());
|
||||
if (iWinBoLLActivityTest != null) {
|
||||
mActivityListMap.remove(iWinBoLLActivity.getTag());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void printAvtivityListInfo() {
|
||||
if (!mActivityListMap.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(mActivityListMap.size()));
|
||||
Iterator<Map.Entry<String, IWinBoLLActivity>> iterator = mActivityListMap.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, IWinBoLLActivity> entry = iterator.next();
|
||||
sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag());
|
||||
//ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag());
|
||||
}
|
||||
sb.append("\nMap entries end.");
|
||||
LogUtils.d(TAG, sb.toString());
|
||||
} else {
|
||||
LogUtils.d(TAG, "The map is empty.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Fri Sep 26 05:36:14 HKT 2025
|
||||
stageCount=9
|
||||
#Mon Sep 22 02:57:06 HKT 2025
|
||||
stageCount=8
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.10
|
||||
publishVersion=15.10.8
|
||||
publishVersion=15.10.7
|
||||
buildCount=0
|
||||
baseBetaVersion=15.10.9
|
||||
baseBetaVersion=15.10.8
|
||||
|
||||
@@ -32,6 +32,8 @@ dependencies {
|
||||
|
||||
// Html 解析
|
||||
api 'org.jsoup:jsoup:1.13.1'
|
||||
|
||||
api 'com.google.code.gson:gson:2.10.1'
|
||||
|
||||
// SSH
|
||||
//api 'com.jcraft:jsch:0.1.55'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Mon Sep 01 07:56:11 HKT 2025
|
||||
stageCount=7
|
||||
#Sun Sep 21 22:28:28 GMT 2025
|
||||
stageCount=0
|
||||
libraryProject=libapputils
|
||||
baseVersion=15.8
|
||||
publishVersion=15.8.6
|
||||
buildCount=0
|
||||
baseBetaVersion=15.8.7
|
||||
baseVersion=15.9
|
||||
publishVersion=15.9.0
|
||||
buildCount=2
|
||||
baseBetaVersion=15.9.1
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package cc.winboll.studio.libapputils.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/15 20:05:03
|
||||
* @Describe AppUtils
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class AppUtils {
|
||||
|
||||
public static final String TAG = "AppUtils";
|
||||
|
||||
public static String getAppNameByPackageName(Context context, String packageName) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
|
||||
return (String) packageManager.getApplicationLabel(applicationInfo);
|
||||
} catch (NameNotFoundException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,16 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -22,7 +28,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
@@ -97,36 +102,6 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 把字符串写入文件,指定 UTF-8 编码
|
||||
//
|
||||
public static void writeStringToFile(String szFilePath, String szContent) throws IOException {
|
||||
File file = new File(szFilePath);
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
writer.write(szContent);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
//
|
||||
// 读取文件到字符串,指定 UTF-8 编码
|
||||
//
|
||||
public static String readStringFromFile(String szFilePath) throws IOException {
|
||||
File file = new File(szFilePath);
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
StringBuilder content = new StringBuilder();
|
||||
int character;
|
||||
while ((character = reader.read()) != -1) {
|
||||
content.append((char) character);
|
||||
}
|
||||
reader.close();
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
public static boolean copyFile(File srcFile, File dstFile) {
|
||||
if (!srcFile.exists()) {
|
||||
LogUtils.d(TAG, "The original file does not exist.");
|
||||
@@ -154,4 +129,113 @@ public class FileUtils {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 读取文件为字节数组(Java 7 语法)
|
||||
*/
|
||||
public static byte[] readByteArrayFromFile(String filePath) {
|
||||
FileInputStream fis = null;
|
||||
ByteArrayOutputStream bos = null;
|
||||
try {
|
||||
fis = new FileInputStream(filePath);
|
||||
bos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
bos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
return bos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
// 手动关闭流(Java 7 不支持 try-with-resources)
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入字节数组到文件(Java 7 语法)
|
||||
*/
|
||||
public static boolean writeByteArrayToFile(byte[] data, String filePath) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(filePath);
|
||||
fos.write(data);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String readStringFromFile(String filePath) {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(filePath));
|
||||
StringBuilder content = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
content.append(line).append(System.getProperty("line.separator"));
|
||||
}
|
||||
// 去除最后一个换行符(可选)
|
||||
if (content.length() > 0) {
|
||||
content.deleteCharAt(content.length() - 1);
|
||||
}
|
||||
return content.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean writeStringToFile(String content, String filePath, boolean append) {
|
||||
BufferedWriter writer = null;
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(filePath, append));
|
||||
writer.write(content);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
package cc.winboll.studio.libapputils.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/04 13:36
|
||||
* @Describe RSA加密工具
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.util.Base64;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Objects;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class RSAUtils {
|
||||
private static final String TAG = "RSAUtils";
|
||||
private static final int KEY_SIZE = 2048;
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
private static final String PUBLIC_KEY_FILE = "public.key";
|
||||
private static final String PRIVATE_KEY_FILE = "private.key";
|
||||
private static final String CIPHER_ALGORITHM = KEY_ALGORITHM + "/ECB/PKCS1Padding"; // 保留原加密方式
|
||||
|
||||
private final String keyPath;
|
||||
private static volatile RSAUtils INSTANCE;
|
||||
|
||||
/**
|
||||
* 构造方法:初始化密钥存储路径(内部存储)
|
||||
*/
|
||||
private RSAUtils(Context context) {
|
||||
keyPath = context.getFilesDir() + File.separator + "keys" + File.separator; // 修正路径格式
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static synchronized RSAUtils getInstance(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new RSAUtils(context);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密钥文件是否存在
|
||||
*/
|
||||
public boolean keysExist() {
|
||||
File publicKeyFile = new File(keyPath + PUBLIC_KEY_FILE);
|
||||
File privateKeyFile = new File(keyPath + PRIVATE_KEY_FILE);
|
||||
return publicKeyFile.exists() && privateKeyFile.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥对并保存到文件
|
||||
*/
|
||||
public void generateAndSaveKeys() throws Exception {
|
||||
LogUtils.d(TAG, "开始生成 RSA 密钥对(2048位)");
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
||||
generator.initialize(KEY_SIZE);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
|
||||
saveKey(PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded());
|
||||
saveKey(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded());
|
||||
LogUtils.d(TAG, "密钥对生成并保存成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或生成密钥对(线程安全)
|
||||
*/
|
||||
public KeyPair getOrGenerateKeys() throws Exception {
|
||||
if (!keysExist()) {
|
||||
synchronized (RSAUtils.class) { // 双重检查锁,避免多线程重复生成
|
||||
if (!keysExist()) {
|
||||
generateAndSaveKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
return readKeysFromFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件读取密钥对
|
||||
*/
|
||||
private KeyPair readKeysFromFile() throws Exception {
|
||||
LogUtils.d(TAG, "读取密钥对文件");
|
||||
try {
|
||||
byte[] publicKeyBytes = readFileToBytes(keyPath + PUBLIC_KEY_FILE);
|
||||
byte[] privateKeyBytes = readFileToBytes(keyPath + PRIVATE_KEY_FILE);
|
||||
|
||||
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
|
||||
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
|
||||
|
||||
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
PublicKey publicKey = factory.generatePublic(publicSpec);
|
||||
PrivateKey privateKey = factory.generatePrivate(privateSpec);
|
||||
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
LogUtils.e(TAG, "密钥文件读取失败:" + e.getMessage());
|
||||
throw new Exception("密钥文件损坏或格式错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存密钥到文件(通用方法)
|
||||
*/
|
||||
private void saveKey(String fileName, byte[] keyBytes) throws IOException {
|
||||
Objects.requireNonNull(keyBytes, "密钥字节数据不可为空");
|
||||
File dir = new File(keyPath);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("创建密钥目录失败:" + keyPath);
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyPath + fileName);
|
||||
fos.write(keyBytes);
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数组(Java 7 兼容)
|
||||
*/
|
||||
private byte[] readFileToBytes(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
throw new IOException("文件不存在或为目录:" + filePath);
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
byte[] data = new byte[(int) file.length()];
|
||||
int bytesRead = fis.read(data);
|
||||
if (bytesRead != data.length) {
|
||||
throw new IOException("文件读取不完整");
|
||||
}
|
||||
return data;
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密(带参数校验)
|
||||
*/
|
||||
public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception {
|
||||
Objects.requireNonNull(plainText, "明文不可为空");
|
||||
Objects.requireNonNull(publicKey, "公钥不可为空");
|
||||
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
|
||||
// 检查数据长度是否超过 RSA 限制(2048位密钥最大明文为 214字节,PKCS1Padding)
|
||||
int maxPlainTextSize = cipher.getBlockSize() - 11; // PKCS1Padding 固定填充长度
|
||||
if (plainText.getBytes("UTF-8").length > maxPlainTextSize) {
|
||||
throw new IllegalArgumentException("明文过长,最大支持 " + maxPlainTextSize + " 字节");
|
||||
}
|
||||
|
||||
return cipher.doFinal(plainText.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密(带参数校验)
|
||||
*/
|
||||
public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception {
|
||||
Objects.requireNonNull(encryptedData, "密文不可为空");
|
||||
Objects.requireNonNull(privateKey, "私钥不可为空");
|
||||
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedData);
|
||||
return new String(decryptedBytes, "UTF-8");
|
||||
}
|
||||
/**
|
||||
* 将 HTTP 传输的 Base64 字符串还原为加密字节数组(Java 7 兼容)
|
||||
* @param httpString Base64 字符串(非 null)
|
||||
* @return 加密字节数组
|
||||
* @throws IllegalArgumentException 解码失败时抛出
|
||||
*/
|
||||
public byte[] httpStringToEncryptBytes(String httpString) {
|
||||
Objects.requireNonNull(httpString, "HTTP 字符串不可为空");
|
||||
|
||||
// 计算缺失的填充符数量(Java 7 不支持 repeat(),手动拼接)
|
||||
int pad = httpString.length() % 4;
|
||||
StringBuilder paddedString = new StringBuilder(httpString);
|
||||
if (pad != 0) {
|
||||
for (int i = 0; i < pad; i++) {
|
||||
paddedString.append('='); // 补全 '='
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 Base64 解码(Android 原生 Base64 类兼容 Java 7)
|
||||
return Base64.decode(paddedString.toString(), Base64.URL_SAFE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
package cc.winboll.studio.libapputils.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/04 17:21
|
||||
* @Describe 应用登录与接口工具
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.models.ResponseData;
|
||||
import cc.winboll.studio.libappbase.models.UserInfoModel;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class YunUtils {
|
||||
public static final String TAG = "YunUtils";
|
||||
// 私有静态实例,类加载时创建
|
||||
private static volatile YunUtils INSTANCE;
|
||||
Context mContext;
|
||||
UserInfoModel mUserInfoModel;
|
||||
String token = "";
|
||||
String mDataFolderPath = "";
|
||||
String mUserInfoModelPath = "";
|
||||
|
||||
private static final int CONNECT_TIMEOUT = 15; // 连接超时时间(秒)
|
||||
private static final int READ_TIMEOUT = 20; // 读取超时时间(秒)
|
||||
private static volatile YunUtils instance;
|
||||
private OkHttpClient okHttpClient;
|
||||
private Handler mainHandler; // 主线程 Handler
|
||||
|
||||
// 私有构造方法,防止外部实例化
|
||||
private YunUtils(Context context) {
|
||||
LogUtils.d(TAG, "YunUtils");
|
||||
mContext = context;
|
||||
mDataFolderPath = mContext.getExternalFilesDir(TAG).toString();
|
||||
File fTest = new File(mDataFolderPath);
|
||||
if (!fTest.exists()) {
|
||||
fTest.mkdirs();
|
||||
}
|
||||
mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson";
|
||||
|
||||
okHttpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
.build();
|
||||
mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper
|
||||
}
|
||||
|
||||
// 公共静态方法,返回唯一实例
|
||||
public static synchronized YunUtils getInstance(Context context) {
|
||||
LogUtils.d(TAG, "getInstance");
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new YunUtils(context);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void checkLoginStatus() {
|
||||
String token = getLocalToken();
|
||||
LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token));
|
||||
}
|
||||
|
||||
String getLocalToken() {
|
||||
UserInfoModel userInfoModel = loadUserInfoModel();
|
||||
return (userInfoModel == null) ?"": userInfoModel.getToken();
|
||||
}
|
||||
|
||||
public void login(String host, UserInfoModel userInfoModel) {
|
||||
LogUtils.d(TAG, "login");
|
||||
|
||||
// 发送 POST 请求
|
||||
String apiUrl = host + "/login/index.php";
|
||||
// 序列化对象为JSON
|
||||
Gson gson = new Gson();
|
||||
String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON
|
||||
//String jsonData = userInfoModel.toString();
|
||||
LogUtils.d(TAG, "要发送的数据 : " + jsonData);
|
||||
|
||||
sendPostRequest(apiUrl, jsonData, new OnResponseListener() {
|
||||
// 成功回调(主线程)
|
||||
@Override
|
||||
public void onSuccess(String responseBody) {
|
||||
LogUtils.d(TAG, "onSuccess");
|
||||
LogUtils.d(TAG, String.format("responseBody %s", responseBody));
|
||||
Gson gson = new Gson();
|
||||
ResponseData result = gson.fromJson(responseBody, ResponseData.class); // 转为 Result 实例
|
||||
if(result.getStatus().equals(ResponseData.STATUS_SUCCESS)) {
|
||||
|
||||
UserInfoModel userInfoModel = result.getData();
|
||||
if (userInfoModel != null) {
|
||||
LogUtils.d(TAG, "收到网站 UserInfoModel");
|
||||
String token = userInfoModel.getToken();
|
||||
saveLocalToken(token);
|
||||
checkLoginStatus();
|
||||
}
|
||||
|
||||
} else if(result.getStatus().equals(ResponseData.STATUS_ERROR)) {
|
||||
try {
|
||||
String decodedMessage = URLDecoder.decode(result.getMessage(), "UTF-8");
|
||||
LogUtils.d(TAG, "服务器返回信息: " + decodedMessage);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 失败回调(主线程)
|
||||
@Override
|
||||
public void onFailure(String errorMsg) {
|
||||
LogUtils.d(TAG, errorMsg);
|
||||
// 处理错误
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void saveLocalToken(String token) {
|
||||
UserInfoModel userInfoModel = new UserInfoModel();
|
||||
userInfoModel.setToken(token);
|
||||
saveUserInfoModel(userInfoModel);
|
||||
}
|
||||
|
||||
UserInfoModel loadUserInfoModel() {
|
||||
LogUtils.d(TAG, "loadUserInfoModel");
|
||||
if (new File(mUserInfoModelPath).exists()) {
|
||||
try {
|
||||
// 加载加密后的模型数据
|
||||
byte[] encryptedData = FileUtils.readByteArrayFromFile(mUserInfoModelPath);
|
||||
// 加载 RSA 工具
|
||||
RSAUtils utils = RSAUtils.getInstance(mContext);
|
||||
KeyPair keyPair = utils.getOrGenerateKeys();
|
||||
//PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
// 私钥解密模型数据
|
||||
String szInfo = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
|
||||
LogUtils.d(TAG, String.format("szInfo %s", szInfo));
|
||||
mUserInfoModel = UserInfoModel.parseStringToBean(szInfo, UserInfoModel.class);
|
||||
if (mUserInfoModel == null) {
|
||||
LogUtils.d(TAG, "模型数据解析为空数据。");
|
||||
}
|
||||
LogUtils.d(TAG, "UserInfoModel 解密加载结束。");
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
} else {
|
||||
LogUtils.d(TAG, "云服务登录信息不存在。");
|
||||
mUserInfoModel = null;
|
||||
}
|
||||
return mUserInfoModel;
|
||||
}
|
||||
|
||||
void saveUserInfoModel(UserInfoModel userInfoModel) {
|
||||
LogUtils.d(TAG, "saveUserInfoModel");
|
||||
try {
|
||||
String szInfo = userInfoModel.toString();
|
||||
LogUtils.d(TAG, "原始数据: " + szInfo);
|
||||
|
||||
RSAUtils utils = RSAUtils.getInstance(mContext);
|
||||
KeyPair keyPair = utils.getOrGenerateKeys();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
// 公钥加密(传入字节数组,避免中间字符串转换)
|
||||
byte[] encryptedData = utils.encryptWithPublicKey(szInfo, publicKey);
|
||||
|
||||
// 保存加密字节数组到文件(直接操作字节,无需转字符串)
|
||||
FileUtils.writeByteArrayToFile(encryptedData, mUserInfoModelPath);
|
||||
LogUtils.d(TAG, "加密数据已保存");
|
||||
|
||||
// 测试解密(仅调试用)
|
||||
String szInfo2 = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
|
||||
LogUtils.d(TAG, "解密结果: " + szInfo2);
|
||||
|
||||
mUserInfoModel = UserInfoModel.parseStringToBean(szInfo2, UserInfoModel.class);
|
||||
if (mUserInfoModel == null) {
|
||||
LogUtils.d(TAG, "模型解析失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, "加密/解密失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 发送 POST 请求(JSON 数据)
|
||||
public void sendPostRequest(String url, String data, OnResponseListener listener) {
|
||||
RequestBody requestBody = RequestBody.create(
|
||||
MediaType.parse("application/json; charset=utf-8"), // 关键头信息
|
||||
data.getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.addHeader("Content-Type", "application/json") // 显式添加头
|
||||
.build();
|
||||
|
||||
executeRequest(request, listener);
|
||||
}
|
||||
|
||||
// 发送 GET 请求
|
||||
public void sendGetRequest(String url, OnResponseListener listener) {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build();
|
||||
executeRequest(request, listener);
|
||||
}
|
||||
|
||||
// 执行请求(子线程处理)
|
||||
private void executeRequest(final Request request, final OnResponseListener listener) {
|
||||
okHttpClient.newCall(request).enqueue(new Callback() {
|
||||
// 响应成功(子线程)
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
try {
|
||||
if (!response.isSuccessful()) {
|
||||
postFailure(listener, "响应码错误:" + response.code());
|
||||
return;
|
||||
}
|
||||
String responseBody = response.body().string();
|
||||
postSuccess(listener, responseBody);
|
||||
} catch (Exception e) {
|
||||
postFailure(listener, "解析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 响应失败(子线程)
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
postFailure(listener, "网络失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
// 主线程回调(使用 Handler)
|
||||
private void postSuccess(final OnResponseListener listener, final String msg) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onSuccess(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void postFailure(final OnResponseListener listener, final String msg) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onFailure(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnResponseListener {
|
||||
/**
|
||||
* 成功响应(主线程回调)
|
||||
* @param responseBody 响应体字符串
|
||||
*/
|
||||
void onSuccess(String responseBody);
|
||||
|
||||
/**
|
||||
* 失败回调(包含错误信息)
|
||||
* @param errorMsg 错误描述
|
||||
*/
|
||||
void onFailure(String errorMsg);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user