├── .gitignore ├── .idea ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── dl │ │ └── hymvp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── dl │ │ │ └── hymvp │ │ │ ├── App.java │ │ │ ├── MainActivity.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BaseModel.java │ │ │ ├── BaseMvpActivity.java │ │ │ ├── BasePresenter.java │ │ │ ├── BaseView.java │ │ │ ├── IActivity.java │ │ │ └── IPresenter.java │ │ │ ├── bean │ │ │ ├── Gank.java │ │ │ └── Survey.java │ │ │ ├── http │ │ │ ├── ApiEngine.java │ │ │ ├── ApiException.java │ │ │ ├── ApiService.java │ │ │ ├── BaseObserver.java │ │ │ ├── BaseResponse.java │ │ │ ├── DES.java │ │ │ ├── GsonResponseBodyConverter.java │ │ │ ├── MyBase64.java │ │ │ ├── NetworkInterceptor.java │ │ │ ├── ResponseConverterFactory.java │ │ │ └── ResultException.java │ │ │ ├── mvp │ │ │ ├── contract │ │ │ │ └── MainContract.java │ │ │ ├── model │ │ │ │ └── MainModel.java │ │ │ └── presenter │ │ │ │ └── MainPresenter.java │ │ │ ├── rx │ │ │ └── RxTransformer.java │ │ │ └── util │ │ │ ├── NetUtil.java │ │ │ └── Preconditions.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── dl │ └── hymvp │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | Android API 8 Platform 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | // buildToolsVersion rootProject.ext.android["buildToolsVersion"] 6 | useLibrary 'org.apache.http.legacy' 7 | 8 | compileOptions { 9 | targetCompatibility JavaVersion.VERSION_1_8 10 | sourceCompatibility JavaVersion.VERSION_1_8 11 | } 12 | 13 | defaultConfig { 14 | applicationId "com.example.dl.hymvp" 15 | minSdkVersion rootProject.ext.android["minSdkVersion"] 16 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 17 | versionCode rootProject.ext.android["versionCode"] 18 | versionName rootProject.ext.android["versionName"] 19 | testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"] 20 | } 21 | buildTypes { 22 | 23 | debug { 24 | // minifyEnabled false //混淆 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | 28 | release { 29 | // minifyEnabled true//混淆 30 | // shrinkResources true 31 | zipAlignEnabled true 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | disable "ResourceType" 39 | abortOnError false 40 | } 41 | } 42 | 43 | 44 | dependencies { 45 | implementation fileTree(dir: 'libs', include: ['*.jar']) 46 | 47 | //support 48 | implementation rootProject.ext.dependencies["appcompat-v7"] 49 | 50 | implementation rootProject.ext.dependencies["rxjava"] 51 | implementation rootProject.ext.dependencies["rxandroid"] 52 | implementation rootProject.ext.dependencies["retrofit"] 53 | 54 | 55 | implementation 'com.squareup.okhttp3:okhttp:3.10.0' 56 | implementation 'com.squareup.okio:okio:1.14.0' 57 | implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0' 58 | 59 | 60 | implementation 'com.squareup.retrofit2:retrofit:2.4.0' 61 | implementation 'com.squareup.retrofit2:converter-gson:2.3.0' 62 | // implementation 'com.squareup.retrofit2:adapter-rxjava:2.2.0' 63 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' 64 | 65 | 66 | implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1' 67 | 68 | 69 | implementation 'com.jakewharton:butterknife:8.8.1' 70 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 71 | 72 | 73 | implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar' 74 | 75 | implementation 'org.greenrobot:eventbus:3.1.1' 76 | 77 | 78 | //test 79 | testImplementation rootProject.ext.dependencies["junit"] 80 | androidTestImplementation rootProject.ext.dependencies["runner"] 81 | androidTestImplementation rootProject.ext.dependencies["espresso"] 82 | } 83 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/dl/hymvp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.dl.hymvp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/App.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Incremental change is better than ambitious failure. 7 | * 8 | * @author : MysticCoder 9 | * @date : 2018/3/15 10 | * @desc : 11 | */ 12 | 13 | public class App extends Application { 14 | 15 | private static App mContext; 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | mContext = this; 21 | } 22 | 23 | public static App getContext() { 24 | return mContext; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import android.widget.Toast; 9 | 10 | import com.example.dl.hymvp.base.BaseActivity; 11 | import com.example.dl.hymvp.base.BaseMvpActivity; 12 | import com.example.dl.hymvp.base.BasePresenter; 13 | import com.example.dl.hymvp.bean.Gank; 14 | import com.example.dl.hymvp.bean.Survey; 15 | import com.example.dl.hymvp.mvp.contract.MainContract; 16 | import com.example.dl.hymvp.mvp.presenter.MainPresenter; 17 | 18 | 19 | /** 20 | * Incremental change is better than ambitious failure. 21 | * 22 | * @author : MysticCoder 23 | * @date : 2018/3/15 24 | * @desc : 25 | */ 26 | 27 | public class MainActivity extends BaseMvpActivity implements MainContract.MainView { 28 | 29 | @Override 30 | public int initView(@Nullable Bundle savedInstanceState) { 31 | return R.layout.activity_main; 32 | } 33 | 34 | @Override 35 | public void initData(@Nullable Bundle savedInstanceState) { 36 | 37 | findViewById(R.id.get).setOnClickListener(v -> { 38 | mPresenter.getSurveyList("189ba5a013dc"); 39 | }); 40 | 41 | } 42 | 43 | 44 | 45 | @Override 46 | protected MainPresenter createPresenter() { 47 | return new MainPresenter(this); 48 | } 49 | 50 | 51 | 52 | @Override 53 | public void showLoading() { 54 | } 55 | 56 | @Override 57 | public void hideLoading() { 58 | 59 | } 60 | 61 | @Override 62 | public void onGetSurvey(Survey survey) { 63 | 64 | Log.d("成功" ,survey.toString()); 65 | Toast.makeText(this, survey.toString(), Toast.LENGTH_SHORT).show(); 66 | } 67 | 68 | 69 | 70 | } 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; 8 | import com.trello.rxlifecycle2.components.support.RxFragment; 9 | 10 | import org.greenrobot.eventbus.EventBus; 11 | 12 | import butterknife.ButterKnife; 13 | import butterknife.Unbinder; 14 | 15 | /** 16 | * Incremental change is better than ambitious failure. 17 | * 18 | * @author : MysticCoder 19 | * @date : 2018/3/15 20 | * @desc : 21 | */ 22 | 23 | /** 24 | * 这里采用{@link com.trello.rxlifecycle2.RxLifecycle}框架来管理RxJava订阅生命周期 25 | * 将Activity基类继承自{@link RxAppCompatActivity} 考虑到Java没有多继承,若随着项目需要可能会使用到其他的第三方库需要继承, 26 | * 最简单的方法就是把{@link RxAppCompatActivity}的源码直接对应填到BaseActivity 即可^_^ 27 | */ 28 | public abstract class BaseActivity extends RxAppCompatActivity implements IActivity{ 29 | private Unbinder mUnbinder; 30 | @Override 31 | protected void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | 34 | try { 35 | int layoutResID = initView(savedInstanceState); 36 | //如果initView返回0,框架则不会调用setContentView(),当然也不会 Bind ButterKnife 37 | if (layoutResID != 0) { 38 | setContentView(layoutResID); 39 | //绑定到butterknife 40 | mUnbinder = ButterKnife.bind(this); 41 | //如果要使用 Eventbus 请将此方法返回 true 42 | if (useEventBus()) { 43 | //注册 Eventbus 44 | EventBus.getDefault().register(this); 45 | } 46 | } 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | initData(savedInstanceState); 51 | 52 | } 53 | 54 | 55 | @Override 56 | protected void onDestroy() { 57 | super.onDestroy(); 58 | if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) { 59 | mUnbinder.unbind(); 60 | this.mUnbinder = null; 61 | } 62 | 63 | //如果要使用 Eventbus 请将此方法返回 true 64 | if (useEventBus()) { 65 | //解除注册 Eventbus 66 | EventBus.getDefault().unregister(this); 67 | } 68 | 69 | } 70 | 71 | /** 72 | * 子类Activity要使用EventBus只需要重写此方法返回true即可 73 | * @return 74 | */ 75 | @Override 76 | public boolean useEventBus() { 77 | return false; 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/BaseModel.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import com.example.dl.hymvp.http.ApiEngine; 4 | import com.example.dl.hymvp.http.ApiService; 5 | 6 | /** 7 | * Incremental change is better than ambitious failure. 8 | * 9 | * @author : MysticCoder 10 | * @date : 2018/3/15 11 | * @desc : 12 | */ 13 | 14 | 15 | public interface BaseModel { 16 | 17 | ApiService mApiService = ApiEngine.getInstance().getApiService(); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/BaseMvpActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | /** 7 | * Incremental change is better than ambitious failure. 8 | * 9 | * @author : MysticCoder 10 | * @date : 2018/3/15 11 | * @desc : 12 | */ 13 | 14 | 15 | public abstract class BaseMvpActivity

extends BaseActivity { 16 | 17 | protected P mPresenter; 18 | 19 | @Override 20 | protected void onCreate(@Nullable Bundle savedInstanceState) { 21 | 22 | mPresenter = createPresenter(); 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | /** 27 | * create presenter 28 | * @return presenter 29 | */ 30 | protected abstract P createPresenter(); 31 | 32 | 33 | @Override 34 | protected void onDestroy() { 35 | super.onDestroy(); 36 | if (mPresenter != null) { 37 | mPresenter.detachView(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | 7 | import com.example.dl.hymvp.util.Preconditions; 8 | import com.trello.rxlifecycle2.RxLifecycle; 9 | 10 | 11 | import java.lang.ref.Reference; 12 | import java.lang.ref.WeakReference; 13 | 14 | import io.reactivex.disposables.CompositeDisposable; 15 | import io.reactivex.disposables.Disposable; 16 | 17 | 18 | /** 19 | * Incremental change is better than ambitious failure. 20 | * 21 | * @author : MysticCoder 22 | * @date : 2018/3/15 23 | * @desc : 24 | */ 25 | 26 | public class BasePresenter implements IPresenter { 27 | 28 | /** 29 | * 由于Presenter 经常性的持有Activity 的强引用,如果在一些请求结束之前Activity 被销毁了,Activity对象将无法被回收,此时就会发生内存泄露。 30 | * 这里我们使用虚引用和泛型来对MVP中的内存泄漏问题进行改良。 31 | */ 32 | protected Reference mView; 33 | protected M mModel; 34 | 35 | protected CompositeDisposable mCompositeDisposable; 36 | 37 | 38 | protected V getView() { 39 | return mView.get(); 40 | } 41 | /** 42 | * 如果当前页面同时需要 Model 层和 View 层,则使用此构造函数(默认) 43 | * 44 | * @param model 45 | * @param view 46 | */ 47 | public BasePresenter(M model, V view) { 48 | Preconditions.checkNotNull(model, "%s cannot be null", BaseModel.class.getName()); 49 | Preconditions.checkNotNull(view, "%s cannot be null", BaseView.class.getName()); 50 | this.mModel = model; 51 | // this.mView = view; 52 | attachView(view); 53 | } 54 | 55 | /** 56 | * 如果当前页面不需要操作数据,只需要 View 层,则使用此构造函数 57 | * 58 | * @param view 59 | */ 60 | public BasePresenter(V view) { 61 | Preconditions.checkNotNull(view, "%s cannot be null", BaseView.class.getName()); 62 | // this.mView = view; 63 | attachView(view); 64 | } 65 | 66 | // public BasePresenter() { 67 | // attachView(); 68 | // } 69 | 70 | 71 | /*****************************************************************************************************/ 72 | /** 73 | * 将 {@link Disposable} 添加到 {@link CompositeDisposable} 中统一管理 74 | * 可在 {@link Activity#onDestroy()} 中使用 {@link #unDispose()} 停止正在执行的 RxJava 任务,避免内存泄漏 75 | * 目前已使用 {@link RxLifecycle} 避免内存泄漏,此方法作为备用方案 76 | * 77 | * @param disposable 78 | */ 79 | protected void addDisposabel(Disposable disposable) { 80 | if (mCompositeDisposable == null) { 81 | mCompositeDisposable = new CompositeDisposable(); 82 | } 83 | //将所有 Disposable 放入集中处理 84 | mCompositeDisposable.add(disposable); 85 | Log.d("订阅",mCompositeDisposable.toString()+"个数"+mCompositeDisposable.size()); 86 | } 87 | 88 | public void unDispose(){ 89 | 90 | if (mCompositeDisposable != null) { 91 | mCompositeDisposable.clear();//保证 Activity 结束时取消所有正在执行的订阅 92 | } 93 | } 94 | 95 | /*****************************************************************************************************/ 96 | 97 | @Override 98 | public void attachView(V view) { 99 | 100 | this.mView= new WeakReference<>(view); 101 | } 102 | 103 | @Override 104 | public void detachView() { 105 | if (mView != null) { 106 | mView.clear(); 107 | mView = null; 108 | } 109 | 110 | unDispose();//备用方案 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import com.trello.rxlifecycle2.LifecycleTransformer; 4 | 5 | /** 6 | * Incremental change is better than ambitious failure. 7 | * 8 | * @author : MysticCoder 9 | * @date : 2018/3/15 10 | * @desc : 11 | */ 12 | 13 | 14 | public interface BaseView { 15 | 16 | LifecycleTransformer bindToLifecycle(); 17 | 18 | 19 | /** 20 | * 显示加载 21 | */ 22 | void showLoading(); 23 | 24 | /** 25 | * 隐藏加载 26 | */ 27 | void hideLoading(); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/IActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | 7 | /** 8 | * Incremental change is better than ambitious failure. 9 | * 10 | * @author : MysticCoder 11 | * @date : 2018/3/16 12 | * @desc : 13 | */ 14 | 15 | 16 | public interface IActivity { 17 | 18 | /** 19 | * 初始化 View, 如果 {@link #initView(Bundle)} 返回 0, 则不会调用 {@link Activity#setContentView(int)} 20 | * 21 | * @param savedInstanceState 22 | * @return 23 | */ 24 | int initView(@Nullable Bundle savedInstanceState); 25 | 26 | /** 27 | * 初始化数据 28 | * 29 | * @param savedInstanceState 30 | */ 31 | void initData(@Nullable Bundle savedInstanceState); 32 | 33 | 34 | /** 35 | * 是否使用 EventBus 36 | * 37 | * @return 38 | */ 39 | boolean useEventBus(); 40 | 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/base/IPresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.base; 2 | 3 | /** 4 | * Incremental change is better than ambitious failure. 5 | * 6 | * @author : MysticCoder 7 | * @date : 2018/3/15 8 | * @desc : 9 | */ 10 | 11 | 12 | public interface IPresenter { 13 | 14 | 15 | void attachView(V view); 16 | 17 | void detachView(); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/bean/Gank.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Incremental change is better than ambitious failure. 7 | * 8 | * @author : MysticCoder 9 | * @date : 2018/3/15 10 | * @desc : 11 | */ 12 | public class Gank { 13 | 14 | private boolean error; 15 | 16 | /** 17 | * _id : 58006bb3421aa95dd351b12a 18 | * createdAt : 2016-10-14T13:22:59.462Z 19 | * desc : 效果超棒的微笑下拉刷新。这是在 SwipeRefreshLayout基础上修改的下拉刷新库。 20 | * images : ["http://img.gank.io/616cb9ae-2d42-4f97-a7c9-28c291c7b66f"] 21 | * publishedAt : 2016-10-28T11:29:36.510Z 22 | * source : web 23 | * type : Android 24 | * url : https://github.com/songixan/SmileRefresh 25 | * used : true 26 | * who : null 27 | */ 28 | 29 | private List results; 30 | 31 | public boolean isError() { 32 | return error; 33 | } 34 | 35 | public void setError(boolean error) { 36 | this.error = error; 37 | } 38 | 39 | public List getResults() { 40 | return results; 41 | } 42 | 43 | public void setResults(List results) { 44 | this.results = results; 45 | } 46 | 47 | public static class Result { 48 | 49 | private String _id; 50 | private String createdAt; 51 | private String desc; 52 | private String publishedAt; 53 | private String source; 54 | private String type; 55 | private String url; 56 | private boolean used; 57 | private String who; 58 | private List images; 59 | 60 | public String get_id() { 61 | return _id; 62 | } 63 | 64 | public void set_id(String _id) { 65 | this._id = _id; 66 | } 67 | 68 | public String getCreatedAt() { 69 | return createdAt; 70 | } 71 | 72 | public void setCreatedAt(String createdAt) { 73 | this.createdAt = createdAt; 74 | } 75 | 76 | public String getDesc() { 77 | return desc; 78 | } 79 | 80 | public void setDesc(String desc) { 81 | this.desc = desc; 82 | } 83 | 84 | public String getPublishedAt() { 85 | return publishedAt; 86 | } 87 | 88 | public void setPublishedAt(String publishedAt) { 89 | this.publishedAt = publishedAt; 90 | } 91 | 92 | public String getSource() { 93 | return source; 94 | } 95 | 96 | public void setSource(String source) { 97 | this.source = source; 98 | } 99 | 100 | public String getType() { 101 | return type; 102 | } 103 | 104 | public void setType(String type) { 105 | this.type = type; 106 | } 107 | 108 | public String getUrl() { 109 | return url; 110 | } 111 | 112 | public void setUrl(String url) { 113 | this.url = url; 114 | } 115 | 116 | public boolean isUsed() { 117 | return used; 118 | } 119 | 120 | public void setUsed(boolean used) { 121 | this.used = used; 122 | } 123 | 124 | public String getWho() { 125 | return who; 126 | } 127 | 128 | public void setWho(String who) { 129 | this.who = who; 130 | } 131 | 132 | public List getImages() { 133 | return images; 134 | } 135 | 136 | public void setImages(List images) { 137 | this.images = images; 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return "Result{" + 143 | "_id='" + _id + '\'' + 144 | ", createdAt='" + createdAt + '\'' + 145 | ", desc='" + desc + '\'' + 146 | ", publishedAt='" + publishedAt + '\'' + 147 | ", source='" + source + '\'' + 148 | ", type='" + type + '\'' + 149 | ", url='" + url + '\'' + 150 | ", used=" + used + 151 | ", who='" + who + '\'' + 152 | ", images=" + images + 153 | '}'; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/bean/Survey.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.bean; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * Incremental change is better than ambitious failure. 8 | * 9 | * @author : MysticCoder 10 | * @date : 2017/12/4 11 | * @desc : 12 | */ 13 | 14 | 15 | public class Survey { 16 | private List surveylist; 17 | 18 | public List getSurveylist() { 19 | return surveylist; 20 | } 21 | 22 | public void setSurveylist(List surveylist) { 23 | this.surveylist = surveylist; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "SurveyModel{" + 29 | "surveylist=" + surveylist + 30 | '}'; 31 | } 32 | 33 | public static class SurveylistBean implements Serializable { 34 | /** 35 | * author : 北京市垂杨柳医院 36 | * remark : 为不断改善医院住院服务质量,我院正在进行住院病人的满意度调查。非常感谢您抽出宝贵时间参加本次调查,提供您的看法与意见,能倾听您的意见,我们感到十分荣幸。谢谢! 37 | * id : 93 38 | * title : 住院病人满意度调查 39 | */ 40 | 41 | private String author; 42 | private String remark; 43 | private int id; 44 | 45 | @Override 46 | public String toString() { 47 | return "SurveylistBean{" + 48 | "author='" + author + '\'' + 49 | ", remark='" + remark + '\'' + 50 | ", id=" + id + 51 | ", title='" + title + '\'' + 52 | '}'; 53 | } 54 | 55 | private String title; 56 | 57 | public String getAuthor() { 58 | return author; 59 | } 60 | 61 | public void setAuthor(String author) { 62 | this.author = author; 63 | } 64 | 65 | public String getRemark() { 66 | return remark; 67 | } 68 | 69 | public void setRemark(String remark) { 70 | this.remark = remark; 71 | } 72 | 73 | public int getId() { 74 | return id; 75 | } 76 | 77 | public void setId(int id) { 78 | this.id = id; 79 | } 80 | 81 | public String getTitle() { 82 | return title; 83 | } 84 | 85 | public void setTitle(String title) { 86 | this.title = title; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/ApiEngine.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import android.util.Log; 4 | 5 | import com.example.dl.hymvp.App; 6 | import com.example.dl.hymvp.util.NetUtil; 7 | import com.google.gson.Gson; 8 | import com.google.gson.GsonBuilder; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.io.UnsupportedEncodingException; 13 | import java.net.URLDecoder; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | 17 | import okhttp3.Cache; 18 | import okhttp3.CacheControl; 19 | import okhttp3.Interceptor; 20 | import okhttp3.OkHttpClient; 21 | import okhttp3.Request; 22 | import okhttp3.Response; 23 | import okhttp3.logging.HttpLoggingInterceptor; 24 | import retrofit2.Retrofit; 25 | import retrofit2.converter.gson.GsonConverterFactory; 26 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 27 | 28 | /** 29 | * Incremental change is better than ambitious failure. 30 | * 31 | * @author : MysticCoder 32 | * @date : 2018/3/15 33 | * @desc : 34 | */ 35 | public class ApiEngine { 36 | 37 | private static final long DEFAULT_TIMEOUT = 10; 38 | 39 | 40 | private volatile static ApiEngine apiEngine; 41 | private Retrofit retrofit; 42 | 43 | private ApiEngine() { 44 | 45 | //日志拦截器 46 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor((message)-> { 47 | 48 | try { 49 | String text = URLDecoder.decode(message, "utf-8"); 50 | Log.e("OKHttp-----", text); 51 | } catch (UnsupportedEncodingException e) { 52 | e.printStackTrace(); 53 | Log.e("OKHttp-----", message); 54 | } 55 | }); 56 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 57 | 58 | //缓存 59 | int size = 1024 * 1024 * 100; 60 | File cacheFile = new File(App.getContext().getCacheDir(), "OkHttpCache"); 61 | Cache cache = new Cache(cacheFile, size); 62 | 63 | 64 | // addInterceptor() 添加应用拦截器 65 | //● 不需要担心中间过程的响应,如重定向和重试. 66 | //● 总是只调用一次,即使HTTP响应是从缓存中获取. 67 | //● 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match. 68 | //● 允许短路而不调用 Chain.proceed(),即中止调用. 69 | //● 允许重试,使 Chain.proceed()调用多次. 70 | 71 | 72 | // addNetworkInterceptor() 添加网络拦截器 73 | //● 能够操作中间过程的响应,如重定向和重试. 74 | //● 当网络短路而返回缓存响应时不被调用. 75 | //● 只观察在网络上传输的数据. 76 | //● 携带请求来访问连接. 77 | 78 | OkHttpClient client = new OkHttpClient.Builder() 79 | .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 80 | .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 81 | .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 82 | .addNetworkInterceptor(new NetworkInterceptor()) 83 | // .addNetworkInterceptor(loggingInterceptor) 84 | .addInterceptor(loggingInterceptor) 85 | .cache(cache) 86 | .build(); 87 | 88 | retrofit = new Retrofit.Builder() 89 | .baseUrl(ApiService.BASE_URL) 90 | .client(client) 91 | //然后将下面的GsonConverterFactory.create()替换成我们自定义的ResponseConverterFactory.create() 92 | .addConverterFactory(ResponseConverterFactory.create()) 93 | // .addConverterFactory(GsonConverterFactory.create()) 94 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 95 | .build(); 96 | 97 | } 98 | 99 | public static ApiEngine getInstance() { 100 | if (apiEngine == null) { 101 | synchronized (ApiEngine.class) { 102 | if (apiEngine == null) { 103 | apiEngine = new ApiEngine(); 104 | } 105 | } 106 | } 107 | return apiEngine; 108 | } 109 | 110 | public ApiService getApiService() { 111 | return retrofit.create(ApiService.class); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import com.google.gson.JsonParseException; 4 | import com.google.gson.JsonSerializer; 5 | 6 | import org.apache.http.conn.ConnectTimeoutException; 7 | import org.json.JSONException; 8 | 9 | import java.io.NotSerializableException; 10 | import java.net.ConnectException; 11 | import java.net.SocketTimeoutException; 12 | import java.net.UnknownHostException; 13 | import java.text.ParseException; 14 | 15 | import retrofit2.HttpException; 16 | 17 | 18 | 19 | public class ApiException extends Exception { 20 | 21 | 22 | //对应HTTP的状态码 23 | private static final int UNAUTHORIZED = 401; 24 | private static final int FORBIDDEN = 403; 25 | private static final int NOT_FOUND = 404; 26 | private static final int REQUEST_TIMEOUT = 408; 27 | private static final int INTERNAL_SERVER_ERROR = 500; 28 | private static final int BAD_GATEWAY = 502; 29 | private static final int SERVICE_UNAVAILABLE = 503; 30 | private static final int GATEWAY_TIMEOUT = 504; 31 | 32 | 33 | 34 | private final int code; 35 | private String message; 36 | 37 | public ApiException(Throwable throwable, int code) { 38 | super(throwable); 39 | this.code = code; 40 | this.message = throwable.getMessage(); 41 | } 42 | 43 | public int getCode() { 44 | return code; 45 | } 46 | 47 | @Override 48 | public String getMessage() { 49 | return message; 50 | } 51 | 52 | public static ApiException handleException(Throwable e) { 53 | 54 | Throwable throwable = e; 55 | //获取最根源的异常 56 | while (throwable.getCause() != null) { 57 | e = throwable; 58 | throwable = throwable.getCause(); 59 | } 60 | 61 | ApiException ex; 62 | if (e instanceof HttpException) { //HTTP错误 63 | HttpException httpException = (HttpException) e; 64 | ex = new ApiException(e, httpException.code()); 65 | switch (httpException.code()) { 66 | case UNAUTHORIZED: 67 | case FORBIDDEN: 68 | //权限错误,需要实现重新登录操作 69 | // onPermissionError(ex); 70 | break; 71 | case NOT_FOUND: 72 | case REQUEST_TIMEOUT: 73 | case GATEWAY_TIMEOUT: 74 | case INTERNAL_SERVER_ERROR: 75 | case BAD_GATEWAY: 76 | case SERVICE_UNAVAILABLE: 77 | default: 78 | ex.message = "默认网络异常"; //均视为网络错误 79 | break; 80 | } 81 | return ex; 82 | } else if (e instanceof SocketTimeoutException) { 83 | ex = new ApiException(e, ERROR.TIMEOUT_ERROR); 84 | ex.message = "网络连接超时,请检查您的网络状态,稍后重试!"; 85 | return ex; 86 | } else if (e instanceof ConnectException) { 87 | ex = new ApiException(e, ERROR.TIMEOUT_ERROR); 88 | ex.message = "网络连接异常,请检查您的网络状态,稍后重试!"; 89 | return ex; 90 | } else if (e instanceof ConnectTimeoutException) { 91 | ex = new ApiException(e, ERROR.TIMEOUT_ERROR); 92 | ex.message = "网络连接超时,请检查您的网络状态,稍后重试!"; 93 | return ex; 94 | } else if (e instanceof UnknownHostException) { 95 | ex = new ApiException(e, ERROR.TIMEOUT_ERROR); 96 | ex.message = "网络连接异常,请检查您的网络状态,稍后重试!"; 97 | return ex; 98 | } else if (e instanceof NullPointerException) { 99 | ex = new ApiException(e, ERROR.NULL_POINTER_EXCEPTION); 100 | ex.message = "空指针异常"; 101 | return ex; 102 | } else if (e instanceof javax.net.ssl.SSLHandshakeException) { 103 | ex = new ApiException(e, ERROR.SSL_ERROR); 104 | ex.message = "证书验证失败"; 105 | return ex; 106 | } else if (e instanceof ClassCastException) { 107 | ex = new ApiException(e, ERROR.CAST_ERROR); 108 | ex.message = "类型转换错误"; 109 | return ex; 110 | } else if (e instanceof JsonParseException 111 | || e instanceof JSONException 112 | // || e instanceof JsonSyntaxException 113 | || e instanceof JsonSerializer 114 | || e instanceof NotSerializableException 115 | || e instanceof ParseException) { 116 | ex = new ApiException(e, ERROR.PARSE_ERROR); 117 | ex.message = "解析错误"; 118 | return ex; 119 | } else if (e instanceof IllegalStateException) { 120 | ex = new ApiException(e, ERROR.ILLEGAL_STATE_ERROR); 121 | ex.message = e.getMessage(); 122 | return ex; 123 | } else { 124 | ex = new ApiException(e, ERROR.UNKNOWN); 125 | ex.message = "未知错误"; 126 | return ex; 127 | } 128 | } 129 | 130 | /** 131 | * 约定异常 132 | */ 133 | public static class ERROR { 134 | /** 135 | * 未知错误 136 | */ 137 | public static final int UNKNOWN = 1000; 138 | /** 139 | * 连接超时 140 | */ 141 | public static final int TIMEOUT_ERROR = 1001; 142 | /** 143 | * 空指针错误 144 | */ 145 | public static final int NULL_POINTER_EXCEPTION = 1002; 146 | 147 | /** 148 | * 证书出错 149 | */ 150 | public static final int SSL_ERROR = 1003; 151 | 152 | /** 153 | * 类转换错误 154 | */ 155 | public static final int CAST_ERROR = 1004; 156 | 157 | /** 158 | * 解析错误 159 | */ 160 | public static final int PARSE_ERROR = 1005; 161 | 162 | /** 163 | * 非法数据异常 164 | */ 165 | public static final int ILLEGAL_STATE_ERROR = 1006; 166 | 167 | } 168 | 169 | } 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/ApiService.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | 4 | import com.example.dl.hymvp.bean.Gank; 5 | import com.example.dl.hymvp.bean.Survey; 6 | 7 | 8 | import io.reactivex.Observable; 9 | import retrofit2.http.Field; 10 | import retrofit2.http.FormUrlEncoded; 11 | import retrofit2.http.GET; 12 | import retrofit2.http.POST; 13 | import retrofit2.http.Path; 14 | 15 | 16 | /** 17 | * Incremental change is better than ambitious failure. 18 | * 19 | * @author : MysticCoder 20 | * @date : 2018/3/15 21 | * @desc : 22 | */ 23 | 24 | public interface ApiService { 25 | 26 | // String BASE_URL="http://gank.io/"; 27 | // 28 | // @GET("api/data/Android/10/{page}") 29 | // Observable getGank(@Path("page") String page); 30 | 31 | 32 | String BASE_URL = "http://api.medrd.com/"; 33 | 34 | 35 | @FormUrlEncoded 36 | @POST("api/user/surveytitlelistNew")//获取调查问卷列表 37 | Observable getSurveyList(@Field("did")String did); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/BaseObserver.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | import io.reactivex.Observer; 7 | import io.reactivex.disposables.Disposable; 8 | 9 | /** 10 | * Incremental change is better than ambitious failure. 11 | * 12 | * @author : MysticCoder 13 | * @date : 2017/12/4 14 | * @desc : 15 | */ 16 | 17 | 18 | public abstract class BaseObserver implements Observer { 19 | 20 | 21 | 22 | @Override 23 | public void onSubscribe(Disposable d) { 24 | 25 | } 26 | 27 | @Override 28 | public void onNext(T response) { 29 | 30 | onSuccess(response); 31 | } 32 | 33 | @Override 34 | public void onError(Throwable e) { 35 | 36 | 37 | if (e instanceof ResultException){ 38 | onFailure(e.getMessage()); 39 | }else { 40 | 41 | String error = ApiException.handleException(e).getMessage(); 42 | 43 | _onError(error); 44 | } 45 | 46 | 47 | } 48 | 49 | @Override 50 | public void onComplete() { 51 | } 52 | 53 | /** 54 | * 请求成功 55 | * 56 | * @param response 服务器返回的数据 57 | */ 58 | public abstract void onSuccess(T response); 59 | 60 | /** 61 | * 服务器返回数据,但code不在约定成功范围内 62 | * 63 | * @param msg 服务器返回的数据 64 | */ 65 | public abstract void onFailure(String msg); 66 | 67 | 68 | // public abstract void onError(String errorMsg); 69 | 70 | 71 | 72 | 73 | private void _onSuccess(T responce){ 74 | 75 | } 76 | 77 | private void _onFailure(String msg) { 78 | 79 | if (TextUtils.isEmpty(msg)) { 80 | // ToastUtils.show(R.string.response_return_error); 81 | } else { 82 | // ToastUtils.show(msg); 83 | } 84 | } 85 | private void _onError(String err ){ 86 | 87 | Log.e("APIException",err); 88 | 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | /** 4 | * Incremental change is better than ambitious failure. 5 | * 6 | * @author : MysticCoder 7 | * @date : 2017/12/4 8 | * @desc : 9 | */ 10 | 11 | 12 | public class BaseResponse{ 13 | 14 | private int code; 15 | private String msg; 16 | private String data; 17 | private String seed; 18 | 19 | private T decryptedData; 20 | 21 | public T getDecryptedData() { 22 | return decryptedData; 23 | } 24 | 25 | public void setDecryptedData(T decryptedData) { 26 | this.decryptedData = decryptedData; 27 | } 28 | 29 | private boolean isSuccess; 30 | 31 | 32 | public String getData() { 33 | return data; 34 | } 35 | 36 | public void setData(String data) { 37 | this.data = data; 38 | } 39 | 40 | public String getMsg() { 41 | return msg; 42 | } 43 | 44 | public void setMsg(String msg) { 45 | this.msg = msg; 46 | } 47 | 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | public void setCode(int code) { 53 | this.code = code; 54 | } 55 | 56 | public String getSeed() { 57 | return seed; 58 | } 59 | 60 | public void setSeed(String seed) { 61 | this.seed = seed; 62 | } 63 | 64 | public void setSuccess(boolean isSuccess){ 65 | this.isSuccess = isSuccess; 66 | } 67 | 68 | public boolean isSuccess(){ 69 | return this.code >= 2000 && this.code < 4000; 70 | } 71 | 72 | 73 | @Override 74 | public String toString() { 75 | return "BaseResponse{" + 76 | "code=" + code + 77 | ", msg='" + msg + '\'' + 78 | ", data='" + data + '\'' + 79 | ", seed='" + seed + '\'' + 80 | ", decryptedData=" + decryptedData + 81 | ", isSuccess=" + isSuccess + 82 | '}'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/DES.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLDecoder; 5 | import java.security.Key; 6 | 7 | import javax.crypto.Cipher; 8 | import javax.crypto.SecretKeyFactory; 9 | import javax.crypto.spec.DESKeySpec; 10 | 11 | public class DES { 12 | 13 | public static String decode(String str, String seed){ 14 | DES des = new DES(seed); 15 | String deCode = str; 16 | try { 17 | if(str != null) 18 | deCode = des.getDesString(str); 19 | else if(deCode == null || deCode.length() == 0) 20 | deCode = str; 21 | } catch (Exception e) { 22 | deCode=str; 23 | } 24 | return deCode; 25 | } 26 | 27 | public static String encode(String str, String seed){ 28 | DES des = new DES(seed); 29 | return des.getEncString(str); 30 | } 31 | 32 | 33 | Key key; 34 | 35 | public DES(String str) { 36 | setKey(str);// 生成密匙 37 | } 38 | 39 | // public DES() { 40 | // setKey(); 41 | // } 42 | 43 | /** 44 | * 根据参数生成KEY 45 | */ 46 | private void setKey(String strKey) { 47 | try { 48 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); 49 | byte[] androidKey =getRawKey(strKey); 50 | DESKeySpec keySpec = new DESKeySpec(androidKey); 51 | this.key = keyFactory.generateSecret(keySpec); 52 | } catch (Exception e) { 53 | 54 | } } 55 | 56 | private byte[] getRawKey(String seed){ 57 | if (seed != null) { 58 | byte[] bs = seed.getBytes(); 59 | return new byte[]{bs[6],bs[3],bs[5],bs[2],bs[10],bs[4],bs[8],bs[2]}; 60 | } 61 | return new byte[]{ 82, -65, 35, 88, -87, 105, 72, -34 }; 62 | } 63 | 64 | private void setKey(){ 65 | try { 66 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); 67 | byte[] androidKey = new byte[]{ 82, -65, 35, 88, -87, 105, 72, -34 }; 68 | DESKeySpec keySpec = new DESKeySpec(androidKey); 69 | this.key = keyFactory.generateSecret(keySpec); 70 | } catch (Exception e) { 71 | 72 | } 73 | } 74 | 75 | /** 76 | * 加密String明文输入,String密文输出 77 | */ 78 | private String getEncString(String strMing) { 79 | byte[] byteMi = null; 80 | byte[] byteMing = null; 81 | String strMi = ""; 82 | try { 83 | byteMing = strMing.getBytes("UTF8"); 84 | byteMi = this.getEncCode(byteMing); 85 | strMi = MyBase64.encodeBytes(byteMi); 86 | } catch (Exception e) { 87 | } finally { 88 | byteMi = null; 89 | } 90 | return strMi; 91 | } 92 | 93 | /** 94 | * 解密 以String密文输入,String明文输出 95 | * 96 | * @param strMi 97 | * @return 98 | */ 99 | private String getDesString(String strMi) { 100 | byte[] byteMing = null; 101 | byte[] byteMi = null; 102 | String strMing = strMi; 103 | try { 104 | byteMi = MyBase64.decode(strMi); 105 | if(byteMi.length >0){ 106 | byteMing = this.getDesCode(byteMi); 107 | strMing = new String(byteMing, "UTF8"); 108 | }else{ 109 | strMing = strMi; 110 | } 111 | } catch (Exception e) { 112 | strMing = strMi; 113 | } finally { 114 | byteMing = null; 115 | byteMi = null; 116 | } 117 | return strMing; 118 | } 119 | 120 | /** 121 | * 加密以byte[]明文输入,byte[]密文输出 122 | * 123 | * @param byteS 124 | * @return 125 | */ 126 | private byte[] getEncCode(byte[] byteS) { 127 | byte[] byteFina = null; 128 | Cipher cipher; 129 | try { 130 | cipher = Cipher.getInstance("DES"); 131 | cipher.init(Cipher.ENCRYPT_MODE, key); 132 | byteFina = cipher.doFinal(byteS); 133 | } catch (Exception e) { 134 | } finally { 135 | cipher = null; 136 | } 137 | return byteFina; 138 | } 139 | 140 | /** 141 | * 解密以byte[]密文输入,以byte[]明文输出 142 | * 143 | * @param byteD 144 | * @return 145 | */ 146 | private byte[] getDesCode(byte[] byteD) { 147 | Cipher cipher; 148 | byte[] byteFina = null; 149 | try { 150 | cipher = Cipher.getInstance("DES"); 151 | cipher.init(Cipher.DECRYPT_MODE, key); 152 | byteFina = cipher.doFinal(byteD); 153 | } catch (Exception e) { 154 | } finally { 155 | cipher = null; 156 | } 157 | return byteFina; 158 | } 159 | 160 | private String filter(String str){ 161 | String newString = ""; 162 | try { 163 | newString = URLDecoder.decode(str, "UTF-8"); 164 | } catch (UnsupportedEncodingException e) { 165 | e.printStackTrace(); 166 | newString = ""; 167 | } 168 | return newString; 169 | } 170 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/GsonResponseBodyConverter.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.google.gson.Gson; 6 | 7 | import java.io.IOException; 8 | import java.lang.reflect.Type; 9 | 10 | import okhttp3.ResponseBody; 11 | import retrofit2.Converter; 12 | 13 | 14 | /** 15 | * Incremental change is better than ambitious failure. 16 | * 17 | * @author : MysticCoder 18 | * @date : 2017/12/6 19 | * @desc : 20 | */ 21 | 22 | 23 | public class GsonResponseBodyConverter implements Converter { 24 | private final Gson gson; 25 | private final Type type; 26 | 27 | 28 | public GsonResponseBodyConverter(Gson gson, Type type){ 29 | this.gson = gson; 30 | this.type = type; 31 | } 32 | @Override 33 | public T convert(@NonNull ResponseBody value) throws IOException { 34 | 35 | String response = value.string(); 36 | 37 | BaseResponse result = gson.fromJson(response, BaseResponse.class); 38 | if (result.isSuccess() && null!=result.getData()){ 39 | String jsonStr = DES.decode(result.getData(), result.getSeed()); 40 | return gson.fromJson(jsonStr,type); 41 | 42 | 43 | }else { 44 | 45 | //抛一个自定义ResultException 传入失败时候的状态码,和信息 46 | throw new ResultException(result.getCode(),result.getMsg()); 47 | 48 | } 49 | 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/MyBase64.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | /** 4 | *

Encodes and decodes to and from Base64 notation.

5 | *

Homepage: http://iharder.net/base64.

6 | * 7 | *

Example:

8 | * 9 | * String encoded = Base64.encode( myByteArray ); 10 | *
11 | * byte[] myByteArray = Base64.decode( encoded ); 12 | * 13 | *

The options parameter, which appears in a few places, is used to pass 14 | * several pieces of information to the encoder. In the "higher level" methods such as 15 | * encodeBytes( bytes, options ) the options parameter can be used to indicate such 16 | * things as first gzipping the bytes before encoding them, not inserting linefeeds, 17 | * and encoding using the URL-safe and Ordered dialects.

18 | * 19 | *

Note, according to RFC3548, 20 | * Section 2.1, implementations should not add line feeds unless explicitly told 21 | * to do so. I've got Base64 set to this behavior now, although earlier versions 22 | * broke lines by default.

23 | * 24 | *

The constants defined in Base64 can be OR-ed together to combine options, so you 25 | * might make a call like this:

26 | * 27 | * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); 28 | *

to compress the data before encoding it and then making the output have newline characters.

29 | *

Also...

30 | * String encoded = Base64.encodeBytes( crazyString.getBytes() ); 31 | * 32 | * 33 | * 34 | *

35 | * Change Log: 36 | *

37 | * 137 | * 138 | *

139 | * I am placing this code in the Public Domain. Do with it as you will. 140 | * This software comes with no guarantees or warranties but with 141 | * plenty of well-wishing instead! 142 | * Please visit http://iharder.net/base64 143 | * periodically to check for updates or to contribute improvements. 144 | *

145 | * 146 | * @author Robert Harder 147 | * @author rob@iharder.net 148 | * @version 2.3.7 149 | */ 150 | public class MyBase64 151 | { 152 | 153 | /* ******** P U B L I C F I E L D S ******** */ 154 | 155 | 156 | /** No options specified. Value is zero. */ 157 | public final static int NO_OPTIONS = 0; 158 | 159 | /** Specify encoding in first bit. Value is one. */ 160 | public final static int ENCODE = 1; 161 | 162 | 163 | /** Specify decoding in first bit. Value is zero. */ 164 | public final static int DECODE = 0; 165 | 166 | 167 | /** Specify that data should be gzip-compressed in second bit. Value is two. */ 168 | public final static int GZIP = 2; 169 | 170 | /** Specify that gzipped data should not be automatically gunzipped. */ 171 | public final static int DONT_GUNZIP = 4; 172 | 173 | 174 | /** Do break lines when encoding. Value is 8. */ 175 | public final static int DO_BREAK_LINES = 8; 176 | 177 | /** 178 | * Encode using Base64-like encoding that is URL- and Filename-safe as described 179 | * in Section 4 of RFC3548: 180 | * http://www.faqs.org/rfcs/rfc3548.html. 181 | * It is important to note that data encoded this way is not officially valid Base64, 182 | * or at the very least should not be called Base64 without also specifying that is 183 | * was encoded using the URL- and Filename-safe dialect. 184 | */ 185 | public final static int URL_SAFE = 16; 186 | 187 | 188 | /** 189 | * Encode using the special "ordered" dialect of Base64 described here: 190 | * http://www.faqs.org/qa/rfcc-1940.html. 191 | */ 192 | public final static int ORDERED = 32; 193 | 194 | 195 | /* ******** P R I V A T E F I E L D S ******** */ 196 | 197 | 198 | /** Maximum line length (76) of Base64 output. */ 199 | private final static int MAX_LINE_LENGTH = 76; 200 | 201 | 202 | /** The equals sign (=) as a byte. */ 203 | private final static byte EQUALS_SIGN = (byte)'='; 204 | 205 | 206 | /** The new line character (\n) as a byte. */ 207 | private final static byte NEW_LINE = (byte)'\n'; 208 | 209 | 210 | /** Preferred encoding. */ 211 | private final static String PREFERRED_ENCODING = "US-ASCII"; 212 | 213 | 214 | private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding 215 | private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding 216 | 217 | 218 | /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ 219 | 220 | /** The 64 valid Base64 values. */ 221 | /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ 222 | private final static byte[] _STANDARD_ALPHABET = { 223 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 224 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 225 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 226 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 227 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 228 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 229 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 230 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 231 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 232 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' 233 | }; 234 | 235 | 236 | /** 237 | * Translates a Base64 value to either its 6-bit reconstruction value 238 | * or a negative number indicating some other meaning. 239 | **/ 240 | private final static byte[] _STANDARD_DECODABET = { 241 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 242 | -5,-5, // Whitespace: Tab and Linefeed 243 | -9,-9, // Decimal 11 - 12 244 | -5, // Whitespace: Carriage Return 245 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 246 | -9,-9,-9,-9,-9, // Decimal 27 - 31 247 | -5, // Whitespace: Space 248 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 249 | 62, // Plus sign at decimal 43 250 | -9,-9,-9, // Decimal 44 - 46 251 | 63, // Slash at decimal 47 252 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 253 | -9,-9,-9, // Decimal 58 - 60 254 | -1, // Equals sign at decimal 61 255 | -9,-9,-9, // Decimal 62 - 64 256 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 257 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 258 | -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 259 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 260 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 261 | -9,-9,-9,-9,-9 // Decimal 123 - 127 262 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 263 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 264 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 265 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 266 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 267 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 268 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 269 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 270 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 271 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 272 | }; 273 | 274 | 275 | /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ 276 | 277 | /** 278 | * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 279 | * http://www.faqs.org/rfcs/rfc3548.html. 280 | * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." 281 | */ 282 | private final static byte[] _URL_SAFE_ALPHABET = { 283 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 284 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 285 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 286 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 287 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 288 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 289 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 290 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 291 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 292 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' 293 | }; 294 | 295 | /** 296 | * Used in decoding URL- and Filename-safe dialects of Base64. 297 | */ 298 | private final static byte[] _URL_SAFE_DECODABET = { 299 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 300 | -5,-5, // Whitespace: Tab and Linefeed 301 | -9,-9, // Decimal 11 - 12 302 | -5, // Whitespace: Carriage Return 303 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 304 | -9,-9,-9,-9,-9, // Decimal 27 - 31 305 | -5, // Whitespace: Space 306 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 307 | -9, // Plus sign at decimal 43 308 | -9, // Decimal 44 309 | 62, // Minus sign at decimal 45 310 | -9, // Decimal 46 311 | -9, // Slash at decimal 47 312 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 313 | -9,-9,-9, // Decimal 58 - 60 314 | -1, // Equals sign at decimal 61 315 | -9,-9,-9, // Decimal 62 - 64 316 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 317 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 318 | -9,-9,-9,-9, // Decimal 91 - 94 319 | 63, // Underscore at decimal 95 320 | -9, // Decimal 96 321 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 322 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 323 | -9,-9,-9,-9,-9 // Decimal 123 - 127 324 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 325 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 326 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 327 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 328 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 329 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 330 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 331 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 332 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 333 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 334 | }; 335 | 336 | 337 | 338 | /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ 339 | 340 | /** 341 | * I don't get the point of this technique, but someone requested it, 342 | * and it is described here: 343 | * http://www.faqs.org/qa/rfcc-1940.html. 344 | */ 345 | private final static byte[] _ORDERED_ALPHABET = { 346 | (byte)'-', 347 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', 348 | (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', 349 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 350 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 351 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 352 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 353 | (byte)'_', 354 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 355 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 356 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 357 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' 358 | }; 359 | 360 | /** 361 | * Used in decoding the "ordered" dialect of Base64. 362 | */ 363 | private final static byte[] _ORDERED_DECODABET = { 364 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 365 | -5,-5, // Whitespace: Tab and Linefeed 366 | -9,-9, // Decimal 11 - 12 367 | -5, // Whitespace: Carriage Return 368 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 369 | -9,-9,-9,-9,-9, // Decimal 27 - 31 370 | -5, // Whitespace: Space 371 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 372 | -9, // Plus sign at decimal 43 373 | -9, // Decimal 44 374 | 0, // Minus sign at decimal 45 375 | -9, // Decimal 46 376 | -9, // Slash at decimal 47 377 | 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine 378 | -9,-9,-9, // Decimal 58 - 60 379 | -1, // Equals sign at decimal 61 380 | -9,-9,-9, // Decimal 62 - 64 381 | 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' 382 | 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' 383 | -9,-9,-9,-9, // Decimal 91 - 94 384 | 37, // Underscore at decimal 95 385 | -9, // Decimal 96 386 | 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' 387 | 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' 388 | -9,-9,-9,-9,-9 // Decimal 123 - 127 389 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 390 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 391 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 392 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 393 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 394 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 395 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 396 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 397 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 398 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 399 | }; 400 | 401 | 402 | /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ 403 | 404 | 405 | /** 406 | * Returns one of the _SOMETHING_ALPHABET byte arrays depending on 407 | * the options specified. 408 | * It's possible, though silly, to specify ORDERED and URLSAFE 409 | * in which case one of them will be picked, though there is 410 | * no guarantee as to which one will be picked. 411 | */ 412 | private final static byte[] getAlphabet( int options ) { 413 | if ((options & URL_SAFE) == URL_SAFE) { 414 | return _URL_SAFE_ALPHABET; 415 | } else if ((options & ORDERED) == ORDERED) { 416 | return _ORDERED_ALPHABET; 417 | } else { 418 | return _STANDARD_ALPHABET; 419 | } 420 | } // end getAlphabet 421 | 422 | 423 | /** 424 | * Returns one of the _SOMETHING_DECODABET byte arrays depending on 425 | * the options specified. 426 | * It's possible, though silly, to specify ORDERED and URL_SAFE 427 | * in which case one of them will be picked, though there is 428 | * no guarantee as to which one will be picked. 429 | */ 430 | private final static byte[] getDecodabet( int options ) { 431 | if( (options & URL_SAFE) == URL_SAFE) { 432 | return _URL_SAFE_DECODABET; 433 | } else if ((options & ORDERED) == ORDERED) { 434 | return _ORDERED_DECODABET; 435 | } else { 436 | return _STANDARD_DECODABET; 437 | } 438 | } // end getAlphabet 439 | 440 | 441 | 442 | /** Defeats instantiation. */ 443 | private MyBase64(){} 444 | 445 | 446 | 447 | 448 | /* ******** E N C O D I N G M E T H O D S ******** */ 449 | 450 | 451 | /** 452 | * Encodes up to the first three bytes of array threeBytes 453 | * and returns a four-byte array in Base64 notation. 454 | * The actual number of significant bytes in your array is 455 | * given by numSigBytes. 456 | * The array threeBytes needs only be as big as 457 | * numSigBytes. 458 | * Code can reuse a byte array by passing a four-byte array as b4. 459 | * 460 | * @param b4 A reusable byte array to reduce array instantiation 461 | * @param threeBytes the array to convert 462 | * @param numSigBytes the number of significant bytes in your array 463 | * @return four byte array in Base64 notation. 464 | * @since 1.5.1 465 | */ 466 | private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { 467 | encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); 468 | return b4; 469 | } // end encode3to4 470 | 471 | 472 | /** 473 | *

Encodes up to three bytes of the array source 474 | * and writes the resulting four Base64 bytes to destination. 475 | * The source and destination arrays can be manipulated 476 | * anywhere along their length by specifying 477 | * srcOffset and destOffset. 478 | * This method does not check to make sure your arrays 479 | * are large enough to accomodate srcOffset + 3 for 480 | * the source array or destOffset + 4 for 481 | * the destination array. 482 | * The actual number of significant bytes in your array is 483 | * given by numSigBytes.

484 | *

This is the lowest level of the encoding methods with 485 | * all possible parameters.

486 | * 487 | * @param source the array to convert 488 | * @param srcOffset the index where conversion begins 489 | * @param numSigBytes the number of significant bytes in your array 490 | * @param destination the array to hold the conversion 491 | * @param destOffset the index where output will be put 492 | * @return the destination array 493 | * @since 1.3 494 | */ 495 | private static byte[] encode3to4( 496 | byte[] source, int srcOffset, int numSigBytes, 497 | byte[] destination, int destOffset, int options ) { 498 | 499 | byte[] ALPHABET = getAlphabet( options ); 500 | 501 | // 1 2 3 502 | // 01234567890123456789012345678901 Bit position 503 | // --------000000001111111122222222 Array position from threeBytes 504 | // --------| || || || | Six bit groups to index ALPHABET 505 | // >>18 >>12 >> 6 >> 0 Right shift necessary 506 | // 0x3f 0x3f 0x3f Additional AND 507 | 508 | // Create buffer with zero-padding if there are only one or two 509 | // significant bytes passed in the array. 510 | // We have to shift left 24 in order to flush out the 1's that appear 511 | // when Java treats a value as negative that is cast from a byte to an int. 512 | int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) 513 | | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) 514 | | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); 515 | 516 | switch( numSigBytes ) 517 | { 518 | case 3: 519 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 520 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 521 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 522 | destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; 523 | return destination; 524 | 525 | case 2: 526 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 527 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 528 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 529 | destination[ destOffset + 3 ] = EQUALS_SIGN; 530 | return destination; 531 | 532 | case 1: 533 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 534 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 535 | destination[ destOffset + 2 ] = EQUALS_SIGN; 536 | destination[ destOffset + 3 ] = EQUALS_SIGN; 537 | return destination; 538 | 539 | default: 540 | return destination; 541 | } // end switch 542 | } // end encode3to4 543 | 544 | 545 | 546 | /** 547 | * Performs Base64 encoding on the raw ByteBuffer, 548 | * writing it to the encoded ByteBuffer. 549 | * This is an experimental feature. Currently it does not 550 | * pass along any options (such as {@link #DO_BREAK_LINES} 551 | * or {@link #GZIP}. 552 | * 553 | * @param raw input buffer 554 | * @param encoded output buffer 555 | * @since 2.3 556 | */ 557 | public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){ 558 | byte[] raw3 = new byte[3]; 559 | byte[] enc4 = new byte[4]; 560 | 561 | while( raw.hasRemaining() ){ 562 | int rem = Math.min(3,raw.remaining()); 563 | raw.get(raw3,0,rem); 564 | MyBase64.encode3to4(enc4, raw3, rem, MyBase64.NO_OPTIONS ); 565 | encoded.put(enc4); 566 | } // end input remaining 567 | } 568 | 569 | 570 | /** 571 | * Performs Base64 encoding on the raw ByteBuffer, 572 | * writing it to the encoded CharBuffer. 573 | * This is an experimental feature. Currently it does not 574 | * pass along any options (such as {@link #DO_BREAK_LINES} 575 | * or {@link #GZIP}. 576 | * 577 | * @param raw input buffer 578 | * @param encoded output buffer 579 | * @since 2.3 580 | */ 581 | public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){ 582 | byte[] raw3 = new byte[3]; 583 | byte[] enc4 = new byte[4]; 584 | 585 | while( raw.hasRemaining() ){ 586 | int rem = Math.min(3,raw.remaining()); 587 | raw.get(raw3,0,rem); 588 | MyBase64.encode3to4(enc4, raw3, rem, MyBase64.NO_OPTIONS ); 589 | for( int i = 0; i < 4; i++ ){ 590 | encoded.put( (char)(enc4[i] & 0xFF) ); 591 | } 592 | } // end input remaining 593 | } 594 | 595 | 596 | 597 | 598 | /** 599 | * Serializes an object and returns the Base64-encoded 600 | * version of that serialized object. 601 | * 602 | *

As of v 2.3, if the object 603 | * cannot be serialized or there is another error, 604 | * the method will throw an java.io.IOException. This is new to v2.3! 605 | * In earlier versions, it just returned a null value, but 606 | * in retrospect that's a pretty poor way to handle it.

607 | * 608 | * The object is not GZip-compressed before being encoded. 609 | * 610 | * @param serializableObject The object to encode 611 | * @return The Base64-encoded object 612 | * @throws java.io.IOException if there is an error 613 | * @throws NullPointerException if serializedObject is null 614 | * @since 1.4 615 | */ 616 | public static String encodeObject(java.io.Serializable serializableObject ) 617 | throws java.io.IOException { 618 | return encodeObject( serializableObject, NO_OPTIONS ); 619 | } // end encodeObject 620 | 621 | 622 | 623 | /** 624 | * Serializes an object and returns the Base64-encoded 625 | * version of that serialized object. 626 | * 627 | *

As of v 2.3, if the object 628 | * cannot be serialized or there is another error, 629 | * the method will throw an java.io.IOException. This is new to v2.3! 630 | * In earlier versions, it just returned a null value, but 631 | * in retrospect that's a pretty poor way to handle it.

632 | * 633 | * The object is not GZip-compressed before being encoded. 634 | *

635 | * Example options:

 636 |      *   GZIP: gzip-compresses object before encoding it.
 637 |      *   DO_BREAK_LINES: break lines at 76 characters
 638 |      * 
639 | *

640 | * Example: encodeObject( myObj, Base64.GZIP ) or 641 | *

642 | * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) 643 | * 644 | * @param serializableObject The object to encode 645 | * @param options Specified options 646 | * @return The Base64-encoded object 647 | * @see MyBase64#GZIP 648 | * @see MyBase64#DO_BREAK_LINES 649 | * @throws java.io.IOException if there is an error 650 | * @since 2.0 651 | */ 652 | public static String encodeObject(java.io.Serializable serializableObject, int options ) 653 | throws java.io.IOException { 654 | 655 | if( serializableObject == null ){ 656 | throw new NullPointerException( "Cannot serialize a null object." ); 657 | } // end if: null 658 | 659 | // Streams 660 | java.io.ByteArrayOutputStream baos = null; 661 | java.io.OutputStream b64os = null; 662 | java.util.zip.GZIPOutputStream gzos = null; 663 | java.io.ObjectOutputStream oos = null; 664 | 665 | 666 | //noinspection CaughtExceptionImmediatelyRethrown 667 | try { 668 | // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream 669 | baos = new java.io.ByteArrayOutputStream(); 670 | b64os = new OutputStream( baos, ENCODE | options ); 671 | if( (options & GZIP) != 0 ){ 672 | // Gzip 673 | gzos = new java.util.zip.GZIPOutputStream(b64os); 674 | oos = new java.io.ObjectOutputStream( gzos ); 675 | } else { 676 | // Not gzipped 677 | oos = new java.io.ObjectOutputStream( b64os ); 678 | } 679 | oos.writeObject( serializableObject ); 680 | } // end try 681 | catch( java.io.IOException e ) { 682 | // Catch it and then throw it immediately so that 683 | // the finally{} block is called for cleanup. 684 | throw e; 685 | } // end catch 686 | finally { 687 | try{ oos.close(); } catch( Exception e ){} 688 | try{ gzos.close(); } catch( Exception e ){} 689 | try{ b64os.close(); } catch( Exception e ){} 690 | try{ baos.close(); } catch( Exception e ){} 691 | } // end finally 692 | 693 | // Return value according to relevant encoding. 694 | try { 695 | return new String( baos.toByteArray(), PREFERRED_ENCODING ); 696 | } // end try 697 | catch (java.io.UnsupportedEncodingException uue){ 698 | // Fall back to some Java default 699 | return new String( baos.toByteArray() ); 700 | } // end catch 701 | 702 | } // end encode 703 | 704 | 705 | 706 | /** 707 | * Encodes a byte array into Base64 notation. 708 | * Does not GZip-compress data. 709 | * 710 | * @param source The data to convert 711 | * @return The data in Base64-encoded form 712 | * @throws NullPointerException if source array is null 713 | * @since 1.4 714 | */ 715 | public static String encodeBytes(byte[] source ) { 716 | // Since we're not going to have the GZIP encoding turned on, 717 | // we're not going to have an java.io.IOException thrown, so 718 | // we should not force the user to have to catch it. 719 | String encoded = null; 720 | try { 721 | encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); 722 | } catch (java.io.IOException ex) { 723 | assert false : ex.getMessage(); 724 | } // end catch 725 | assert encoded != null; 726 | return encoded; 727 | } // end encodeBytes 728 | 729 | 730 | 731 | /** 732 | * Encodes a byte array into Base64 notation. 733 | *

734 | * Example options:

 735 |      *   GZIP: gzip-compresses object before encoding it.
 736 |      *   DO_BREAK_LINES: break lines at 76 characters
 737 |      *     Note: Technically, this makes your encoding non-compliant.
 738 |      * 
739 | *

740 | * Example: encodeBytes( myData, Base64.GZIP ) or 741 | *

742 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) 743 | * 744 | * 745 | *

As of v 2.3, if there is an error with the GZIP stream, 746 | * the method will throw an java.io.IOException. This is new to v2.3! 747 | * In earlier versions, it just returned a null value, but 748 | * in retrospect that's a pretty poor way to handle it.

749 | * 750 | * 751 | * @param source The data to convert 752 | * @param options Specified options 753 | * @return The Base64-encoded data as a String 754 | * @see MyBase64#GZIP 755 | * @see MyBase64#DO_BREAK_LINES 756 | * @throws java.io.IOException if there is an error 757 | * @throws NullPointerException if source array is null 758 | * @since 2.0 759 | */ 760 | public static String encodeBytes(byte[] source, int options ) throws java.io.IOException { 761 | return encodeBytes( source, 0, source.length, options ); 762 | } // end encodeBytes 763 | 764 | 765 | /** 766 | * Encodes a byte array into Base64 notation. 767 | * Does not GZip-compress data. 768 | * 769 | *

As of v 2.3, if there is an error, 770 | * the method will throw an java.io.IOException. This is new to v2.3! 771 | * In earlier versions, it just returned a null value, but 772 | * in retrospect that's a pretty poor way to handle it.

773 | * 774 | * 775 | * @param source The data to convert 776 | * @param off Offset in array where conversion should begin 777 | * @param len Length of data to convert 778 | * @return The Base64-encoded data as a String 779 | * @throws NullPointerException if source array is null 780 | * @throws IllegalArgumentException if source array, offset, or length are invalid 781 | * @since 1.4 782 | */ 783 | public static String encodeBytes(byte[] source, int off, int len ) { 784 | // Since we're not going to have the GZIP encoding turned on, 785 | // we're not going to have an java.io.IOException thrown, so 786 | // we should not force the user to have to catch it. 787 | String encoded = null; 788 | try { 789 | encoded = encodeBytes( source, off, len, NO_OPTIONS ); 790 | } catch (java.io.IOException ex) { 791 | assert false : ex.getMessage(); 792 | } // end catch 793 | assert encoded != null; 794 | return encoded; 795 | } // end encodeBytes 796 | 797 | 798 | 799 | /** 800 | * Encodes a byte array into Base64 notation. 801 | *

802 | * Example options:

 803 |      *   GZIP: gzip-compresses object before encoding it.
 804 |      *   DO_BREAK_LINES: break lines at 76 characters
 805 |      *     Note: Technically, this makes your encoding non-compliant.
 806 |      * 
807 | *

808 | * Example: encodeBytes( myData, Base64.GZIP ) or 809 | *

810 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) 811 | * 812 | * 813 | *

As of v 2.3, if there is an error with the GZIP stream, 814 | * the method will throw an java.io.IOException. This is new to v2.3! 815 | * In earlier versions, it just returned a null value, but 816 | * in retrospect that's a pretty poor way to handle it.

817 | * 818 | * 819 | * @param source The data to convert 820 | * @param off Offset in array where conversion should begin 821 | * @param len Length of data to convert 822 | * @param options Specified options 823 | * @return The Base64-encoded data as a String 824 | * @see MyBase64#GZIP 825 | * @see MyBase64#DO_BREAK_LINES 826 | * @throws java.io.IOException if there is an error 827 | * @throws NullPointerException if source array is null 828 | * @throws IllegalArgumentException if source array, offset, or length are invalid 829 | * @since 2.0 830 | */ 831 | public static String encodeBytes(byte[] source, int off, int len, int options ) throws java.io.IOException { 832 | byte[] encoded = encodeBytesToBytes( source, off, len, options ); 833 | 834 | // Return value according to relevant encoding. 835 | try { 836 | return new String( encoded, PREFERRED_ENCODING ); 837 | } // end try 838 | catch (java.io.UnsupportedEncodingException uue) { 839 | return new String( encoded ); 840 | } // end catch 841 | 842 | } // end encodeBytes 843 | 844 | 845 | 846 | 847 | /** 848 | * Similar to {@link #encodeBytes(byte[])} but returns 849 | * a byte array instead of instantiating a String. This is nav_common efficient 850 | * if you're working with I/O streams and have large data sets to encode. 851 | * 852 | * 853 | * @param source The data to convert 854 | * @return The Base64-encoded data as a byte[] (of ASCII characters) 855 | * @throws NullPointerException if source array is null 856 | * @since 2.3.1 857 | */ 858 | public static byte[] encodeBytesToBytes( byte[] source ) { 859 | byte[] encoded = null; 860 | try { 861 | encoded = encodeBytesToBytes( source, 0, source.length, MyBase64.NO_OPTIONS ); 862 | } catch( java.io.IOException ex ) { 863 | assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 864 | } 865 | return encoded; 866 | } 867 | 868 | 869 | /** 870 | * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns 871 | * a byte array instead of instantiating a String. This is nav_common efficient 872 | * if you're working with I/O streams and have large data sets to encode. 873 | * 874 | * 875 | * @param source The data to convert 876 | * @param off Offset in array where conversion should begin 877 | * @param len Length of data to convert 878 | * @param options Specified options 879 | * @return The Base64-encoded data as a String 880 | * @see MyBase64#GZIP 881 | * @see MyBase64#DO_BREAK_LINES 882 | * @throws java.io.IOException if there is an error 883 | * @throws NullPointerException if source array is null 884 | * @throws IllegalArgumentException if source array, offset, or length are invalid 885 | * @since 2.3.1 886 | */ 887 | public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { 888 | 889 | if( source == null ){ 890 | throw new NullPointerException( "Cannot serialize a null array." ); 891 | } // end if: null 892 | 893 | if( off < 0 ){ 894 | throw new IllegalArgumentException( "Cannot have negative offset: " + off ); 895 | } // end if: off < 0 896 | 897 | if( len < 0 ){ 898 | throw new IllegalArgumentException( "Cannot have length offset: " + len ); 899 | } // end if: len < 0 900 | 901 | if( off + len > source.length ){ 902 | throw new IllegalArgumentException( 903 | String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); 904 | } // end if: off < 0 905 | 906 | 907 | 908 | // Compress? 909 | if( (options & GZIP) != 0 ) { 910 | java.io.ByteArrayOutputStream baos = null; 911 | java.util.zip.GZIPOutputStream gzos = null; 912 | OutputStream b64os = null; 913 | 914 | //noinspection CaughtExceptionImmediatelyRethrown 915 | try { 916 | // GZip -> Base64 -> ByteArray 917 | baos = new java.io.ByteArrayOutputStream(); 918 | b64os = new OutputStream( baos, ENCODE | options ); 919 | gzos = new java.util.zip.GZIPOutputStream( b64os ); 920 | 921 | gzos.write( source, off, len ); 922 | gzos.close(); 923 | } // end try 924 | catch( java.io.IOException e ) { 925 | // Catch it and then throw it immediately so that 926 | // the finally{} block is called for cleanup. 927 | throw e; 928 | } // end catch 929 | finally { 930 | try{ gzos.close(); } catch( Exception e ){} 931 | try{ b64os.close(); } catch( Exception e ){} 932 | try{ baos.close(); } catch( Exception e ){} 933 | } // end finally 934 | 935 | return baos.toByteArray(); 936 | } // end if: compress 937 | 938 | // Else, don't compress. Better not to use streams at all then. 939 | else { 940 | boolean breakLines = (options & DO_BREAK_LINES) != 0; 941 | 942 | //int len43 = len * 4 / 3; 943 | //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 944 | // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding 945 | // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines 946 | // Try to determine nav_common precisely how big the array needs to be. 947 | // If we get it right, we don't have to do an array copy, and 948 | // we save a bunch of memory. 949 | int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding 950 | if( breakLines ){ 951 | encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters 952 | } 953 | byte[] outBuff = new byte[ encLen ]; 954 | 955 | 956 | int d = 0; 957 | int e = 0; 958 | int len2 = len - 2; 959 | int lineLength = 0; 960 | for( ; d < len2; d+=3, e+=4 ) { 961 | encode3to4( source, d+off, 3, outBuff, e, options ); 962 | 963 | lineLength += 4; 964 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) 965 | { 966 | outBuff[e+4] = NEW_LINE; 967 | e++; 968 | lineLength = 0; 969 | } // end if: end of line 970 | } // en dfor: each piece of array 971 | 972 | if( d < len ) { 973 | encode3to4( source, d+off, len - d, outBuff, e, options ); 974 | e += 4; 975 | } // end if: some padding needed 976 | 977 | 978 | // Only resize array if we didn't guess it right. 979 | if( e <= outBuff.length - 1 ){ 980 | // If breaking lines and the last byte falls right at 981 | // the line length (76 bytes per line), there will be 982 | // one extra byte, and the array will need to be resized. 983 | // Not too bad of an estimate on array size, I'd say. 984 | byte[] finalOut = new byte[e]; 985 | System.arraycopy(outBuff,0, finalOut,0,e); 986 | //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); 987 | return finalOut; 988 | } else { 989 | //System.err.println("No need to resize array."); 990 | return outBuff; 991 | } 992 | 993 | } // end else: don't compress 994 | 995 | } // end encodeBytesToBytes 996 | 997 | 998 | 999 | 1000 | 1001 | /* ******** D E C O D I N G M E T H O D S ******** */ 1002 | 1003 | 1004 | /** 1005 | * Decodes four bytes from array source 1006 | * and writes the resulting bytes (up to three of them) 1007 | * to destination. 1008 | * The source and destination arrays can be manipulated 1009 | * anywhere along their length by specifying 1010 | * srcOffset and destOffset. 1011 | * This method does not check to make sure your arrays 1012 | * are large enough to accomodate srcOffset + 4 for 1013 | * the source array or destOffset + 3 for 1014 | * the destination array. 1015 | * This method returns the actual number of bytes that 1016 | * were converted from the Base64 encoding. 1017 | *

This is the lowest level of the decoding methods with 1018 | * all possible parameters.

1019 | * 1020 | * 1021 | * @param source the array to convert 1022 | * @param srcOffset the index where conversion begins 1023 | * @param destination the array to hold the conversion 1024 | * @param destOffset the index where output will be put 1025 | * @param options alphabet type is pulled from this (standard, url-safe, ordered) 1026 | * @return the number of decoded bytes converted 1027 | * @throws NullPointerException if source or destination arrays are null 1028 | * @throws IllegalArgumentException if srcOffset or destOffset are invalid 1029 | * or there is not enough room in the array. 1030 | * @since 1.3 1031 | */ 1032 | private static int decode4to3( 1033 | byte[] source, int srcOffset, 1034 | byte[] destination, int destOffset, int options ) { 1035 | 1036 | // Lots of error checking and exception throwing 1037 | if( source == null ){ 1038 | throw new NullPointerException( "Source array was null." ); 1039 | } // end if 1040 | if( destination == null ){ 1041 | throw new NullPointerException( "Destination array was null." ); 1042 | } // end if 1043 | if( srcOffset < 0 || srcOffset + 3 >= source.length ){ 1044 | throw new IllegalArgumentException( String.format( 1045 | "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); 1046 | } // end if 1047 | if( destOffset < 0 || destOffset +2 >= destination.length ){ 1048 | throw new IllegalArgumentException( String.format( 1049 | "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); 1050 | } // end if 1051 | 1052 | 1053 | byte[] DECODABET = getDecodabet( options ); 1054 | 1055 | // Example: Dk== 1056 | if( source[ srcOffset + 2] == EQUALS_SIGN ) { 1057 | // Two ways to do the same thing. Don't know which way I like best. 1058 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1059 | // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 1060 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1061 | | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); 1062 | 1063 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1064 | return 1; 1065 | } 1066 | 1067 | // Example: DkL= 1068 | else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { 1069 | // Two ways to do the same thing. Don't know which way I like best. 1070 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1071 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1072 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 1073 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1074 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1075 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); 1076 | 1077 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1078 | destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); 1079 | return 2; 1080 | } 1081 | 1082 | // Example: DkLE 1083 | else { 1084 | // Two ways to do the same thing. Don't know which way I like best. 1085 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1086 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1087 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 1088 | // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 1089 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1090 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1091 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) 1092 | | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); 1093 | 1094 | 1095 | destination[ destOffset ] = (byte)( outBuff >> 16 ); 1096 | destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); 1097 | destination[ destOffset + 2 ] = (byte)( outBuff ); 1098 | 1099 | return 3; 1100 | } 1101 | } // end decodeToBytes 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | /** 1108 | * Low-level access to decoding ASCII characters in 1109 | * the form of a byte array. Ignores GUNZIP option, if 1110 | * it's set. This is not generally a recommended method, 1111 | * although it is used internally as part of the decoding process. 1112 | * Special case: if len = 0, an empty array is returned. Still, 1113 | * if you need nav_common speed and reduced memory footprint (and aren't 1114 | * gzipping), consider this method. 1115 | * 1116 | * @param source The Base64 encoded data 1117 | * @return decoded data 1118 | * @since 2.3.1 1119 | */ 1120 | public static byte[] decode( byte[] source ) 1121 | throws java.io.IOException { 1122 | byte[] decoded = null; 1123 | // try { 1124 | decoded = decode( source, 0, source.length, MyBase64.NO_OPTIONS ); 1125 | // } catch( java.io.IOException ex ) { 1126 | // assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 1127 | // } 1128 | return decoded; 1129 | } 1130 | 1131 | 1132 | 1133 | /** 1134 | * Low-level access to decoding ASCII characters in 1135 | * the form of a byte array. Ignores GUNZIP option, if 1136 | * it's set. This is not generally a recommended method, 1137 | * although it is used internally as part of the decoding process. 1138 | * Special case: if len = 0, an empty array is returned. Still, 1139 | * if you need nav_common speed and reduced memory footprint (and aren't 1140 | * gzipping), consider this method. 1141 | * 1142 | * @param source The Base64 encoded data 1143 | * @param off The offset of where to begin decoding 1144 | * @param len The length of characters to decode 1145 | * @param options Can specify options such as alphabet type to use 1146 | * @return decoded data 1147 | * @throws java.io.IOException If bogus characters exist in source data 1148 | * @since 1.3 1149 | */ 1150 | public static byte[] decode( byte[] source, int off, int len, int options ) 1151 | throws java.io.IOException { 1152 | 1153 | // Lots of error checking and exception throwing 1154 | if( source == null ){ 1155 | throw new NullPointerException( "Cannot decode null source array." ); 1156 | } // end if 1157 | if( off < 0 || off + len > source.length ){ 1158 | throw new IllegalArgumentException( String.format( 1159 | "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); 1160 | } // end if 1161 | 1162 | if( len == 0 ){ 1163 | return new byte[0]; 1164 | }else if( len < 4 ){ 1165 | throw new IllegalArgumentException( 1166 | "Base64-encoded string must have at least four characters, but length specified was " + len ); 1167 | } // end if 1168 | 1169 | byte[] DECODABET = getDecodabet( options ); 1170 | 1171 | int len34 = len * 3 / 4; // Estimate on array size 1172 | byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output 1173 | int outBuffPosn = 0; // Keep track of where we're writing 1174 | 1175 | byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space 1176 | int b4Posn = 0; // Keep track of four byte input buffer 1177 | int i = 0; // Source array counter 1178 | byte sbiDecode = 0; // Special value from DECODABET 1179 | 1180 | for( i = off; i < off+len; i++ ) { // Loop through source 1181 | 1182 | sbiDecode = DECODABET[ source[i]&0xFF ]; 1183 | 1184 | // White space, Equals sign, or legit Base64 character 1185 | // Note the values such as -5 and -9 in the 1186 | // DECODABETs at the top of the file. 1187 | if( sbiDecode >= WHITE_SPACE_ENC ) { 1188 | if( sbiDecode >= EQUALS_SIGN_ENC ) { 1189 | b4[ b4Posn++ ] = source[i]; // Save non-whitespace 1190 | if( b4Posn > 3 ) { // Time to decode? 1191 | outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); 1192 | b4Posn = 0; 1193 | 1194 | // If that was the equals sign, break out of 'for' loop 1195 | if( source[i] == EQUALS_SIGN ) { 1196 | break; 1197 | } // end if: equals sign 1198 | } // end if: quartet built 1199 | } // end if: equals sign or better 1200 | } // end if: white space, equals sign or better 1201 | else { 1202 | // There's a bad input character in the Base64 stream. 1203 | throw new java.io.IOException( String.format( 1204 | "Bad Base64 input character decimal %d in array position %d", ((int)source[i])&0xFF, i ) ); 1205 | } // end else: 1206 | } // each input character 1207 | 1208 | byte[] out = new byte[ outBuffPosn ]; 1209 | System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 1210 | return out; 1211 | } // end decode 1212 | 1213 | 1214 | 1215 | 1216 | /** 1217 | * Decodes data from Base64 notation, automatically 1218 | * detecting gzip-compressed data and decompressing it. 1219 | * 1220 | * @param s the string to decode 1221 | * @return the decoded data 1222 | * @throws java.io.IOException If there is a problem 1223 | * @since 1.4 1224 | */ 1225 | public static byte[] decode( String s ) throws java.io.IOException { 1226 | return decode( s, NO_OPTIONS ); 1227 | } 1228 | 1229 | 1230 | 1231 | /** 1232 | * Decodes data from Base64 notation, automatically 1233 | * detecting gzip-compressed data and decompressing it. 1234 | * 1235 | * @param s the string to decode 1236 | * @param options encode options such as URL_SAFE 1237 | * @return the decoded data 1238 | * @throws java.io.IOException if there is an error 1239 | * @throws NullPointerException if s is null 1240 | * @since 1.4 1241 | */ 1242 | public static byte[] decode(String s, int options ) throws java.io.IOException { 1243 | 1244 | if( s == null ){ 1245 | throw new NullPointerException( "Input string was null." ); 1246 | } // end if 1247 | 1248 | byte[] bytes; 1249 | try { 1250 | bytes = s.getBytes( PREFERRED_ENCODING ); 1251 | } // end try 1252 | catch( java.io.UnsupportedEncodingException uee ) { 1253 | bytes = s.getBytes(); 1254 | } // end catch 1255 | // 1256 | 1257 | // Decode 1258 | bytes = decode( bytes, 0, bytes.length, options ); 1259 | 1260 | // Check to see if it's gzip-compressed 1261 | // GZIP Magic Two-Byte Number: 0x8b1f (35615) 1262 | boolean dontGunzip = (options & DONT_GUNZIP) != 0; 1263 | if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { 1264 | 1265 | int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); 1266 | if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { 1267 | java.io.ByteArrayInputStream bais = null; 1268 | java.util.zip.GZIPInputStream gzis = null; 1269 | java.io.ByteArrayOutputStream baos = null; 1270 | byte[] buffer = new byte[2048]; 1271 | int length = 0; 1272 | 1273 | try { 1274 | baos = new java.io.ByteArrayOutputStream(); 1275 | bais = new java.io.ByteArrayInputStream( bytes ); 1276 | gzis = new java.util.zip.GZIPInputStream( bais ); 1277 | 1278 | while( ( length = gzis.read( buffer ) ) >= 0 ) { 1279 | baos.write(buffer,0,length); 1280 | } // end while: reading input 1281 | 1282 | // No error? Get new bytes. 1283 | bytes = baos.toByteArray(); 1284 | 1285 | } // end try 1286 | catch( java.io.IOException e ) { 1287 | e.printStackTrace(); 1288 | // Just return originally-decoded bytes 1289 | } // end catch 1290 | finally { 1291 | try{ baos.close(); } catch( Exception e ){} 1292 | try{ gzis.close(); } catch( Exception e ){} 1293 | try{ bais.close(); } catch( Exception e ){} 1294 | } // end finally 1295 | 1296 | } // end if: gzipped 1297 | } // end if: bytes.length >= 2 1298 | 1299 | return bytes; 1300 | } // end decode 1301 | 1302 | 1303 | 1304 | /** 1305 | * Attempts to decode Base64 data and deserialize a Java 1306 | * Object within. Returns null if there was an error. 1307 | * 1308 | * @param encodedObject The Base64 data to decode 1309 | * @return The decoded and deserialized object 1310 | * @throws NullPointerException if encodedObject is null 1311 | * @throws java.io.IOException if there is a general error 1312 | * @throws ClassNotFoundException if the decoded object is of a 1313 | * class that cannot be found by the JVM 1314 | * @since 1.5 1315 | */ 1316 | public static Object decodeToObject(String encodedObject ) 1317 | throws java.io.IOException, ClassNotFoundException { 1318 | return decodeToObject(encodedObject,NO_OPTIONS,null); 1319 | } 1320 | 1321 | 1322 | /** 1323 | * Attempts to decode Base64 data and deserialize a Java 1324 | * Object within. Returns null if there was an error. 1325 | * If loader is not null, it will be the class loader 1326 | * used when deserializing. 1327 | * 1328 | * @param encodedObject The Base64 data to decode 1329 | * @param options Various parameters related to decoding 1330 | * @param loader Optional class loader to use in deserializing classes. 1331 | * @return The decoded and deserialized object 1332 | * @throws NullPointerException if encodedObject is null 1333 | * @throws java.io.IOException if there is a general error 1334 | * @throws ClassNotFoundException if the decoded object is of a 1335 | * class that cannot be found by the JVM 1336 | * @since 2.3.4 1337 | */ 1338 | public static Object decodeToObject( 1339 | String encodedObject, int options, final ClassLoader loader ) 1340 | throws java.io.IOException, ClassNotFoundException { 1341 | 1342 | // Decode and gunzip if necessary 1343 | byte[] objBytes = decode( encodedObject, options ); 1344 | 1345 | java.io.ByteArrayInputStream bais = null; 1346 | java.io.ObjectInputStream ois = null; 1347 | Object obj = null; 1348 | 1349 | //noinspection CaughtExceptionImmediatelyRethrown,CaughtExceptionImmediatelyRethrown 1350 | try { 1351 | bais = new java.io.ByteArrayInputStream( objBytes ); 1352 | 1353 | // If no custom class loader is provided, use Java's builtin OIS. 1354 | if( loader == null ){ 1355 | ois = new java.io.ObjectInputStream( bais ); 1356 | } // end if: no loader provided 1357 | 1358 | // Else make a customized object input stream that uses 1359 | // the provided class loader. 1360 | else { 1361 | ois = new java.io.ObjectInputStream(bais){ 1362 | @Override 1363 | public Class resolveClass(java.io.ObjectStreamClass streamClass) 1364 | throws java.io.IOException, ClassNotFoundException { 1365 | Class c = Class.forName(streamClass.getName(), false, loader); 1366 | if( c == null ){ 1367 | return super.resolveClass(streamClass); 1368 | } else { 1369 | return c; // Class loader knows of this class. 1370 | } // end else: not null 1371 | } // end resolveClass 1372 | }; // end ois 1373 | } // end else: no custom class loader 1374 | 1375 | obj = ois.readObject(); 1376 | } // end try 1377 | catch( java.io.IOException e ) { 1378 | throw e; // Catch and throw in order to execute finally{} 1379 | } // end catch 1380 | catch( ClassNotFoundException e ) { 1381 | throw e; // Catch and throw in order to execute finally{} 1382 | } // end catch 1383 | finally { 1384 | try{ bais.close(); } catch( Exception e ){} 1385 | try{ ois.close(); } catch( Exception e ){} 1386 | } // end finally 1387 | 1388 | return obj; 1389 | } // end decodeObject 1390 | 1391 | 1392 | 1393 | /** 1394 | * Convenience method for encoding data to a file. 1395 | * 1396 | *

As of v 2.3, if there is a error, 1397 | * the method will throw an java.io.IOException. This is new to v2.3! 1398 | * In earlier versions, it just returned false, but 1399 | * in retrospect that's a pretty poor way to handle it.

1400 | * 1401 | * @param dataToEncode byte array of data to encode in base64 form 1402 | * @param filename Filename for saving encoded data 1403 | * @throws java.io.IOException if there is an error 1404 | * @throws NullPointerException if dataToEncode is null 1405 | * @since 2.1 1406 | */ 1407 | public static void encodeToFile( byte[] dataToEncode, String filename ) 1408 | throws java.io.IOException { 1409 | 1410 | if( dataToEncode == null ){ 1411 | throw new NullPointerException( "Data to encode was null." ); 1412 | } // end iff 1413 | 1414 | OutputStream bos = null; 1415 | //noinspection CaughtExceptionImmediatelyRethrown 1416 | try { 1417 | bos = new OutputStream( 1418 | new java.io.FileOutputStream( filename ), MyBase64.ENCODE ); 1419 | bos.write( dataToEncode ); 1420 | } // end try 1421 | catch( java.io.IOException e ) { 1422 | throw e; // Catch and throw to execute finally{} block 1423 | } // end catch: java.io.IOException 1424 | finally { 1425 | try{ bos.close(); } catch( Exception e ){} 1426 | } // end finally 1427 | 1428 | } // end encodeToFile 1429 | 1430 | 1431 | /** 1432 | * Convenience method for decoding data to a file. 1433 | * 1434 | *

As of v 2.3, if there is a error, 1435 | * the method will throw an java.io.IOException. This is new to v2.3! 1436 | * In earlier versions, it just returned false, but 1437 | * in retrospect that's a pretty poor way to handle it.

1438 | * 1439 | * @param dataToDecode Base64-encoded data as a string 1440 | * @param filename Filename for saving decoded data 1441 | * @throws java.io.IOException if there is an error 1442 | * @since 2.1 1443 | */ 1444 | public static void decodeToFile(String dataToDecode, String filename ) 1445 | throws java.io.IOException { 1446 | 1447 | OutputStream bos = null; 1448 | //noinspection CaughtExceptionImmediatelyRethrown 1449 | try{ 1450 | bos = new OutputStream( 1451 | new java.io.FileOutputStream( filename ), MyBase64.DECODE ); 1452 | bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); 1453 | } // end try 1454 | catch( java.io.IOException e ) { 1455 | throw e; // Catch and throw to execute finally{} block 1456 | } // end catch: java.io.IOException 1457 | finally { 1458 | try{ bos.close(); } catch( Exception e ){} 1459 | } // end finally 1460 | 1461 | } // end decodeToFile 1462 | 1463 | 1464 | 1465 | 1466 | /** 1467 | * Convenience method for reading a base64-encoded 1468 | * file and decoding it. 1469 | * 1470 | *

As of v 2.3, if there is a error, 1471 | * the method will throw an java.io.IOException. This is new to v2.3! 1472 | * In earlier versions, it just returned false, but 1473 | * in retrospect that's a pretty poor way to handle it.

1474 | * 1475 | * @param filename Filename for reading encoded data 1476 | * @return decoded byte array 1477 | * @throws java.io.IOException if there is an error 1478 | * @since 2.1 1479 | */ 1480 | public static byte[] decodeFromFile( String filename ) 1481 | throws java.io.IOException { 1482 | 1483 | byte[] decodedData = null; 1484 | InputStream bis = null; 1485 | //noinspection CaughtExceptionImmediatelyRethrown 1486 | try 1487 | { 1488 | // Set up some useful variables 1489 | java.io.File file = new java.io.File( filename ); 1490 | byte[] buffer = null; 1491 | int length = 0; 1492 | int numBytes = 0; 1493 | 1494 | // Check for size of file 1495 | if( file.length() > Integer.MAX_VALUE ) 1496 | { 1497 | throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); 1498 | } // end if: file too big for int index 1499 | buffer = new byte[ (int)file.length() ]; 1500 | 1501 | // Open a stream 1502 | bis = new InputStream( 1503 | new java.io.BufferedInputStream( 1504 | new java.io.FileInputStream( file ) ), MyBase64.DECODE ); 1505 | 1506 | // Read until done 1507 | while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { 1508 | length += numBytes; 1509 | } // end while 1510 | 1511 | // Save in a variable to return 1512 | decodedData = new byte[ length ]; 1513 | System.arraycopy( buffer, 0, decodedData, 0, length ); 1514 | 1515 | } // end try 1516 | catch( java.io.IOException e ) { 1517 | throw e; // Catch and release to execute finally{} 1518 | } // end catch: java.io.IOException 1519 | finally { 1520 | try{ bis.close(); } catch( Exception e) {} 1521 | } // end finally 1522 | 1523 | return decodedData; 1524 | } // end decodeFromFile 1525 | 1526 | 1527 | 1528 | /** 1529 | * Convenience method for reading a binary file 1530 | * and base64-encoding it. 1531 | * 1532 | *

As of v 2.3, if there is a error, 1533 | * the method will throw an java.io.IOException. This is new to v2.3! 1534 | * In earlier versions, it just returned false, but 1535 | * in retrospect that's a pretty poor way to handle it.

1536 | * 1537 | * @param filename Filename for reading binary data 1538 | * @return base64-encoded string 1539 | * @throws java.io.IOException if there is an error 1540 | * @since 2.1 1541 | */ 1542 | public static String encodeFromFile(String filename ) 1543 | throws java.io.IOException { 1544 | 1545 | String encodedData = null; 1546 | InputStream bis = null; 1547 | //noinspection CaughtExceptionImmediatelyRethrown 1548 | try 1549 | { 1550 | // Set up some useful variables 1551 | java.io.File file = new java.io.File( filename ); 1552 | byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5) 1553 | int length = 0; 1554 | int numBytes = 0; 1555 | 1556 | // Open a stream 1557 | bis = new InputStream( 1558 | new java.io.BufferedInputStream( 1559 | new java.io.FileInputStream( file ) ), MyBase64.ENCODE ); 1560 | 1561 | // Read until done 1562 | while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { 1563 | length += numBytes; 1564 | } // end while 1565 | 1566 | // Save in a variable to return 1567 | encodedData = new String( buffer, 0, length, MyBase64.PREFERRED_ENCODING ); 1568 | 1569 | } // end try 1570 | catch( java.io.IOException e ) { 1571 | throw e; // Catch and release to execute finally{} 1572 | } // end catch: java.io.IOException 1573 | finally { 1574 | try{ bis.close(); } catch( Exception e) {} 1575 | } // end finally 1576 | 1577 | return encodedData; 1578 | } // end encodeFromFile 1579 | 1580 | /** 1581 | * Reads infile and encodes it to outfile. 1582 | * 1583 | * @param infile Input file 1584 | * @param outfile Output file 1585 | * @throws java.io.IOException if there is an error 1586 | * @since 2.2 1587 | */ 1588 | public static void encodeFileToFile(String infile, String outfile ) 1589 | throws java.io.IOException { 1590 | 1591 | String encoded = MyBase64.encodeFromFile( infile ); 1592 | java.io.OutputStream out = null; 1593 | //noinspection CaughtExceptionImmediatelyRethrown 1594 | try{ 1595 | out = new java.io.BufferedOutputStream( 1596 | new java.io.FileOutputStream( outfile ) ); 1597 | out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. 1598 | } // end try 1599 | catch( java.io.IOException e ) { 1600 | throw e; // Catch and release to execute finally{} 1601 | } // end catch 1602 | finally { 1603 | try { out.close(); } 1604 | catch( Exception ex ){} 1605 | } // end finally 1606 | } // end encodeFileToFile 1607 | 1608 | 1609 | /** 1610 | * Reads infile and decodes it to outfile. 1611 | * 1612 | * @param infile Input file 1613 | * @param outfile Output file 1614 | * @throws java.io.IOException if there is an error 1615 | * @since 2.2 1616 | */ 1617 | public static void decodeFileToFile(String infile, String outfile ) 1618 | throws java.io.IOException { 1619 | 1620 | byte[] decoded = MyBase64.decodeFromFile( infile ); 1621 | java.io.OutputStream out = null; 1622 | //noinspection CaughtExceptionImmediatelyRethrown 1623 | try{ 1624 | out = new java.io.BufferedOutputStream( 1625 | new java.io.FileOutputStream( outfile ) ); 1626 | out.write( decoded ); 1627 | } // end try 1628 | catch( java.io.IOException e ) { 1629 | throw e; // Catch and release to execute finally{} 1630 | } // end catch 1631 | finally { 1632 | try { out.close(); } 1633 | catch( Exception ex ){} 1634 | } // end finally 1635 | } // end decodeFileToFile 1636 | 1637 | 1638 | /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 1639 | 1640 | 1641 | 1642 | /** 1643 | * A {@link InputStream} will read data from another 1644 | * java.io.InputStream, given in the constructor, 1645 | * and encode/decode to/from Base64 notation on the fly. 1646 | * 1647 | * @see MyBase64 1648 | * @since 1.3 1649 | */ 1650 | public static class InputStream extends java.io.FilterInputStream { 1651 | 1652 | private boolean encode; // Encoding or decoding 1653 | private int position; // Current position in the buffer 1654 | private byte[] buffer; // Small buffer holding converted data 1655 | private int bufferLength; // Length of buffer (3 or 4) 1656 | private int numSigBytes; // Number of meaningful bytes in the buffer 1657 | private int lineLength; 1658 | private boolean breakLines; // Break lines at less than 80 characters 1659 | private int options; // Record options used to create the stream. 1660 | private byte[] decodabet; // Local copies to avoid extra method calls 1661 | 1662 | 1663 | /** 1664 | * Constructs a {@link InputStream} in DECODE mode. 1665 | * 1666 | * @param in the java.io.InputStream from which to read data. 1667 | * @since 1.3 1668 | */ 1669 | public InputStream( java.io.InputStream in ) { 1670 | this( in, DECODE ); 1671 | } // end constructor 1672 | 1673 | 1674 | /** 1675 | * Constructs a {@link InputStream} in 1676 | * either ENCODE or DECODE mode. 1677 | *

1678 | * Valid options:

1679 |          *   ENCODE or DECODE: Encode or Decode as data is read.
1680 |          *   DO_BREAK_LINES: break lines at 76 characters
1681 |          *     (only meaningful when encoding)
1682 |          * 
1683 | *

1684 | * Example: new Base64.InputStream( in, Base64.DECODE ) 1685 | * 1686 | * 1687 | * @param in the java.io.InputStream from which to read data. 1688 | * @param options Specified options 1689 | * @see MyBase64#ENCODE 1690 | * @see MyBase64#DECODE 1691 | * @see MyBase64#DO_BREAK_LINES 1692 | * @since 2.0 1693 | */ 1694 | public InputStream( java.io.InputStream in, int options ) { 1695 | 1696 | super( in ); 1697 | this.options = options; // Record for later 1698 | this.breakLines = (options & DO_BREAK_LINES) > 0; 1699 | this.encode = (options & ENCODE) > 0; 1700 | this.bufferLength = encode ? 4 : 3; 1701 | this.buffer = new byte[ bufferLength ]; 1702 | this.position = -1; 1703 | this.lineLength = 0; 1704 | this.decodabet = getDecodabet(options); 1705 | } // end constructor 1706 | 1707 | /** 1708 | * Reads enough of the input stream to convert 1709 | * to/from Base64 and returns the next byte. 1710 | * 1711 | * @return next byte 1712 | * @since 1.3 1713 | */ 1714 | @Override 1715 | public int read() throws java.io.IOException { 1716 | 1717 | // Do we need to get data? 1718 | if( position < 0 ) { 1719 | if( encode ) { 1720 | byte[] b3 = new byte[3]; 1721 | int numBinaryBytes = 0; 1722 | for( int i = 0; i < 3; i++ ) { 1723 | int b = in.read(); 1724 | 1725 | // If end of stream, b is -1. 1726 | if( b >= 0 ) { 1727 | b3[i] = (byte)b; 1728 | numBinaryBytes++; 1729 | } else { 1730 | break; // out of for loop 1731 | } // end else: end of stream 1732 | 1733 | } // end for: each needed input byte 1734 | 1735 | if( numBinaryBytes > 0 ) { 1736 | encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); 1737 | position = 0; 1738 | numSigBytes = 4; 1739 | } // end if: got data 1740 | else { 1741 | return -1; // Must be end of stream 1742 | } // end else 1743 | } // end if: encoding 1744 | 1745 | // Else decoding 1746 | else { 1747 | byte[] b4 = new byte[4]; 1748 | int i = 0; 1749 | for( i = 0; i < 4; i++ ) { 1750 | // Read four "meaningful" bytes: 1751 | int b = 0; 1752 | do{ b = in.read(); } 1753 | while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); 1754 | 1755 | if( b < 0 ) { 1756 | break; // Reads a -1 if end of stream 1757 | } // end if: end of stream 1758 | 1759 | b4[i] = (byte)b; 1760 | } // end for: each needed input byte 1761 | 1762 | if( i == 4 ) { 1763 | numSigBytes = decode4to3( b4, 0, buffer, 0, options ); 1764 | position = 0; 1765 | } // end if: got four characters 1766 | else if( i == 0 ){ 1767 | return -1; 1768 | } // end else if: also padded correctly 1769 | else { 1770 | // Must have broken out from above. 1771 | throw new java.io.IOException( "Improperly padded Base64 input." ); 1772 | } // end 1773 | 1774 | } // end else: decode 1775 | } // end else: get data 1776 | 1777 | // Got data? 1778 | if( position >= 0 ) { 1779 | // End of relevant data? 1780 | if( /*!encode &&*/ position >= numSigBytes ){ 1781 | return -1; 1782 | } // end if: got data 1783 | 1784 | if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { 1785 | lineLength = 0; 1786 | return '\n'; 1787 | } // end if 1788 | else { 1789 | lineLength++; // This isn't important when decoding 1790 | // but throwing an extra "if" seems 1791 | // just as wasteful. 1792 | 1793 | int b = buffer[ position++ ]; 1794 | 1795 | if( position >= bufferLength ) { 1796 | position = -1; 1797 | } // end if: end 1798 | 1799 | return b & 0xFF; // This is how you "cast" a byte that's 1800 | // intended to be unsigned. 1801 | } // end else 1802 | } // end if: position >= 0 1803 | 1804 | // Else error 1805 | else { 1806 | throw new java.io.IOException( "Error in Base64 code reading stream." ); 1807 | } // end else 1808 | } // end read 1809 | 1810 | 1811 | /** 1812 | * Calls {@link #read()} repeatedly until the end of stream 1813 | * is reached or len bytes are read. 1814 | * Returns number of bytes read into array or -1 if 1815 | * end of stream is encountered. 1816 | * 1817 | * @param dest array to hold values 1818 | * @param off offset for array 1819 | * @param len max number of bytes to read into array 1820 | * @return bytes read into array or -1 if end of stream is encountered. 1821 | * @since 1.3 1822 | */ 1823 | @Override 1824 | public int read( byte[] dest, int off, int len ) 1825 | throws java.io.IOException { 1826 | int i; 1827 | int b; 1828 | for( i = 0; i < len; i++ ) { 1829 | b = read(); 1830 | 1831 | if( b >= 0 ) { 1832 | dest[off + i] = (byte) b; 1833 | } 1834 | else if( i == 0 ) { 1835 | return -1; 1836 | } 1837 | else { 1838 | break; // Out of 'for' loop 1839 | } // Out of 'for' loop 1840 | } // end for: each byte read 1841 | return i; 1842 | } // end read 1843 | 1844 | } // end inner class InputStream 1845 | 1846 | 1847 | 1848 | 1849 | 1850 | 1851 | /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ 1852 | 1853 | 1854 | 1855 | /** 1856 | * A {@link OutputStream} will write data to another 1857 | * java.io.OutputStream, given in the constructor, 1858 | * and encode/decode to/from Base64 notation on the fly. 1859 | * 1860 | * @see MyBase64 1861 | * @since 1.3 1862 | */ 1863 | public static class OutputStream extends java.io.FilterOutputStream { 1864 | 1865 | private boolean encode; 1866 | private int position; 1867 | private byte[] buffer; 1868 | private int bufferLength; 1869 | private int lineLength; 1870 | private boolean breakLines; 1871 | private byte[] b4; // Scratch used in a few places 1872 | private boolean suspendEncoding; 1873 | private int options; // Record for later 1874 | private byte[] decodabet; // Local copies to avoid extra method calls 1875 | 1876 | /** 1877 | * Constructs a {@link OutputStream} in ENCODE mode. 1878 | * 1879 | * @param out the java.io.OutputStream to which data will be written. 1880 | * @since 1.3 1881 | */ 1882 | public OutputStream( java.io.OutputStream out ) { 1883 | this( out, ENCODE ); 1884 | } // end constructor 1885 | 1886 | 1887 | /** 1888 | * Constructs a {@link OutputStream} in 1889 | * either ENCODE or DECODE mode. 1890 | *

1891 | * Valid options:

1892 |          *   ENCODE or DECODE: Encode or Decode as data is read.
1893 |          *   DO_BREAK_LINES: don't break lines at 76 characters
1894 |          *     (only meaningful when encoding)
1895 |          * 
1896 | *

1897 | * Example: new Base64.OutputStream( out, Base64.ENCODE ) 1898 | * 1899 | * @param out the java.io.OutputStream to which data will be written. 1900 | * @param options Specified options. 1901 | * @see MyBase64#ENCODE 1902 | * @see MyBase64#DECODE 1903 | * @see MyBase64#DO_BREAK_LINES 1904 | * @since 1.3 1905 | */ 1906 | public OutputStream( java.io.OutputStream out, int options ) { 1907 | super( out ); 1908 | this.breakLines = (options & DO_BREAK_LINES) != 0; 1909 | this.encode = (options & ENCODE) != 0; 1910 | this.bufferLength = encode ? 3 : 4; 1911 | this.buffer = new byte[ bufferLength ]; 1912 | this.position = 0; 1913 | this.lineLength = 0; 1914 | this.suspendEncoding = false; 1915 | this.b4 = new byte[4]; 1916 | this.options = options; 1917 | this.decodabet = getDecodabet(options); 1918 | } // end constructor 1919 | 1920 | 1921 | /** 1922 | * Writes the byte to the output stream after 1923 | * converting to/from Base64 notation. 1924 | * When encoding, bytes are buffered three 1925 | * at a time before the output stream actually 1926 | * gets a write() call. 1927 | * When decoding, bytes are buffered four 1928 | * at a time. 1929 | * 1930 | * @param theByte the byte to write 1931 | * @since 1.3 1932 | */ 1933 | @Override 1934 | public void write(int theByte) 1935 | throws java.io.IOException { 1936 | // Encoding suspended? 1937 | if( suspendEncoding ) { 1938 | this.out.write( theByte ); 1939 | return; 1940 | } // end if: supsended 1941 | 1942 | // Encode? 1943 | if( encode ) { 1944 | buffer[ position++ ] = (byte)theByte; 1945 | if( position >= bufferLength ) { // Enough to encode. 1946 | 1947 | this.out.write( encode3to4( b4, buffer, bufferLength, options ) ); 1948 | 1949 | lineLength += 4; 1950 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) { 1951 | this.out.write( NEW_LINE ); 1952 | lineLength = 0; 1953 | } // end if: end of line 1954 | 1955 | position = 0; 1956 | } // end if: enough to output 1957 | } // end if: encoding 1958 | 1959 | // Else, Decoding 1960 | else { 1961 | // Meaningful Base64 character? 1962 | if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { 1963 | buffer[ position++ ] = (byte)theByte; 1964 | if( position >= bufferLength ) { // Enough to output. 1965 | 1966 | int len = MyBase64.decode4to3( buffer, 0, b4, 0, options ); 1967 | out.write( b4, 0, len ); 1968 | position = 0; 1969 | } // end if: enough to output 1970 | } // end if: meaningful base64 character 1971 | else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { 1972 | throw new java.io.IOException( "Invalid character in Base64 data." ); 1973 | } // end else: not white space either 1974 | } // end else: decoding 1975 | } // end write 1976 | 1977 | 1978 | 1979 | /** 1980 | * Calls {@link #write(int)} repeatedly until len 1981 | * bytes are written. 1982 | * 1983 | * @param theBytes array from which to read bytes 1984 | * @param off offset for array 1985 | * @param len max number of bytes to read into array 1986 | * @since 1.3 1987 | */ 1988 | @Override 1989 | public void write( byte[] theBytes, int off, int len ) 1990 | throws java.io.IOException { 1991 | // Encoding suspended? 1992 | if( suspendEncoding ) { 1993 | this.out.write( theBytes, off, len ); 1994 | return; 1995 | } // end if: supsended 1996 | 1997 | for( int i = 0; i < len; i++ ) { 1998 | write( theBytes[ off + i ] ); 1999 | } // end for: each byte written 2000 | 2001 | } // end write 2002 | 2003 | 2004 | 2005 | /** 2006 | * Method added by PHIL. [Thanks, PHIL. -Rob] 2007 | * This pads the buffer without closing the stream. 2008 | * @throws java.io.IOException if there's an error. 2009 | */ 2010 | public void flushBase64() throws java.io.IOException { 2011 | if( position > 0 ) { 2012 | if( encode ) { 2013 | out.write( encode3to4( b4, buffer, position, options ) ); 2014 | position = 0; 2015 | } // end if: encoding 2016 | else { 2017 | throw new java.io.IOException( "Base64 input not properly padded." ); 2018 | } // end else: decoding 2019 | } // end if: buffer partially full 2020 | 2021 | } // end flush 2022 | 2023 | 2024 | /** 2025 | * Flushes and closes (I think, in the superclass) the stream. 2026 | * 2027 | * @since 1.3 2028 | */ 2029 | @Override 2030 | public void close() throws java.io.IOException { 2031 | // 1. Ensure that pending characters are written 2032 | flushBase64(); 2033 | 2034 | // 2. Actually close the stream 2035 | // Base class both flushes and closes. 2036 | super.close(); 2037 | 2038 | buffer = null; 2039 | out = null; 2040 | } // end close 2041 | 2042 | 2043 | 2044 | /** 2045 | * Suspends encoding of the stream. 2046 | * May be helpful if you need to embed a piece of 2047 | * base64-encoded data in a stream. 2048 | * 2049 | * @throws java.io.IOException if there's an error flushing 2050 | * @since 1.5.1 2051 | */ 2052 | public void suspendEncoding() throws java.io.IOException { 2053 | flushBase64(); 2054 | this.suspendEncoding = true; 2055 | } // end suspendEncoding 2056 | 2057 | 2058 | /** 2059 | * Resumes encoding of the stream. 2060 | * May be helpful if you need to embed a piece of 2061 | * base64-encoded data in a stream. 2062 | * 2063 | * @since 1.5.1 2064 | */ 2065 | public void resumeEncoding() { 2066 | this.suspendEncoding = false; 2067 | } // end resumeEncoding 2068 | 2069 | 2070 | 2071 | } // end inner class OutputStream 2072 | 2073 | 2074 | } // end class Base64 -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/NetworkInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import com.example.dl.hymvp.App; 4 | import com.example.dl.hymvp.util.NetUtil; 5 | 6 | import java.io.IOException; 7 | 8 | 9 | import okhttp3.CacheControl; 10 | import okhttp3.Interceptor; 11 | import okhttp3.Request; 12 | import okhttp3.Response; 13 | 14 | /** 15 | * Incremental change is better than ambitious failure. 16 | * 17 | * @author : MysticCoder 18 | * @date : 2018/3/15 19 | * @desc : 20 | */ 21 | public class NetworkInterceptor implements Interceptor { 22 | 23 | @Override 24 | public Response intercept(Chain chain) throws IOException { 25 | 26 | Request request = chain.request(); 27 | 28 | //无网络时强制使用缓存 29 | if (!NetUtil.isConnected(App.getContext())) { 30 | request = request.newBuilder() 31 | .cacheControl(CacheControl.FORCE_CACHE) 32 | .build(); 33 | } 34 | 35 | Response response = chain.proceed(request); 36 | 37 | if (NetUtil.isConnected(App.getContext())) { 38 | // 有网络时,设置超时为0 39 | int maxStale = 0; 40 | response.newBuilder() 41 | .header("Cache-Control", "public, max-age=" + maxStale) 42 | .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 43 | .build(); 44 | } else { 45 | // 无网络时,设置超时为3周 46 | int maxStale = 60 * 60 * 24 * 21; 47 | response.newBuilder() 48 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) 49 | .removeHeader("Pragma") 50 | .build(); 51 | } 52 | 53 | return response; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/ResponseConverterFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Type; 7 | 8 | import okhttp3.RequestBody; 9 | import okhttp3.ResponseBody; 10 | import retrofit2.Converter; 11 | import retrofit2.Retrofit; 12 | 13 | /** 14 | * Incremental change is better than ambitious failure. 15 | * 16 | * @author : MysticCoder 17 | * @date : 2017/12/7 18 | * @desc : 19 | */ 20 | 21 | 22 | public class ResponseConverterFactory extends Converter.Factory { 23 | public static ResponseConverterFactory create() { 24 | return create(new Gson()); 25 | } 26 | 27 | 28 | public static ResponseConverterFactory create(Gson gson) { 29 | return new ResponseConverterFactory(gson); 30 | } 31 | 32 | private final Gson gson; 33 | 34 | private ResponseConverterFactory(Gson gson) { 35 | if (gson == null) {throw new NullPointerException("gson == null");} 36 | this.gson = gson; 37 | } 38 | 39 | @Override 40 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { 41 | //返回我们自定义的Gson响应体变换器 42 | return new GsonResponseBodyConverter<>(gson, type); 43 | } 44 | 45 | // @Override 46 | // public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { 47 | // return new GsonResponseBodyConverter<>(gson,type); 48 | // } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/http/ResultException.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.http; 2 | 3 | /** 4 | * Incremental change is better than ambitious failure. 5 | * 6 | * @author : MysticCoder 7 | * @date : 2017/12/7 8 | * @desc : 9 | */ 10 | 11 | 12 | public class ResultException extends RuntimeException{ 13 | 14 | 15 | private int code; 16 | private String message; 17 | 18 | public ResultException(int code, String message){ 19 | this.code = code; 20 | this.message = message; 21 | } 22 | 23 | public int getCode() { 24 | return code; 25 | } 26 | 27 | public void setCode(int code) { 28 | this.code = code; 29 | } 30 | 31 | @Override 32 | public String getMessage() { 33 | return message; 34 | } 35 | 36 | public void setMessage(String message) { 37 | this.message = message; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/mvp/contract/MainContract.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.mvp.contract; 2 | 3 | import com.example.dl.hymvp.base.BaseModel; 4 | 5 | import com.example.dl.hymvp.base.BaseView; 6 | import com.example.dl.hymvp.bean.Gank; 7 | import com.example.dl.hymvp.bean.Survey; 8 | 9 | import io.reactivex.Observable; 10 | 11 | /** 12 | * Incremental change is better than ambitious failure. 13 | * 14 | * @author : MysticCoder 15 | * @date : 2018/3/16 16 | * @desc : 17 | */ 18 | 19 | public interface MainContract { 20 | 21 | interface MainView extends BaseView { 22 | 23 | void onGetSurvey(Survey survey); 24 | } 25 | 26 | interface MainModel extends BaseModel { 27 | 28 | Observable getSurvey(String did); 29 | } 30 | 31 | 32 | // abstract class MainPresenter extends BasePresenter { 33 | // public abstract void getSurvey(String did); 34 | // } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/mvp/model/MainModel.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.mvp.model; 2 | 3 | import com.example.dl.hymvp.bean.Gank; 4 | import com.example.dl.hymvp.bean.Survey; 5 | import com.example.dl.hymvp.http.ApiEngine; 6 | import com.example.dl.hymvp.mvp.contract.MainContract; 7 | 8 | 9 | import io.reactivex.Observable; 10 | 11 | /** 12 | * Incremental change is better than ambitious failure. 13 | * 14 | * @author : MysticCoder 15 | * @date : 2018/3/16 16 | * @desc : 17 | */ 18 | 19 | 20 | public class MainModel implements MainContract.MainModel { 21 | 22 | 23 | @Override 24 | public Observable getSurvey(String did) { 25 | return mApiService.getSurveyList(did); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/mvp/presenter/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.mvp.presenter; 2 | 3 | import android.database.Observable; 4 | 5 | import com.example.dl.hymvp.base.BasePresenter; 6 | import com.example.dl.hymvp.bean.Gank; 7 | import com.example.dl.hymvp.bean.Survey; 8 | import com.example.dl.hymvp.http.BaseObserver; 9 | import com.example.dl.hymvp.mvp.contract.MainContract; 10 | import com.example.dl.hymvp.mvp.model.MainModel; 11 | import com.example.dl.hymvp.rx.RxTransformer; 12 | 13 | import io.reactivex.Observer; 14 | import io.reactivex.disposables.Disposable; 15 | import io.reactivex.functions.Consumer; 16 | 17 | /** 18 | * Incremental change is better than ambitious failure. 19 | * 20 | * @author : MysticCoder 21 | * @date : 2018/3/20 22 | * @desc : 23 | */ 24 | 25 | 26 | public class MainPresenter extends BasePresenter { 27 | 28 | public MainPresenter(MainContract.MainView view) { 29 | super(new MainModel(), view); 30 | } 31 | 32 | 33 | 34 | // public void getGank(){ 35 | // mModel.getGank() 36 | // .compose(RxTransformer.transformWithLoading(mView)) 37 | // .subscribe(new BaseObserver() { 38 | // @Override 39 | // public void onSuccess(Gank response) { 40 | // 41 | // } 42 | // 43 | // @Override 44 | // public void onFailure(String msg) { 45 | // 46 | // } 47 | // }); 48 | // 49 | // } 50 | 51 | public void getSurveyList(String did){ 52 | 53 | // //生命周期管理,备用方案,订阅的时候添加Disposable对象到容器 54 | // mModel.getSurvey(did) 55 | // .compose(RxTransformer.transform()) 56 | // .doOnSubscribe(this::addDisposabel) 57 | // .subscribe(new BaseObserver() { 58 | // @Override 59 | // public void onSuccess(Survey response) { 60 | // getView().onGetSurvey(response); 61 | // } 62 | // 63 | // @Override 64 | // public void onFailure(String msg) { 65 | // 66 | // } 67 | // }); 68 | 69 | 70 | 71 | // 72 | // Disposable disposable = mModel.getSurvey(did) 73 | // .compose(RxTransformer.transformWithLoading(getView())) 74 | // .subscribe(survey -> getView().onGetSurvey(survey)); 75 | // 76 | // addDisposabel(disposable); 77 | 78 | 79 | 80 | 81 | mModel.getSurvey(did) 82 | .compose(RxTransformer.transformWithLoading(getView())) 83 | .subscribe(new BaseObserver() { 84 | @Override 85 | public void onSuccess(Survey response) { 86 | getView().onGetSurvey(response); 87 | } 88 | 89 | @Override 90 | public void onFailure(String msg) { 91 | 92 | } 93 | }); 94 | 95 | 96 | 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/rx/RxTransformer.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.rx; 2 | 3 | 4 | import com.example.dl.hymvp.base.BasePresenter; 5 | import com.example.dl.hymvp.base.BaseView; 6 | 7 | import io.reactivex.Observable; 8 | import io.reactivex.ObservableSource; 9 | import io.reactivex.ObservableTransformer; 10 | import io.reactivex.android.schedulers.AndroidSchedulers; 11 | import io.reactivex.annotations.NonNull; 12 | import io.reactivex.disposables.Disposable; 13 | import io.reactivex.functions.Action; 14 | import io.reactivex.functions.Consumer; 15 | import io.reactivex.schedulers.Schedulers; 16 | 17 | /** 18 | * Incremental change is better than ambitious failure. 19 | * 20 | * @author : MysticCoder 21 | * @date : 2018/3/15 22 | * @desc : 23 | */ 24 | 25 | public class RxTransformer { 26 | 27 | /** 28 | * 无参数 仅做切换线程 29 | * 30 | * @param 泛型 31 | * @return 返回Observable 32 | */ 33 | public static ObservableTransformer transform() { 34 | return upstream -> upstream 35 | .subscribeOn(Schedulers.io()) 36 | .unsubscribeOn(Schedulers.io()) 37 | .subscribeOn(AndroidSchedulers.mainThread()) 38 | .observeOn(AndroidSchedulers.mainThread()); 39 | } 40 | 41 | 42 | /** 43 | * 界面请求,不需要加载和隐藏loading时调用 使用RxLifeCycle 44 | * 传入view接口,Activity,Fragment等实现了view接口,Activity,Fragment继承于{@link com.trello.rxlifecycle2.components.support.RxAppCompatActivity} 45 | * 也就实现了bindToLifecycle方法 46 | * @param view View 47 | * @param 泛型 48 | * @return 49 | */ 50 | public static ObservableTransformer transform(final BaseView view) { 51 | return observable -> observable.subscribeOn(Schedulers.io()) 52 | .observeOn(AndroidSchedulers.mainThread()) 53 | .compose(view.bindToLifecycle()); 54 | } 55 | 56 | 57 | /** 58 | * 界面请求,需要加载和隐藏loading时调用,使用RxLifeCycle 59 | * 传入view接口,Activity,Fragment等实现了view接口,Activity,Fragment继承于{@link com.trello.rxlifecycle2.components.support.RxAppCompatActivity} 60 | * 也就实现了bindToLifecycle方法 61 | * @param view View 62 | * @param 泛型 63 | * @return 64 | */ 65 | public static ObservableTransformer transformWithLoading(final BaseView view) { 66 | //隐藏进度条 67 | return observable -> observable.subscribeOn(Schedulers.io()) 68 | .doOnSubscribe(disposable -> { 69 | view.showLoading();//显示进度条 70 | }) 71 | .observeOn(AndroidSchedulers.mainThread()) 72 | .doFinally(view::hideLoading).compose(view.bindToLifecycle()); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/util/NetUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.util; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | /** 8 | * Incremental change is better than ambitious failure. 9 | * 10 | * @author : MysticCoder 11 | * @date : 2018/3/15 12 | * @desc : 13 | */ 14 | 15 | public class NetUtil { 16 | 17 | NetUtil() {/* cannot be instantiated */} 18 | 19 | /** 20 | * 判断网络是否连接 21 | * 22 | * @param context Context 23 | * @return 网络是否连接 24 | */ 25 | public static boolean isConnected(Context context) { 26 | 27 | ConnectivityManager connectivity = (ConnectivityManager) context 28 | .getSystemService(Context.CONNECTIVITY_SERVICE); 29 | 30 | if (null != connectivity) { 31 | NetworkInfo info = connectivity.getActiveNetworkInfo(); 32 | if (null != info && info.isConnected()) { 33 | if (info.getState() == NetworkInfo.State.CONNECTED) { 34 | return true; 35 | } 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | /** 42 | * 判断是否是wifi连接 43 | * 44 | * @param context Context 45 | * @return 是否是wifi连接 46 | */ 47 | public static boolean isWifi(Context context) { 48 | ConnectivityManager cm = (ConnectivityManager) context 49 | .getSystemService(Context.CONNECTIVITY_SERVICE); 50 | return cm != null && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dl/hymvp/util/Preconditions.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp.util; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | /** 6 | * Incremental change is better than ambitious failure. 7 | * 8 | * @author : MysticCoder 9 | * @date : 2018/3/15 10 | * @desc : 11 | */ 12 | 13 | public final class Preconditions { 14 | 15 | private Preconditions() { 16 | throw new IllegalStateException("you can't instantiate me!"); 17 | } 18 | 19 | public static void checkArgument(boolean expression) { 20 | if(!expression) { 21 | throw new IllegalArgumentException(); 22 | } 23 | } 24 | 25 | public static void checkArgument(boolean expression, @Nullable Object errorMessage) { 26 | if(!expression) { 27 | throw new IllegalArgumentException(String.valueOf(errorMessage)); 28 | } 29 | } 30 | 31 | public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { 32 | if(!expression) { 33 | throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); 34 | } 35 | } 36 | 37 | public static void checkState(boolean expression) { 38 | if(!expression) { 39 | throw new IllegalStateException(); 40 | } 41 | } 42 | 43 | public static void checkState(boolean expression, @Nullable Object errorMessage) { 44 | if(!expression) { 45 | throw new IllegalStateException(String.valueOf(errorMessage)); 46 | } 47 | } 48 | 49 | public static void checkState(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { 50 | if(!expression) { 51 | throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); 52 | } 53 | } 54 | 55 | public static T checkNotNull(T reference) { 56 | if(reference == null) { 57 | throw new NullPointerException(); 58 | } else { 59 | return reference; 60 | } 61 | } 62 | 63 | public static T checkNotNull(T reference, @Nullable Object errorMessage) { 64 | if(reference == null) { 65 | throw new NullPointerException(String.valueOf(errorMessage)); 66 | } else { 67 | return reference; 68 | } 69 | } 70 | 71 | public static T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { 72 | if(reference == null) { 73 | throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); 74 | } else { 75 | return reference; 76 | } 77 | } 78 | 79 | public static int checkElementIndex(int index, int size) { 80 | return checkElementIndex(index, size, "index"); 81 | } 82 | 83 | public static int checkElementIndex(int index, int size, @Nullable String desc) { 84 | if(index >= 0 && index < size) { 85 | return index; 86 | } else { 87 | throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); 88 | } 89 | } 90 | 91 | private static String badElementIndex(int index, int size, String desc) { 92 | if(index < 0) { 93 | return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)}); 94 | } else if(size < 0) { 95 | throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); 96 | } else { 97 | return format("%s (%s) must be less than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)}); 98 | } 99 | } 100 | 101 | public static int checkPositionIndex(int index, int size) { 102 | return checkPositionIndex(index, size, "index"); 103 | } 104 | 105 | public static int checkPositionIndex(int index, int size, @Nullable String desc) { 106 | if(index >= 0 && index <= size) { 107 | return index; 108 | } else { 109 | throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); 110 | } 111 | } 112 | 113 | private static String badPositionIndex(int index, int size, String desc) { 114 | if(index < 0) { 115 | return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)}); 116 | } else if(size < 0) { 117 | throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); 118 | } else { 119 | return format("%s (%s) must not be greater than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)}); 120 | } 121 | } 122 | 123 | public static void checkPositionIndexes(int start, int end, int size) { 124 | if(start < 0 || end < start || end > size) { 125 | throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); 126 | } 127 | } 128 | 129 | private static String badPositionIndexes(int start, int end, int size) { 130 | return start >= 0 && start <= size?(end >= 0 && end <= size?format("end index (%s) must not be less than start index (%s)", new Object[]{Integer.valueOf(end), Integer.valueOf(start)}):badPositionIndex(end, size, "end index")):badPositionIndex(start, size, "start index"); 131 | } 132 | 133 | static String format(String template, @Nullable Object... args) { 134 | template = String.valueOf(template); 135 | StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); 136 | int templateStart = 0; 137 | 138 | int i; 139 | int placeholderStart; 140 | for(i = 0; i < args.length; templateStart = placeholderStart + 2) { 141 | placeholderStart = template.indexOf("%s", templateStart); 142 | if(placeholderStart == -1) { 143 | break; 144 | } 145 | 146 | builder.append(template.substring(templateStart, placeholderStart)); 147 | builder.append(args[i++]); 148 | } 149 | 150 | builder.append(template.substring(templateStart)); 151 | if(i < args.length) { 152 | builder.append(" ["); 153 | builder.append(args[i++]); 154 | 155 | while(i < args.length) { 156 | builder.append(", "); 157 | builder.append(args[i++]); 158 | } 159 | 160 | builder.append(']'); 161 | } 162 | 163 | return builder.toString(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | HYMVP 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/dl/hymvp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.dl.hymvp; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "config.gradle" 3 | 4 | buildscript { 5 | 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.0.1' 12 | 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext{ 2 | 3 | android = [ 4 | compileSdkVersion : 26, 5 | buildToolsVersion : "26.0.2", 6 | minSdkVersion : 14, 7 | targetSdkVersion : 26, 8 | versionCode : 150, 9 | versionName : "2.3.5" 10 | ] 11 | 12 | version = [ 13 | androidSupportSdkVersion: "27.0.2", 14 | retrofitSdkVersion : "2.4.0", 15 | butterknifeSdkVersion : "8.8.1", 16 | ] 17 | 18 | 19 | 20 | dependencies = [ 21 | //support 22 | "appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}", 23 | "recyclerview-v7" : "com.android.support:recyclerview-v7:${version["androidSupportSdkVersion"]}", 24 | 25 | //network 26 | "retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}", 27 | 28 | //view 29 | "butterknife" : "com.jakewharton:butterknife:${version["butterknifeSdkVersion"]}", 30 | "butterknife-compiler" : "com.jakewharton:butterknife-compiler:${version["butterknifeSdkVersion"]}", 31 | 32 | 33 | //rx 34 | "rxjava" : "io.reactivex.rxjava2:rxjava:2.1.10", 35 | "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.0.2", 36 | "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar", 37 | 38 | //tools 39 | 40 | 41 | //test 42 | "junit" : "junit:junit:4.12", 43 | "androidJUnitRunner" : "android.support.test.runner.AndroidJUnitRunner", 44 | "runner" : "com.android.support.test:runner:1.0.1", 45 | "espresso" : "com.android.support.test.espresso:espresso-core:3.0.1", 46 | 47 | 48 | ] 49 | 50 | 51 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LegendaryMystic/HYMVP/c98ee668203f910d2cfbc09bdbac79a38cc98073/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 15 16:15:57 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------