Explorar o código

1.增加okhttp包
2.增加glide图片框架包
3.增加一些util类
4.增加bindview注解
5.增加mvp类

FailedToRead %!s(int64=3) %!d(string=hai) anos
pai
achega
3e73afd4e6
Modificáronse 36 ficheiros con 1937 adicións e 18 borrados
  1. 2 0
      .idea/gradle.xml
  2. 5 0
      .idea/jarRepositories.xml
  3. 71 10
      app/build.gradle
  4. 2 2
      app/src/androidTest/java/com/efunbox/reader/ExampleInstrumentedTest.java
  5. 52 3
      app/src/main/AndroidManifest.xml
  6. 50 0
      app/src/main/java/com/efunbox/reader/activity/MainActivity.java
  7. 13 0
      app/src/main/java/com/efunbox/reader/annotation/BindView.java
  8. 67 0
      app/src/main/java/com/efunbox/reader/application/EApplication.java
  9. 87 0
      app/src/main/java/com/efunbox/reader/base/BaseActivity.java
  10. 36 0
      app/src/main/java/com/efunbox/reader/base/BaseFragment.java
  11. 42 0
      app/src/main/java/com/efunbox/reader/base/BaseMvpActivity.java
  12. 32 0
      app/src/main/java/com/efunbox/reader/base/BaseMvpFragment.java
  13. 32 0
      app/src/main/java/com/efunbox/reader/base/BasePresenter.java
  14. 32 0
      app/src/main/java/com/efunbox/reader/base/BaseView.java
  15. 20 0
      app/src/main/java/com/efunbox/reader/contract/MainContract.java
  16. 6 0
      app/src/main/java/com/efunbox/reader/model/MainModel.java
  17. 6 0
      app/src/main/java/com/efunbox/reader/model/MainModelImpl.java
  18. 11 0
      app/src/main/java/com/efunbox/reader/presenter/MainPresenter.java
  19. 10 0
      app/src/main/java/com/efunbox/reader/presenter/MainPresenterImpl.java
  20. 15 0
      app/src/main/java/com/efunbox/reader/util/Consts.java
  21. 228 0
      app/src/main/java/com/efunbox/reader/util/CrashHandler.java
  22. 172 0
      app/src/main/java/com/efunbox/reader/util/DeviceUtil.java
  23. 54 0
      app/src/main/java/com/efunbox/reader/util/DeviceUuidFactory.java
  24. 305 0
      app/src/main/java/com/efunbox/reader/util/GlideUtils.java
  25. 62 0
      app/src/main/java/com/efunbox/reader/util/HttpInterceptor.java
  26. 32 0
      app/src/main/java/com/efunbox/reader/util/LiuHaiScreenUtil.java
  27. 158 0
      app/src/main/java/com/efunbox/reader/util/SizeUtils.java
  28. BIN=BIN
      app/src/main/res/drawable/ic_default_image.9.png
  29. BIN=BIN
      app/src/main/res/drawable/icon.png
  30. BIN=BIN
      app/src/main/res/drawable/openimg.jpg
  31. 20 0
      app/src/main/res/layout/activity_main.xml
  32. 288 2
      app/src/main/res/values/colors.xml
  33. 1 1
      app/src/main/res/values/strings.xml
  34. 22 0
      app/src/main/res/values/themes.xml
  35. 2 0
      build.gradle
  36. 2 0
      gradle.properties

+ 2 - 0
.idea/gradle.xml

@@ -1,9 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
   <component name="GradleSettings">
     <option name="linkedExternalProjectsSettings">
       <GradleProjectSettings>
         <option name="testRunner" value="PLATFORM" />
+        <option name="disableWrapperSourceDistributionNotification" value="true" />
         <option name="distributionType" value="DEFAULT_WRAPPED" />
         <option name="externalProjectPath" value="$PROJECT_DIR$" />
         <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle" />

+ 5 - 0
.idea/jarRepositories.xml

@@ -21,5 +21,10 @@
       <option name="name" value="Google" />
       <option name="url" value="https://dl.google.com/dl/android/maven2/" />
     </remote-repository>
+    <remote-repository>
+      <option name="id" value="MavenRepo" />
+      <option name="name" value="MavenRepo" />
+      <option name="url" value="https://repo.maven.apache.org/maven2/" />
+    </remote-repository>
   </component>
 </project>

+ 71 - 10
app/build.gradle

@@ -2,26 +2,70 @@ plugins {
     id 'com.android.application'
 }
 
+def releaseTime() {
+    return new Date().format("yyyyMMddhhmmss", TimeZone.getTimeZone("UTC"))
+}
+
 android {
-    compileSdkVersion 31
-    buildToolsVersion "31.0.0"
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
 
     defaultConfig {
         applicationId "com.efunbox.reader"
-        minSdkVersion 28
-        targetSdkVersion 31
+        minSdkVersion 21
+        targetSdkVersion 30
         versionCode 1
         versionName "1.0"
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        flavorDimensions "versionCode"
+        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "2006"]//添加一个默认渠道号
+        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+    }
+    signingConfigs {
+        efunbox {
+            keyAlias "edufound_key"
+            keyPassword "edufound321"
+            storeFile file("C:/Users/Candy/Desktop/edufound.keystore")
+            storePassword "edufound123"
+        }
     }
-
     buildTypes {
         release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.efunbox
+            zipAlignEnabled true
+            applicationVariants.all { variant ->
+                variant.outputs.each { output ->
+                    def outputFile = output.outputFileName
+                    if (outputFile != null && output.outputFileName.endsWith('.apk')) {
+                        def fileName = "efunbox_tv_v${defaultConfig.versionCode}_${releaseTime()}_${variant.productFlavors[0].manifestPlaceholders.UMENG_CHANNEL_VALUE}_r.apk"
+                        def channel = variant.productFlavors[0].manifestPlaceholders.UMENG_CHANNEL_VALUE;
+                        def newoutputFile = "";
+                        if (channel == ("2006")) {
+                            newoutputFile = "\\义方\\"
+                        }
+                        output.outputFileName = new File(newoutputFile, fileName)
+                    }
+                }
+
+            }
         }
     }
+
+    productFlavors {
+        //义方
+        channel_efunbox {
+            signingConfig signingConfigs.efunbox
+            manifestPlaceholders = [
+                    appCode: "2006",
+                    appIcon: "@drawable/icon",
+            ]
+        }
+    }
+    productFlavors.all {
+        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: manifestPlaceholders.appCode, icon: manifestPlaceholders.appIcon]
+    }
+
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
@@ -30,8 +74,25 @@ android {
 
 dependencies {
 
-    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation 'androidx.appcompat:appcompat:1.0.0'
     testImplementation 'junit:junit:4.+'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+    implementation 'com.github.bumptech.glide:glide:4.12.0'
+    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
+    implementation 'jp.wasabeef:glide-transformations:4.3.0'
+    implementation('com.zhousf.lib:okhttp3:2.9.9') {
+        exclude(module: 'support-annotations')
+        exclude(module: 'gson')
+    }
+    implementation 'com.google.code.gson:gson:2.8.9'
+    implementation 'com.orhanobut:logger:2.2.0'
+    implementation 'com.tencent:mmkv:1.2.11'
+    implementation 'com.uber.autodispose2:autodispose:2.0.0'
+    implementation 'com.uber.autodispose2:autodispose-android:2.0.0'
+    implementation 'com.uber.autodispose2:autodispose-lifecycle:2.0.0'
+    implementation 'com.uber.autodispose2:autodispose-androidx-lifecycle:2.0.0'
+    // 友盟基础组件库(所有友盟业务SDK都依赖基础组件库)
+    implementation 'com.umeng.umsdk:common:9.4.4'// (必选)
+    implementation 'com.umeng.umsdk:asms:1.4.1'// asms包依赖必选
 }

+ 2 - 2
app/src/androidTest/java/com/efunbox/reader/ExampleInstrumentedTest.java

@@ -1,8 +1,8 @@
 package com.efunbox.reader;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;

+ 52 - 3
app/src/main/AndroidManifest.xml

@@ -2,12 +2,61 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.efunbox.reader">
 
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><!-- 切换⽹络通道 -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 本地信息缓存 -->
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!--开关wifi状态,解决国内机型移动⽹络权限问题需要 -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+
     <application
+            android:name=".application.EApplication"
             android:allowBackup="true"
-            android:icon="@mipmap/ic_launcher"
+            android:icon="${icon}"
             android:label="@string/app_name"
-            android:roundIcon="@mipmap/ic_launcher_round"
+            android:roundIcon="${icon}"
             android:supportsRtl="true"
-            android:theme="@style/Theme.EfunboxReader" />
+            android:theme="@style/AppStartTheme">
+
+        <meta-data
+                android:name="android.notch_support"
+                android:value="true" />
+        <meta-data
+                android:name="android.max_aspect"
+                android:value="2.1" />
+        <meta-data
+                android:name="notch.config"
+                android:value="portrait|landscape" />
+
+        <activity
+                android:name=".activity.MainActivity"
+                android:configChanges="screenLayout|screenSize|keyboardHidden|keyboard|orientation"
+                android:maxAspectRatio="2.1"
+                android:resizeableActivity="true"
+                android:screenOrientation="landscape"
+                android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"
+                android:windowSoftInputMode="adjustNothing|stateHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <!--友盟start-->
+        <meta-data
+                android:name="UMENG_APPKEY"
+                android:value="5e34d2fb4ca3574b1800005b" />
+        <meta-data
+                android:name="UMENG_CHANNEL"
+                android:value="${UMENG_CHANNEL_VALUE}" />
+        <!--友盟end-->
+
+    </application>
+
 
 </manifest>

+ 50 - 0
app/src/main/java/com/efunbox/reader/activity/MainActivity.java

@@ -0,0 +1,50 @@
+package com.efunbox.reader.activity;
+
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.efunbox.reader.R;
+import com.efunbox.reader.annotation.BindView;
+import com.efunbox.reader.base.BaseMvpActivity;
+import com.efunbox.reader.contract.MainContract;
+import com.efunbox.reader.presenter.MainPresenter;
+import com.efunbox.reader.util.GlideUtils;
+
+public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
+
+
+    @BindView(id = R.id.text_test)
+    TextView mText;
+
+    @BindView(id = R.id.image_test)
+    ImageView mImage;
+
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.activity_main;
+    }
+
+    @Override
+    public void initView() {
+        mPresenter = new MainPresenter();
+        mPresenter.attachView(this);
+        mText.setText("测试一下bindview");
+        GlideUtils.loadImageSizeKipMemoryCache(this, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2F50%2Fv2-a96199f59f76eef6698e572c19944e40_hd.jpg&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640338680&t=511cd171a44a99e646ce6063b751c78f", mImage);
+    }
+
+    @Override
+    public void showLoading() {
+
+    }
+
+    @Override
+    public void hideLoading() {
+
+    }
+
+    @Override
+    public void onError(String errMessage) {
+
+    }
+}

+ 13 - 0
app/src/main/java/com/efunbox/reader/annotation/BindView.java

@@ -0,0 +1,13 @@
+package com.efunbox.reader.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BindView {
+    int id() default -1;
+}

+ 67 - 0
app/src/main/java/com/efunbox/reader/application/EApplication.java

@@ -0,0 +1,67 @@
+package com.efunbox.reader.application;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Environment;
+
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+import com.efunbox.reader.util.Consts;
+import com.efunbox.reader.util.HttpInterceptor;
+import com.okhttplib.OkHttpUtil;
+import com.okhttplib.annotation.CacheType;
+import com.okhttplib.annotation.Encoding;
+import com.okhttplib.cookie.PersistentCookieJar;
+import com.okhttplib.cookie.cache.SetCookieCache;
+import com.okhttplib.cookie.persistence.SharedPrefsCookiePersistor;
+import com.umeng.commonsdk.UMConfigure;
+
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.File;
+
+public class EApplication extends Application {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        try {
+            Consts.setmApplicAtion(this);
+            XmlPullParserFactory.newInstance().setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            //初始化OKHTTP
+            String downloadFileDir = Environment.getExternalStorageDirectory().getPath() + "/okHttp_download/";
+            String cacheDir = Environment.getExternalStorageDirectory().getPath() + "/okHttp_cache";
+            OkHttpUtil.init(getApplicationContext())
+                    .setConnectTimeout(15)//连接超时时间
+                    .setWriteTimeout(15)//写超时时间
+                    .setReadTimeout(15)//读超时时间
+                    .setMaxCacheSize(10 * 1024 * 1024)//缓存空间大小
+                    .setCacheType(CacheType.FORCE_NETWORK)//缓存类型
+                    .setHttpLogTAG("HttpLog")//设置请求日志标识
+                    .setIsGzip(false)//Gzip压缩,需要服务端支持
+                    .setShowHttpLog(true)//显示请求日志
+                    .setShowLifecycleLog(false)//显示Activity销毁日志
+                    .setRetryOnConnectionFailure(false)//失败后不自动重连
+                    .setCachedDir(new File(cacheDir))//设置缓存目录
+                    .setDownloadFileDir(downloadFileDir)//文件下载保存目录
+                    .setResponseEncoding(Encoding.UTF_8)//设置全局的服务器响应编码
+                    .setRequestEncoding(Encoding.UTF_8)//设置全局的请求参数编码
+//                    .setHttpsCertificate("12306.cer")//设置全局Https证书
+                    .addResultInterceptor(HttpInterceptor.ResultInterceptor)//请求结果拦截器
+                    .addExceptionInterceptor(HttpInterceptor.ExceptionInterceptor)//请求链路异常拦截器
+                    .setCookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getApplicationContext())))//持久化cookie
+                    .build();
+
+
+            //初始化友盟
+            UMConfigure.init(this, UMConfigure.DEVICE_TYPE_PHONE, "");
+        } catch (XmlPullParserException e) {
+            e.printStackTrace();
+        }
+
+
+    }
+}

+ 87 - 0
app/src/main/java/com/efunbox/reader/base/BaseActivity.java

@@ -0,0 +1,87 @@
+package com.efunbox.reader.base;
+
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.efunbox.reader.annotation.BindView;
+import com.efunbox.reader.util.LiuHaiScreenUtil;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import io.reactivex.rxjava3.annotations.Nullable;
+
+public abstract class BaseActivity extends AppCompatActivity {
+
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(this.getLayoutId());
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            LiuHaiScreenUtil.openFullScreenModel(this);
+        }
+        bindViews(this);
+        initView();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    /**
+     * 设置布局
+     *
+     * @return
+     */
+    public abstract int getLayoutId();
+
+    /**
+     * 初始化视图
+     */
+    public abstract void initView();
+
+
+    public static void bindViews(Activity activity) {
+        Class<? extends Activity> activityClass = activity.getClass();//获取activity的class
+        Field[] fields = activityClass.getDeclaredFields();//获取activity的字段
+        //遍历所有的字段
+        for (Field field : fields) {
+            //获取该字段的注解
+            BindView bindView = field.getAnnotation(BindView.class);
+            //!=null 说明该字段有注解并且是指定的注解
+            if (bindView != null) {
+                //获取到注解总传入的数值value
+                int viewId = bindView.id();
+                if (viewId != -1) {
+                    try {
+                        //获取到activity中findViewById的方法
+                        Method findViewByIdMethod = activityClass.getMethod("findViewById", int.class);
+                        try {
+                            //执行findViewById方法
+                            Object resView = findViewByIdMethod.invoke(activity, viewId);
+                            //允许通过反射访问私有变量
+                            field.setAccessible(true);
+                            //把字段的值设置该view的实例
+                            field.set(activity, resView);
+                        } catch (IllegalAccessException e) {
+                            e.printStackTrace();
+                        } catch (InvocationTargetException e) {
+                            e.printStackTrace();
+                        }
+                    } catch (NoSuchMethodException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+}

+ 36 - 0
app/src/main/java/com/efunbox/reader/base/BaseFragment.java

@@ -0,0 +1,36 @@
+package com.efunbox.reader.base;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import io.reactivex.rxjava3.annotations.Nullable;
+
+public abstract class BaseFragment extends Fragment {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+        View view = inflater.inflate(this.getLayoutId(), container, false);
+
+        initView(view);
+        return view;
+    }
+
+
+    /**
+     * 初始化视图
+     *
+     * @param view
+     */
+    protected abstract void initView(View view);
+
+    protected abstract int getLayoutId();
+}

+ 42 - 0
app/src/main/java/com/efunbox/reader/base/BaseMvpActivity.java

@@ -0,0 +1,42 @@
+package com.efunbox.reader.base;
+
+import android.os.Bundle;
+
+import androidx.lifecycle.Lifecycle;
+import autodispose2.AutoDispose;
+import autodispose2.AutoDisposeConverter;
+import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider;
+import io.reactivex.rxjava3.annotations.Nullable;
+
+public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity implements BaseView {
+
+    protected T mPresenter;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+    }
+
+
+    @Override
+    protected void onDestroy() {
+        if (mPresenter != null) {
+            mPresenter.detachView();
+        }
+
+        super.onDestroy();
+    }
+
+    /**
+     * 绑定生命周期 防止MVP内存泄漏
+     *
+     * @param <T>
+     * @return
+     */
+    @Override
+    public <T> AutoDisposeConverter<T> bindAutoDispose() {
+        return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider
+                .from(this, Lifecycle.Event.ON_DESTROY));
+    }
+}

+ 32 - 0
app/src/main/java/com/efunbox/reader/base/BaseMvpFragment.java

@@ -0,0 +1,32 @@
+package com.efunbox.reader.base;
+
+import androidx.lifecycle.Lifecycle;
+import autodispose2.AutoDispose;
+import autodispose2.AutoDisposeConverter;
+import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider;
+
+public abstract class BaseMvpFragment<T extends BasePresenter> extends BaseFragment implements BaseView {
+
+    protected T mPresenter;
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        if (mPresenter != null) {
+            mPresenter.detachView();
+        }
+        super.onDestroyView();
+    }
+
+    /**
+     * 绑定生命周期 防止MVP内存泄漏
+     *
+     * @param <T>
+     * @return
+     */
+    @Override
+    public <T> AutoDisposeConverter<T> bindAutoDispose() {
+        return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider
+                .from(this, Lifecycle.Event.ON_DESTROY));
+    }
+}

+ 32 - 0
app/src/main/java/com/efunbox/reader/base/BasePresenter.java

@@ -0,0 +1,32 @@
+package com.efunbox.reader.base;
+
+public class BasePresenter<V extends BaseView> {
+    protected V mView;
+
+
+    /**
+     * 绑定view,一般在初始化中调用该方法
+     *
+     * @param view view
+     */
+    public void attachView(V view) {
+        this.mView = view;
+    }
+
+    /**
+     * 解除绑定view,一般在onDestroy中调用
+     */
+
+    public void detachView() {
+        this.mView = null;
+    }
+
+    /**
+     * View是否绑定
+     *
+     * @return
+     */
+    public boolean isViewAttached() {
+        return mView != null;
+    }
+}

+ 32 - 0
app/src/main/java/com/efunbox/reader/base/BaseView.java

@@ -0,0 +1,32 @@
+package com.efunbox.reader.base;
+
+import autodispose2.AutoDisposeConverter;
+
+public interface BaseView {
+
+    /**
+     * 显示加载中
+     */
+    void showLoading();
+
+    /**
+     * 隐藏加载
+     */
+    void hideLoading();
+
+    /**
+     * 数据获取失败
+     *
+     * @param errMessage
+     */
+    void onError(String errMessage);
+
+    /**
+     * 绑定Android生命周期 防止RxJava内存泄漏
+     *
+     * @param <T>
+     * @return
+     */
+    <T> AutoDisposeConverter<T> bindAutoDispose();
+
+}

+ 20 - 0
app/src/main/java/com/efunbox/reader/contract/MainContract.java

@@ -0,0 +1,20 @@
+package com.efunbox.reader.contract;
+
+import com.efunbox.reader.base.BaseView;
+
+public interface MainContract {
+    interface Model {
+    }
+
+    interface View extends BaseView {
+    }
+
+    interface Presenter {
+        /**
+         * 是否登录
+         *
+         * @param deviceCode
+         */
+        void isLogin(String deviceCode);
+    }
+}

+ 6 - 0
app/src/main/java/com/efunbox/reader/model/MainModel.java

@@ -0,0 +1,6 @@
+package com.efunbox.reader.model;
+
+import com.efunbox.reader.contract.MainContract;
+
+public class MainModel implements MainContract.Model {
+}

+ 6 - 0
app/src/main/java/com/efunbox/reader/model/MainModelImpl.java

@@ -0,0 +1,6 @@
+package com.efunbox.reader.model;
+
+import com.efunbox.reader.contract.MainContract;
+
+public class MainModelImpl implements MainContract.Model {
+}

+ 11 - 0
app/src/main/java/com/efunbox/reader/presenter/MainPresenter.java

@@ -0,0 +1,11 @@
+package com.efunbox.reader.presenter;
+
+import com.efunbox.reader.base.BasePresenter;
+import com.efunbox.reader.contract.MainContract;
+
+public class MainPresenter extends BasePresenter implements MainContract.Presenter {
+    @Override
+    public void isLogin(String deviceCode) {
+        
+    }
+}

+ 10 - 0
app/src/main/java/com/efunbox/reader/presenter/MainPresenterImpl.java

@@ -0,0 +1,10 @@
+package com.efunbox.reader.presenter;
+
+import com.efunbox.reader.contract.MainContract;
+
+public class MainPresenterImpl implements MainContract.Presenter {
+    @Override
+    public void isLogin(String deviceCode) {
+        
+    }
+}

+ 15 - 0
app/src/main/java/com/efunbox/reader/util/Consts.java

@@ -0,0 +1,15 @@
+package com.efunbox.reader.util;
+
+import android.app.Application;
+
+public class Consts {
+    public static Application getmApplicAtion() {
+        return mApplicAtion;
+    }
+
+    public static void setmApplicAtion(Application mApplicAtion) {
+        Consts.mApplicAtion = mApplicAtion;
+    }
+
+    private static Application mApplicAtion;
+}

+ 228 - 0
app/src/main/java/com/efunbox/reader/util/CrashHandler.java

@@ -0,0 +1,228 @@
+package com.efunbox.reader.util;
+
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.Process;
+import android.widget.Toast;
+
+import com.orhanobut.logger.Logger;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
+ *
+ * @author user
+ */
+public class CrashHandler implements UncaughtExceptionHandler {
+
+    // 系统默认的UncaughtException处理类
+    private UncaughtExceptionHandler mDefaultHandler;
+    // CrashHandler实例
+    private static CrashHandler INSTANCE = new CrashHandler();
+    // 程序的Context对象
+    private Context mContext;
+    // 用来存储设备信息和异常信息
+    private Map<String, String> infos = new HashMap<String, String>();
+
+    // 用于格式化日期,作为日志文件名的一部分
+    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+
+    private DateFormat formatterWeb = new SimpleDateFormat("yyyyMMdd");
+
+    private DateFormat newFormatter = new SimpleDateFormat("yyyyMMdd_HHmmss");
+
+
+    /**
+     * 保证只有一个CrashHandler实例
+     */
+    private CrashHandler() {
+    }
+
+    /**
+     * 获取CrashHandler实例 ,单例模式
+     */
+    public static CrashHandler getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * 初始化
+     *
+     * @param context
+     */
+    public void init(Context context) {
+        mContext = context;
+        // 获取系统默认的UncaughtException处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        // 设置该CrashHandler为程序的默认处理器
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * 当UncaughtException发生时会转入该函数来处理
+     */
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        if (!handleException(ex) && mDefaultHandler != null) {
+            // 如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else {
+            try {
+                Thread.sleep(4000);
+            } catch (InterruptedException e) {
+                Logger.e(e.getMessage());
+            }
+            // 退出程序
+//            android.os.Process.killProcess(android.os.Process.myPid());
+//            System.exit(1);
+        }
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+        // 收集设备参数信息
+        collectDeviceInfo(mContext);
+        // 保存日志文件
+        final String fileName = saveCrashInfo2File(ex);
+        String date = formatterWeb.format(new Date());
+//        //上传到服务器
+//        final String url = "/logs/android/";
+//        String stu = "";
+//        Logger.e("url:" + url);
+//        if (!EduFoundUtil.isEmpty(Consts.mUserData.userInfo.getStu_no())) {
+//            stu = Consts.mUserData.userInfo.getStu_no();
+//        } else {
+//            stu = newFormatter.format(new Date());
+//        }
+//        final String finalStu = date + "_" + Consts.APP_CODE + "_" + stu + ".log";
+//        new Thread() {
+//            @Override
+//            public void run() {
+//                FtpUtil.ftpUpload(url, finalStu, Consts.LOG_URL + fileName, new FtpUtil.upLoadListrner() {
+//                    @Override
+//                    public void onLoadOver() {
+//                        android.os.Process.killProcess(android.os.Process.myPid());
+//                        System.exit(1);
+//                    }
+//                });
+//            }
+//        }.start();
+//
+//        // 使用Toast来显示异常信息
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                Toast.makeText(mContext, "很抱歉,程序出现异常.", Toast.LENGTH_SHORT).show();
+                Looper.loop();
+                Process.killProcess(Process.myPid());
+                System.exit(1);
+            }
+        }.start();
+
+        return true;
+    }
+
+    /**
+     * 收集设备参数信息
+     *
+     * @param ctx
+     */
+    public void collectDeviceInfo(Context ctx) {
+//        try {
+//            PackageManager pm = ctx.getPackageManager();
+//            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+//            if (pi != null) {
+//                String versionName = pi.versionName == null ? "null" : pi.versionName;
+//                String versionCode = pi.versionCode + "";
+//                infos.put("versionName", versionName);
+//                infos.put("versionCode", versionCode);
+////                infos.put("APP_ID", Consts.APP_CODE);
+////                infos.put("STU_ID", Consts.mUserData.userInfo.getStu_no());
+////                infos.put("UUID", Consts.mUserData.userInfo.getUid());
+//
+//            }
+//        } catch (NameNotFoundException e) {
+//            Logger.e("an error occured when collect package info" + e);
+//        }
+//        Field[] fields = Build.class.getDeclaredFields();
+//        for (Field field : fields) {
+//            try {
+//                field.setAccessible(true);
+//                infos.put(field.getName(), field.get(null).toString());
+//                Logger.e(field.getName() + " : " + field.get(null));
+//            } catch (Exception e) {
+//                Logger.e("an error occured when collect crash info", e);
+//            }
+//        }
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @param ex
+     * @return 返回文件名称, 便于将文件传送到服务器
+     */
+    private String saveCrashInfo2File(Throwable ex) {
+//        File file = new File(Consts.LOG_URL);
+//        if (!file.exists()) {
+//            file.mkdirs();
+//        }
+//        StringBuffer sb = new StringBuffer();
+//        for (Map.Entry<String, String> entry : infos.entrySet()) {
+//            String key = entry.getKey();
+//            String value = entry.getValue();
+//            sb.append(key + "=" + value + "\n");
+//            sb.append("----------------------------------------" + "\n");
+//        }
+//
+//
+//        Writer writer = new StringWriter();
+//        PrintWriter printWriter = new PrintWriter(writer);
+//        ex.printStackTrace(printWriter);
+//        Throwable cause = ex.getCause();
+//        while (cause != null) {
+//            cause.printStackTrace(printWriter);
+//            cause = cause.getCause();
+//        }
+//        printWriter.close();
+//        String result = writer.toString();
+//        sb.append(result);
+//        try {
+//            long timestamp = System.currentTimeMillis();
+//            String mSaveDate = formatter.format(new Date());
+//            String stu = "";
+//            if (!EduFoundUtil.isEmpty(Consts.mUserData.userInfo.getStu_no())) {
+//                stu = Consts.mUserData.userInfo.getStu_no();
+//            }
+//            String fileName = stu + "" + mSaveDate + "--" + timestamp + ".log";
+//            if (Environment.getExternalStorageState().equals(
+//                    Environment.MEDIA_MOUNTED)) {
+//                FileOutputStream fos = new FileOutputStream(
+//                        Consts.LOG_URL + fileName);
+//                fos.write(sb.toString().getBytes());
+//                fos.close();
+//            }
+//            return fileName;
+//        } catch (Exception e) {
+//            Logger.e("an error occured while writing file...", e);
+//        }
+        return null;
+    }
+}

+ 172 - 0
app/src/main/java/com/efunbox/reader/util/DeviceUtil.java

@@ -0,0 +1,172 @@
+package com.efunbox.reader.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.telephony.TelephonyManager;
+import android.util.DisplayMetrics;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.util.Locale;
+
+public class DeviceUtil {
+
+
+    DisplayMetrics metric = new DisplayMetrics();
+
+    /**
+     * @return 序列号 SerialNumber
+     * @author zhangmengjie
+     */
+    public String SerialNumber() {
+
+        return Build.SERIAL;
+    }
+
+
+    public int getWidth(Activity context) {
+        context.getWindowManager().getDefaultDisplay().getMetrics(metric);
+        return metric.widthPixels; // 屏幕宽度(像素)
+    }
+
+    public int getHeight(Activity context) {
+        context.getWindowManager().getDefaultDisplay().getMetrics(metric);
+        return metric.heightPixels; // 屏幕高度(像素)
+    }
+
+    public float getDensity(Activity context) {
+        context.getWindowManager().getDefaultDisplay().getMetrics(metric);
+        return metric.density; // 屏幕密度(0.75 / 1.0 / 1.5)
+    }
+
+    public int getDensityDpi(Activity context) {
+        context.getWindowManager().getDefaultDisplay().getMetrics(metric);
+        return metric.densityDpi; // // 屏幕密度DPI(120 / 160 / 240)
+    }
+
+    // md5加密
+    public String md5Encode(String inStr) {
+        MessageDigest md5 = null;
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        } catch (Exception e) {
+            System.out.println(e.toString());
+            e.printStackTrace();
+            return "";
+        }
+
+        byte[] byteArray = null;
+        try {
+            byteArray = inStr.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        byte[] md5Bytes = md5.digest(byteArray);
+        StringBuffer hexValue = new StringBuffer();
+        for (int i = 0; i < md5Bytes.length; i++) {
+            int val = ((int) md5Bytes[i]) & 0xff;
+            if (val < 16) {
+                hexValue.append("0");
+            }
+            hexValue.append(Integer.toHexString(val));
+        }
+        return hexValue.toString();
+    }
+
+    /**
+     * @return VersionCode
+     * @author zhangmengjie
+     */
+    public String getVersionCode(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        PackageInfo packageInfo;
+        String versionCode = "";
+        try {
+            packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
+            versionCode = packageInfo.versionCode + "";
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return versionCode;
+    }
+
+    /**
+     * @return VersionName
+     * @author zhangmengjie
+     */
+    public String getVersionName(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        PackageInfo packageInfo;
+        String versionName = "";
+        try {
+            packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
+            versionName = packageInfo.versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return versionName;
+    }
+
+    /**
+     * 获取当前手机系统语言。
+     *
+     * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN”
+     */
+    public String getSystemLanguage() {
+        return Locale.getDefault().getLanguage();
+    }
+
+    /**
+     * 获取当前系统上的语言列表(Locale列表)
+     *
+     * @return 语言列表
+     */
+    public Locale[] getSystemLanguageList() {
+        return Locale.getAvailableLocales();
+    }
+
+    /**
+     * 获取当前手机系统版本号
+     *
+     * @return 系统版本号
+     */
+    public String getSystemVersion() {
+        return Build.VERSION.RELEASE;
+    }
+
+    /**
+     * 获取手机型号
+     *
+     * @return 手机型号
+     */
+    public String getSystemModel() {
+        return Build.MODEL;
+    }
+
+    /**
+     * 获取手机厂商
+     *
+     * @return 手机厂商
+     */
+    public String getDeviceBrand() {
+        return Build.BRAND;
+    }
+
+//    /**
+//     * 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限)
+//     *
+//     * @return 手机IMEI
+//     */
+//    public String getIMEI(Context ctx) {
+//        TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
+//        if (tm != null) {
+//            return tm.getDeviceId();
+//        }
+//        return null;
+//    }
+
+}

+ 54 - 0
app/src/main/java/com/efunbox/reader/util/DeviceUuidFactory.java

@@ -0,0 +1,54 @@
+package com.efunbox.reader.util;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import java.io.UnsupportedEncodingException;
+import java.util.UUID;
+
+public class DeviceUuidFactory {
+    public static final String UUID_SPKEY = "uuid";
+    protected static UUID uuid;
+
+    public DeviceUuidFactory(Context context) {
+        if (uuid == null) {
+            synchronized (DeviceUuidFactory.class) {
+                if (uuid == null) {
+                    try {
+                        final String androidId = Settings.Secure.getString(
+                                context.getContentResolver(), Settings.Secure.ANDROID_ID);
+                        if (!"9774d56d682e549c".equals(androidId)) {
+                            uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
+                        } else {
+                            @SuppressLint("MissingPermission") final String deviceId = ((TelephonyManager) context
+                                    .getSystemService(Context.TELEPHONY_SERVICE))
+                                    .getDeviceId();
+                            uuid = deviceId != null ? UUID.nameUUIDFromBytes(deviceId
+                                    .getBytes("utf8")) : UUID.randomUUID();
+                            try {
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    } catch (UnsupportedEncodingException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+//                SPutil.setPrefString(context, UUID_SPKEY, uuid.toString());
+            }
+        }
+    }
+
+
+    /**
+     * @return UUID
+     */
+    public static UUID getUuid() {
+        if (uuid == null || uuid.equals("") || uuid.equals("null")) {
+            return null;
+        }
+        return uuid;
+    }
+}

+ 305 - 0
app/src/main/java/com/efunbox/reader/util/GlideUtils.java

@@ -0,0 +1,305 @@
+package com.efunbox.reader.util;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.MultiTransformation;
+import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.Target;
+import com.efunbox.reader.R;
+
+import java.io.File;
+
+import androidx.annotation.ColorInt;
+import io.reactivex.rxjava3.annotations.Nullable;
+import jp.wasabeef.glide.transformations.BlurTransformation;
+import jp.wasabeef.glide.transformations.CropCircleWithBorderTransformation;
+import jp.wasabeef.glide.transformations.GrayscaleTransformation;
+import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
+
+/**
+ * Glide工具类
+ */
+public class GlideUtils {
+    /*** 占位图 */
+    public static int placeholderImage = R.drawable.ic_default_image;
+    /*** 错误图 */
+    public static int errorImage = R.drawable.ic_default_image;
+
+    /**
+     * 加载图片(默认)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     */
+    public static void loadImage(Context context, String url, ImageView imageView) {
+        RequestOptions options = new RequestOptions()
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+
+    }
+
+    /**
+     * 指定图片大小;使用override()方法指定了一个图片的尺寸。
+     * Glide现在只会将图片加载成width*height像素的尺寸,而不会管你的ImageView的大小是多少了。
+     * 如果你想加载一张图片的原始尺寸的话,可以使用Target.SIZE_ORIGINAL关键字----override(Target.SIZE_ORIGINAL)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     * @param width     图片宽度
+     * @param height    图片高度
+     */
+    public static void loadImageSize(Context context, String url, ImageView imageView, int width, int height) {
+        RequestOptions options = new RequestOptions()
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage)             //错误图
+                .override(width, height)
+                .priority(Priority.HIGH);
+        Glide.with(context).load(url).apply(options).into(imageView);
+
+    }
+
+    /**
+     * 禁用内存缓存功能
+     * diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收五种参数:
+     * <p>
+     * DiskCacheStrategy.NONE: 表示不缓存任何内容。
+     * DiskCacheStrategy.DATA: 表示只缓存原始图片。
+     * DiskCacheStrategy.RESOURCE: 表示只缓存转换过后的图片。
+     * DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
+     * DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)。
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     */
+
+    public static void loadImageSizeKipMemoryCache(Context context, String url, ImageView imageView) {
+        RequestOptions options = new RequestOptions()
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage)             //错误图
+                .skipMemoryCache(true);          //禁用掉Glide的内存缓存功能
+        Glide.with(context).load(url).apply(options).into(imageView);
+
+    }
+
+    /**
+     * 预先加载图片
+     * 在使用图片之前,预先把图片加载到缓存,调用了预加载之后,我们以后想再去加载这张图片就会非常快了,
+     * 因为Glide会直接从缓存当中去读取图片并显示出来
+     *
+     * @param context 上下文
+     * @param url     链接
+     */
+    public static void preloadImage(Context context, String url) {
+        Glide.with(context).load(url).preload();
+    }
+
+    /**
+     * 加载圆形图片
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     */
+    public static void loadCircleImage(Context context, String url, ImageView imageView) {
+        RequestOptions options = new RequestOptions()
+                .centerCrop()
+                .circleCrop()//设置圆形
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage)             //错误图
+                .priority(Priority.HIGH);
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * 加载圆形带边框图片
+     *
+     * @param context     上下文
+     * @param url         链接
+     * @param imageView   ImageView
+     * @param borderSize  边框宽度 px
+     * @param borderColor 边框颜色
+     */
+    public static void loadCircleWithBorderImage(Context context, String url, ImageView imageView,
+                                                 float borderSize, @ColorInt int borderColor) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new CropCircleWithBorderTransformation(SizeUtils.px2dp(context, borderSize), borderColor)
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * 加载圆角图片
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     * @param radius    圆角 px
+     */
+    public static void loadRoundCircleImage(Context context, String url, ImageView imageView,
+                                            float radius) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new RoundedCornersTransformation(SizeUtils.px2dp(context, radius), 0,
+                                RoundedCornersTransformation.CornerType.ALL)
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+
+    }
+
+
+    /**
+     * 加载圆角图片-指定任意部分圆角(图片上、下、左、右四个角度任意定义)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     * @param radius    圆角 px
+     * @param type      圆角位置
+     */
+    public static void loadRoundCircleImage(Context context, String url, ImageView imageView,
+                                            float radius, RoundedCornersTransformation.CornerType type) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new RoundedCornersTransformation(SizeUtils.px2dp(context, radius), 0, type)
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * 加载模糊图片(自定义透明度)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     * @param blur      模糊度,一般1-100够了,越大越模糊
+     */
+    public static void loadBlurImage(Context context, String url, ImageView imageView, int blur) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new BlurTransformation(blur)
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * 加载模糊图片(自定义透明度)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     * @param blur      模糊度,一般1-100够了,越大越模糊
+     * @param sampling  取样
+     */
+    public static void loadBlurImage(Context context, String url, ImageView imageView, int blur, int sampling) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new BlurTransformation(blur, sampling)
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * 加载灰度(黑白)图片(自定义透明度)
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     */
+    public static void loadBlackImage(Context context, String url, ImageView imageView) {
+        RequestOptions options = RequestOptions.bitmapTransform(
+                new MultiTransformation<>(
+                        new CenterCrop(),
+                        new GrayscaleTransformation()
+                ))
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context).load(url).apply(options).into(imageView);
+    }
+
+    /**
+     * Glide.with(this).asGif()    //强制指定加载动态图片
+     * 如果加载的图片不是gif,则asGif()会报错, 当然,asGif()不写也是可以正常加载的。
+     * 加入了一个asBitmap()方法,这个方法的意思就是说这里只允许加载静态图片,不需要Glide去帮我们自动进行图片格式的判断了。
+     * 如果你传入的还是一张GIF图的话,Glide会展示这张GIF图的第一帧,而不会去播放它。
+     *
+     * @param context   上下文
+     * @param url       链接
+     * @param imageView ImageView
+     */
+    private void loadGif(Context context, String url, ImageView imageView) {
+        RequestOptions options = new RequestOptions()
+                .placeholder(placeholderImage) //占位图
+                .error(errorImage);            //错误图
+        Glide.with(context)
+                .load(url)
+                .apply(options)
+                .listener(new RequestListener<Drawable>() {
+                    @Override
+                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
+                        return false;
+                    }
+                })
+                .into(imageView);
+
+    }
+
+    /**
+     * 下载图片
+     * 在RequestListener的onResourceReady方法里面获取下载File图片
+     * new RequestListener<File>() {
+     * *@Override
+     * public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
+     * return false;
+     * }
+     * <p>
+     * *@Override
+     * public boolean onResourceReady(File resource, Object model, Target<File> target, DataSource dataSource, boolean isFirstResource) {
+     * //resource即为下载取得的图片File
+     * return false;
+     * }
+     * }
+     *
+     * @param context         上下文
+     * @param url             下载链接
+     * @param requestListener 下载监听
+     */
+    public void downloadImage(final Context context, final String url, RequestListener<File> requestListener) {
+        Glide.with(context)
+                .downloadOnly()
+                .load(url)
+                .addListener(requestListener).preload();
+    }
+}

+ 62 - 0
app/src/main/java/com/efunbox/reader/util/HttpInterceptor.java

@@ -0,0 +1,62 @@
+package com.efunbox.reader.util;
+
+import com.okhttplib.HttpInfo;
+import com.okhttplib.interceptor.ExceptionInterceptor;
+import com.okhttplib.interceptor.ResultInterceptor;
+
+/**
+ * Http拦截器
+ * 1、请求结果统一预处理拦截器
+ * 2、请求链路异常信息拦截器
+ *
+ * @author zhousf
+ */
+public class HttpInterceptor {
+
+    /**
+     * 请求结果统一预处理拦截器
+     * 该拦截器会对所有网络请求返回结果进行预处理并修改
+     */
+    public static com.okhttplib.interceptor.ResultInterceptor ResultInterceptor = new ResultInterceptor() {
+        @Override
+        public HttpInfo intercept(HttpInfo info) throws Exception {
+            //请求结果预处理:可以进行GSon过滤与解析
+            return info;
+        }
+    };
+
+    /**
+     * 请求链路异常信息拦截器
+     * 该拦截器会发送网络请求时链路异常信息进行拦截处理
+     */
+    public static com.okhttplib.interceptor.ExceptionInterceptor ExceptionInterceptor = new ExceptionInterceptor() {
+        @Override
+        public HttpInfo intercept(HttpInfo info) throws Exception {
+            switch (info.getRetCode()) {
+                case HttpInfo.NonNetwork:
+                    info.setRetDetail("网络中断");
+                    break;
+                case HttpInfo.CheckURL:
+                    info.setRetDetail("网络地址错误[" + info.getNetCode() + "]");
+                    break;
+                case HttpInfo.ProtocolException:
+                    info.setRetDetail("协议类型错误[" + info.getNetCode() + "]");
+                    break;
+                case HttpInfo.CheckNet:
+                    info.setRetDetail("请检查网络连接是否正常[" + info.getNetCode() + "]");
+                    break;
+                case HttpInfo.ConnectionTimeOut:
+                    info.setRetDetail("连接超时");
+                    break;
+                case HttpInfo.WriteAndReadTimeOut:
+                    info.setRetDetail("读写超时");
+                    break;
+                case HttpInfo.ConnectionInterruption:
+                    info.setRetDetail("连接中断");
+                    break;
+            }
+            return info;
+        }
+    };
+}
+

+ 32 - 0
app/src/main/java/com/efunbox/reader/util/LiuHaiScreenUtil.java

@@ -0,0 +1,32 @@
+package com.efunbox.reader.util;
+
+import android.app.Activity;
+import android.os.Build;
+import android.view.DisplayCutout;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.annotation.RequiresApi;
+
+public class LiuHaiScreenUtil {
+
+
+    @RequiresApi(api = Build.VERSION_CODES.P)
+    public static void openFullScreenModel(Activity activity) {
+//        activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+        lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        activity.getWindow().setAttributes(lp);
+        View decorView = activity.getWindow().getDecorView();
+        int systemUiVisibility = decorView.getSystemUiVisibility();
+        int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+        systemUiVisibility |= flags;
+        activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility);
+    }
+}

+ 158 - 0
app/src/main/java/com/efunbox/reader/util/SizeUtils.java

@@ -0,0 +1,158 @@
+package com.efunbox.reader.util;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+
+import com.orhanobut.logger.Logger;
+
+public class SizeUtils {
+
+    private SizeUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * dp转px
+     *
+     * @param context 上下文
+     * @param dpValue dp值
+     * @return px值
+     */
+    public static int dp2px(Context context, float dpValue) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+    /**
+     * px转dp
+     *
+     * @param context 上下文
+     * @param pxValue px值
+     * @return dp值
+     */
+    public static int px2dp(Context context, float pxValue) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (pxValue / scale + 0.5f);
+    }
+
+    /**
+     * sp转px
+     *
+     * @param context 上下文
+     * @param spValue sp值
+     * @return px值
+     */
+    public static int sp2px(Context context, float spValue) {
+        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
+        return (int) (spValue * fontScale + 0.5f);
+    }
+
+    /**
+     * px转sp
+     *
+     * @param context 上下文
+     * @param pxValue px值
+     * @return sp值
+     */
+    public static int px2sp(Context context, float pxValue) {
+        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
+        return (int) (pxValue / fontScale + 0.5f);
+    }
+
+    /**
+     * 各种单位转换
+     * <p>该方法存在于TypedValue</p>
+     *
+     * @param unit    单位
+     * @param value   值
+     * @param metrics DisplayMetrics
+     * @return 转换结果
+     */
+    public static float applyDimension(int unit, float value, DisplayMetrics metrics) {
+        switch (unit) {
+            case TypedValue.COMPLEX_UNIT_PX:
+                return value;
+            case TypedValue.COMPLEX_UNIT_DIP:
+                return value * metrics.density;
+            case TypedValue.COMPLEX_UNIT_SP:
+                return value * metrics.scaledDensity;
+            case TypedValue.COMPLEX_UNIT_PT:
+                return value * metrics.xdpi * (1.0f / 72);
+            case TypedValue.COMPLEX_UNIT_IN:
+                return value * metrics.xdpi;
+            case TypedValue.COMPLEX_UNIT_MM:
+                return value * metrics.xdpi * (1.0f / 25.4f);
+        }
+        return 0;
+    }
+
+    /**
+     * 在onCreate()即可强行获取View的尺寸
+     * <p>需回调onGetSizeListener接口,在onGetSize中获取view宽高</p>
+     * <p>用法示例如下所示</p>
+     * <pre>
+     * SizeUtils.forceGetViewSize(view, new SizeUtils.onGetSizeListener() {
+     *     Override
+     *     public void onGetSize(View view) {
+     *         view.getWidth();
+     *     }
+     * });
+     * </pre>
+     *
+     * @param view     视图
+     * @param listener 监听器
+     */
+    public static void forceGetViewSize(final View view, final onGetSizeListener listener) {
+        view.post(new Runnable() {
+            @Override
+            public void run() {
+                if (listener != null) {
+                    listener.onGetSize(view);
+                }
+            }
+        });
+    }
+
+    /**
+     * 获取到View尺寸的监听
+     */
+    public interface onGetSizeListener {
+        void onGetSize(View view);
+    }
+
+    public static void setListener(onGetSizeListener listener) {
+        mListener = listener;
+    }
+
+    private static onGetSizeListener mListener;
+
+    /**
+     * ListView中提前测量View尺寸,如headerView
+     * <p>用的时候去掉注释拷贝到ListView中即可</p>
+     * <p>参照以下注释代码</p>
+     *
+     * @param view 视图
+     */
+    public static void measureViewInLV(View view) {
+        Logger.e("tips", "U should copy the following code.");
+        /*
+        ViewGroup.LayoutParams p = view.getLayoutParams();
+        if (p == null) {
+            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+        int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
+        int height;
+        int tempHeight = p.height;
+        if (tempHeight > 0) {
+            height = MeasureSpec.makeMeasureSpec(tempHeight,
+                    MeasureSpec.EXACTLY);
+        } else {
+            height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        }
+        view.measure(width, height);
+        */
+    }
+}

BIN=BIN
app/src/main/res/drawable/ic_default_image.9.png


BIN=BIN
app/src/main/res/drawable/icon.png


BIN=BIN
app/src/main/res/drawable/openimg.jpg


+ 20 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <TextView
+            android:id="@+id/text_test"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:text="123"
+            android:textColor="@color/black"
+            android:textSize="30sp"></TextView>
+
+    <ImageView
+            android:id="@+id/image_test"
+            android:layout_width="300dp"
+            android:layout_height="300dp"></ImageView>
+</FrameLayout>

+ 288 - 2
app/src/main/res/values/colors.xml

@@ -5,6 +5,292 @@
     <color name="purple_700">#FF3700B3</color>
     <color name="teal_200">#FF03DAC5</color>
     <color name="teal_700">#FF018786</color>
-    <color name="black">#FF000000</color>
-    <color name="white">#FFFFFFFF</color>
+    <color name="transparent">#00000000</color><!--透明色 -->
+      
+    <color name="white">#FFFFFF</color><!--白色 -->
+      
+    <color name="ivory">#FFFFF0</color><!--象牙色 -->
+      
+    <color name="lightyellow">#FFFFE0</color><!--亮黄色 -->
+      
+    <color name="yellow">#FFFF00</color><!--黄色 -->
+      
+    <color name="snow">#FFFAFA</color><!--雪白色 -->
+      
+    <color name="floralwhite">#FFFAF0</color><!--花白色 -->
+      
+    <color name="lemonchiffon">#FFFACD</color><!--柠檬绸色 -->
+      
+    <color name="cornsilk">#FFF8DC</color><!--米绸色 -->
+      
+    <color name="seashell">#FFF5EE</color><!--海贝色 -->
+      
+    <color name="lavenderblush">#FFF0F5</color><!--淡紫红 -->
+      
+    <color name="papayawhip">#FFEFD5</color><!--番木色 -->
+      
+    <color name="blanchedalmond">#FFEBCD</color><!--白杏色 -->
+      
+    <color name="mistyrose">#FFE4E1</color><!--浅玫瑰色 -->
+      
+    <color name="bisque">#FFE4C4</color><!--桔黄色 -->
+      
+    <color name="moccasin">#FFE4B5</color><!--鹿皮色 -->
+      
+    <color name="navajowhite">#FFDEAD</color><!--纳瓦白 -->
+      
+    <color name="peachpuff">#FFDAB9</color><!--桃色 -->
+      
+    <color name="gold">#FFD700</color><!--金色 -->
+      
+    <color name="pink">#FFC0CB</color><!--粉红色 -->
+      
+    <color name="lightpink">#FFB6C1</color><!--亮粉红色 -->
+      
+    <color name="orange">#FFA500</color><!--橙色 -->
+      
+    <color name="lightsalmon">#FFA07A</color><!--亮肉色 -->
+      
+    <color name="darkorange">#FF8C00</color><!--暗桔黄色 -->
+      
+    <color name="coral">#FF7F50</color><!--珊瑚色 -->
+      
+    <color name="hotpink">#FF69B4</color><!--热粉红色 -->
+      
+    <color name="tomato">#FF6347</color><!--西红柿色 -->
+      
+    <color name="orangered">#FF4500</color><!--红橙色 -->
+      
+    <color name="deeppink">#FF1493</color><!--深粉红色 -->
+      
+    <color name="fuchsia">#FF00FF</color><!--紫红色 -->
+       
+    <color name="red">#FF0000</color><!--红色 -->
+      
+    <color name="oldlace">#FDF5E6</color><!--老花色 -->
+      
+    <color name="lightgoldenrodyellow">#FAFAD2</color><!--亮金黄色 -->
+      
+    <color name="linen">#FAF0E6</color><!--亚麻色 -->
+      
+    <color name="antiquewhite">#FAEBD7</color><!--古董白 -->
+      
+    <color name="salmon">#FA8072</color><!--鲜肉色 -->
+      
+    <color name="ghostwhite">#F8F8FF</color><!--幽灵白 -->
+      
+    <color name="mintcream">#F5FFFA</color><!--薄荷色 -->
+      
+    <color name="whitesmoke">#F5F5F5</color><!--烟白色 -->
+      
+    <color name="beige">#F5F5DC</color><!--米色 -->
+      
+    <color name="wheat">#F5DEB3</color><!--浅黄色 -->
+      
+    <color name="sandybrown">#F4A460</color><!--沙褐色 -->
+      
+    <color name="azure">#F0FFFF</color><!--天蓝色 -->
+      
+    <color name="honeydew">#F0FFF0</color><!--蜜色 -->
+      
+    <color name="aliceblue">#F0F8FF</color><!--艾利斯兰 -->
+      
+    <color name="khaki">#F0E68C</color><!--黄褐色 -->
+      
+    <color name="lightcoral">#F08080</color><!--亮珊瑚色 -->
+      
+    <color name="palegoldenrod">#EEE8AA</color><!--苍麒麟色 -->
+      
+    <color name="violet">#EE82EE</color><!--紫罗兰色 -->
+      
+    <color name="darksalmon">#E9967A</color><!--暗肉色 -->
+      
+    <color name="lavender">#E6E6FA</color><!--淡紫色 -->
+      
+    <color name="lightcyan">#E0FFFF</color><!--亮青色 -->
+      
+    <color name="burlywood">#DEB887</color><!--实木色 -->
+      
+    <color name="plum">#DDA0DD</color><!--洋李色 -->
+      
+    <color name="gainsboro">#DCDCDC</color><!--淡灰色 -->
+      
+    <color name="crimson">#DC143C</color><!--暗深红色 -->
+      
+    <color name="palevioletred">#DB7093</color><!--苍紫罗兰色 -->
+      
+    <color name="goldenrod">#DAA520</color><!--金麒麟色 -->
+      
+    <color name="orchid">#DA70D6</color><!--淡紫色 -->
+      
+    <color name="thistle">#D8BFD8</color><!--蓟色 -->
+      
+    <color name="lightgray">#D3D3D3</color><!--亮灰色 -->
+      
+    <color name="tan">#D2B48C</color><!--茶色 -->
+      
+    <color name="chocolate">#D2691E</color><!--巧可力色 -->
+      
+    <color name="peru">#CD853F</color><!--秘鲁色 -->
+      
+    <color name="indianred">#CD5C5C</color><!--印第安红 -->
+      
+    <color name="mediumvioletred">#C71585</color><!--中紫罗兰色 -->
+      
+    <color name="silver">#C0C0C0</color><!--银色 -->
+      
+    <color name="darkkhaki">#BDB76B</color><!--暗黄褐色 -->
+      
+    <color name="rosybrown">#BC8F8F</color><!--褐玫瑰红 -->
+      
+    <color name="mediumorchid">#BA55D3</color><!--中粉紫色 -->
+      
+    <color name="darkgoldenrod">#B8860B</color><!--暗金黄色 -->
+      
+    <color name="firebrick">#B22222</color><!--火砖色 -->
+      
+    <color name="powderblue">#B0E0E6</color><!--粉蓝色 -->
+      
+    <color name="lightsteelblue">#B0C4DE</color><!--亮钢兰色 -->
+      
+    <color name="paleturquoise">#AFEEEE</color><!--苍宝石绿 -->
+      
+    <color name="greenyellow">#ADFF2F</color><!--黄绿色 -->
+      
+    <color name="lightblue">#ADD8E6</color><!--亮蓝色 -->
+      
+    <color name="darkgray">#A9A9A9</color><!--暗灰色 -->
+      
+    <color name="brown">#A52A2A</color><!--褐色 -->
+      
+    <color name="sienna">#A0522D</color><!--赭色 -->
+      
+    <color name="darkorchid">#9932CC</color><!--暗紫色 -->
+      
+    <color name="palegreen">#98FB98</color><!--苍绿色 -->
+      
+    <color name="darkviolet">#9400D3</color><!--暗紫罗兰色 -->
+      
+    <color name="mediumpurple">#9370DB</color><!--中紫色 -->
+      
+    <color name="lightgreen">#90EE90</color><!--亮绿色 -->
+      
+    <color name="darkseagreen">#8FBC8F</color><!--暗海兰色 -->
+      
+    <color name="saddlebrown">#8B4513</color><!--重褐色 -->
+      
+    <color name="darkmagenta">#8B008B</color><!--暗洋红 -->
+      
+    <color name="darkred">#8B0000</color><!--暗红色 -->
+      
+    <color name="blueviolet">#8A2BE2</color><!--紫罗兰蓝色 -->
+      
+    <color name="lightskyblue">#87CEFA</color><!--亮天蓝色 -->
+      
+    <color name="skyblue">#87CEEB</color><!--天蓝色 -->
+      
+    <color name="gray">#808080</color><!--灰色 -->
+      
+    <color name="olive">#808000</color><!--橄榄色 -->
+      
+    <color name="purple">#800080</color><!--紫色 -->
+      
+    <color name="maroon">#800000</color><!--粟色 -->
+      
+    <color name="aquamarine">#7FFFD4</color><!--碧绿色 -->
+      
+    <color name="chartreuse">#7FFF00</color><!--黄绿色 -->
+      
+    <color name="lawngreen">#7CFC00</color><!--草绿色 -->
+      
+    <color name="mediumslateblue">#7B68EE</color><!--中暗蓝色 -->
+      
+    <color name="lightslategray">#778899</color><!--亮蓝灰 -->
+      
+    <color name="slategray">#708090</color><!--灰石色 -->
+      
+    <color name="olivedrab">#6B8E23</color><!--深绿褐色 -->
+      
+    <color name="slateblue">#6A5ACD</color><!--石蓝色 -->
+      
+    <color name="dimgray">#696969</color><!--暗灰色 -->
+      
+    <color name="mediumaquamarine">#66CDAA</color><!--中绿色 -->
+      
+    <color name="cornflowerblue">#6495ED</color><!--菊兰色 -->
+      
+    <color name="cadetblue">#5F9EA0</color><!--军兰色 -->
+      
+    <color name="darkolivegreen">#556B2F</color><!--暗橄榄绿 -->
+      
+    <color name="indigo">#4B0082</color><!--靛青色 -->
+      
+    <color name="mediumturquoise">#48D1CC</color><!--中绿宝石 -->
+      
+    <color name="darkslateblue">#483D8B</color><!--暗灰蓝色 -->
+      
+    <color name="steelblue">#4682B4</color><!--钢兰色 -->
+      
+    <color name="royalblue">#4169E1</color><!--皇家蓝 -->
+      
+    <color name="turquoise">#40E0D0</color><!--青绿色 -->
+      
+    <color name="mediumseagreen">#3CB371</color><!--中海蓝 -->
+      
+    <color name="limegreen">#32CD32</color><!--橙绿色 -->
+      
+    <color name="darkslategray">#2F4F4F</color><!--暗瓦灰色 -->
+      
+    <color name="seagreen">#2E8B57</color><!--海绿色 -->
+      
+    <color name="forestgreen">#228B22</color><!--森林绿 -->
+      
+    <color name="lightseagreen">#20B2AA</color><!--亮海蓝色 -->
+      
+    <color name="dodgerblue">#1E90FF</color><!--闪兰色 -->
+      
+    <color name="midnightblue">#191970</color><!--中灰兰色 -->
+      
+    <color name="aqua">#00FFFF</color><!--浅绿色 -->
+      
+    <color name="cyan">#00FFFF</color><!--青色 -->
+      
+    <color name="springgreen">#00FF7F</color><!--春绿色 -->
+      
+    <color name="lime">#00FF00</color><!--酸橙色 -->
+      
+    <color name="mediumspringgreen">#00FA9A</color><!--中春绿色 -->
+      
+    <color name="darkturquoise">#00CED1</color><!--暗宝石绿 -->
+      
+    <color name="deepskyblue">#00BFFF</color><!--深天蓝色 -->
+      
+    <color name="darkcyan">#008B8B</color><!--暗青色 -->
+      
+    <color name="teal">#008080</color><!--水鸭色 -->
+      
+    <color name="green">#008000</color><!--绿色 -->
+      
+    <color name="darkgreen">#006400</color><!--暗绿色 -->
+      
+    <color name="blue">#0000FF</color><!--蓝色 -->
+      
+    <color name="mediumblue">#0000CD</color><!--中兰色 -->
+      
+    <color name="darkblue">#00008B</color><!--暗蓝色 -->
+      
+    <color name="navy">#000080</color><!--海军色 -->
+      
+    <color name="black">#000000</color><!--黑色 -->
+      
+    <color name="grassgreen">#99cc33</color><!--草绿色 -->
+     
+    <color name="gray_cc">#cccccc</color><!--灰色cc -->
+      
+    <color name="gray_8f">#8f8f8f</color><!--灰色8f -->
+      
+    <color name="translucent_background">#80000000</color><!--半透明 -->
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
 </resources>

+ 1 - 1
app/src/main/res/values/strings.xml

@@ -1,3 +1,3 @@
 <resources>
-    <string name="app_name">efunboxReader</string>
+    <string name="app_name">朗读配音</string>
 </resources>

+ 22 - 0
app/src/main/res/values/themes.xml

@@ -7,4 +7,26 @@
         <item name="colorAccent">@color/teal_200</item>
         <!-- Customize your theme here. -->
     </style>
+
+    <style name="Theme.AppCompat.Light.NoActionBar.FullScreen" parent="@style/Theme.AppCompat.Light">
+        <item name="windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+
+    </style>
+
+    <style name="AppStartTheme" parent="@style/Theme.AppCompat.Light">
+        <item name="windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowBackground">@drawable/openimg</item>
+    </style>
+
+    <style name="dialog" parent="@android:style/Theme.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">false</item>
+        <item name="android:background">@color/transparent</item>
+        <item name="android:windowBackground">@color/transparent</item>
+        <item name="android:backgroundDimEnabled">true</item>
+    </style>
 </resources>

+ 2 - 0
build.gradle

@@ -3,6 +3,7 @@ buildscript {
     repositories {
         google()
         jcenter()
+        maven { url 'https://repo1.maven.org/maven2/' }
     }
     dependencies {
         classpath "com.android.tools.build:gradle:4.1.3"
@@ -16,6 +17,7 @@ allprojects {
     repositories {
         google()
         jcenter()
+        mavenCentral()
     }
 }
 

+ 2 - 0
gradle.properties

@@ -6,6 +6,8 @@
 # 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.
+android.enableJetifier=true
+android.useAndroidX=true
 org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit