commit 66d4a00ba86dfe906eedbe175f113a4be6af3986 Author: Fanhuitong <981964879@qq.com> Date: Thu Mar 28 10:39:39 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1123a0 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f5bb2e0 --- /dev/null +++ b/app/build.gradle @@ -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' +} diff --git a/app/keystore/tuixin.jks b/app/keystore/tuixin.jks new file mode 100644 index 0000000..d7ecbad Binary files /dev/null and b/app/keystore/tuixin.jks differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/ttstd/template/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/ttstd/template/ExampleInstrumentedTest.java new file mode 100644 index 0000000..743420d --- /dev/null +++ b/app/src/androidTest/java/com/ttstd/template/ExampleInstrumentedTest.java @@ -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 Testing documentation + */ +@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()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1a9b385 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/template/activity/main/MainActivity.java b/app/src/main/java/com/ttstd/template/activity/main/MainActivity.java new file mode 100644 index 0000000..aa1f984 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/activity/main/MainActivity.java @@ -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() { + + } + + +} diff --git a/app/src/main/java/com/ttstd/template/activity/template/TemplateActivity.java b/app/src/main/java/com/ttstd/template/activity/template/TemplateActivity.java new file mode 100644 index 0000000..6b86678 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/activity/template/TemplateActivity.java @@ -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(); + } +} diff --git a/app/src/main/java/com/ttstd/template/activity/template/TemplateContact.java b/app/src/main/java/com/ttstd/template/activity/template/TemplateContact.java new file mode 100644 index 0000000..14e64e3 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/activity/template/TemplateContact.java @@ -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 { + + } + + public interface TemplateView extends BaseView { + + } +} diff --git a/app/src/main/java/com/ttstd/template/activity/template/TemplatePresenter.java b/app/src/main/java/com/ttstd/template/activity/template/TemplatePresenter.java new file mode 100644 index 0000000..96c294c --- /dev/null +++ b/app/src/main/java/com/ttstd/template/activity/template/TemplatePresenter.java @@ -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 lifecycle; + + public void setLifecycle(BehaviorSubject lifecycle) { + this.lifecycle = lifecycle; + } + + public BehaviorSubject getLifecycle() { + return lifecycle; + } + + @Override + public void attachView(TemplateContact.TemplateView view) { + this.mView = view; + } + + @Override + public void detachView() { + this.mView = null; + } + + +} diff --git a/app/src/main/java/com/ttstd/template/base/BaseActivity.java b/app/src/main/java/com/ttstd/template/base/BaseActivity.java new file mode 100644 index 0000000..cde9933 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BaseActivity.java @@ -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 { + public final BehaviorSubject 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 lifecycle() { + return lifecycleSubject.hide(); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer bindUntilEvent(@NonNull ActivityEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer 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(); + } +} diff --git a/app/src/main/java/com/ttstd/template/base/BaseApplication.java b/app/src/main/java/com/ttstd/template/base/BaseApplication.java new file mode 100644 index 0000000..6d0e04b --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BaseApplication.java @@ -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); + } +} diff --git a/app/src/main/java/com/ttstd/template/base/BaseFragment.java b/app/src/main/java/com/ttstd/template/base/BaseFragment.java new file mode 100644 index 0000000..629b500 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BaseFragment.java @@ -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 { + public final BehaviorSubject lifecycleSubject = BehaviorSubject.create(); + + protected boolean isViewInitiated; + protected boolean isVisibleToUser; + protected boolean isDataInitiated; + + @Override + @NonNull + @CheckResult + public final Observable lifecycle() { + return lifecycleSubject.hide(); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer bindUntilEvent(@NonNull FragmentEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer 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(); + } +} diff --git a/app/src/main/java/com/ttstd/template/base/BaseLightActivity.java b/app/src/main/java/com/ttstd/template/base/BaseLightActivity.java new file mode 100644 index 0000000..6ec1beb --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BaseLightActivity.java @@ -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 { + public final BehaviorSubject lifecycleSubject = BehaviorSubject.create(); + + public BaseLightActivity() { + super(); + } + + @ContentView + public BaseLightActivity(@LayoutRes int contentLayoutId) { + super(contentLayoutId); + } + + @Override + @NonNull + @CheckResult + public final Observable lifecycle() { + return lifecycleSubject.hide(); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer bindUntilEvent(@NonNull ActivityEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer 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(); + } +} diff --git a/app/src/main/java/com/ttstd/template/base/BasePresenter.java b/app/src/main/java/com/ttstd/template/base/BasePresenter.java new file mode 100644 index 0000000..8e81b2a --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BasePresenter.java @@ -0,0 +1,7 @@ +package com.ttstd.template.base; + +public interface BasePresenter { + void attachView(V view); + + void detachView(); +} diff --git a/app/src/main/java/com/ttstd/template/base/BaseView.java b/app/src/main/java/com/ttstd/template/base/BaseView.java new file mode 100644 index 0000000..75bcf3f --- /dev/null +++ b/app/src/main/java/com/ttstd/template/base/BaseView.java @@ -0,0 +1,4 @@ +package com.ttstd.template.base; + +public interface BaseView { +} diff --git a/app/src/main/java/com/ttstd/template/bean/BaseResponse.java b/app/src/main/java/com/ttstd/template/bean/BaseResponse.java new file mode 100644 index 0000000..82bb9f9 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/bean/BaseResponse.java @@ -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 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(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/template/comm/CommonConfig.java b/app/src/main/java/com/ttstd/template/comm/CommonConfig.java new file mode 100644 index 0000000..b7ef7e8 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/comm/CommonConfig.java @@ -0,0 +1,5 @@ +package com.ttstd.template.comm; + +public class CommonConfig { + public static final String MMKV_ID = "InterProcessKV"; +} diff --git a/app/src/main/java/com/ttstd/template/disklrucache/CacheHelper.java b/app/src/main/java/com/ttstd/template/disklrucache/CacheHelper.java new file mode 100644 index 0000000..4daf039 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/disklrucache/CacheHelper.java @@ -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 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); + } + +} diff --git a/app/src/main/java/com/ttstd/template/disklrucache/DiskLruCacheHelper.java b/app/src/main/java/com/ttstd/template/disklrucache/DiskLruCacheHelper.java new file mode 100644 index 0000000..bcbfd94 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/disklrucache/DiskLruCacheHelper.java @@ -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 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); + } + +} diff --git a/app/src/main/java/com/ttstd/template/disklrucache/Utils.java b/app/src/main/java/com/ttstd/template/disklrucache/Utils.java new file mode 100644 index 0000000..7c0e980 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/disklrucache/Utils.java @@ -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); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/template/fragment/TemplateFragment.java b/app/src/main/java/com/ttstd/template/fragment/TemplateFragment.java new file mode 100644 index 0000000..c120f50 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/fragment/TemplateFragment.java @@ -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() { + + } +} diff --git a/app/src/main/java/com/ttstd/template/network/MD5Util.java b/app/src/main/java/com/ttstd/template/network/MD5Util.java new file mode 100644 index 0000000..09dda45 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/network/MD5Util.java @@ -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); + } + +} diff --git a/app/src/main/java/com/ttstd/template/network/NetInterfaceManager.java b/app/src/main/java/com/ttstd/template/network/NetInterfaceManager.java new file mode 100644 index 0000000..812fbfc --- /dev/null +++ b/app/src/main/java/com/ttstd/template/network/NetInterfaceManager.java @@ -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 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(); + } + +} diff --git a/app/src/main/java/com/ttstd/template/network/RetryCallback.java b/app/src/main/java/com/ttstd/template/network/RetryCallback.java new file mode 100644 index 0000000..48be199 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/network/RetryCallback.java @@ -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 implements Callback { + + private static final String TAG = RetryCallback.class.getSimpleName(); + + private int mRetryCount; + private long mRetryInterval; + + private int mCurrentRetryCount; + + private boolean isExecuting; + + private Call mCall; + + private Timer timer = new Timer(); + + public RetryCallback(Call call, int retryCount, long retryInterval) { + isExecuting = true; + mCall = call; + mRetryCount = retryCount; + mRetryInterval = retryInterval; + } + + @Override + public final void onResponse(@NonNull Call call, @NonNull Response 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 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 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(); +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/template/network/UrlAddress.java b/app/src/main/java/com/ttstd/template/network/UrlAddress.java new file mode 100644 index 0000000..1089c26 --- /dev/null +++ b/app/src/main/java/com/ttstd/template/network/UrlAddress.java @@ -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"; +} diff --git a/app/src/main/java/com/ttstd/template/network/interceptor/RepeatRequestInterceptor.java b/app/src/main/java/com/ttstd/template/network/interceptor/RepeatRequestInterceptor.java new file mode 100644 index 0000000..0c9f3ab --- /dev/null +++ b/app/src/main/java/com/ttstd/template/network/interceptor/RepeatRequestInterceptor.java @@ -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 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 "-"; + } + } +} diff --git a/app/src/main/java/com/ttstd/template/utils/SystemUtils.java b/app/src/main/java/com/ttstd/template/utils/SystemUtils.java new file mode 100644 index 0000000..752813e --- /dev/null +++ b/app/src/main/java/com/ttstd/template/utils/SystemUtils.java @@ -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 runningApps = am.getRunningAppProcesses(); + if (runningApps == null) { + return false; + } + for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) { + if (procInfo.pid == pid) { + return procInfo.processName.equals(packageName); + } + } + return false; + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b0d0301 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_health.xml b/app/src/main/res/layout/fragment_health.xml new file mode 100644 index 0000000..c18b330 --- /dev/null +++ b/app/src/main/res/layout/fragment_health.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..61da551 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..db5080a Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..da31a87 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b216f2d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e96783c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..fa03541 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,248 @@ + + + #6200EE + #3700B3 + #03DAC5 + + + #00000000 + #FFE2C59B + #FFFFFFF0 + #FFFFFFE0 + #FFFFFF00 + #FFFFFAFA + #FFFFFAF0 + #FFFFFACD + #FFFFF8DC + #FFFFF5EE + #FFFFF0F5 + #FFFFEFD5 + #FFFFEBCD + #FFFFE4E1 + #FFFFE4C4 + #FFFFE4B5 + #FFFFDEAD + #FFFFDAB9 + #FFFFD700 + #FFFFC0CB + #FFFFB6C1 + #FFFFA500 + #FFFFA07A + #FFFF8C00 + #FFFF7F50 + #FFFF69B4 + #FFFF6347 + #FFFF4500 + #FFFF1493 + #FFFF00FF + #FFFF0000 + #FFFDF5E6 + #FFFAFAD2 + #FFFAF0E6 + #FFFAEBD7 + #FFFA8072 + #FFF8F8FF + #FFF5FFFA + #FFF5F5F5 + #FFF5F5DC + #FFF5DEB3 + #FFF4A460 + #FFF0FFFF + #FFF0F8FF + #FFF0E68C + #FFF08080 + #FFEEE8AA + #FFEE82EE + #FFE9967A + #FFE6E6FA + #FFE0FFFF + #FFDEB887 + #FFDDA0DD + #FFDCDCDC + #FFDC143C + #FFDB7093 + #FFDAA520 + #FFDA70D6 + #FFD8BFD8 + #FFD3D3D3 + #FFD2B48C + #FFD2691E + #FFCD853F + #FFCD5C5C + #FFC71585 + #FFC0C0C0 + #FFBDB76B + #FFBC8F8F + #FFBA55D3 + #FFB8860B + #FFB22222 + #FFB0E0E6 + #FFB0C4DE + #FFAFEEEE + #FFADFF2F + #FFADD8E6 + #FFA9A9A9 + #FFA52A2A + #FFA0522D + #FF9932CC + #FF98FB98 + #FF9400D3 + #FF9370DB + #FF90EE90 + #FF8FBC8F + #FF8B4513 + #FF8B008B + #FF8B0000 + #FF8A2BE2 + #FF87CEFA + #FF87CEEB + #FF808080 + #FF808000 + #FF800080 + #FF800000 + #FF7FFFD4 + #FF7FFF00 + #FF7CFC00 + #FF7B68EE + #FF778899 + #FF708090 + #FF6B8E23 + #FF6A5ACD + #FF696969 + #FF66CDAA + #FF6495ED + #FF5F9EA0 + #FF556B2F + #FF4B0082 + #FF48D1CC + #FF483D8B + #FF4682B4 + #FF4169E1 + #FF40E0D0 + #FF3CB371 + #FF32CD32 + #FF2F4F4F + #FF2E8B57 + #FF228B22 + #FF20B2AA + #FF1E90FF + #FF191970 + #FF00FFFF + #FF00FF7F + #FF00FF00 + #FF00FA9A + #FF00CED1 + #FF00BFFF + #FF008B8B + #FF008080 + #FF008000 + #FF006400 + #FF0000FF + #FF0000CD + #FF00008B + #FF000080 + #FF2B2B2B + + + #ffffff + #ffffe0 + #fffaf0 + #fffacd + #fff8dc + #fff5ee + #fff0f5 + #ffefd5 + #ffebcd + #ffe4e1 + #ffdead + #ffdab9 + #ffb6c1 + #ffa07a + #ff8c00 + #ff69b4 + #ff4500 + #ff1493 + #ff00ff + #fdf5e6 + #fafad2 + #faebd7 + #f8f8ff + #f5fffa + #f5f5f5 + #f4a460 + #f0fff0 + #f0f8ff + #f08080 + #eee8aa + #e9967a + #e0ffff + #deb887 + #dcdcdc + #db7093 + #d3d3d3 + #d3d3d3 + #cd5c5c + #c71585 + #bdb76b + #bc8f8f + #ba55d3 + #b8860b + #b0e0e6 + #b0c4de + #afeeee + #adff2f + #add8e6 + #a9a9a9 + #a9a9a9 + #9932cc + #98fb98 + #9400d3 + #9370db + #90ee90 + #8fbc8f + #8b4513 + #8b008b + #8b0000 + #8a2be2 + #87cefa + #87ceeb + #808080 + #7cfc00 + #7b68ee + #778899 + #778899 + #708090 + #708090 + #6b8e23 + #6a5acd + #696969 + #696969 + #66cdaa + #6495ed + #5f9ea0 + #556b2f + #48d1cc + #483d8b + #4682b4 + #4169e1 + #3cb371 + #32cd32 + #2f4f4f + #2f4f4f + #2e8b57 + #228b22 + #20b2aa + #1e90ff + #191970 + #00ffff + #00ff7f + #00fa9a + #00ced1 + #00bfff + #008b8b + #006400 + #0000cd + #00008b + #000000 + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ddcbbdc --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + AndroidAppTemplate + + + Hello blank fragment + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7ce5214 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..c6e50a7 --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/network.xml b/app/src/main/res/xml/network.xml new file mode 100644 index 0000000..dca93c0 --- /dev/null +++ b/app/src/main/res/xml/network.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/test/java/com/ttstd/template/ExampleUnitTest.java b/app/src/test/java/com/ttstd/template/ExampleUnitTest.java new file mode 100644 index 0000000..c10a316 --- /dev/null +++ b/app/src/test/java/com/ttstd/template/ExampleUnitTest.java @@ -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 Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..cff5877 --- /dev/null +++ b/build.gradle @@ -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 +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..199d16e --- /dev/null +++ b/gradle.properties @@ -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 + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e32620a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..b34cf45 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name='AndroidAppTemplate' +include ':app'