diff --git a/.gitignore b/.gitignore index 88afb93..5dea002 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ local.properties ## 忽略模块应用编译配置 /settings.gradle +/gradle.properties ## 忽略 srv 纠结问题 /srv/ diff --git a/androiddemo/.gitignore b/androiddemo/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/androiddemo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/androiddemo/app_update_description.txt b/androiddemo/app_update_description.txt new file mode 100644 index 0000000..e69de29 diff --git a/androiddemo/build.gradle b/androiddemo/build.gradle new file mode 100644 index 0000000..8357611 --- /dev/null +++ b/androiddemo/build.gradle @@ -0,0 +1,74 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + productFlavors { + beta { + } + stage { + } + } + + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.androiddemo" + minSdkVersion 26 + targetSdkVersion 29 + versionCode 1 + // versionName 更新后需要手动设置 + // .winboll/winbollBuildProps.properties 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "1.0" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + + // 吐司类库 + implementation 'com.github.getActivity:ToastUtils:10.5' + + // Android 类库 + // https://mvnrepository.com/artifact/com.android.support/support-v4 + implementation 'com.android.support:support-v4:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-compat + implementation 'com.android.support:support-compat:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-media-compat + implementation 'com.android.support:support-media-compat:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-core-utils + implementation 'com.android.support:support-core-utils:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-core-ui + implementation 'com.android.support:support-core-ui:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-fragment + implementation 'com.android.support:support-fragment:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7 + implementation 'com.android.support:recyclerview-v7:28.0.0' +} diff --git a/androiddemo/build.properties b/androiddemo/build.properties new file mode 100644 index 0000000..4d98e2f --- /dev/null +++ b/androiddemo/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Tue Mar 11 18:02:14 GMT 2025 +stageCount=0 +libraryProject= +baseVersion=1.0 +publishVersion=1.0.0 +buildCount=1 +baseBetaVersion=1.0.1 diff --git a/androiddemo/proguard-rules.pro b/androiddemo/proguard-rules.pro new file mode 100644 index 0000000..64b4a05 --- /dev/null +++ b/androiddemo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/androiddemo/src/beta/AndroidManifest.xml b/androiddemo/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/androiddemo/src/beta/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/androiddemo/src/beta/res/values/strings.xml b/androiddemo/src/beta/res/values/strings.xml new file mode 100644 index 0000000..e0c92e9 --- /dev/null +++ b/androiddemo/src/beta/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + + Android Demo + + + diff --git a/androiddemo/src/main/AndroidManifest.xml b/androiddemo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..43eac9a --- /dev/null +++ b/androiddemo/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java new file mode 100644 index 0000000..e13af9a --- /dev/null +++ b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java @@ -0,0 +1,334 @@ +package cc.winboll.studio.androiddemo; + +import android.app.Activity; +import android.app.Application; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.Thread.UncaughtExceptionHandler; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class GlobalApplication extends Application { + + private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); + + @Override + public void onCreate() { + super.onCreate(); + CrashHandler.getInstance().registerGlobal(this); + CrashHandler.getInstance().registerPart(this); + } + + public static void write(InputStream input, OutputStream output) throws IOException { + byte[] buf = new byte[1024 * 8]; + int len; + while ((len = input.read(buf)) != -1) { + output.write(buf, 0, len); + } + } + + public static void write(File file, byte[] data) throws IOException { + File parent = file.getParentFile(); + if (parent != null && !parent.exists()) parent.mkdirs(); + + ByteArrayInputStream input = new ByteArrayInputStream(data); + FileOutputStream output = new FileOutputStream(file); + try { + write(input, output); + } finally { + closeIO(input, output); + } + } + + public static String toString(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + write(input, output); + try { + return output.toString("UTF-8"); + } finally { + closeIO(input, output); + } + } + + public static void closeIO(Closeable... closeables) { + for (Closeable closeable : closeables) { + try { + if (closeable != null) closeable.close(); + } catch (IOException ignored) {} + } + } + + public static class CrashHandler { + + public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler(); + + private static CrashHandler sInstance; + + private PartCrashHandler mPartCrashHandler; + + public static CrashHandler getInstance() { + if (sInstance == null) { + sInstance = new CrashHandler(); + } + return sInstance; + } + + public void registerGlobal(Context context) { + registerGlobal(context, null); + } + + public void registerGlobal(Context context, String crashDir) { + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir)); + } + + public void unregister() { + Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER); + } + + public void registerPart(Context context) { + unregisterPart(context); + mPartCrashHandler = new PartCrashHandler(context.getApplicationContext()); + MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler); + } + + public void unregisterPart(Context context) { + if (mPartCrashHandler != null) { + mPartCrashHandler.isRunning.set(false); + mPartCrashHandler = null; + } + } + + private static class PartCrashHandler implements Runnable { + + private final Context mContext; + + public AtomicBoolean isRunning = new AtomicBoolean(true); + + public PartCrashHandler(Context context) { + this.mContext = context; + } + + @Override + public void run() { + while (isRunning.get()) { + try { + Looper.loop(); + } catch (final Throwable e) { + e.printStackTrace(); + if (isRunning.get()) { + MAIN_HANDLER.post(new Runnable(){ + + @Override + public void run() { + Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show(); + } + }); + } else { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else { + throw new RuntimeException(e); + } + } + } + } + } + } + + private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler { + + private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss"); + + private final Context mContext; + + private final File mCrashDir; + + public UncaughtExceptionHandlerImpl(Context context, String crashDir) { + this.mContext = context; + this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + try { + + String log = buildLog(throwable); + writeLog(log); + + try { + Intent intent = new Intent(mContext, CrashActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_TEXT, log); + mContext.startActivity(intent); + } catch (Throwable e) { + e.printStackTrace(); + writeLog(e.toString()); + } + + throwable.printStackTrace(); + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(0); + + } catch (Throwable e) { + if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable); + } + } + + private String buildLog(Throwable throwable) { + String time = DATE_FORMAT.format(new Date()); + + String versionName = "unknown"; + long versionCode = 0; + try { + PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); + versionName = packageInfo.versionName; + versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode; + } catch (Throwable ignored) {} + + LinkedHashMap head = new LinkedHashMap(); + head.put("Time Of Crash", time); + head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL)); + head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT)); + head.put("App Version", String.format("%s (%d)", versionName, versionCode)); + head.put("Kernel", getKernel()); + head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown"); + head.put("Fingerprint", Build.FINGERPRINT); + + StringBuilder builder = new StringBuilder(); + + for (String key : head.keySet()) { + if (builder.length() != 0) builder.append("\n"); + builder.append(key); + builder.append(" : "); + builder.append(head.get(key)); + } + + builder.append("\n\n"); + builder.append(Log.getStackTraceString(throwable)); + + return builder.toString(); + } + + private void writeLog(String log) { + String time = DATE_FORMAT.format(new Date()); + File file = new File(mCrashDir, "crash_" + time + ".txt"); + try { + write(file, log.getBytes("UTF-8")); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private static String getKernel() { + try { + return GlobalApplication.toString(new FileInputStream("/proc/version")).trim(); + } catch (Throwable e) { + return e.getMessage(); + } + } + } + } + + public static final class CrashActivity extends Activity { + + private String mLog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setTheme(android.R.style.Theme_DeviceDefault); + setTitle("App Crash"); + + mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT); + + ScrollView contentView = new ScrollView(this); + contentView.setFillViewport(true); + + HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this); + + TextView textView = new TextView(this); + int padding = dp2px(16); + textView.setPadding(padding, padding, padding, padding); + textView.setText(mLog); + textView.setTextIsSelectable(true); + textView.setTypeface(Typeface.DEFAULT); + textView.setLinksClickable(true); + + horizontalScrollView.addView(textView); + contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + + setContentView(contentView); + } + + private void restart() { + Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + finish(); + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(0); + } + + private static int dp2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, android.R.id.copy, 0, android.R.string.copy) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.copy: + ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog)); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + restart(); + } + } +} \ No newline at end of file diff --git a/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java new file mode 100644 index 0000000..dcef375 --- /dev/null +++ b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java @@ -0,0 +1,15 @@ +package cc.winboll.studio.androiddemo; + +import android.app.Activity; +import android.os.Bundle; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + } + +} \ No newline at end of file diff --git a/androiddemo/src/main/res/drawable/ic_launcher.png b/androiddemo/src/main/res/drawable/ic_launcher.png new file mode 100644 index 0000000..b824ebd Binary files /dev/null and b/androiddemo/src/main/res/drawable/ic_launcher.png differ diff --git a/androiddemo/src/main/res/layout/activity_main.xml b/androiddemo/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f8b9a89 --- /dev/null +++ b/androiddemo/src/main/res/layout/activity_main.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/androiddemo/src/main/res/values-v21/styles.xml b/androiddemo/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..0aed032 --- /dev/null +++ b/androiddemo/src/main/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/androiddemo/src/main/res/values/colors.xml b/androiddemo/src/main/res/values/colors.xml new file mode 100644 index 0000000..294809a --- /dev/null +++ b/androiddemo/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #009688 + #00796B + #FF9800 + \ No newline at end of file diff --git a/androiddemo/src/main/res/values/strings.xml b/androiddemo/src/main/res/values/strings.xml new file mode 100644 index 0000000..21acbbd --- /dev/null +++ b/androiddemo/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Android Demo + diff --git a/androiddemo/src/main/res/values/styles.xml b/androiddemo/src/main/res/values/styles.xml new file mode 100644 index 0000000..6799c28 --- /dev/null +++ b/androiddemo/src/main/res/values/styles.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/androiddemo/src/stage/AndroidManifest.xml b/androiddemo/src/stage/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/androiddemo/src/stage/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/androiddemo/src/stage/res/values/strings.xml b/androiddemo/src/stage/res/values/strings.xml new file mode 100644 index 0000000..ace0c41 --- /dev/null +++ b/androiddemo/src/stage/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/androidxdemo/.gitignore b/androidxdemo/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/androidxdemo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/androidxdemo/app_update_description.txt b/androidxdemo/app_update_description.txt new file mode 100644 index 0000000..e69de29 diff --git a/androidxdemo/build.gradle b/androidxdemo/build.gradle new file mode 100644 index 0000000..fcf214f --- /dev/null +++ b/androidxdemo/build.gradle @@ -0,0 +1,76 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + productFlavors { + beta { + } + stage { + } + } + + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.androidxdemo" + minSdkVersion 26 + targetSdkVersion 29 + versionCode 1 + // versionName 更新后需要手动设置 + // .winboll/winbollBuildProps.properties 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "1.0" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + + // SSH + implementation 'com.jcraft:jsch:0.1.55' + // Html 解析 + implementation 'org.jsoup:jsoup:1.13.1' + // 二维码类库 + implementation 'com.google.zxing:core:3.4.1' + implementation 'com.journeyapps:zxing-android-embedded:3.6.0' + // 应用介绍页类库 + implementation 'io.github.medyo:android-about-page:2.0.0' + // 吐司类库 + implementation 'com.github.getActivity:ToastUtils:10.5' + // 网络连接类库 + implementation 'com.squareup.okhttp3:okhttp:4.4.1' + // Android 类库 + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.viewpager:viewpager:1.0.0' + implementation 'androidx.vectordrawable:vectordrawable:1.1.0' + implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' + implementation 'androidx.fragment:fragment:1.1.0' + implementation 'com.google.android.material:material:1.4.0' +} diff --git a/androidxdemo/build.properties b/androidxdemo/build.properties new file mode 100644 index 0000000..8d115d9 --- /dev/null +++ b/androidxdemo/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Tue Mar 11 18:25:43 GMT 2025 +stageCount=0 +libraryProject= +baseVersion=1.0 +publishVersion=1.0.0 +buildCount=4 +baseBetaVersion=1.0.1 diff --git a/androidxdemo/proguard-rules.pro b/androidxdemo/proguard-rules.pro new file mode 100644 index 0000000..64b4a05 --- /dev/null +++ b/androidxdemo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/androidxdemo/src/beta/AndroidManifest.xml b/androidxdemo/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/androidxdemo/src/beta/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/androidxdemo/src/beta/res/values/strings.xml b/androidxdemo/src/beta/res/values/strings.xml new file mode 100644 index 0000000..bf50bcf --- /dev/null +++ b/androidxdemo/src/beta/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + AndroidX Demo + + + diff --git a/androidxdemo/src/main/AndroidManifest.xml b/androidxdemo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6d0c707 --- /dev/null +++ b/androidxdemo/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java new file mode 100644 index 0000000..7abaf88 --- /dev/null +++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java @@ -0,0 +1,334 @@ +package cc.winboll.studio.androidxdemo; + +import android.app.Activity; +import android.app.Application; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.Thread.UncaughtExceptionHandler; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class GlobalApplication extends Application { + + private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); + + @Override + public void onCreate() { + super.onCreate(); + CrashHandler.getInstance().registerGlobal(this); + CrashHandler.getInstance().registerPart(this); + } + + public static void write(InputStream input, OutputStream output) throws IOException { + byte[] buf = new byte[1024 * 8]; + int len; + while ((len = input.read(buf)) != -1) { + output.write(buf, 0, len); + } + } + + public static void write(File file, byte[] data) throws IOException { + File parent = file.getParentFile(); + if (parent != null && !parent.exists()) parent.mkdirs(); + + ByteArrayInputStream input = new ByteArrayInputStream(data); + FileOutputStream output = new FileOutputStream(file); + try { + write(input, output); + } finally { + closeIO(input, output); + } + } + + public static String toString(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + write(input, output); + try { + return output.toString("UTF-8"); + } finally { + closeIO(input, output); + } + } + + public static void closeIO(Closeable... closeables) { + for (Closeable closeable : closeables) { + try { + if (closeable != null) closeable.close(); + } catch (IOException ignored) {} + } + } + + public static class CrashHandler { + + public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler(); + + private static CrashHandler sInstance; + + private PartCrashHandler mPartCrashHandler; + + public static CrashHandler getInstance() { + if (sInstance == null) { + sInstance = new CrashHandler(); + } + return sInstance; + } + + public void registerGlobal(Context context) { + registerGlobal(context, null); + } + + public void registerGlobal(Context context, String crashDir) { + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir)); + } + + public void unregister() { + Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER); + } + + public void registerPart(Context context) { + unregisterPart(context); + mPartCrashHandler = new PartCrashHandler(context.getApplicationContext()); + MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler); + } + + public void unregisterPart(Context context) { + if (mPartCrashHandler != null) { + mPartCrashHandler.isRunning.set(false); + mPartCrashHandler = null; + } + } + + private static class PartCrashHandler implements Runnable { + + private final Context mContext; + + public AtomicBoolean isRunning = new AtomicBoolean(true); + + public PartCrashHandler(Context context) { + this.mContext = context; + } + + @Override + public void run() { + while (isRunning.get()) { + try { + Looper.loop(); + } catch (final Throwable e) { + e.printStackTrace(); + if (isRunning.get()) { + MAIN_HANDLER.post(new Runnable(){ + + @Override + public void run() { + Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show(); + } + }); + } else { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else { + throw new RuntimeException(e); + } + } + } + } + } + } + + private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler { + + private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss"); + + private final Context mContext; + + private final File mCrashDir; + + public UncaughtExceptionHandlerImpl(Context context, String crashDir) { + this.mContext = context; + this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + try { + + String log = buildLog(throwable); + writeLog(log); + + try { + Intent intent = new Intent(mContext, CrashActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_TEXT, log); + mContext.startActivity(intent); + } catch (Throwable e) { + e.printStackTrace(); + writeLog(e.toString()); + } + + throwable.printStackTrace(); + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(0); + + } catch (Throwable e) { + if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable); + } + } + + private String buildLog(Throwable throwable) { + String time = DATE_FORMAT.format(new Date()); + + String versionName = "unknown"; + long versionCode = 0; + try { + PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); + versionName = packageInfo.versionName; + versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode; + } catch (Throwable ignored) {} + + LinkedHashMap head = new LinkedHashMap(); + head.put("Time Of Crash", time); + head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL)); + head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT)); + head.put("App Version", String.format("%s (%d)", versionName, versionCode)); + head.put("Kernel", getKernel()); + head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown"); + head.put("Fingerprint", Build.FINGERPRINT); + + StringBuilder builder = new StringBuilder(); + + for (String key : head.keySet()) { + if (builder.length() != 0) builder.append("\n"); + builder.append(key); + builder.append(" : "); + builder.append(head.get(key)); + } + + builder.append("\n\n"); + builder.append(Log.getStackTraceString(throwable)); + + return builder.toString(); + } + + private void writeLog(String log) { + String time = DATE_FORMAT.format(new Date()); + File file = new File(mCrashDir, "crash_" + time + ".txt"); + try { + write(file, log.getBytes("UTF-8")); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private static String getKernel() { + try { + return GlobalApplication.toString(new FileInputStream("/proc/version")).trim(); + } catch (Throwable e) { + return e.getMessage(); + } + } + } + } + + public static final class CrashActivity extends Activity { + + private String mLog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setTheme(android.R.style.Theme_DeviceDefault); + setTitle("App Crash"); + + mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT); + + ScrollView contentView = new ScrollView(this); + contentView.setFillViewport(true); + + HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this); + + TextView textView = new TextView(this); + int padding = dp2px(16); + textView.setPadding(padding, padding, padding, padding); + textView.setText(mLog); + textView.setTextIsSelectable(true); + textView.setTypeface(Typeface.DEFAULT); + textView.setLinksClickable(true); + + horizontalScrollView.addView(textView); + contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + + setContentView(contentView); + } + + private void restart() { + Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + finish(); + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(0); + } + + private static int dp2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, android.R.id.copy, 0, android.R.string.copy) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.copy: + ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog)); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + restart(); + } + } +} \ No newline at end of file diff --git a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java new file mode 100644 index 0000000..de5c892 --- /dev/null +++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java @@ -0,0 +1,19 @@ +package cc.winboll.studio.androidxdemo; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + } + +} \ No newline at end of file diff --git a/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/androidxdemo/src/main/res/drawable/ic_launcher_background.xml b/androidxdemo/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/androidxdemo/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/androidxdemo/src/main/res/layout/activity_main.xml b/androidxdemo/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..034ba40 --- /dev/null +++ b/androidxdemo/src/main/res/layout/activity_main.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a2f5908 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..1b52399 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ff10afd Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..115a4c7 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..dcd3cd8 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..459ca60 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca12fe Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8e19b41 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b824ebd Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4c19a13 Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/androidxdemo/src/main/res/values/colors.xml b/androidxdemo/src/main/res/values/colors.xml new file mode 100644 index 0000000..479769a --- /dev/null +++ b/androidxdemo/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #009688 + #00796B + #FF9800 + \ No newline at end of file diff --git a/androidxdemo/src/main/res/values/strings.xml b/androidxdemo/src/main/res/values/strings.xml new file mode 100644 index 0000000..6aea12c --- /dev/null +++ b/androidxdemo/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + AndroidX Demo + + diff --git a/androidxdemo/src/main/res/values/styles.xml b/androidxdemo/src/main/res/values/styles.xml new file mode 100644 index 0000000..0eb88fe --- /dev/null +++ b/androidxdemo/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/androidxdemo/src/stage/AndroidManifest.xml b/androidxdemo/src/stage/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/androidxdemo/src/stage/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/androidxdemo/src/stage/res/values/strings.xml b/androidxdemo/src/stage/res/values/strings.xml new file mode 100644 index 0000000..ace0c41 --- /dev/null +++ b/androidxdemo/src/stage/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/appbase/build.gradle b/appbase/build.gradle index cf07e1d..1a1874e 100644 --- a/appbase/build.gradle +++ b/appbase/build.gradle @@ -29,7 +29,7 @@ android { // versionName 更新后需要手动设置 // .winboll/winbollBuildProps.properties 文件的 stageCount=0 // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" - versionName "2.1" + versionName "15.0" if(true) { versionName = genVersionName("${versionName}") } @@ -46,25 +46,4 @@ android { dependencies { api project(':libappbase') api fileTree(dir: 'libs', include: ['*.jar']) - - // SSH - implementation 'com.jcraft:jsch:0.1.55' - // Html 解析 - implementation 'org.jsoup:jsoup:1.13.1' - // 二维码类库 - implementation 'com.google.zxing:core:3.4.1' - implementation 'com.journeyapps:zxing-android-embedded:3.6.0' - // 应用介绍页类库 - implementation 'io.github.medyo:android-about-page:2.0.0' - // 吐司类库 - implementation 'com.github.getActivity:ToastUtils:10.5' - // 网络连接类库 - implementation 'com.squareup.okhttp3:okhttp:4.4.1' - // Android 类库 - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.viewpager:viewpager:1.0.0' - implementation 'androidx.vectordrawable:vectordrawable:1.1.0' - implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' - implementation 'androidx.fragment:fragment:1.1.0' - implementation 'com.google.android.material:material:1.4.0' } diff --git a/appbase/build.properties b/appbase/build.properties index 2fdff2f..0545eaa 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Mar 09 09:15:39 HKT 2025 +#Tue Mar 18 10:15:08 HKT 2025 stageCount=6 libraryProject=libappbase -baseVersion=2.1 -publishVersion=2.1.5 +baseVersion=15.0 +publishVersion=15.0.5 buildCount=0 -baseBetaVersion=2.1.6 +baseBetaVersion=15.0.6 diff --git a/appbase/src/main/AndroidManifest.xml b/appbase/src/main/AndroidManifest.xml index 4fdc630..ad99884 100644 --- a/appbase/src/main/AndroidManifest.xml +++ b/appbase/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ android:name=".App" android:icon="@drawable/ic_launcher" android:label="@string/app_name" - android:theme="@style/AppTheme" + android:theme="@style/MyAPPBaseTheme" android:resizeableActivity="true"> - @@ -179,16 +179,16 @@ - - + + diff --git a/appbase/src/main/res/values/colors.xml b/appbase/src/main/res/values/colors.xml index 18d6383..87d3836 100644 --- a/appbase/src/main/res/values/colors.xml +++ b/appbase/src/main/res/values/colors.xml @@ -1,6 +1,7 @@ - #005800FF - #005800FF - #005800FF + #FF00B322 + #FF005C12 + #FF8DFFA2 + #FFFFFB8D diff --git a/appbase/src/main/res/values/styles.xml b/appbase/src/main/res/values/styles.xml index ec1f403..d8e7e1b 100644 --- a/appbase/src/main/res/values/styles.xml +++ b/appbase/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ - diff --git a/gradle.properties-android-demo b/gradle.properties-android-demo new file mode 100644 index 0000000..7d87c1c --- /dev/null +++ b/gradle.properties-android-demo @@ -0,0 +1,19 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=false +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=false diff --git a/gradle.properties b/gradle.properties-androidx-demo similarity index 88% rename from gradle.properties rename to gradle.properties-androidx-demo index 71ba985..2f26404 100644 --- a/gradle.properties +++ b/gradle.properties-androidx-demo @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m +org.gradle.jvmargs=-Xmx2048m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -17,10 +17,3 @@ org.gradle.jvmargs=-Xmx4096m android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true - -org.gradle.caching=true - -android.disableAutomaticComponentCreation=true - -android.injected.testOnly=false - diff --git a/libappbase/build.gradle b/libappbase/build.gradle index 2ced735..aa9ecad 100644 --- a/libappbase/build.gradle +++ b/libappbase/build.gradle @@ -21,26 +21,4 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) - - // SSH - implementation 'com.jcraft:jsch:0.1.55' - // Html 解析 - implementation 'org.jsoup:jsoup:1.13.1' - // 二维码类库 - implementation 'com.google.zxing:core:3.4.1' - implementation 'com.journeyapps:zxing-android-embedded:3.6.0' - // 应用介绍页类库 - implementation 'io.github.medyo:android-about-page:2.0.0' - // 吐司类库 - implementation 'com.github.getActivity:ToastUtils:10.5' - // 网络连接类库 - implementation 'com.squareup.okhttp3:okhttp:4.4.1' - - // Android 类库 - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.viewpager:viewpager:1.0.0' - implementation 'androidx.vectordrawable:vectordrawable:1.1.0' - implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' - implementation 'androidx.fragment:fragment:1.1.0' - implementation 'com.google.android.material:material:1.4.0' } diff --git a/libappbase/build.properties b/libappbase/build.properties index 95f4ce3..429b1bd 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Mar 09 09:15:24 HKT 2025 +#Tue Mar 18 10:14:53 HKT 2025 stageCount=6 libraryProject=libappbase -baseVersion=2.1 -publishVersion=2.1.5 +baseVersion=15.0 +publishVersion=15.0.5 buildCount=0 -baseBetaVersion=2.1.6 +baseBetaVersion=15.0.6 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java index 76a2f84..a5d77d1 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java @@ -6,17 +6,13 @@ package cc.winboll.studio.libappbase; * @Describe 全局应用类 */ import android.app.Application; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; -import android.view.Gravity; -import com.hjq.toast.ToastUtils; -import com.hjq.toast.style.WhiteToastStyle; +import cc.winboll.studio.libappbase.utils.ToastUtils; public class GlobalApplication extends Application { @@ -78,8 +74,8 @@ public class GlobalApplication extends Application { ToastUtils.init(this); // 设置 Toast 布局样式 //ToastUtils.setView(R.layout.toast_custom_view); - ToastUtils.setStyle(new WhiteToastStyle()); - ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); + //ToastUtils.setStyle(new WhiteToastStyle()); + //ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); } public static String getAppName(Context context) { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java index 626149e..8163acf 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java @@ -21,11 +21,9 @@ import android.view.MenuItem; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; -import androidx.appcompat.widget.Toolbar; import cc.winboll.studio.libappbase.R; -import androidx.appcompat.app.AppCompatActivity; -public final class GlobalCrashActivity extends AppCompatActivity implements MenuItem.OnMenuItemClickListener { +public final class GlobalCrashActivity extends Activity implements MenuItem.OnMenuItemClickListener { private static final int MENUITEM_COPY = 0; private static final int MENUITEM_RESTART = 1; @@ -47,10 +45,10 @@ public final class GlobalCrashActivity extends AppCompatActivity implements Menu setContentView(R.layout.activity_globalcrash); mGlobalCrashReportView = findViewById(R.id.activityglobalcrashGlobalCrashReportView1); mGlobalCrashReportView.setReport(mLog); - setSupportActionBar(mGlobalCrashReportView.getToolbar()); + setActionBar(mGlobalCrashReportView.getToolbar()); - getSupportActionBar().setTitle(CrashHandler.TITTLE); - getSupportActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext())); + getActionBar().setTitle(CrashHandler.TITTLE); + getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext())); } @Override diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java index e72dd74..efdfb3a 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java @@ -15,8 +15,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.appcompat.widget.Toolbar; import cc.winboll.studio.libappbase.R; +import android.widget.Toolbar; public class GlobalCrashReportView extends LinearLayout { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java index ec71977..9b8721d 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java @@ -16,16 +16,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.RelativeLayout; import android.widget.ScrollView; import android.widget.Spinner; import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.views.HorizontalListView; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; @@ -51,7 +50,7 @@ public class LogView extends RelativeLayout { Spinner mLogLevelSpinner; ArrayAdapter mLogLevelSpinnerAdapter; // 标签列表 - RecyclerView recyclerView; + HorizontalListView mListViewTags; public LogView(Context context) { super(context); @@ -190,11 +189,10 @@ public class LogView extends RelativeLayout { cbALLTAG.setChecked(isAllSelect); // 加载标签表 - recyclerView = findViewById(cc.winboll.studio.libappbase.R.id.viewlogRecyclerView1); - LinearLayoutManager layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false); - recyclerView.setLayoutManager(layoutManager); - mTAGListAdapter = new TAGListAdapter(mapTAGList); - recyclerView.setAdapter(mTAGListAdapter); + mListViewTags = findViewById(cc.winboll.studio.libappbase.R.id.tags_listview); + mListViewTags.setVerticalOffset(10); + mTAGListAdapter = new TAGListAdapter(mContext, mapTAGList); + mListViewTags.setAdapter(mTAGListAdapter); // 可以添加点击监听器来处理勾选框状态变化后的逻辑,比如获取当前勾选情况等 mTAGListAdapter.notifyDataSetChanged(); @@ -305,15 +303,32 @@ public class LogView extends RelativeLayout { } - public class TAGListAdapter extends RecyclerView.Adapter { + public class TAGListAdapter extends BaseAdapter { + private Context context; private Map mapOrigin; private List itemList; - public TAGListAdapter(Map map) { + public TAGListAdapter(Context context, Map map) { + this.context = context; mapOrigin = map; loadMap(mapOrigin); } + + @Override + public int getCount() { + return itemList.size(); + } + + @Override + public Object getItem(int p) { + return itemList.get(p); + } + + @Override + public long getItemId(int p) { + return p; + } void loadMap(Map map) { itemList = new ArrayList(); @@ -329,16 +344,20 @@ public class LogView extends RelativeLayout { loadMap(mapOrigin); super.notifyDataSetChanged(); } - - @NonNull + @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_logtag, parent, false); - return new ViewHolder(view); - } + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.item_logtag, parent, false); + holder = new ViewHolder(); + holder.tvText = convertView.findViewById(R.id.viewlogtagTextView1); + holder.cbChecked = convertView.findViewById(R.id.viewlogtagCheckBox1); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final TAGItemModel item = itemList.get(position); holder.tvText.setText(item.getTag()); holder.cbChecked.setChecked(item.isChecked()); @@ -349,22 +368,13 @@ public class LogView extends RelativeLayout { LogUtils.setTAGListEnable(item.getTag(), ((CheckBox)v).isChecked()); } }); + + return convertView; } - @Override - public int getItemCount() { - return itemList.size(); - } - - public class ViewHolder extends RecyclerView.ViewHolder { + public class ViewHolder { TextView tvText; CheckBox cbChecked; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - tvText = itemView.findViewById(R.id.viewlogtagTextView1); - cbChecked = itemView.findViewById(R.id.viewlogtagCheckBox1); - } } } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java new file mode 100644 index 0000000..476a20a --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.libappbase.utils; +import android.content.Context; +import android.widget.Toast; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/12 12:02:31 + */ +public class ToastUtils { + + public static final String TAG = "ToastUtils"; + + volatile static ToastUtils _ToastUtils; + Context mContext; + + ToastUtils() { + } + + synchronized static ToastUtils getInstance() { + if (_ToastUtils == null) { + _ToastUtils = new ToastUtils(); + } + return _ToastUtils; + } + + public static void init(Context context) { + getInstance().mContext = context; + } + + public static void show(String message) { + Toast.makeText(getInstance().mContext, message, Toast.LENGTH_SHORT).show(); + } +} diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java new file mode 100644 index 0000000..782d8fa --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java @@ -0,0 +1,65 @@ +package cc.winboll.studio.libappbase.views; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/12 12:29:01 + * @Describe 水平布局的 ListView + */ +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ListView; +import cc.winboll.studio.libappbase.LogUtils; + +public class HorizontalListView extends ListView { + + public static final String TAG = "HorizontalListView"; + int verticalOffset = 0; + + public HorizontalListView(Context context) { + super(context); + } + + public HorizontalListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public HorizontalListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setVerticalOffset(int verticalOffset) { + this.verticalOffset = verticalOffset; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + int childCount = getChildCount(); + int left = getPaddingLeft(); + int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); + LogUtils.d(TAG, String.format("HorizontalListView的高度 %d", viewHeight)); + + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + // 计算每个子视图的宽度和高度 + int width = child.getMeasuredWidth(); + int height = child.getMeasuredHeight(); + //LogUtils.d(TAG, String.format("child : width %d , height %d", width, height)); + // 设置子视图的位置,实现水平布局 + + child.layout(left, verticalOffset, left + width, verticalOffset + height); + left += width; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + //super.onMeasure(widthMeasureSpec, newHeightMeasureSpec); + int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + //LogUtils.d(TAG, String.format("newWidthMeasureSpec %d, newHeightMeasureSpec %d", newWidthMeasureSpec, newHeightMeasureSpec)); + super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); + } +} + diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java index 389b346..0219949 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java @@ -14,9 +14,9 @@ import android.widget.RemoteViews; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.R; import cc.winboll.studio.libappbase.utils.ServiceUtils; -import com.hjq.toast.ToastUtils; import android.content.ServiceConnection; import android.os.IBinder; +import cc.winboll.studio.libappbase.utils.ToastUtils; public class StatusWidget extends AppWidgetProvider { @@ -35,6 +35,7 @@ public class StatusWidget extends AppWidgetProvider { public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (intent.getAction().equals(ACTION_STATUS_UPDATE)) { + ToastUtils.show("Test"); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, StatusWidget.class)); for (int appWidgetId : appWidgetIds) { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java index afbf9cf..19ceed3 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java @@ -9,7 +9,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import cc.winboll.studio.libappbase.LogUtils; -import com.hjq.toast.ToastUtils; +import cc.winboll.studio.libappbase.utils.ToastUtils; public class StatusWidgetClickListener extends BroadcastReceiver { diff --git a/libappbase/src/main/res/drawable/view_border.xml b/libappbase/src/main/res/drawable/bg_border.xml similarity index 100% rename from libappbase/src/main/res/drawable/view_border.xml rename to libappbase/src/main/res/drawable/bg_border.xml diff --git a/libappbase/src/main/res/drawable/bg_border_round.xml b/libappbase/src/main/res/drawable/bg_border_round.xml new file mode 100644 index 0000000..2036d51 --- /dev/null +++ b/libappbase/src/main/res/drawable/bg_border_round.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/libappbase/src/main/res/drawable/bg_shadow.xml b/libappbase/src/main/res/drawable/bg_shadow.xml index 6d3d898..f4d1bd7 100644 --- a/libappbase/src/main/res/drawable/bg_shadow.xml +++ b/libappbase/src/main/res/drawable/bg_shadow.xml @@ -10,8 +10,8 @@ + android:endColor="@color/colorAccent" + android:startColor="@color/colorAccent" /> + + + + + + + + diff --git a/libappbase/src/main/res/layout/view_globalcrashreport.xml b/libappbase/src/main/res/layout/view_globalcrashreport.xml index 405d0b4..1aff5e5 100644 --- a/libappbase/src/main/res/layout/view_globalcrashreport.xml +++ b/libappbase/src/main/res/layout/view_globalcrashreport.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent" android:id="@+id/viewglobalcrashreportLinearLayout1"> - diff --git a/libappbase/src/main/res/layout/view_log.xml b/libappbase/src/main/res/layout/view_log.xml index 1b98af1..fdf3f3d 100644 --- a/libappbase/src/main/res/layout/view_log.xml +++ b/libappbase/src/main/res/layout/view_log.xml @@ -1,121 +1,130 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#FF000000"> - + -