This commit is contained in:
2024-03-28 10:39:39 +08:00
commit 66d4a00ba8
58 changed files with 3499 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
/.idea/

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

194
app/build.gradle Normal file
View File

@@ -0,0 +1,194 @@
apply plugin: 'com.android.application'
static def appName() {
return "AndroidAppTemplate"
}
static def releaseTime() {
return new Date().format("yyyyMMdd_HHmmss", TimeZone.getDefault())
}
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.ttstd.template"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk {
//选择要添加的对应 cpu 类型的 .so 库。
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', "x86"
// 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
}
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
dexOptions {
jumboMode true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding{
enabled = true
}
dataBinding {
enabled true
}
}
//签名
signingConfigs {
tuixin {// 签名文件
storeFile file("keystore/tuixin.jks")
storePassword "123456"
keyAlias "universal"
keyPassword "123456"
v2SigningEnabled false
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
buildConfigField "String", "platform", '"tuixin"'
versionNameSuffix "-debug"
debuggable true
//Zipalign优化
zipAlignEnabled true
minifyEnabled false
signingConfig signingConfigs.tuixin
applicationVariants.all { variant ->
variant.outputs.each { output ->
if (outputFile != null) {
def fileName = "${appName()}-${variant.versionCode}-V${variant.versionName}-${releaseTime()}-${buildType.name}.apk"
output.outputFileName = fileName
}
}
}
}
release {
buildConfigField "String", "platform", '"tuixin"'
//Zipalign优化
zipAlignEnabled true
//混淆
minifyEnabled false
//前一部分代表系统默认的android程序的混淆文件该文件已经包含了基本的混淆声明后一个文件是自己的定义混淆文件
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.tuixin
// 将release版本的包名重命名加上版本及日期
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = ""
if (outputFile != null) {
def fileName = "${appName()}-${variant.versionCode}-V${variant.versionName}-${releaseTime()}-${buildType.name}.apk"
output.outputFileName = new File(outputFile, fileName)
}
}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
//BindView
implementation 'com.jakewharton:butterknife:10.2.3'
// If you are using Kotlin, replace annotationProcessor with kapt.
// annotationProcessor rootProject.ext.dependencies["butterknife-compiler"]
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
//okhttp
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
// gson converter
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 标准转换器,去掉 Retrofit以Mutipart上传参数时String参数会多一对双引号
implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
//RxJava
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
//生命周期管理
implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2'
//rxbinding
implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
//Google
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.zxing:core:3.5.0'
implementation 'com.google.android.material:material:1.4.0'
//图片加载框架
implementation 'com.github.bumptech.glide:glide:4.13.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
//磁盘缓存
implementation 'com.jakewharton:disklrucache:2.0.2'
//Aria
implementation 'com.arialyy.aria:core:3.8.15'
annotationProcessor 'com.arialyy.aria:compiler:3.8.15'
//MMKV
implementation 'com.tencent:mmkv-static:1.2.14'
//bugly
implementation 'com.tencent.bugly:crashreport:4.1.9.2'
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
//工具类
implementation 'com.blankj:utilcodex:1.31.0'
//autosize会改变第三方view的大小
//https://github.com/JessYanCoding/AndroidAutoSize
implementation 'com.github.JessYanCoding:AndroidAutoSize:1.2.1'
implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar'
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
//沉浸状态栏
implementation 'com.gitee.zackratos:UltimateBarX:0.8.0'
// //验证码输入
// implementation 'com.jacktuotuo.customview:verificationcodeview:1.0.5'
//动态权限框架
// implementation 'com.hjq:xxpermissions:6.0'
}

BIN
app/keystore/tuixin.jks Normal file

Binary file not shown.

21
app/proguard-rules.pro vendored Normal file
View File

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

View File

@@ -0,0 +1,27 @@
package com.ttstd.template;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.ttstd.template", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ttstd.template">
<!-- 访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.ttstd.template.base.BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.ttstd.template.activity.main.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<!-- 这个 Demo 主要展示副单位的用法, 如果只使用副单位 (pt、in、mm) 就可以直接以像素作为单位填写设计图的尺寸, 不需再把像素转化为 dp-->
<meta-data
android:name="design_width_in_dp"
android:value="1080"/>
<meta-data
android:name="design_height_in_dp"
android:value="1920"/>
</application>
</manifest>

View File

@@ -0,0 +1,25 @@
package com.ttstd.template.activity.main;
import com.ttstd.template.R;
import com.ttstd.template.base.BaseActivity;
public class MainActivity extends BaseActivity {
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void initView() {
}
@Override
public void initData() {
}
}

View File

@@ -0,0 +1,46 @@
package com.ttstd.template.activity.template;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.ttstd.template.R;
import com.ttstd.template.base.BaseLightActivity;
import butterknife.ButterKnife;
public class TemplateActivity extends BaseLightActivity implements TemplateContact.TemplateView {
private static final String TAG = TemplateActivity.class.getSimpleName();
private TemplatePresenter mPresenter;
@Override
public int getLayoutId() {
return R.layout.activity_video;
}
@Override
public void initView() {
ButterKnife.bind(this);
mPresenter = new TemplatePresenter(this);
mPresenter.setLifecycle(lifecycleSubject);
mPresenter.attachView(this);
}
@Override
public void initData() {
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Configuration config = getResources().getConfiguration();
}
}

View File

@@ -0,0 +1,14 @@
package com.ttstd.template.activity.template;
import com.ttstd.template.base.BasePresenter;
import com.ttstd.template.base.BaseView;
public class TemplateContact {
interface Presenter extends BasePresenter<TemplateView> {
}
public interface TemplateView extends BaseView {
}
}

View File

@@ -0,0 +1,44 @@
package com.ttstd.template.activity.template;
import android.content.Context;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.template.disklrucache.CacheHelper;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public class TemplatePresenter implements TemplateContact.Presenter {
private static final String TAG = TemplatePresenter.class.getSimpleName();
private TemplateContact.TemplateView mView;
private Context mContext;
private CacheHelper mCacheHelper;
public TemplatePresenter(Context context) {
this.mContext = context;
this.mCacheHelper = new CacheHelper(context);
}
private BehaviorSubject<ActivityEvent> lifecycle;
public void setLifecycle(BehaviorSubject<ActivityEvent> lifecycle) {
this.lifecycle = lifecycle;
}
public BehaviorSubject<ActivityEvent> getLifecycle() {
return lifecycle;
}
@Override
public void attachView(TemplateContact.TemplateView view) {
this.mView = view;
}
@Override
public void detachView() {
this.mView = null;
}
}

View File

@@ -0,0 +1,134 @@
package com.ttstd.template.base;
import android.app.ActivityManager;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.ContentView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import com.ttstd.template.R;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
public final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
setContentView(this.getLayoutId());
initView();
initData();
//最近任务和应用图标不一样
if (Build.VERSION.SDK_INT > 27) {
ActivityManager.TaskDescription description = new ActivityManager.TaskDescription(getString(R.string.app_name), R.mipmap.ic_launcher, getColor(R.color.colorPrimary));
this.setTaskDescription(description);
}
}
/**
* 设置布局
*/
public abstract int getLayoutId();
/**
* 初始化视图
*/
public abstract void initView();
/**
* 初始化数据
*/
public abstract void initData();
public BaseActivity() {
super();
}
@ContentView
public BaseActivity(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}

View File

@@ -0,0 +1,78 @@
package com.ttstd.template.base;
import android.app.Application;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.tencent.mmkv.MMKV;
import com.ttstd.template.BuildConfig;
import com.ttstd.template.network.NetInterfaceManager;
import com.ttstd.template.utils.SystemUtils;
public class BaseApplication extends Application {
private static final String TAG = BaseApplication.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
//非主进程不初始化
if (SystemUtils.isMainProcessName(this, android.os.Process.myPid())) {
}
init();
}
private void init() {
if (!BuildConfig.DEBUG) {
catchException();
}
String rootDir = MMKV.initialize(this);
Log.i(TAG, "mmkv root: " + rootDir);
// CrashReport.initCrashReport(getApplicationContext(), "df75262f6a", false);
// CrashReport.setDeviceId(this, Utils.getSerial());
xcrash.XCrash.init(this);
NetInterfaceManager.init(this);
}
private void catchException() {
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
"在:" + e.getStackTrace()[0].getClassName());
}
}
);
//下面是新增方法!
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
while (true) {
try {
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
} catch (Exception e) {
Log.e("捕获异常主线程:", Thread.currentThread().getName() +
"在:" + e.getStackTrace()[0].getClassName());
e.printStackTrace();
}
}
}
});
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
}

View File

@@ -0,0 +1,156 @@
package com.ttstd.template.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
public final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
prepareFetchData();
}
public abstract void fetchData();
public boolean prepareFetchData() {
return prepareFetchData(false);
}
public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
//注释掉保证每次都更新数据
isDataInitiated = true;
return true;
}
return false;
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,129 @@
package com.ttstd.template.base;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.ContentView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import com.ttstd.template.R;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseLightActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
public final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BaseLightActivity() {
super();
}
@ContentView
public BaseLightActivity(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
setContentView(this.getLayoutId());
initView();
initData();
}
/**
* 设置布局
*/
public abstract int getLayoutId();
/**
* 初始化视图
*/
public abstract void initView();
/**
* 初始化数据
*/
public abstract void initData();
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}

View File

@@ -0,0 +1,7 @@
package com.ttstd.template.base;
public interface BasePresenter<V extends BaseView> {
void attachView(V view);
void detachView();
}

View File

@@ -0,0 +1,4 @@
package com.ttstd.template.base;
public interface BaseView {
}

View File

@@ -0,0 +1,24 @@
package com.ttstd.template.bean;
import androidx.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import java.io.Serializable;
public class BaseResponse<T> implements Serializable {
private static final long serialVersionUID = 5468533687801294972L;
public int code;
public String msg;
public T data;
@NonNull
@Override
public String toString() {
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
}
}

View File

@@ -0,0 +1,5 @@
package com.ttstd.template.comm;
public class CommonConfig {
public static final String MMKV_ID = "InterProcessKV";
}

View File

@@ -0,0 +1,474 @@
package com.ttstd.template.disklrucache;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;
import com.jakewharton.disklrucache.DiskLruCache;
import com.tencent.mmkv.MMKV;
import com.ttstd.template.comm.CommonConfig;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
/**
* 磁盘缓存帮助类
*/
public class CacheHelper {
private static final String TAG = "DiskLruCacheHelper";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private static final String DIR_NAME = "diskCache";
private static final int MAX_COUNT = 1024 * 1024 * 1024;
private static final int DEFAULT_APP_VERSION = 1;
private DiskLruCache mDiskLruCache;
public CacheHelper(Context context) {
mDiskLruCache = generateCache(context, DIR_NAME, MAX_COUNT);
}
public CacheHelper(Context context, String dirName) {
mDiskLruCache = generateCache(context, dirName, MAX_COUNT);
}
public CacheHelper(Context context, String dirName, int maxCount) {
mDiskLruCache = generateCache(context, dirName, maxCount);
}
//custom cache dir
public CacheHelper(File dir) {
mDiskLruCache = generateCache(null, dir, MAX_COUNT);
}
public CacheHelper(Context context, File dir) {
mDiskLruCache = generateCache(context, dir, MAX_COUNT);
}
public CacheHelper(Context context, File dir, int maxCount) {
mDiskLruCache = generateCache(context, dir, maxCount);
}
private DiskLruCache generateCache(Context context, File dir, int maxCount) {
if (!dir.exists() || !dir.isDirectory()) {
throw new IllegalArgumentException(
dir + " is not a directory or does not exists. ");
}
int appVersion = context == null ? DEFAULT_APP_VERSION : Utils.getAppVersion(context);
DiskLruCache diskLruCache = null;
try {
diskLruCache = DiskLruCache.open(
dir,
appVersion,
DEFAULT_APP_VERSION,
maxCount);
} catch (IOException e) {
e.printStackTrace();
}
return diskLruCache;
}
private DiskLruCache generateCache(Context context, String dirName, int maxCount) {
DiskLruCache diskLruCache = null;
try {
diskLruCache = DiskLruCache.open(
getDiskCacheDir(context, dirName),
Utils.getAppVersion(context),
DEFAULT_APP_VERSION,
maxCount);
} catch (IOException e) {
e.printStackTrace();
}
return diskLruCache;
}
// =======================================
// ============== String 数据 读写 =============
// =======================================
public void put(String key, String value) {
Log.e(TAG, "put: key = " + key + " value = " + value);
mMMKV.encode(key + "_time", System.currentTimeMillis());
mMMKV.encode(key + "_mmkv", value);
DiskLruCache.Editor edit = null;
BufferedWriter bw = null;
try {
edit = editor(key);
if (edit == null) {
return;
}
OutputStream os = edit.newOutputStream(0);
bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write(value);
edit.commit();//write CLEAN
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "put: " + e.getMessage());
try {
//s
edit.abort();//write REMOVE
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "put: " + e1.getMessage());
}
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "put: " + e.getMessage());
}
}
}
public String getAsString(String key) {
Log.e(TAG, "getAsString: " + key);
InputStream inputStream = null;
try {
//write READ
inputStream = get(key);
if (inputStream == null) {
return mMMKV.decodeString(key + "_mmkv", null);
}
StringBuilder sb = new StringBuilder();
int len = 0;
byte[] buf = new byte[128];
while ((len = inputStream.read(buf)) != -1) {
sb.append(new String(buf, 0, len));
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "getAsString: " + e.getMessage());
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "getAsString: " + e1.getMessage());
}
}
}
return null;
}
public void put(String key, JSONObject jsonObject) {
put(key, jsonObject.toString());
}
public JSONObject getAsJson(String key) {
String val = getAsString(key);
try {
if (val != null) {
return new JSONObject(val);
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
// =======================================
// ============ JSONArray 数据 读写 =============
// =======================================
public void put(String key, JSONArray jsonArray) {
put(key, jsonArray.toString());
}
public JSONArray getAsJSONArray(String key) {
String JSONString = getAsString(key);
try {
JSONArray obj = new JSONArray(JSONString);
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============== byte 数据 读写 =============
// =======================================
/**
* 保存 byte数据 到 缓存中
*
* @param key 保存的key
* @param value 保存的数据
*/
public void put(String key, byte[] value) {
OutputStream out = null;
DiskLruCache.Editor editor = null;
try {
editor = editor(key);
if (editor == null) {
return;
}
out = editor.newOutputStream(0);
out.write(value);
out.flush();
editor.commit();//write CLEAN
} catch (Exception e) {
e.printStackTrace();
try {
editor.abort();//write REMOVE
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public byte[] getAsBytes(String key) {
byte[] res = null;
InputStream is = get(key);
if (is == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
byte[] buf = new byte[256];
int len = 0;
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
res = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
// =======================================
// ============== 序列化 数据 读写 =============
// =======================================
public void put(String key, Serializable value) {
DiskLruCache.Editor editor = editor(key);
ObjectOutputStream oos = null;
if (editor == null) {
return;
}
try {
OutputStream os = editor.newOutputStream(0);
oos = new ObjectOutputStream(os);
oos.writeObject(value);
oos.flush();
editor.commit();
} catch (IOException e) {
e.printStackTrace();
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public <T> T getAsSerializable(String key) {
T t = null;
InputStream is = get(key);
ObjectInputStream ois = null;
if (is == null) {
return null;
}
try {
ois = new ObjectInputStream(is);
t = (T) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return t;
}
// =======================================
// ============== bitmap 数据 读写 =============
// =======================================
public void put(String key, Bitmap bitmap) {
put(key, Utils.bitmap2Bytes(bitmap));
}
public Bitmap getAsBitmap(String key) {
byte[] bytes = getAsBytes(key);
if (bytes == null) {
return null;
}
return Utils.bytes2Bitmap(bytes);
}
// =======================================
// ============= drawable 数据 读写 =============
// =======================================
public void put(String key, Drawable value) {
put(key, Utils.drawable2Bitmap(value));
}
public Drawable getAsDrawable(String key) {
byte[] bytes = getAsBytes(key);
if (bytes == null) {
return null;
}
return Utils.bitmap2Drawable(Utils.bytes2Bitmap(bytes));
}
// =======================================
// ============= other methods =============
// =======================================
public boolean remove(String key) {
try {
key = Utils.hashKeyForDisk(key);
return mDiskLruCache.remove(key);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public void close() throws IOException {
mDiskLruCache.close();
}
public void delete() throws IOException {
mDiskLruCache.delete();
mMMKV.clearAll();
}
public void flush() throws IOException {
mDiskLruCache.flush();
}
public boolean isClosed() {
return mDiskLruCache.isClosed();
}
public long size() {
return mDiskLruCache.size();
}
public void setMaxSize(long maxSize) {
mDiskLruCache.setMaxSize(maxSize);
}
public File getDirectory() {
return mDiskLruCache.getDirectory();
}
public long getMaxSize() {
return mDiskLruCache.getMaxSize();
}
// =======================================
// ===遇到文件比较大的,可以直接通过流读写 =====
// =======================================
//basic editor
public DiskLruCache.Editor editor(String key) {
try {
key = Utils.hashKeyForDisk(key);
//wirte DIRTY
DiskLruCache.Editor edit = mDiskLruCache.edit(key);
//edit maybe null :the entry is editing
if (edit == null) {
Log.w(TAG, "the entry spcified key:" + key + " is editing by other . ");
}
return edit;
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "editor: " + e.getMessage());
}
return null;
}
//basic get
public InputStream get(String key) {
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(Utils.hashKeyForDisk(key));
if (snapshot == null) //not find entry , or entry.readable = false
{
Log.e(TAG, "not find entry , or entry.readable = false");
return null;
}
//write READ
return snapshot.getInputStream(0);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============== 序列化 数据 读写 =============
// =======================================
private File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
if (context.getExternalCacheDir() != null) {
cachePath = context.getExternalCacheDir().getPath();
} else if (context.getExternalFilesDir("cache") != null) {
cachePath = context.getExternalFilesDir("cache").getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
}

View File

@@ -0,0 +1,427 @@
package com.ttstd.template.disklrucache;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;
import com.jakewharton.disklrucache.DiskLruCache;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
/**
* 磁盘缓存帮助类
*/
public class DiskLruCacheHelper {
private static final String DIR_NAME = "diskCache";
private static final int MAX_COUNT = 5 * 1024 * 1024;
private static final int DEFAULT_APP_VERSION = 1;
private static final String TAG = "DiskLruCacheHelper";
private DiskLruCache mDiskLruCache;
public DiskLruCacheHelper(Context context) throws IOException {
mDiskLruCache = generateCache(context, DIR_NAME, MAX_COUNT);
}
public DiskLruCacheHelper(Context context, String dirName) throws IOException {
mDiskLruCache = generateCache(context, dirName, MAX_COUNT);
}
public DiskLruCacheHelper(Context context, String dirName, int maxCount) throws IOException {
mDiskLruCache = generateCache(context, dirName, maxCount);
}
//custom cache dir
public DiskLruCacheHelper(File dir) throws IOException {
mDiskLruCache = generateCache(null, dir, MAX_COUNT);
}
public DiskLruCacheHelper(Context context, File dir) throws IOException {
mDiskLruCache = generateCache(context, dir, MAX_COUNT);
}
public DiskLruCacheHelper(Context context, File dir, int maxCount) throws IOException {
mDiskLruCache = generateCache(context, dir, maxCount);
}
private DiskLruCache generateCache(Context context, File dir, int maxCount) throws IOException {
if (!dir.exists() || !dir.isDirectory()) {
throw new IllegalArgumentException(
dir + " is not a directory or does not exists. ");
}
int appVersion = context == null ? DEFAULT_APP_VERSION : Utils.getAppVersion(context);
DiskLruCache diskLruCache = DiskLruCache.open(
dir,
appVersion,
DEFAULT_APP_VERSION,
maxCount);
return diskLruCache;
}
private DiskLruCache generateCache(Context context, String dirName, int maxCount) throws IOException {
DiskLruCache diskLruCache = DiskLruCache.open(
getDiskCacheDir(context, dirName),
Utils.getAppVersion(context),
DEFAULT_APP_VERSION,
maxCount);
return diskLruCache;
}
// =======================================
// ============== String 数据 读写 =============
// =======================================
public void put(String key, String value) {
DiskLruCache.Editor edit = null;
BufferedWriter bw = null;
try {
edit = editor(key);
if (edit == null) return;
OutputStream os = edit.newOutputStream(0);
bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write(value);
edit.commit();//write CLEAN
} catch (IOException e) {
e.printStackTrace();
try {
//s
edit.abort();//write REMOVE
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String getAsString(String key) {
InputStream inputStream = null;
try {
//write READ
inputStream = get(key);
if (inputStream == null) return null;
StringBuilder sb = new StringBuilder();
int len = 0;
byte[] buf = new byte[128];
while ((len = inputStream.read(buf)) != -1) {
sb.append(new String(buf, 0, len));
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
if (inputStream != null)
try {
inputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return null;
}
public void put(String key, JSONObject jsonObject) {
put(key, jsonObject.toString());
}
public JSONObject getAsJson(String key) {
String val = getAsString(key);
try {
if (val != null)
return new JSONObject(val);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
// =======================================
// ============ JSONArray 数据 读写 =============
// =======================================
public void put(String key, JSONArray jsonArray) {
put(key, jsonArray.toString());
}
public JSONArray getAsJSONArray(String key) {
String JSONString = getAsString(key);
try {
JSONArray obj = new JSONArray(JSONString);
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============== byte 数据 读写 =============
// =======================================
/**
* 保存 byte数据 到 缓存中
*
* @param key 保存的key
* @param value 保存的数据
*/
public void put(String key, byte[] value) {
OutputStream out = null;
DiskLruCache.Editor editor = null;
try {
editor = editor(key);
if (editor == null) {
return;
}
out = editor.newOutputStream(0);
out.write(value);
out.flush();
editor.commit();//write CLEAN
} catch (Exception e) {
e.printStackTrace();
try {
editor.abort();//write REMOVE
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public byte[] getAsBytes(String key) {
byte[] res = null;
InputStream is = get(key);
if (is == null) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
byte[] buf = new byte[256];
int len = 0;
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
res = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
// =======================================
// ============== 序列化 数据 读写 =============
// =======================================
public void put(String key, Serializable value) {
DiskLruCache.Editor editor = editor(key);
ObjectOutputStream oos = null;
if (editor == null) return;
try {
OutputStream os = editor.newOutputStream(0);
oos = new ObjectOutputStream(os);
oos.writeObject(value);
oos.flush();
editor.commit();
} catch (IOException e) {
e.printStackTrace();
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
try {
if (oos != null)
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public <T> T getAsSerializable(String key) {
T t = null;
InputStream is = get(key);
ObjectInputStream ois = null;
if (is == null) return null;
try {
ois = new ObjectInputStream(is);
t = (T) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return t;
}
// =======================================
// ============== bitmap 数据 读写 =============
// =======================================
public void put(String key, Bitmap bitmap) {
put(key, Utils.bitmap2Bytes(bitmap));
}
public Bitmap getAsBitmap(String key) {
byte[] bytes = getAsBytes(key);
if (bytes == null) return null;
return Utils.bytes2Bitmap(bytes);
}
// =======================================
// ============= drawable 数据 读写 =============
// =======================================
public void put(String key, Drawable value) {
put(key, Utils.drawable2Bitmap(value));
}
public Drawable getAsDrawable(String key) {
byte[] bytes = getAsBytes(key);
if (bytes == null) {
return null;
}
return Utils.bitmap2Drawable(Utils.bytes2Bitmap(bytes));
}
// =======================================
// ============= other methods =============
// =======================================
public boolean remove(String key) {
try {
key = Utils.hashKeyForDisk(key);
return mDiskLruCache.remove(key);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public void close() throws IOException {
mDiskLruCache.close();
}
public void delete() throws IOException {
mDiskLruCache.delete();
}
public void flush() throws IOException {
mDiskLruCache.flush();
}
public boolean isClosed() {
return mDiskLruCache.isClosed();
}
public long size() {
return mDiskLruCache.size();
}
public void setMaxSize(long maxSize) {
mDiskLruCache.setMaxSize(maxSize);
}
public File getDirectory() {
return mDiskLruCache.getDirectory();
}
public long getMaxSize() {
return mDiskLruCache.getMaxSize();
}
// =======================================
// ===遇到文件比较大的,可以直接通过流读写 =====
// =======================================
//basic editor
public DiskLruCache.Editor editor(String key) {
try {
key = Utils.hashKeyForDisk(key);
//wirte DIRTY
DiskLruCache.Editor edit = mDiskLruCache.edit(key);
//edit maybe null :the entry is editing
if (edit == null) {
Log.w(TAG, "the entry spcified key:" + key + " is editing by other . ");
}
return edit;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//basic get
public InputStream get(String key) {
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(Utils.hashKeyForDisk(key));
if (snapshot == null) //not find entry , or entry.readable = false
{
Log.e(TAG, "not find entry , or entry.readable = false");
return null;
}
//write READ
return snapshot.getInputStream(0);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============== 序列化 数据 读写 =============
// =======================================
private File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
}

View File

@@ -0,0 +1,101 @@
package com.ttstd.template.disklrucache;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Utils {
public static int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
public static String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
public static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
public static byte[] bitmap2Bytes(Bitmap bm) {
if (bm == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
public static Bitmap bytes2Bitmap(byte[] bytes) {
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
/**
* Drawable → Bitmap
*/
public static Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
// 取 drawable 的长宽
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
// 建立对应 bitmap
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
// 建立对应 bitmap 的画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
// 把 drawable 内容画到画布中
drawable.draw(canvas);
return bitmap;
}
/*
* Bitmap → Drawable
*/
@SuppressWarnings("deprecation")
public static Drawable bitmap2Drawable(Bitmap bm) {
if (bm == null) {
return null;
}
BitmapDrawable bd = new BitmapDrawable(bm);
bd.setTargetDensity(bm.getDensity());
return new BitmapDrawable(bm);
}
}

View File

@@ -0,0 +1,96 @@
package com.ttstd.template.fragment;
import android.app.Activity;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.ttstd.template.R;
import com.ttstd.template.base.BaseFragment;
import butterknife.ButterKnife;
/**
* A simple {@link Fragment} subclass.
* Use the {@link TemplateFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class TemplateFragment extends BaseFragment {
private static final String TAG = TemplateFragment.class.getSimpleName();
private View rootView;// 设置为全局的
private Activity mContext;
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public TemplateFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment TemplateFragment.
*/
// TODO: Rename and change types and number of parameters
public static TemplateFragment newInstance(String param1, String param2) {
TemplateFragment fragment = new TemplateFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.e(TAG, "onCreateView: ");
if (null != rootView) {
ViewGroup parent = (ViewGroup) rootView.getParent();
if (null != parent) {
parent.removeView(rootView);
}
} else { // 如ongoing果rootView为空 ,就实例化该视图
rootView = inflater.inflate(R.layout.fragment_health, container, false);
mContext = (Activity) rootView.getContext();
ButterKnife.bind(this, rootView);
initView();
}
return rootView;
}
@Override
public void fetchData() {
}
private void initView() {
}
}

View File

@@ -0,0 +1,112 @@
package com.ttstd.template.network;
import android.annotation.SuppressLint;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public static String packetMD5(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
md5StrBuff.append("0").append(
Integer.toHexString(0xFF & byteArray[i]));
else
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
return md5StrBuff.toString();
}
@SuppressLint("DefaultLocale")
public static String getUpperMD5Str(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
md5StrBuff.append("0").append(
Integer.toHexString(0xFF & byteArray[i]));
else
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
return md5StrBuff.toString().toUpperCase();
}
/**
* 获取16位的MD5 值得
*
* @param str
* @return
*/
@SuppressLint("DefaultLocale")
public static String getUpperMD5Str16(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
md5StrBuff.append("0").append(
Integer.toHexString(0xFF & byteArray[i]));
else
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
return md5StrBuff.toString().toUpperCase().substring(8, 24);
}
}

View File

@@ -0,0 +1,158 @@
package com.ttstd.template.network;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Environment;
import com.tencent.mmkv.MMKV;
import com.ttstd.template.bean.BaseResponse;
import com.ttstd.template.comm.CommonConfig;
import com.ttstd.template.network.interceptor.RepeatRequestInterceptor;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import io.reactivex.rxjava3.disposables.Disposable;
import okhttp3.Cache;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class NetInterfaceManager {
private static final String TAG = NetInterfaceManager.class.getSimpleName();
@SuppressLint("StaticFieldLeak")
private static NetInterfaceManager INSTANCE;
private Context mContext;
private ContentResolver crv;
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private Retrofit mRetrofit;
private OkHttpClient okHttpClient;
// private CacheHelper mCacheHelper;
private final ConcurrentHashMap<String, Long> requestIdsMap = new ConcurrentHashMap<>();
//超时时间
public static final int timeOut = 30;
// 缓存文件最大限制大小20M
private static final long cacheSize = 1024 * 1024 * 64;
private NetInterfaceManager(Context context) {
if (context == null) {
throw new RuntimeException("Context is NULL");
}
this.mContext = context;
this.crv = mContext.getContentResolver();
// this.mCacheHelper = new CacheHelper(mContext);
if (null == mRetrofit) {
if (okHttpClient == null) {
//如果无法生存缓存文件目录,检测权限使用已经加上,检测手机是否把文件读写权限禁止了
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(timeOut, TimeUnit.SECONDS); // 设置连接超时时间
builder.writeTimeout(timeOut, TimeUnit.SECONDS);// 设置写入超时时间
builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间
builder.retryOnConnectionFailure(true);// 设置进行连接失败重试
builder.addInterceptor(new RepeatRequestInterceptor());
// 设置缓存文件路径
String cacheDirectory = getCacheDir() + "/OkHttpCache";
Cache cache = new Cache(new File(cacheDirectory), cacheSize);
builder.cache(cache);// 设置缓存
okHttpClient = builder.build();
}
mRetrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(UrlAddress.ROOT_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
}
}
private String getCacheDir() {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
if (mContext.getExternalCacheDir() != null) {
cachePath = mContext.getExternalCacheDir().getPath();
} else if (mContext.getExternalFilesDir("cache") != null) {
cachePath = mContext.getExternalFilesDir("cache").getPath();
} else {
cachePath = mContext.getCacheDir().getPath();
}
} else {
cachePath = mContext.getCacheDir().getPath();
}
return cachePath;
}
public static void init(Context context) {
if (context == null) {
throw new RuntimeException("context is NULL");
}
if (INSTANCE == null) {
INSTANCE = new NetInterfaceManager(context);
}
}
public static NetInterfaceManager getInstance() {
if (INSTANCE == null) {
throw new IllegalStateException("You must be init NetworkManager first");
}
return INSTANCE;
}
public OkHttpClient getOkHttpClient() {
return okHttpClient;
}
/*
*
* Observable
*
* */
/*
*
* API
*
* */
/*
*
* execution
*
* */
public interface ObserverCallback {
void onSubscribe(Disposable d);
void onNext(BaseResponse response);
void onError(Throwable e);
void onComplete();
}
public interface onCompleteCallback {
void onComplete();
}
public interface onNextCallback {
void onNext(Object o);
void onError(Object o);
void onComplete();
}
}

View File

@@ -0,0 +1,92 @@
package com.ttstd.template.network;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
import io.reactivex.rxjava3.annotations.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public abstract class RetryCallback<T> implements Callback<T> {
private static final String TAG = RetryCallback.class.getSimpleName();
private int mRetryCount;
private long mRetryInterval;
private int mCurrentRetryCount;
private boolean isExecuting;
private Call<T> mCall;
private Timer timer = new Timer();
public RetryCallback(Call<T> call, int retryCount, long retryInterval) {
isExecuting = true;
mCall = call;
mRetryCount = retryCount;
mRetryInterval = retryInterval;
}
@Override
public final void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
Log.e(TAG, "onResponse");
isExecuting = false;
if (!response.isSuccessful() && mCurrentRetryCount < mRetryCount) {
mCurrentRetryCount++;
retryRequest(call);
} else {
onRequestResponse(call, response);
}
}
@Override
public final void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
Log.e(TAG, "onFailure");
isExecuting = false;
if (mCurrentRetryCount < mRetryCount) {
mCurrentRetryCount++;
Log.e(TAG, "onFailure: " + "RetryCount: " + mCurrentRetryCount);
Log.e(TAG, "onFailure: " + "RetryResponse: " + call.request());
retryRequest(call);
} else {
onRequestFail(call, t);
}
}
private void retryRequest(final Call<T> call) {
Log.e(TAG, "retryRequest");
onStartRetry();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
synchronized (RetryCallback.this) {
mCall = call.clone();
mCall.enqueue(RetryCallback.this);
isExecuting = true;
}
}
};
timer.schedule(timerTask, mRetryInterval);
}
public void cancelCall() {
synchronized (this) {
if (!isExecuting) {
timer.cancel();
} else {
mCall.cancel();
}
}
}
public abstract void onRequestResponse(Call call, Response response);
public abstract void onRequestFail(Call call, Throwable t);
public abstract void onStartRetry();
}

View File

@@ -0,0 +1,80 @@
package com.ttstd.template.network;
public class UrlAddress {
/*主页接口*/
public static final String ROOT_URL = "https://led.zuoyepad.com/android/";
/*设备激活*/
public static final String SN_ACTIVATION = "sn/snActivation";
/*获取设备是否激活*/
public static final String GET_SN_IS_ACTIVATION = "sn/getSnIsActivation";
/*获取设备激活二维码链接*/
public static final String ACTIVATION_QRCODE = "pay/getActivationQrcode";
/*获取文件*/
public static final String GET_FILES = "file/getFiles";
/*获取操作指南*/
public static final String GET_OPERATION_GUIDE = "file/getFiles";
/*上传屏幕截图*/
public final static String UPLOAD_SCREEN_SNAPSHOT = "sn/uploadScreenshot";
/*浏览器网址管控*/
public final static String SET_BROWSER_URL = "control/getBrowser";
/*浏览器书签管控*/
public final static String SET_BROWSER_LABEL = "control/getLabel";
/*上传控制面版截图*/
public static final String UPLOAD_CONTROL_SCREENSHOT = "sn/uploadControlScreenshot";
/*设备信息接口*/
public static final String SNINFO = "sn/getSnInfo";
/*获取用户头像和信息*/
public static final String GET_USER_AVATAR_INFO = "sn/getUserAvatarInfo";
/*获取设备类型*/
public static final String GET_SN_TYPE = "sn/getSnType";
/*获取正在运行的app*/
public static final String RUN_NEW_APP = "app/runNewApp";
/*获取所有应用*/
public final static String GET_ALL_PACKAGE = "app/queryAllApp";
/*绑定设备消息*/
public final static String BIND_DEVICES = "sn/bindSn";
/*获取系统设置*/
public final static String GET_SETTINGS = "control/getSetting";
/*获取强制下载*/
public final static String GET_FORCE_INSTALL = "app/getForceDownload";
/*发送卸载或者安装信息*/
public final static String SEND_INSTALLEDORREMOVED = "app/addAppInstall";
/*发送设备基本信息*/
public final static String UPDATE_SNINFO = "sn/updateAdminSn";
/*根据包名获取更新*/
public final static String GET_NEWESTAPPUPDATE = "app/newestAppUpdate";
/*获取禁用包名*/
public static final String GET_APP_ICON = "getAppIcon";
/*获取灰度更新*/
public static final String GET_TEST_APP_INFO = "app/getTestAppInfo";
/*获取wifi*/
public static final String GET_WIFI_ALIAS_PW = "getWifi";
/*获取屏幕管控*/
public final static String GET_SCREEN_LOCK = "sn/getScreenshot";
/*获取锁屏密码*/
public static final String LOCK_SCREEN_PWD = "sn/getLockScreenPwd";
/*解除锁屏*/
public static final String UPDATE_LOCK_SCREEN = "sn/updateLockScreen";
/*发送绑定验证码*/
public static final String SEND_BIND_VER_CODE = "Sn/sendBindVerCode";
/*手动绑定设备*/
public static final String EQUIPMENT_BIND = "Sn/equipmentBind";
/*上传应用图标*/
public static final String UPLOAD_APP_IMG = "collectData/uploadAppImg";
/*获取应用库是否有图标*/
public static final String GET_IS_APP_IMG = "collectData/getIsAppImg";
/*通过ip获取信息*/
public static final String PCONLINE_WHOIS = "http://whois.pconline.com.cn/";
public static final String WHOIS = "ipJson.jsp";
}

View File

@@ -0,0 +1,107 @@
package com.ttstd.template.network.interceptor;
import android.util.Log;
import com.ttstd.template.BuildConfig;
import com.ttstd.template.network.MD5Util;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Interceptor;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
/**
* v1.0 2022-07-15 16:16:52
*/
public class RepeatRequestInterceptor implements Interceptor {
private static final String TAG = RepeatRequestInterceptor.class.getSimpleName();
private final ConcurrentHashMap<String, Long> requestIdsMap = new ConcurrentHashMap<>();
public static final String REPEAT_REQUEST_PROTOCOL = "OKHTTP_REPEAT_REQUEST_PROTOCOL";
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
//会消费请求,导致请求多次
String content = responseBody.string();
// Response copy = response.newBuilder().body(responseBody).build();
ResponseBody copy = ResponseBody.create(responseBody.contentType(), content);
if (BuildConfig.DEBUG) {
Log.e(TAG, "请求体返回:| Response: " + request.url() + "\t body: " + content);
}
//相同的请求
String requestKey = MD5Util.getUpperMD5Str(request.method() + request.url().toString() + requestBodyToString(request.body()));
long time = System.currentTimeMillis();//请求时间
try {
if (requestIdsMap.size() > 0 && requestIdsMap.containsKey(requestKey)) {
log("重复请求:", requestKey, request);
//下面这行写了不会抛出onerror
// chain.call().cancel();
return new Response.Builder()
.protocol(Protocol.get(REPEAT_REQUEST_PROTOCOL))
.request(request) //multi thread
.build();
}
requestIdsMap.put(requestKey, time);
log("注册请求:", requestKey, request);
// RepeatRequestInterceptor.Builder builder = request.newBuilder();
// builder.addHeader("header", jsonObject.toString());
return response.newBuilder().body(copy).build();
} catch (IOException e) {
Log.e(TAG, "intercept: " + e.getMessage());
throw e;
} finally {
if (requestIdsMap.containsKey(requestKey) && requestIdsMap.containsValue(time)) {//请求任务完成删除map中的数据
requestIdsMap.remove(requestKey);
log("移除请求:", requestKey, request);
}
}
}
private void log(String action, String requestKey, Request request) {
if (BuildConfig.DEBUG) {
Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url().encodedPath() + "\t" + bodyToString(request));
} else {
Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method());
}
}
private static String bodyToString(final Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
if (buffer.size() > 4096) {
return "-too long";
}
return buffer.readUtf8();
} catch (Exception e) {
return "-";
}
}
private static String requestBodyToString(RequestBody body) {
try {
final Buffer buffer = new Buffer();
body.writeTo(buffer);
if (buffer.size() > 4096) {
return "-too long";
}
return buffer.readUtf8();
} catch (Exception e) {
return "-";
}
}
}

View File

@@ -0,0 +1,24 @@
package com.ttstd.template.utils;
import android.app.ActivityManager;
import android.content.Context;
import java.util.List;
public class SystemUtils {
public static boolean isMainProcessName(Context cxt, int pid) {
String packageName = cxt.getPackageName();
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return false;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName.equals(packageName);
}
}
return false;
}
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.main.MainActivity">
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.TemplateFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,248 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<!--https://www.jianshu.com/p/8dc258dfd189-->
<color name="transparent">#00000000</color><!--透明色-->
<color name="colorYang">#FFE2C59B</color><!--羊皮纸色-->
<color name="ivory">#FFFFFFF0</color><!--象牙色-->
<color name="lightYellow">#FFFFFFE0</color><!--亮黄色-->
<color name="yellow">#FFFFFF00</color><!--黄色-->
<color name="snow">#FFFFFAFA</color><!--雪白色-->
<color name="floralWhite">#FFFFFAF0</color><!--花白色-->
<color name="lemonChiffon">#FFFFFACD</color><!--柠檬绸色-->
<color name="cornSilk">#FFFFF8DC</color><!--米绸色-->
<color name="seashell">#FFFFF5EE</color><!--海贝色-->
<color name="lavenderBlush">#FFFFF0F5</color><!--淡紫红色-->
<color name="papayaWhip">#FFFFEFD5</color><!--番木色-->
<color name="blanchedAlmond">#FFFFEBCD</color><!--白杏色-->
<color name="mistyRose">#FFFFE4E1</color><!--浅玫瑰色-->
<color name="bisque">#FFFFE4C4</color><!--桔黄色-->
<color name="moccasin">#FFFFE4B5</color><!--鹿皮色-->
<color name="navajoWhite">#FFFFDEAD</color><!--纳瓦白-->
<color name="peachPuff">#FFFFDAB9</color><!--桃色-->
<color name="gold">#FFFFD700</color><!--金色-->
<color name="pink">#FFFFC0CB</color><!--粉红色-->
<color name="lightPink">#FFFFB6C1</color><!--亮粉红色-->
<color name="orange">#FFFFA500</color><!--橙色-->
<color name="lightSalmon">#FFFFA07A</color><!--亮肉色-->
<color name="darkOrange">#FFFF8C00</color><!--暗桔黄色-->
<color name="coral">#FFFF7F50</color><!--珊瑚色-->
<color name="hotPink">#FFFF69B4</color><!--热粉红色-->
<color name="tomato">#FFFF6347</color><!--西红柿色-->
<color name="orangeRed">#FFFF4500</color><!--红橙色-->
<color name="deepPink">#FFFF1493</color><!--深粉红色-->
<color name="fuchsia">#FFFF00FF</color><!--紫红色-->
<color name="red">#FFFF0000</color><!--红色-->
<color name="oldLace">#FFFDF5E6</color><!--老花色-->
<color name="lightGoldenrodYellow">#FFFAFAD2</color><!--亮金黄色-->
<color name="linen">#FFFAF0E6</color><!--亚麻色-->
<color name="antiqueWhite">#FFFAEBD7</color><!--古董白色-->
<color name="salmon">#FFFA8072</color><!--鲜肉色-->
<color name="ghostWhite">#FFF8F8FF</color><!--幽灵白-->
<color name="mintCream">#FFF5FFFA</color><!--薄荷色-->
<color name="whiteSmoke">#FFF5F5F5</color><!--烟白色-->
<color name="beige">#FFF5F5DC</color><!--米色-->
<color name="wheat">#FFF5DEB3</color><!--浅黄色-->
<color name="sandyBrown">#FFF4A460</color><!--沙褐色-->
<color name="azure">#FFF0FFFF</color><!--天蓝色-->
<color name="aliceBlue">#FFF0F8FF</color><!--艾利斯兰色-->
<color name="khaki">#FFF0E68C</color><!--黄褐色-->
<color name="lightCoral">#FFF08080</color><!--亮珊瑚色-->
<color name="paleGoldenrod">#FFEEE8AA</color><!--苍麒麟色-->
<color name="violet">#FFEE82EE</color><!--紫罗兰色-->
<color name="darkSalmon">#FFE9967A</color><!--暗肉色-->
<color name="lavender">#FFE6E6FA</color><!--淡紫色-->
<color name="lightCyan">#FFE0FFFF</color><!--亮青色-->
<color name="burlyWood">#FFDEB887</color><!--实木色-->
<color name="plum">#FFDDA0DD</color><!--洋李色-->
<color name="lightGrey">#FFDCDCDC</color><!--淡灰色-->
<color name="crimson">#FFDC143C</color><!--暗深红色-->
<color name="paleVioletRed">#FFDB7093</color><!--苍紫罗兰色-->
<color name="goldenrod">#FFDAA520</color><!--金麒麟色-->
<color name="orchid">#FFDA70D6</color><!--淡紫色-->
<color name="thistle">#FFD8BFD8</color><!--蓟色-->
<color name="lightGray">#FFD3D3D3</color><!--亮灰色-->
<color name="tan">#FFD2B48C</color><!--茶色-->
<color name="chocolate">#FFD2691E</color><!--巧可力色-->
<color name="peru">#FFCD853F</color><!--秘鲁色-->
<color name="indianRed">#FFCD5C5C</color><!--印第安红色-->
<color name="mediumVioletRed">#FFC71585</color><!--中紫罗兰色-->
<color name="silver">#FFC0C0C0</color><!--银色-->
<color name="darkKhaki">#FFBDB76B</color><!--暗黄褐色-->
<color name="rosyBrown">#FFBC8F8F</color><!--褐玫瑰红色-->
<color name="mediumOrchid">#FFBA55D3</color><!--中粉紫色-->
<color name="darkGoldenrod">#FFB8860B</color><!--暗金黄色-->
<color name="firebrick">#FFB22222</color><!--火砖色-->
<color name="powderBlue">#FFB0E0E6</color><!--粉蓝色-->
<color name="lightSteelBlue">#FFB0C4DE</color><!--亮钢兰色-->
<color name="paleTurquoise">#FFAFEEEE</color><!--苍宝石绿色-->
<color name="greenYellow">#FFADFF2F</color><!--黄绿色-->
<color name="lightBlue">#FFADD8E6</color><!--亮蓝色-->
<color name="darkGray">#FFA9A9A9</color><!--暗灰色-->
<color name="brown">#FFA52A2A</color><!--褐色-->
<color name="sienna">#FFA0522D</color><!--赭色-->
<color name="darkOrchid">#FF9932CC</color><!--暗紫色-->
<color name="paleGreen">#FF98FB98</color><!--苍绿色-->
<color name="darkViolet">#FF9400D3</color><!--暗紫罗兰色-->
<color name="mediumPurple">#FF9370DB</color><!--中紫色-->
<color name="lightGreen">#FF90EE90</color><!--亮绿色-->
<color name="darkSeaGreen">#FF8FBC8F</color><!--暗海兰色-->
<color name="saddleBrown">#FF8B4513</color><!--重褐色-->
<color name="darkMagenta">#FF8B008B</color><!--暗洋红色-->
<color name="darkRed">#FF8B0000</color><!--暗红色-->
<color name="blueViolet">#FF8A2BE2</color><!--紫罗兰蓝色-->
<color name="lightSkyBlue">#FF87CEFA</color><!--亮天蓝色-->
<color name="skyBlue">#FF87CEEB</color><!--天蓝色-->
<color name="gray">#FF808080</color><!--灰色-->
<color name="olive">#FF808000</color><!--橄榄色-->
<color name="purple">#FF800080</color><!--紫色-->
<color name="maroon">#FF800000</color><!--粟色-->
<color name="aquamarine">#FF7FFFD4</color><!--碧绿色-->
<color name="chartreuse">#FF7FFF00</color><!--黄绿色-->
<color name="lawnGreen">#FF7CFC00</color><!--草绿色-->
<color name="mediumSlateBlue">#FF7B68EE</color><!--中暗蓝色-->
<color name="lightSlateGray">#FF778899</color><!--亮蓝灰色-->
<color name="slateGray">#FF708090</color><!--灰石色-->
<color name="oliveDrab">#FF6B8E23</color><!--深绿褐色-->
<color name="slateBlue">#FF6A5ACD</color><!--石蓝色-->
<color name="dimGray">#FF696969</color><!--暗灰色-->
<color name="mediumAquamarine">#FF66CDAA</color><!--中绿色-->
<color name="cornFlowerBlue">#FF6495ED</color><!--菊兰色-->
<color name="cadetBlue">#FF5F9EA0</color><!--军兰色-->
<color name="darkOliveGreen">#FF556B2F</color><!--暗橄榄绿色-->
<color name="indigo">#FF4B0082</color><!--靛青色-->
<color name="mediumTurquoise">#FF48D1CC</color><!--中绿宝石色-->
<color name="darkSlateBlue">#FF483D8B</color><!--暗灰蓝色-->
<color name="steelBlue">#FF4682B4</color><!--钢兰色-->
<color name="royalBlue">#FF4169E1</color><!--皇家蓝色-->
<color name="turquoise">#FF40E0D0</color><!--青绿色-->
<color name="mediumSeaGreen">#FF3CB371</color><!--中海蓝色-->
<color name="limeGreen">#FF32CD32</color><!--橙绿色-->
<color name="darkSlateGray">#FF2F4F4F</color><!--暗瓦灰色-->
<color name="seaGreen">#FF2E8B57</color><!--海绿色-->
<color name="forestGreen">#FF228B22</color><!--森林绿色-->
<color name="lightSeaGreen">#FF20B2AA</color><!--亮海蓝色-->
<color name="dodgerBlue">#FF1E90FF</color><!--闪兰色-->
<color name="midnightBlue">#FF191970</color><!--中灰兰色-->
<color name="aqua">#FF00FFFF</color><!--浅绿色-->
<color name="springGreen">#FF00FF7F</color><!--春绿色-->
<color name="lime">#FF00FF00</color><!--酸橙色-->
<color name="mediumSpringGreen">#FF00FA9A</color><!--中春绿色-->
<color name="darkTurquoise">#FF00CED1</color><!--暗宝石绿色-->
<color name="deepSkyBlue">#FF00BFFF</color><!--深天蓝色-->
<color name="darkCyan">#FF008B8B</color><!--暗青色-->
<color name="teal">#FF008080</color><!--水鸭色-->
<color name="green">#FF008000</color><!--绿色-->
<color name="darkGreen">#FF006400</color><!--暗绿色-->
<color name="blue">#FF0000FF</color><!--蓝色-->
<color name="mediumBlue">#FF0000CD</color><!--中兰色-->
<color name="darkBlue">#FF00008B</color><!--暗蓝色-->
<color name="navy">#FF000080</color><!--海军色-->
<color name="unBlack">#FF2B2B2B</color><!--不知名黑色-->
<!--https://www.cnblogs.com/elleniou/archive/2012/04/25/2469676.html-->
<color name="white">#ffffff</color><!--白色 -->
<color name="lightyellow">#ffffe0</color><!--亮黄色 -->
<color name="floralwhite">#fffaf0</color><!--花白色 -->
<color name="lemonchiffon">#fffacd</color><!--柠檬绸色 -->
<color name="cornsilk">#fff8dc</color><!--米绸色 -->
<color name="seaShell">#fff5ee</color><!--海贝色 -->
<color name="lavenderblush">#fff0f5</color><!--淡紫红 -->
<color name="papayawhip">#ffefd5</color><!--番木色 -->
<color name="blanchedalmond">#ffebcd</color><!--白杏色 -->
<color name="mistyrose">#ffe4e1</color><!--浅玫瑰色 -->
<color name="navajowhite">#ffdead</color><!--纳瓦白 -->
<color name="peachpuff">#ffdab9</color><!--桃色 -->
<color name="lightpink">#ffb6c1</color><!--亮粉红色 -->
<color name="lightsalmon">#ffa07a</color><!--亮肉色 -->
<color name="darkorange">#ff8c00</color><!--暗桔黄色 -->
<color name="hotpink">#ff69b4</color><!--热粉红色 -->
<color name="orangered">#ff4500</color><!--红橙色 -->
<color name="deeppink">#ff1493</color><!--深粉红色 -->
<color name="magenta">#ff00ff</color><!--红紫色 -->
<color name="oldlace">#fdf5e6</color><!--老花色 -->
<color name="lightgoldenrodyellow">#fafad2</color><!--亮金黄色 -->
<color name="antiquewhite">#faebd7</color><!--古董白 -->
<color name="ghostwhite">#f8f8ff</color><!--幽灵白 -->
<color name="mintcream">#f5fffa</color><!--薄荷色 -->
<color name="whitesmoke">#f5f5f5</color><!--烟白色 -->
<color name="sandybrown">#f4a460</color><!--沙褐色 -->
<color name="honeydew">#f0fff0</color><!--蜜色 -->
<color name="aliceblue">#f0f8ff</color><!--艾利斯兰 -->
<color name="lightcoral">#f08080</color><!--亮珊瑚色 -->
<color name="palegoldenrod">#eee8aa</color><!--苍麒麟色 -->
<color name="darksalmon">#e9967a</color><!--暗肉色 -->
<color name="lightcyan">#e0ffff</color><!--亮青色 -->
<color name="burlywood">#deb887</color><!--实木色 -->
<color name="gainsboro">#dcdcdc</color><!--淡灰色 -->
<color name="palevioletred">#db7093</color><!--苍紫罗兰色 -->
<color name="lightgray">#d3d3d3</color><!--亮灰色 -->
<color name="lightgrey">#d3d3d3</color><!--亮灰色 -->
<color name="indianred">#cd5c5c</color><!--印第安红 -->
<color name="mediumvioletred">#c71585</color><!--中紫罗兰色 -->
<color name="darkkhaki">#bdb76b</color><!--暗黄褐色 -->
<color name="rosybrown">#bc8f8f</color><!--褐玫瑰红 -->
<color name="mediumorchid">#ba55d3</color><!--中粉紫色 -->
<color name="darkgoldenrod">#b8860b</color><!--暗金黄色 -->
<color name="powderblue">#b0e0e6</color><!--粉蓝色 -->
<color name="lightsteelblue">#b0c4de</color><!--亮钢兰色 -->
<color name="paleturquoise">#afeeee</color><!--苍宝石绿 -->
<color name="greenyellow">#adff2f</color><!--黄绿色 -->
<color name="lightblue">#add8e6</color><!--亮蓝色 -->
<color name="darkgray">#a9a9a9</color><!--暗灰色 -->
<color name="darkgrey">#a9a9a9</color><!--暗灰色 -->
<color name="darkorchid">#9932cc</color><!--暗紫色 -->
<color name="palegreen">#98fb98</color><!--苍绿色 -->
<color name="darkviolet">#9400d3</color><!--暗紫罗兰色 -->
<color name="mediumpurple">#9370db</color><!--中紫色 -->
<color name="lightgreen">#90ee90</color><!--亮绿色 -->
<color name="darkseagreen">#8fbc8f</color><!--暗海兰色 -->
<color name="saddlebrown">#8b4513</color><!--重褐色 -->
<color name="darkmagenta">#8b008b</color><!--暗洋红 -->
<color name="darkred">#8b0000</color><!--暗红色 -->
<color name="blueviolet">#8a2be2</color><!--紫罗兰蓝色 -->
<color name="lightskyblue">#87cefa</color><!--亮天蓝色 -->
<color name="skyblue">#87ceeb</color><!--天蓝色 -->
<color name="grey">#808080</color><!--灰色 -->
<color name="lawngreen">#7cfc00</color><!--草绿色 -->
<color name="mediumslateblue">#7b68ee</color><!--中暗蓝色 -->
<color name="lightslategray">#778899</color><!--亮蓝灰 -->
<color name="lightslategrey">#778899</color><!--亮蓝灰 -->
<color name="slategray">#708090</color><!--灰石色 -->
<color name="slategrey">#708090</color><!--灰石色 -->
<color name="olivedrab">#6b8e23</color><!--深绿褐色 -->
<color name="slateblue">#6a5acd</color><!--石蓝色 -->
<color name="dimgray">#696969</color><!--暗灰色 -->
<color name="dimgrey">#696969</color><!--暗灰色 -->
<color name="mediumaquamarine">#66cdaa</color><!--中绿色 -->
<color name="cornflowerblue">#6495ed</color><!--菊兰色 -->
<color name="cadetblue">#5f9ea0</color><!--军兰色 -->
<color name="darkolivegreen">#556b2f</color><!--暗橄榄绿 -->
<color name="mediumturquoise">#48d1cc</color><!--中绿宝石 -->
<color name="darkslateblue">#483d8b</color><!--暗灰蓝色 -->
<color name="steelblue">#4682b4</color><!--钢兰色 -->
<color name="royalblue">#4169e1</color><!--皇家蓝 -->
<color name="mediumseagreen">#3cb371</color><!--中海蓝 -->
<color name="limegreen">#32cd32</color><!--橙绿色 -->
<color name="darkslategray">#2f4f4f</color><!--暗瓦灰色 -->
<color name="darkslategrey">#2f4f4f</color><!--暗瓦灰色 -->
<color name="seagreen">#2e8b57</color><!--海绿色 -->
<color name="forestgreen">#228b22</color><!--森林绿 -->
<color name="lightseagreen">#20b2aa</color><!--亮海蓝色 -->
<color name="dodgerblue">#1e90ff</color><!--闪兰色 -->
<color name="midnightblue">#191970</color><!--中灰兰色 -->
<color name="cyan">#00ffff</color><!--青色 -->
<color name="springgreen">#00ff7f</color><!--春绿色 -->
<color name="mediumspringgreen">#00fa9a</color><!--中春绿色 -->
<color name="darkturquoise">#00ced1</color><!--暗宝石绿 -->
<color name="deepskyblue">#00bfff</color><!--深天蓝色 -->
<color name="darkcyan">#008b8b</color><!--暗青色 -->
<color name="darkgreen">#006400</color><!--暗绿色 -->
<color name="mediumblue">#0000cd</color><!--中兰色 -->
<color name="darkblue">#00008b</color><!--暗蓝色 -->
<color name="black">#000000</color><!--黑色 -->
</resources>

View File

@@ -0,0 +1,6 @@
<resources>
<string name="app_name">AndroidAppTemplate</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

View File

@@ -0,0 +1,18 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">-->
<!-- &lt;!&ndash; Customize your theme here. &ndash;&gt;-->
<!-- <item name="colorPrimary">@color/colorPrimary</item>-->
<!-- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>-->
<!-- <item name="colorAccent">@color/colorAccent</item>-->
<!-- </style>-->
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="files_root"
path="Android/data/com.herenit.webdoc/" />
<external-path
name="external_storage_root"
path="." />
</paths>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

View File

@@ -0,0 +1,17 @@
package com.ttstd.template;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

35
build.gradle Normal file
View File

@@ -0,0 +1,35 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
maven {url 'http://developer.huawei.com/repo/'}
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
maven {url 'http://developer.huawei.com/repo/'}
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

20
gradle.properties Normal file
View File

@@ -0,0 +1,20 @@
# 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=-Xmx1536m
# 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=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Thu Dec 21 10:49:41 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

172
gradlew vendored Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
settings.gradle Normal file
View File

@@ -0,0 +1,2 @@
rootProject.name='AndroidAppTemplate'
include ':app'