[
  {
    "path": ".gitignore",
    "content": "*.iml\r\n.gradle\r\n.idea\r\n/local.properties\r\n/.idea/libraries\r\n/.idea/modules.xml\r\n/.idea/workspace.xml\r\n.DS_Store\r\n/build\r\n/captures\r\n.externalNativeBuild\r\n"
  },
  {
    "path": "README.md",
    "content": "# wj-todo-wanandroid\r\n用心打造一款极致体验的TODO开源客户端，数据接口来自鸿神的玩Android，不放过每一个细节，用心写代码,如果您觉得还不错的话，就点个Star吧~（您的每次支持，是我开源的动力）\r\n# APK\r\n<a href=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/release/app-release.apk\">app-release.apk</a>\r\n# 项目架构\r\n该项目使用最简单的MVC架构，整体代码实现层次分明，高内聚低耦合，代码逻辑清晰，通俗易懂，使用BottomNavigationView+ViewPager+Fragment完成UI主体实现，引入butterknife依赖注入框架，简化了代码的编写，网络层的编写，主要是引入了我另一个开源框架<a href=\"https://github.com/wjwang0914/wj-http\">wj-http（主要是对Retrofit2进行了二次封装，方便使用，提升开发效率）</a>\r\n<ul>\r\n<li><a href=\"https://github.com/wjwang0914/wj-http\">wj-http</a></li>\r\n<li><a href=\"https://github.com/JakeWharton/butterknife\">butterknife</a></li>\r\n<li><a href=\"https://github.com/CymChad/BaseRecyclerViewAdapterHelper\">BaseRecyclerViewAdapterHelper</a></li>\r\n<li><a href=\"https://github.com/franmontiel/PersistentCookieJar\">PersistentCookieJar</a></li>\r\n</ul>\r\n# 项目运行截图\r\n<div align=\"center\">\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/000.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/001.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/002.png\" width=30%>\r\n</div>\r\n\r\n<div align=\"center\">\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/003.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/004.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/005.png\" width=30%>\r\n</div>\r\n\r\n<div align=\"center\">\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/006.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/008.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/011.png\" width=30%>\r\n</div>\r\n\r\n<div align=\"center\">\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/009.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/007.png\" width=30%>\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/012.png\" width=30%>\r\n</div>\r\n\r\n<div align=\"center\">\r\n<img src=\"https://raw.githubusercontent.com/wjwang0914/wj-todo-wanandroid/master/screenshots/010.png\" width=30%>\r\n</div>\r\n\r\n## Thanks\r\n\r\n### API：\r\n鸿洋大大提供的\r\n[WanAndroid API](http://www.wanandroid.com/)\r\n\r\n### ICON：\r\n[iconfont](http://www.iconfont.cn/)"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\r\n/release\r\n/keystore\r\n*.iml"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\r\n\r\nandroid {\r\n    compileSdkVersion 28\r\n    defaultConfig {\r\n        applicationId \"com.wj.android.todo\"\r\n        minSdkVersion 21\r\n        targetSdkVersion 28\r\n        versionCode 1\r\n        versionName \"1.0\"\r\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\r\n    }\r\n\r\n    signingConfigs {\r\n        release {\r\n            keyAlias 'wj'\r\n            keyPassword '19900914'\r\n            storeFile file('keystore/wj.jks')\r\n            storePassword '19900914'\r\n        }\r\n    }\r\n\r\n    buildTypes {\r\n        release {\r\n            signingConfig signingConfigs.release\r\n            minifyEnabled false\r\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\r\n            buildConfigField('String', 'BASE_URL', FORMAL_BASE_URL)\r\n        }\r\n\r\n        debug {\r\n            buildConfigField('String', 'BASE_URL', TEST_BASE_URL)\r\n        }\r\n    }\r\n}\r\n\r\ndependencies {\r\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\r\n    implementation 'com.android.support:appcompat-v7:28.0.0-beta01'\r\n    implementation 'com.android.support:design:28.0.0-beta01'\r\n    testImplementation 'junit:junit:4.12'\r\n    androidTestImplementation 'com.android.support.test:runner:1.0.2'\r\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'\r\n\r\n    implementation project(\":wj-http\")\r\n\r\n    implementation 'com.jakewharton:butterknife:8.8.1'\r\n    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'\r\n\r\n    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.40'\r\n    implementation 'com.android.support:recyclerview-v7:28.0.0-beta01'\r\n    implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'\r\n    //implementation 'com.afollestad.material-dialogs:core:0.9.6.0'\r\n}\r\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\r\n# You can control the set of applied configuration files using the\r\n# proguardFiles setting in build.gradle.\r\n#\r\n# For more details, see\r\n#   http://developer.android.com/guide/developing/tools/proguard.html\r\n\r\n# If your project uses WebView with JS, uncomment the following\r\n# and specify the fully qualified class name to the JavaScript interface\r\n# class:\r\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\r\n#   public *;\r\n#}\r\n\r\n# Uncomment this to preserve the line number information for\r\n# debugging stack traces.\r\n#-keepattributes SourceFile,LineNumberTable\r\n\r\n# If you keep the line number information, uncomment this to\r\n# hide the original source file name.\r\n#-renamesourcefileattribute SourceFile\r\n"
  },
  {
    "path": "app/src/androidTest/java/com/wj/android/todo/ExampleInstrumentedTest.java",
    "content": "package com.wj.android.todo;\r\n\r\nimport android.content.Context;\r\nimport android.support.test.InstrumentationRegistry;\r\nimport android.support.test.runner.AndroidJUnit4;\r\n\r\nimport org.junit.Test;\r\nimport org.junit.runner.RunWith;\r\n\r\nimport static org.junit.Assert.*;\r\n\r\n/**\r\n * Instrumented test, which will execute on an Android device.\r\n *\r\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\r\n */\r\n@RunWith(AndroidJUnit4.class)\r\npublic class ExampleInstrumentedTest {\r\n    @Test\r\n    public void useAppContext() {\r\n        // Context of the app under test.\r\n        Context appContext = InstrumentationRegistry.getTargetContext();\r\n\r\n        assertEquals(\"com.wj.android.todo\", appContext.getPackageName());\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    package=\"com.wj.android.todo\">\r\n    <!-- To auto-complete the email text field in the login form with the user's emails -->\r\n    <uses-permission android:name=\"android.permission.INTERNET\" />\r\n\r\n    <application\r\n        android:name=\".MainApplication\"\r\n        android:allowBackup=\"true\"\r\n        android:icon=\"@mipmap/ic_launcher\"\r\n        android:label=\"@string/app_name\"\r\n        android:supportsRtl=\"true\"\r\n        android:theme=\"@style/AppTheme\"\r\n        android:networkSecurityConfig=\"@xml/network_security_config\">\r\n        <activity\r\n            android:name=\".activity.SplashActivity\"\r\n            android:label=\"@string/app_name\">\r\n            <intent-filter>\r\n                <action android:name=\"android.intent.action.MAIN\" />\r\n\r\n                <category android:name=\"android.intent.category.LAUNCHER\" />\r\n            </intent-filter>\r\n        </activity>\r\n        <activity android:name=\".activity.MainActivity\"\r\n            android:launchMode=\"singleTask\"/>\r\n        <activity android:name=\".activity.LoginActivity\"\r\n            android:launchMode=\"singleTask\"/>\r\n        <activity android:name=\".activity.RegisterActivity\"\r\n            android:launchMode=\"singleTask\"/>\r\n        <activity android:name=\".activity.AddTodoActivity\" />\r\n        <activity android:name=\".activity.EditTodoActivity\" />\r\n    </application>\r\n\r\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/MainApplication.java",
    "content": "package com.wj.android.todo;\r\n\r\nimport android.app.Application;\r\n\r\nimport com.wj.android.http.XRetrofit;\r\nimport com.wj.android.todo.manager.PersistentCookieJarManager;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/7 16:03\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class MainApplication extends Application {\r\n\r\n    @Override\r\n    public void onCreate() {\r\n        super.onCreate();\r\n        XRetrofit.init()\r\n                .debug(BuildConfig.DEBUG)\r\n                .cookieJar(PersistentCookieJarManager.getInstance(this).getPersistentCookieJar());\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/AddTodoActivity.java",
    "content": "package com.wj.android.todo.activity;\r\n\r\nimport android.app.DatePickerDialog;\r\nimport android.content.Intent;\r\nimport android.support.design.widget.TextInputEditText;\r\nimport android.text.TextUtils;\r\nimport android.view.View;\r\nimport android.widget.DatePicker;\r\nimport android.widget.ImageView;\r\nimport android.widget.TextView;\r\n\r\nimport com.google.gson.JsonObject;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.bean.TodoDesBean;\r\nimport com.wj.android.todo.http.HttpUtils;\r\nimport com.wj.android.todo.http.ResponseItem;\r\nimport com.wj.android.todo.util.TimeUtils;\r\n\r\nimport java.util.Calendar;\r\nimport java.util.Date;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 19:21\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class AddTodoActivity extends BaseActivity {\r\n    @BindView(R.id.title)\r\n    TextView mTitle;\r\n    @BindView(R.id.back)\r\n    ImageView mBack;\r\n    @BindView(R.id.todo_date)\r\n    TextView mTodoDate;\r\n    @BindView(R.id.todo_name)\r\n    TextInputEditText mTodoName;\r\n    @BindView(R.id.todo_des)\r\n    TextInputEditText mTodoDes;\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_add_todo;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        mTitle.setText(R.string.add_todo);\r\n        mBack.setVisibility(View.VISIBLE);\r\n\r\n        mTodoDate.setText(TimeUtils.date2String(new Date(),\"yyyy-MM-dd\"));\r\n    }\r\n\r\n    @OnClick(R.id.back)\r\n    void onClickBack() {\r\n        finish();\r\n    }\r\n\r\n    @OnClick(R.id.todo_date)\r\n    void onClickTodoDate() {\r\n        Calendar calendar = Calendar.getInstance();\r\n\r\n        DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {\r\n            @Override\r\n            public void onDateSet(DatePicker datePicker, int year, int month, int dayOfMonth) {\r\n                mTodoDate.setText(String.format(\"%d-%d-%d\", year, month+1, dayOfMonth));\r\n            }\r\n        },calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));\r\n        datePickerDialog.getDatePicker().setMinDate(new Date().getTime());\r\n        datePickerDialog.show();\r\n    }\r\n\r\n    @OnClick(R.id.save_todo)\r\n    void onClickAddTodo() {\r\n        mTodoName.setError(null);\r\n        if (TextUtils.isEmpty(mTodoName.getText())) {\r\n            mTodoName.setError(getString(R.string.input_todo_name_toast));\r\n            mTodoName.setFocusable(true);\r\n            mTodoName.setFocusableInTouchMode(true);\r\n            mTodoName.requestFocus();\r\n            return;\r\n        }\r\n\r\n        requestAddTodoData();\r\n    }\r\n\r\n    private void requestAddTodoData() {\r\n        HttpUtils.requestAddTodoData(this, mTodoName.getText().toString(), mTodoDes.getText().toString(), mTodoDate.getText().toString());\r\n    }\r\n\r\n    public void updateUI(ResponseItem<TodoDesBean> response) {\r\n        if (response.isSuccess()) {\r\n            showToast(getString(R.string.add_todo_success));\r\n            Intent intent = new Intent();\r\n            intent.putExtra(\"add_todo\", response.getData());\r\n            setResult(0x200, intent);\r\n            finish();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean isLoadingEnable(int requestId) {\r\n        return super.isLoadingEnable(requestId);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/EditTodoActivity.java",
    "content": "package com.wj.android.todo.activity;\r\n\r\nimport android.app.DatePickerDialog;\r\nimport android.content.Intent;\r\nimport android.os.Bundle;\r\nimport android.support.design.widget.TextInputEditText;\r\nimport android.text.TextUtils;\r\nimport android.view.View;\r\nimport android.widget.Button;\r\nimport android.widget.DatePicker;\r\nimport android.widget.ImageView;\r\nimport android.widget.TextView;\r\n\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.bean.TodoDesBean;\r\nimport com.wj.android.todo.http.HttpUtils;\r\nimport com.wj.android.todo.http.ResponseItem;\r\nimport com.wj.android.todo.util.TimeUtils;\r\n\r\nimport java.util.Calendar;\r\nimport java.util.Date;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 19:21\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class EditTodoActivity extends BaseActivity {\r\n    @BindView(R.id.title)\r\n    TextView mTitle;\r\n    @BindView(R.id.back)\r\n    ImageView mBack;\r\n    @BindView(R.id.todo_date)\r\n    TextView mTodoDate;\r\n    @BindView(R.id.todo_name)\r\n    TextInputEditText mTodoName;\r\n    @BindView(R.id.todo_des)\r\n    TextInputEditText mTodoDes;\r\n    @BindView(R.id.save_todo)\r\n    Button mSaveTodo;\r\n\r\n    private TodoDesBean mTodoDesBean;\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_edit_todo;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        mTitle.setText(R.string.update_todo);\r\n        mBack.setVisibility(View.VISIBLE);\r\n\r\n        Bundle bundle = getIntent().getExtras();\r\n        if (bundle == null) {\r\n            return;\r\n        }\r\n        mTodoDesBean = (TodoDesBean) bundle.getSerializable(\"todo_des\");\r\n        if (mTodoDesBean == null) {\r\n            return;\r\n        }\r\n        mTodoName.setText(mTodoDesBean.getTitle());\r\n        if (!TextUtils.isEmpty(mTodoDesBean.getContent())) {\r\n            mTodoDes.setText(mTodoDesBean.getContent());\r\n        }\r\n        mTodoDate.setText(mTodoDesBean.getDateStr());\r\n\r\n        if (mTodoDesBean.getStatus() == 1) {\r\n            mSaveTodo.setVisibility(View.GONE);\r\n            mTodoName.setEnabled(false);\r\n            mTodoName.setFocusable(false);\r\n            mTodoDes.setEnabled(false);\r\n            mTodoDes.setFocusable(false);\r\n            mTodoDate.setEnabled(false);\r\n            mTodoDate.setFocusable(false);\r\n        }\r\n    }\r\n\r\n    @OnClick(R.id.back)\r\n    void onClickBack() {\r\n        finish();\r\n    }\r\n\r\n    @OnClick(R.id.todo_date)\r\n    void onClickTodoDate() {\r\n        Calendar calendar = Calendar.getInstance();\r\n\r\n        DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {\r\n            @Override\r\n            public void onDateSet(DatePicker datePicker, int year, int month, int dayOfMonth) {\r\n                mTodoDate.setText(String.format(\"%d-%d-%d\", year, month+1, dayOfMonth));\r\n            }\r\n        },calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));\r\n        datePickerDialog.getDatePicker().setMinDate(new Date().getTime());\r\n        datePickerDialog.show();\r\n    }\r\n\r\n    @OnClick(R.id.save_todo)\r\n    void onClickAddTodo() {\r\n        mTodoName.setError(null);\r\n        if (TextUtils.isEmpty(mTodoName.getText())) {\r\n            mTodoName.setError(getString(R.string.input_todo_name_toast));\r\n            mTodoName.setFocusable(true);\r\n            mTodoName.setFocusableInTouchMode(true);\r\n            mTodoName.requestFocus();\r\n            return;\r\n        }\r\n\r\n        requestUpdateTodoData();\r\n    }\r\n\r\n    private void requestUpdateTodoData() {\r\n        HttpUtils.requestUpdateTodoData(this, mTodoDesBean.getId(), mTodoName.getText().toString(), mTodoDes.getText().toString(), mTodoDate.getText().toString());\r\n    }\r\n\r\n    public void updateUI(ResponseItem<TodoDesBean> response) {\r\n        if (response.isSuccess()) {\r\n            showToast(getString(R.string.update_todo_success));\r\n            Intent intent = new Intent();\r\n            intent.putExtra(\"update_todo\", response.getData());\r\n            setResult(0x210, intent);\r\n            finish();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean isLoadingEnable(int requestId) {\r\n        return super.isLoadingEnable(requestId);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/LoginActivity.java",
    "content": "package com.wj.android.todo.activity;\r\nimport android.text.TextUtils;\r\nimport android.widget.AutoCompleteTextView;\r\nimport android.widget.EditText;\r\n\r\nimport com.google.gson.JsonObject;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.http.HttpUtils;\r\nimport com.wj.android.todo.http.ResponseItem;\r\nimport com.wj.android.todo.manager.SharePreferenceManager;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\n\r\npublic class LoginActivity extends BaseActivity{\r\n    @BindView(R.id.account)\r\n    AutoCompleteTextView mAccount;\r\n    @BindView(R.id.password)\r\n    EditText mPassword;\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_login;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n\r\n    }\r\n\r\n    @OnClick(R.id.regitster)\r\n    void clickRegister() {\r\n        startActivity(RegisterActivity.class);\r\n    }\r\n\r\n    @OnClick(R.id.login)\r\n    void clickLogin() {\r\n        mAccount.setError(null);\r\n        mPassword.setError(null);\r\n        if (TextUtils.isEmpty(mAccount.getText())) {\r\n            mAccount.setError(getString(R.string.input_account));\r\n            mAccount.setFocusable(true);\r\n            mAccount.setFocusableInTouchMode(true);\r\n            mAccount.requestFocus();\r\n            return;\r\n        }\r\n\r\n        if (TextUtils.isEmpty(mPassword.getText())) {\r\n            mPassword.setError(getString(R.string.input_password));\r\n            mPassword.setFocusable(true);\r\n            mPassword.setFocusableInTouchMode(true);\r\n            mPassword.requestFocus();\r\n            return;\r\n        }\r\n\r\n        requestLogin();\r\n    }\r\n\r\n    @Override\r\n    public boolean isLoadingEnable(int requestId) {\r\n        return super.isLoadingEnable(requestId);\r\n    }\r\n\r\n    private void requestLogin() {\r\n        HttpUtils.requestLogin(this, mAccount.getText().toString(), mPassword.getText().toString());\r\n    }\r\n\r\n    public void updateUI(ResponseItem<JsonObject> response) {\r\n        if (response.isSuccess()) {\r\n            SharePreferenceManager.getInstance(this).setUserName(response.getData().get(\"username\").getAsString());\r\n            startActivity(MainActivity.class);\r\n            finish();\r\n        }\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/MainActivity.java",
    "content": "package com.wj.android.todo.activity;\r\n\r\nimport android.content.Intent;\r\nimport android.support.annotation.Nullable;\r\nimport android.support.v4.app.Fragment;\r\nimport android.support.v4.app.FragmentManager;\r\nimport android.support.v4.app.FragmentPagerAdapter;\r\nimport android.support.v4.view.ViewPager;\r\nimport android.view.View;\r\nimport android.widget.ImageView;\r\nimport android.widget.TextView;\r\n\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.bean.TodoDesBean;\r\nimport com.wj.android.todo.fragment.SettingFragment;\r\nimport com.wj.android.todo.fragment.TodoFragment;\r\nimport com.wj.android.todo.widget.BottomNavigationViewEx;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/7 11:17\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class MainActivity extends BaseActivity {\r\n    private static final int REQUEST_CODE_ADD_TODO = 0x100;\r\n\r\n    @BindView(R.id.view_pager)\r\n    ViewPager mViewPager;\r\n    @BindView(R.id.navigation)\r\n    BottomNavigationViewEx mNavigationView;\r\n    @BindView(R.id.title)\r\n    TextView mTitle;\r\n    @BindView(R.id.add_todo)\r\n    ImageView mAddTodo;\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_main;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        mTitle.setText(R.string.to_do_list);\r\n        mAddTodo.setVisibility(View.VISIBLE);\r\n\r\n        mViewPager.setOffscreenPageLimit(3);\r\n\r\n        List<Fragment> fragments = new ArrayList<>(3);\r\n        fragments.add(TodoFragment.newInstance(false));\r\n        fragments.add(TodoFragment.newInstance(true));\r\n        fragments.add(SettingFragment.newInstance());\r\n\r\n        MainViewPagerAdapter adapter = new MainViewPagerAdapter(getSupportFragmentManager(), fragments);\r\n        mViewPager.setAdapter(adapter);\r\n\r\n        mNavigationView.setupWithViewPager(mViewPager);\r\n    }\r\n\r\n    @Override\r\n    protected void applyEvent() {\r\n        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {\r\n            @Override\r\n            public void onPageScrolled(int position, float v, int i1) {\r\n\r\n            }\r\n\r\n            @Override\r\n            public void onPageSelected(int position) {\r\n                switch (position) {\r\n                    case 0:\r\n                        mTitle.setText(R.string.to_do_list);\r\n                        mAddTodo.setVisibility(View.VISIBLE);\r\n                        break;\r\n                    case 1:\r\n                        mTitle.setText(R.string.complete_list);\r\n                        mAddTodo.setVisibility(View.INVISIBLE);\r\n                        break;\r\n                    case 2:\r\n                        mTitle.setText(R.string.setting);\r\n                        mAddTodo.setVisibility(View.INVISIBLE);\r\n                        break;\r\n                }\r\n            }\r\n\r\n            @Override\r\n            public void onPageScrollStateChanged(int position) {\r\n\r\n            }\r\n        });\r\n    }\r\n\r\n    @OnClick(R.id.add_todo)\r\n    void onClickAddTodo() {\r\n        startActivityForResult(AddTodoActivity.class, REQUEST_CODE_ADD_TODO);\r\n    }\r\n\r\n    private static class MainViewPagerAdapter extends FragmentPagerAdapter {\r\n\r\n        private List<Fragment> mFragments;\r\n\r\n        public MainViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {\r\n            super(fm);\r\n            mFragments = fragments;\r\n        }\r\n\r\n        @Override\r\n        public Fragment getItem(int position) {\r\n            return mFragments.get(position);\r\n        }\r\n\r\n        @Override\r\n        public int getCount() {\r\n            return mFragments.size();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\r\n        super.onActivityResult(requestCode, resultCode, data);\r\n        if (requestCode == REQUEST_CODE_ADD_TODO) {\r\n            switch (resultCode) {\r\n                case 0x200:\r\n                    TodoDesBean todoDesBean = (TodoDesBean) data.getSerializableExtra(\"add_todo\");\r\n                    TodoFragment todoFragment = (TodoFragment)((MainViewPagerAdapter)mViewPager.getAdapter()).getItem(0);\r\n                    todoFragment.updateAddTodoData(todoDesBean);\r\n                    break;\r\n            }\r\n        }\r\n    }\r\n\r\n    public void updateDoneOrCancelData(TodoDesBean todoDesBean, int postition) {\r\n        TodoFragment todoFragment = (TodoFragment)((MainViewPagerAdapter)mViewPager.getAdapter()).getItem(postition);\r\n        todoFragment.updateAddTodoData(todoDesBean);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/RegisterActivity.java",
    "content": "package com.wj.android.todo.activity;\r\n\r\nimport android.text.TextUtils;\r\nimport android.view.View;\r\nimport android.widget.EditText;\r\nimport android.widget.ImageView;\r\nimport android.widget.TextView;\r\n\r\nimport com.google.gson.JsonObject;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.http.HttpUtils;\r\nimport com.wj.android.todo.http.ResponseItem;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 18:20\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class RegisterActivity extends BaseActivity {\r\n    @BindView(R.id.title)\r\n    TextView mTitle;\r\n    @BindView(R.id.back)\r\n    ImageView mBack;\r\n    @BindView(R.id.account)\r\n    EditText mAccount;\r\n    @BindView(R.id.password)\r\n    EditText mPassword;\r\n    @BindView(R.id.repassword)\r\n    EditText mRepassword;\r\n\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_register;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        mTitle.setText(R.string.register);\r\n        mBack.setVisibility(View.VISIBLE);\r\n    }\r\n\r\n    @OnClick(R.id.regitster)\r\n    void onClickRegister() {\r\n        mAccount.setError(null);\r\n        mPassword.setError(null);\r\n        if (TextUtils.isEmpty(mAccount.getText())) {\r\n            mAccount.setError(getString(R.string.input_account));\r\n            mAccount.setFocusable(true);\r\n            mAccount.setFocusableInTouchMode(true);\r\n            mAccount.requestFocus();\r\n            return;\r\n        }\r\n\r\n        if (TextUtils.isEmpty(mPassword.getText())) {\r\n            mPassword.setError(getString(R.string.input_password));\r\n            mPassword.setFocusable(true);\r\n            mPassword.setFocusableInTouchMode(true);\r\n            mPassword.requestFocus();\r\n            return;\r\n        }\r\n\r\n        if (TextUtils.isEmpty(mRepassword.getText())) {\r\n            mRepassword.setError(getString(R.string.input_repassword));\r\n            mRepassword.setFocusable(true);\r\n            mRepassword.setFocusableInTouchMode(true);\r\n            mRepassword.requestFocus();\r\n            return;\r\n        }\r\n\r\n        if (!TextUtils.equals(mPassword.getText(), mRepassword.getText())) {\r\n            mRepassword.setError(getString(R.string.valid_repassword));\r\n            return;\r\n        }\r\n        requestRegister();\r\n    }\r\n\r\n    private void requestRegister() {\r\n        HttpUtils.requestRegister(this, mAccount.getText().toString(), mPassword.getText().toString(), mRepassword.getText().toString());\r\n    }\r\n\r\n    @OnClick(R.id.back)\r\n    void onClickBack() {\r\n        finish();\r\n    }\r\n\r\n    public void updateUI(ResponseItem<JsonObject> response) {\r\n        if (response.isSuccess()) {\r\n            showToast(getResources().getString(R.string.register_success));\r\n            finish();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean isLoadingEnable(int requestId) {\r\n        return super.isLoadingEnable(requestId);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/SplashActivity.java",
    "content": "package com.wj.android.todo.activity;\r\n\r\nimport android.os.Bundle;\r\nimport android.view.View;\r\nimport android.view.animation.AlphaAnimation;\r\nimport android.view.animation.Animation;\r\n\r\nimport com.wj.android.todo.BuildConfig;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.base.BaseActivity;\r\nimport com.wj.android.todo.constant.Constant;\r\nimport com.wj.android.todo.manager.PersistentCookieJarManager;\r\n\r\nimport java.util.List;\r\n\r\nimport butterknife.BindView;\r\nimport okhttp3.Cookie;\r\nimport okhttp3.HttpUrl;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 16:01\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class SplashActivity extends BaseActivity {\r\n\r\n    @BindView(R.id.root_view)\r\n    View mView;\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.activity_splash;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        AlphaAnimation animation = new AlphaAnimation(0.3f, 1.0f);\r\n        animation.setDuration(3000);\r\n        mView.startAnimation(animation);\r\n\r\n        animation.setAnimationListener(new Animation.AnimationListener() {\r\n            @Override\r\n            public void onAnimationStart(Animation animation) {\r\n            }\r\n\r\n            @Override\r\n            public void onAnimationEnd(Animation animation) {\r\n                redirectTo();\r\n            }\r\n\r\n            @Override\r\n            public void onAnimationRepeat(Animation animation) {\r\n            }\r\n        });\r\n    }\r\n\r\n    private void redirectTo() {\r\n        List<Cookie> cookies = PersistentCookieJarManager.getInstance(this).getPersistentCookieJar().loadForRequest(HttpUrl.parse(BuildConfig.BASE_URL));\r\n        if (cookies.isEmpty()) {\r\n            startActivity(LoginActivity.class);\r\n        } else {\r\n            Bundle bundle = new Bundle();\r\n            bundle.putString(\"user_name\", cookies.get(0).name());\r\n            startActivity(MainActivity.class, bundle);\r\n        }\r\n        finish();\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/activity/base/BaseActivity.java",
    "content": "package com.wj.android.todo.activity.base;\n\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.widget.Toast;\nimport com.wj.android.http.BaseView;\nimport com.wj.android.todo.R;\nimport com.wj.android.todo.exception.ApiException;\n\nimport butterknife.ButterKnife;\n\n/**\n * 作者：wangwnejie on 2018/3/22 16:57\n * 邮箱：wang20080990@163.com\n */\n\npublic abstract class BaseActivity extends AppCompatActivity implements BaseView {\n    private static final String TAG = BaseActivity.class.getSimpleName();\n    private ProgressDialog mProgressDialog;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.i(TAG, String.format(\"%s:onCreate\", this));\n        if (getLayoutId() != 0) {\n            setContentView(getLayoutId());\n        }\n        ButterKnife.bind(this);\n\n        initView();\n        initData();\n        applyEvent();\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        Log.i(TAG, String.format(\"%s:onStart\", this));\n    }\n\n    @Override\n    protected void onRestart() {\n        super.onRestart();\n        Log.i(TAG, String.format(\"%s:onRestart\", this));\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Log.i(TAG, String.format(\"%s:onResume\", this));\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        Log.i(TAG, String.format(\"%s:onPause\", this));\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        Log.i(TAG, String.format(\"%s:onStop\", this));\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        Log.i(TAG, String.format(\"%s:onDestroy\", this));\n        if (mProgressDialog != null) {\n            mProgressDialog.dismiss();\n        }\n\n    }\n    /**\n     * 返回当前界面布局文件\n     * @return\n     */\n    protected abstract int getLayoutId();\n\n    /**\n     * 初始化View\n     */\n    protected void initView(){}\n\n    /**\n     * 初始化数据\n     */\n    protected abstract void initData();\n\n    /**\n     * 设置事件监听\n     */\n    protected void applyEvent(){}\n\n    @Override\n    public boolean isLoadingEnable(int requestId) {\n        return false;\n    }\n\n    @Override\n    public void start(int requestId) {\n        if (isLoadingEnable(requestId) && !isFinishing()) {\n            mProgressDialog = new ProgressDialog(this,ProgressDialog.THEME_HOLO_LIGHT);\n            mProgressDialog.setCanceledOnTouchOutside(false);\n            mProgressDialog.setCancelable(false);\n            mProgressDialog.setMessage(getResources().getString(R.string.loading));\n            mProgressDialog.show();\n        }\n    }\n\n    @Override\n    public void error(Throwable t, int code, int requestId) {\n        if (t instanceof ApiException) {\n            showToast(t.getMessage());\n        } else {\n            showToast(getResources().getString(R.string.service_error));\n        }\n    }\n\n    @Override\n    public void end(int requestId) {\n        if (isLoadingEnable(requestId) && !isFinishing()) {\n            mProgressDialog.dismiss();\n        }\n    }\n\n    public void showToast(String msg) {\n        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();\n    }\n\n    public void startActivity(Class<?> cls) {\n        Intent intent = new Intent(this, cls);\n        startActivity(intent);\n    }\n\n    public void startActivity(Class<?> cls, Bundle bundle) {\n        Intent intent = new Intent(this, cls);\n        intent.putExtras(bundle);\n        startActivity(intent);\n    }\n\n    public void startActivityForResult(Class<?> cls, int requestCode) {\n        Intent intent = new Intent(this, cls);\n        startActivityForResult(intent, requestCode);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/adapter/TodoSectionAdapter.java",
    "content": "package com.wj.android.todo.adapter;\r\n\r\nimport android.text.TextUtils;\r\n\r\nimport com.chad.library.adapter.base.BaseSectionQuickAdapter;\r\nimport com.chad.library.adapter.base.BaseViewHolder;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.bean.TodoSection;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/9 14:41\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class TodoSectionAdapter extends BaseSectionQuickAdapter<TodoSection, BaseViewHolder> {\r\n    private boolean isDone;\r\n\r\n    public TodoSectionAdapter(int layoutResId, int sectionHeadResId, List<TodoSection> data, boolean isDone) {\r\n        super(layoutResId, sectionHeadResId, data);\r\n        this.isDone = isDone;\r\n    }\r\n\r\n    @Override\r\n    protected void convertHead(BaseViewHolder helper, TodoSection item) {\r\n        helper.setText(R.id.todo_head, item.header);\r\n        if (isDone) {\r\n            helper.setTextColor(R.id.todo_head, mContext.getResources().getColor(R.color.done_todo_date));\r\n        } else {\r\n            helper.setTextColor(R.id.todo_head, mContext.getResources().getColor(R.color.colorPrimary));\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void convert(BaseViewHolder helper, TodoSection item) {\r\n        helper.setText(R.id.item_name, item.t.getTitle());\r\n        if (TextUtils.isEmpty(item.t.getContent())) {\r\n            helper.setGone(R.id.item_des, false);\r\n        } else {\r\n            helper.setGone(R.id.item_des, true);\r\n            helper.setText(R.id.item_des, item.t.getContent());\r\n        }\r\n\r\n        if (isDone) {\r\n            helper.setGone(R.id.item_done_time, true);\r\n            helper.setText(R.id.item_done_time, String.format(mContext.getResources().getString(R.string.done_todo_date),item.t.getCompleteDateStr()));\r\n            helper.setImageResource(R.id.item_complete, R.drawable.cancel_todo);\r\n        } else {\r\n            helper.setGone(R.id.item_done_time, false);\r\n            helper.setImageResource(R.id.item_complete, R.drawable.complete_todo);\r\n        }\r\n\r\n        helper.addOnClickListener(R.id.item_complete);\r\n        helper.addOnClickListener(R.id.item_delete);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/bean/TodoDesBean.java",
    "content": "package com.wj.android.todo.bean;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/9 14:29\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class TodoDesBean implements Serializable{\r\n\r\n    private Object completeDate;\r\n    private String completeDateStr;\r\n    private String content;\r\n    private long date;\r\n    private String dateStr;\r\n    private int id;\r\n    private int status;\r\n    private String title;\r\n    private int type;\r\n    private int userId;\r\n\r\n    public Object getCompleteDate() {\r\n        return completeDate;\r\n    }\r\n\r\n    public void setCompleteDate(Object completeDate) {\r\n        this.completeDate = completeDate;\r\n    }\r\n\r\n    public String getCompleteDateStr() {\r\n        return completeDateStr;\r\n    }\r\n\r\n    public void setCompleteDateStr(String completeDateStr) {\r\n        this.completeDateStr = completeDateStr;\r\n    }\r\n\r\n    public String getContent() {\r\n        return content;\r\n    }\r\n\r\n    public void setContent(String content) {\r\n        this.content = content;\r\n    }\r\n\r\n    public long getDate() {\r\n        return date;\r\n    }\r\n\r\n    public void setDate(long date) {\r\n        this.date = date;\r\n    }\r\n\r\n    public String getDateStr() {\r\n        return dateStr;\r\n    }\r\n\r\n    public void setDateStr(String dateStr) {\r\n        this.dateStr = dateStr;\r\n    }\r\n\r\n    public int getId() {\r\n        return id;\r\n    }\r\n\r\n    public void setId(int id) {\r\n        this.id = id;\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public void setStatus(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public String getTitle() {\r\n        return title;\r\n    }\r\n\r\n    public void setTitle(String title) {\r\n        this.title = title;\r\n    }\r\n\r\n    public int getType() {\r\n        return type;\r\n    }\r\n\r\n    public void setType(int type) {\r\n        this.type = type;\r\n    }\r\n\r\n    public int getUserId() {\r\n        return userId;\r\n    }\r\n\r\n    public void setUserId(int userId) {\r\n        this.userId = userId;\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/bean/TodoListBean.java",
    "content": "package com.wj.android.todo.bean;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/9 14:55\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class TodoListBean {\r\n    private int curPage;\r\n    private int offset;\r\n    private boolean over;\r\n    private int pageCount;\r\n    private int size;\r\n    private int total;\r\n    private List<TodoDesBean> datas;\r\n\r\n    public int getCurPage() {\r\n        return curPage;\r\n    }\r\n\r\n    public void setCurPage(int curPage) {\r\n        this.curPage = curPage;\r\n    }\r\n\r\n    public int getOffset() {\r\n        return offset;\r\n    }\r\n\r\n    public void setOffset(int offset) {\r\n        this.offset = offset;\r\n    }\r\n\r\n    public boolean isOver() {\r\n        return over;\r\n    }\r\n\r\n    public void setOver(boolean over) {\r\n        this.over = over;\r\n    }\r\n\r\n    public int getPageCount() {\r\n        return pageCount;\r\n    }\r\n\r\n    public void setPageCount(int pageCount) {\r\n        this.pageCount = pageCount;\r\n    }\r\n\r\n    public int getSize() {\r\n        return size;\r\n    }\r\n\r\n    public void setSize(int size) {\r\n        this.size = size;\r\n    }\r\n\r\n    public int getTotal() {\r\n        return total;\r\n    }\r\n\r\n    public void setTotal(int total) {\r\n        this.total = total;\r\n    }\r\n\r\n    public List<TodoDesBean> getDatas() {\r\n        return datas;\r\n    }\r\n\r\n    public void setDatas(List<TodoDesBean> datas) {\r\n        this.datas = datas;\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/bean/TodoSection.java",
    "content": "package com.wj.android.todo.bean;\r\n\r\nimport com.chad.library.adapter.base.entity.SectionEntity;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/9 14:33\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class TodoSection extends SectionEntity<TodoDesBean> {\r\n\r\n    public TodoSection(boolean isHeader, String header) {\r\n        super(isHeader, header);\r\n    }\r\n\r\n    public TodoSection(TodoDesBean todoBean) {\r\n        super(todoBean);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/constant/Constant.java",
    "content": "package com.wj.android.todo.constant;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/7 15:24\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class Constant {\r\n    //public static final String BASE_URL = \"http://www.wanandroid.com\";\r\n\r\n    public static final String LOGIN_URI = \"/user/login\";\r\n    public static final String REGISTER_URI = \"/user/register\";\r\n    public static final String ADD_TODO_URI = \"/lg/todo/add/json\";\r\n    public static final String TODO_LIST_URI = \"/lg/todo/listnotdo/0/json/%d\";\r\n    public static final String DELETE_TODO_URI = \"/lg/todo/delete/%d/json\";\r\n    public static final String UPDATE_TODO_URI = \"/lg/todo/update/%d/json\";\r\n    public static final String DONE_TODO_URI = \"/lg/todo/done/%d/json\";\r\n    public static final String DONE_LIST_URI = \"/lg/todo/listdone/0/json/%d\";\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/constant/TimeConstants.java",
    "content": "package com.wj.android.todo.constant;\n\nimport android.support.annotation.IntDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/03/13\n *     desc  : constants of time\n * </pre>\n */\npublic final class TimeConstants {\n\n    public static final int MSEC = 1;\n    public static final int SEC  = 1000;\n    public static final int MIN  = 60000;\n    public static final int HOUR = 3600000;\n    public static final int DAY  = 86400000;\n\n    @IntDef({MSEC, SEC, MIN, HOUR, DAY})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Unit {\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/exception/ApiException.java",
    "content": "package com.wj.android.todo.exception;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 14:06\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class ApiException extends RuntimeException{\r\n\r\n    private int errorCode;\r\n\r\n    public ApiException(String message, int errorCode) {\r\n        super(message);\r\n        setErrorCode(errorCode);\r\n    }\r\n\r\n    public int getErrorCode() {\r\n        return errorCode;\r\n    }\r\n\r\n    public void setErrorCode(int errorCode) {\r\n        this.errorCode = errorCode;\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/fragment/SettingFragment.java",
    "content": "package com.wj.android.todo.fragment;\r\n\r\nimport android.os.Bundle;\r\nimport android.text.TextUtils;\r\nimport android.widget.TextView;\r\n\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.LoginActivity;\r\nimport com.wj.android.todo.activity.MainActivity;\r\nimport com.wj.android.todo.fragment.base.BaseFragment;\r\nimport com.wj.android.todo.manager.PersistentCookieJarManager;\r\nimport com.wj.android.todo.manager.SharePreferenceManager;\r\n\r\nimport java.util.List;\r\n\r\nimport butterknife.BindView;\r\nimport butterknife.OnClick;\r\nimport okhttp3.Cookie;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 13:42\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class SettingFragment extends BaseFragment {\r\n    @BindView(R.id.user_name)\r\n    TextView mUserName;\r\n\r\n    public static SettingFragment newInstance() {\r\n\r\n        Bundle args = new Bundle();\r\n        SettingFragment fragment = new SettingFragment();\r\n        fragment.setArguments(args);\r\n        return fragment;\r\n    }\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.fragment_setting;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        String userName = SharePreferenceManager.getInstance(getContext()).getUserName();\r\n        if (!TextUtils.isEmpty(userName)) {\r\n            mUserName.setText(SharePreferenceManager.getInstance(getContext()).getUserName());\r\n        }\r\n\r\n    }\r\n\r\n    @OnClick(R.id.logout)\r\n    void onClickLogout() {\r\n        SharePreferenceManager.getInstance(getContext()).clear();\r\n        PersistentCookieJarManager.getInstance(getContext()).getPersistentCookieJar().clear();\r\n        startActivity(LoginActivity.class);\r\n        getActivity().finish();\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/fragment/TodoFragment.java",
    "content": "package com.wj.android.todo.fragment;\r\n\r\nimport android.content.DialogInterface;\r\nimport android.content.Intent;\r\nimport android.os.Bundle;\r\nimport android.support.annotation.Nullable;\r\nimport android.support.design.widget.BottomSheetDialog;\r\nimport android.support.v4.widget.SwipeRefreshLayout;\r\nimport android.support.v7.app.AlertDialog;\r\nimport android.support.v7.widget.LinearLayoutManager;\r\nimport android.support.v7.widget.RecyclerView;\r\nimport android.text.TextUtils;\r\nimport android.view.View;\r\nimport android.widget.TextView;\r\n\r\nimport com.chad.library.adapter.base.BaseQuickAdapter;\r\nimport com.wj.android.todo.R;\r\nimport com.wj.android.todo.activity.EditTodoActivity;\r\nimport com.wj.android.todo.activity.MainActivity;\r\nimport com.wj.android.todo.adapter.TodoSectionAdapter;\r\nimport com.wj.android.todo.bean.TodoDesBean;\r\nimport com.wj.android.todo.bean.TodoListBean;\r\nimport com.wj.android.todo.bean.TodoSection;\r\nimport com.wj.android.todo.fragment.base.BaseFragment;\r\nimport com.wj.android.todo.http.HttpUtils;\r\nimport com.wj.android.todo.http.ResponseItem;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.LinkedHashSet;\r\nimport java.util.List;\r\n\r\nimport butterknife.BindView;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 13:42\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class TodoFragment extends BaseFragment {\r\n    private static final String KEY_IS_DONE = \"is_done\";\r\n    private static final int REQUEST_CODE_EDIT_TODO = 0x110;\r\n\r\n    @BindView(R.id.todo_rv)\r\n    RecyclerView mRecyclerView;\r\n    @BindView(R.id.swipe_layout)\r\n    SwipeRefreshLayout mSwipeRefreshLayout;\r\n\r\n    private int page = 1;\r\n    private TodoListBean mTodoListBean;\r\n    private TodoSectionAdapter mAdapter;\r\n    private int deletePosition = -1;\r\n    private int donePosition = -1;\r\n\r\n    private boolean isDone;\r\n\r\n    public static TodoFragment newInstance(boolean isDone) {\r\n\r\n        Bundle args = new Bundle();\r\n        args.putBoolean(KEY_IS_DONE, isDone);\r\n        TodoFragment fragment = new TodoFragment();\r\n        fragment.setArguments(args);\r\n        return fragment;\r\n    }\r\n\r\n    @Override\r\n    public void onCreate(@Nullable Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n\r\n        Bundle bundle = getArguments();\r\n        if (bundle != null) {\r\n            isDone = bundle.getBoolean(KEY_IS_DONE);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected int getLayoutId() {\r\n        return R.layout.fragment_todo;\r\n    }\r\n\r\n    @Override\r\n    protected void initData() {\r\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));\r\n\r\n        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {\r\n            @Override\r\n            public void onRefresh() {\r\n                page = 1;\r\n                requestTodoListData();\r\n            }\r\n        });\r\n\r\n        requestTodoListData();\r\n    }\r\n\r\n\r\n\r\n    private void requestTodoListData() {\r\n        HttpUtils.requestTodoList(this, page, isDone);\r\n    }\r\n\r\n    private void deleteTodoById(int todoId) {\r\n        HttpUtils.deleteTodoById(this,todoId);\r\n    }\r\n\r\n    private void doneTodoById(int todoId) {\r\n        HttpUtils.doneTodoById(this, todoId, isDone ? 0 : 1);\r\n    }\r\n\r\n    public void updateUI(final ResponseItem<TodoListBean> response) {\r\n        if (response.isSuccess() && response.getData() != null) {\r\n            if (page == 1) {\r\n                mTodoListBean = response.getData();\r\n                mAdapter = new TodoSectionAdapter(R.layout.todo_item_view,R.layout.todo_item_head,getTodoSectionData(mTodoListBean.getDatas()),isDone);\r\n                mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {\r\n                    @Override\r\n                    public void onLoadMoreRequested() {\r\n                        if (mTodoListBean.getPageCount() == 1) {\r\n                            mAdapter.loadMoreEnd(true);\r\n                        } else if (page >= mTodoListBean.getPageCount()) {\r\n                            mAdapter.loadMoreEnd(false);\r\n                        } else {\r\n                            page++;\r\n                            requestTodoListData();\r\n                        }\r\n                    }\r\n                }, mRecyclerView);\r\n                mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {\r\n                    @Override\r\n                    public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {\r\n                        switch (view.getId()){\r\n                            case R.id.item_complete:\r\n                                donePosition = position;\r\n                                doneTodoById(mAdapter.getData().get(position).t.getId());\r\n                                break;\r\n                            case R.id.item_delete:\r\n                                showDialog(position);\r\n                                break;\r\n                        }\r\n                    }\r\n                });\r\n                mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {\r\n                    @Override\r\n                    public void onItemClick(BaseQuickAdapter adapter, View view, int position) {\r\n                        TodoSection todoSection = mAdapter.getData().get(position);\r\n                        //showTodoDes(todoSection);\r\n                        Bundle bundle = new Bundle();\r\n                        bundle.putSerializable(\"todo_des\", todoSection.t);\r\n                        startActivityForResult(EditTodoActivity.class, bundle, REQUEST_CODE_EDIT_TODO);\r\n                    }\r\n                });\r\n                mAdapter.openLoadAnimation(BaseQuickAdapter.SLIDEIN_BOTTOM);\r\n                mRecyclerView.setAdapter(mAdapter);\r\n            } else {\r\n                mAdapter.addData(getTodoSectionData(response.getData().getDatas()));\r\n                mAdapter.loadMoreComplete();\r\n            }\r\n\r\n        }\r\n    }\r\n\r\n    private void showTodoDes(TodoSection todoSection) {\r\n        View view = View.inflate(getContext(),R.layout.dialog_todo_des_view, null);\r\n        TextView todoName = view.findViewById(R.id.todo_name);\r\n        todoName.setText(todoSection.t.getTitle());\r\n        TextView todoContent = view.findViewById(R.id.todo_content);\r\n        if (TextUtils.isEmpty(todoSection.t.getContent())) {\r\n            todoContent.setText(R.string.no_text);\r\n        } else {\r\n            todoContent.setText(todoSection.t.getContent());\r\n        }\r\n        BottomSheetDialog sheetDialog = new BottomSheetDialog(getContext());\r\n        sheetDialog.setContentView(view);\r\n        sheetDialog.show();\r\n\r\n\r\n    }\r\n\r\n    private List<TodoSection> getTodoSectionData(List<TodoDesBean> datas) {\r\n        List<TodoSection> todoSections = new ArrayList<>();\r\n        LinkedHashSet<String> dates = new LinkedHashSet<>();\r\n        for (TodoDesBean todoDesBean : datas) {\r\n            dates.add(todoDesBean.getDateStr());\r\n        }\r\n        for (String date : dates) {\r\n            TodoSection todoSectionHead = new TodoSection(true, date);\r\n            todoSections.add(todoSectionHead);\r\n            for (TodoDesBean todoDesBean : datas) {\r\n                if (TextUtils.equals(date, todoDesBean.getDateStr())) {\r\n                    TodoSection todoSectionContent = new TodoSection(todoDesBean);\r\n                    todoSections.add(todoSectionContent);\r\n                }\r\n            }\r\n        }\r\n        return todoSections;\r\n    }\r\n\r\n    @Override\r\n    public boolean isLoadingEnable(int requestId) {\r\n        return requestId == 1;\r\n    }\r\n\r\n    @Override\r\n    public void start(int requestId) {\r\n        if (requestId == 1) {\r\n            super.start(requestId);\r\n        } else {\r\n            if (page == 1) {\r\n                mSwipeRefreshLayout.setRefreshing(true);\r\n            }\r\n        }\r\n\r\n    }\r\n\r\n    @Override\r\n    public void error(Throwable t, int code, int requestId) {\r\n        super.error(t,code,requestId);\r\n        if (requestId == 0) {\r\n            if (page > 1) {\r\n                mAdapter.loadMoreFail();\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void end(int requestId) {\r\n        if (requestId == 1) {\r\n            super.end(requestId);\r\n        } else {\r\n            if (page == 1) {\r\n                mSwipeRefreshLayout.setRefreshing(false);\r\n            }\r\n        }\r\n    }\r\n\r\n    public void updateAddTodoData(TodoDesBean todoDesBean) {\r\n        List<TodoSection> todoSections = mAdapter.getData();\r\n        for (int i = 0; i < todoSections.size(); i++) {\r\n            TodoSection todoSection = todoSections.get(i);\r\n            if (todoSection.isHeader && TextUtils.equals(todoSection.header, todoDesBean.getDateStr())) {\r\n                TodoSection section = new TodoSection(todoDesBean);\r\n                mAdapter.getData().add(i+1,section);\r\n                mAdapter.notifyItemInserted(i+1);\r\n                mRecyclerView.scrollToPosition(i+1);\r\n                return;\r\n            }\r\n        }\r\n        TodoSection sectionHead = new TodoSection(true,todoDesBean.getDateStr());\r\n        mAdapter.getData().add(0, sectionHead);\r\n        TodoSection section = new TodoSection(todoDesBean);\r\n        mAdapter.getData().add(1, section);\r\n        mAdapter.notifyItemRangeInserted(0,2);\r\n        mRecyclerView.scrollToPosition(0);\r\n    }\r\n\r\n    public void updateRemovedData(ResponseItem response) {\r\n        if (response.isSuccess() && deletePosition != -1) {\r\n            if (mAdapter.getData().get(deletePosition-1).isHeader && (mAdapter.getData().size()== deletePosition+2 || mAdapter.getData().get(deletePosition+1).isHeader)) {\r\n                mAdapter.getData().remove(deletePosition-1);\r\n                mAdapter.getData().remove(deletePosition-1);\r\n                mAdapter.notifyItemRangeRemoved(deletePosition-1,2);\r\n            } else {\r\n                mAdapter.getData().remove(deletePosition);\r\n                mAdapter.notifyItemRemoved(deletePosition);\r\n            }\r\n            showToast(getString(R.string.delete_todo_success));\r\n        }\r\n\r\n    }\r\n\r\n    public void updateDoneData(ResponseItem<TodoDesBean> response) {\r\n        if (response.isSuccess() && donePosition != -1) {\r\n            if (mAdapter.getData().get(donePosition-1).isHeader && (mAdapter.getData().size()== donePosition+2 || mAdapter.getData().get(donePosition+1).isHeader)) {\r\n                mAdapter.getData().remove(donePosition-1);\r\n                mAdapter.getData().remove(donePosition-1);\r\n                mAdapter.notifyItemRangeRemoved(donePosition-1,2);\r\n            } else {\r\n                mAdapter.getData().remove(donePosition);\r\n                mAdapter.notifyItemRemoved(donePosition);\r\n            }\r\n            showToast(getString(isDone ? R.string.notdo_todo_success : R.string.done_todo_success));\r\n            ((MainActivity)getActivity()).updateDoneOrCancelData(response.getData(), isDone ? 0 : 1);\r\n        }\r\n    }\r\n\r\n    private void showDialog(final int position) {\r\n        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());\r\n        builder.setTitle(R.string.delete_todo);\r\n        builder.setMessage(R.string.sure_delete_todo);\r\n        builder.setNegativeButton(R.string.cancel,null);\r\n        builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {\r\n            @Override\r\n            public void onClick(DialogInterface dialogInterface, int i) {\r\n                deletePosition = position;\r\n                deleteTodoById(mAdapter.getData().get(position).t.getId());\r\n            }\r\n        });\r\n        builder.show();\r\n    }\r\n\r\n    @Override\r\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\r\n        super.onActivityResult(requestCode, resultCode, data);\r\n        if (requestCode == REQUEST_CODE_EDIT_TODO) {\r\n            switch (resultCode) {\r\n                case 0x210:\r\n                    page = 1;\r\n                    requestTodoListData();\r\n                    break;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/fragment/base/BaseFragment.java",
    "content": "package com.wj.android.todo.fragment.base;\n\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\n\nimport com.wj.android.http.BaseView;\nimport com.wj.android.todo.R;\nimport com.wj.android.todo.exception.ApiException;\n\nimport butterknife.ButterKnife;\nimport butterknife.Unbinder;\n\n/**\n * 作者：wangwnejie on 2018/4/2 16:58\n * 邮箱：wang20080990@163.com\n */\n\npublic abstract class BaseFragment extends Fragment implements BaseView {\n    private Unbinder mUnbinder;\n    private ProgressDialog mProgressDialog;\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        View view = inflater.inflate(getLayoutId(), container, false);\n        mUnbinder = ButterKnife.bind(this,view);\n        return view;\n    }\n\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        initData();\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        mUnbinder.unbind();\n    }\n\n    /**\n     * 返回当前界面布局文件\n     * @return\n     */\n    protected abstract int getLayoutId();\n\n    /**\n     * 初始化数据\n     */\n    protected abstract void initData();\n\n    public boolean isLoadingEnable(int requestId) {\n        return false;\n    }\n\n    @Override\n    public void start(int requestId) {\n        if (isLoadingEnable(requestId) && !getActivity().isFinishing()) {\n            mProgressDialog = new ProgressDialog(getContext(),ProgressDialog.THEME_HOLO_LIGHT);\n            mProgressDialog.setCanceledOnTouchOutside(false);\n            mProgressDialog.setCancelable(false);\n            mProgressDialog.setMessage(getResources().getString(R.string.loading));\n            mProgressDialog.show();\n        }\n    }\n\n    @Override\n    public void error(Throwable t, int code, int requestId) {\n        if (isAdded()) {\n            if (t instanceof ApiException) {\n                showToast(t.getMessage());\n            } else {\n                showToast(getResources().getString(R.string.service_error));\n            }\n        }\n    }\n\n    @Override\n    public void end(int requestId) {\n        if (isLoadingEnable(requestId) && !getActivity().isFinishing()) {\n            mProgressDialog.dismiss();\n        }\n\n    }\n\n    public void showToast(String msg) {\n        Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();\n    }\n\n    public void startActivity(Class<?> cls) {\n        Intent intent = new Intent(getActivity(), cls);\n        startActivity(intent);\n    }\n\n    public void startActivity(Class<?> cls, Bundle bundle) {\n        Intent intent = new Intent(getActivity(), cls);\n        intent.putExtras(bundle);\n        startActivity(intent);\n    }\n\n    public void startActivityForResult(Class<?> cls, Bundle bundle,int requestCode) {\n        Intent intent = new Intent(getActivity(), cls);\n        intent.putExtras(bundle);\n        startActivityForResult(intent, requestCode);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/http/HttpUtils.java",
    "content": "package com.wj.android.todo.http;\r\n\r\nimport android.text.TextUtils;\r\n\r\nimport com.google.gson.JsonObject;\r\nimport com.wj.android.http.BaseView;\r\nimport com.wj.android.http.XRetrofit;\r\nimport com.wj.android.todo.BuildConfig;\r\nimport com.wj.android.todo.activity.AddTodoActivity;\r\nimport com.wj.android.todo.activity.EditTodoActivity;\r\nimport com.wj.android.todo.activity.LoginActivity;\r\nimport com.wj.android.todo.activity.RegisterActivity;\r\nimport com.wj.android.todo.bean.TodoDesBean;\r\nimport com.wj.android.todo.bean.TodoListBean;\r\nimport com.wj.android.todo.constant.Constant;\r\nimport com.wj.android.todo.fragment.TodoFragment;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/7 15:21\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class HttpUtils {\r\n\r\n    private static String buildUrl(String uri) {\r\n        return String.format(\"%s%s\", BuildConfig.BASE_URL, uri);\r\n    }\r\n\r\n    public static void requestLogin(BaseView baseView, String userName, String password){\r\n        Map<String, String> params = new HashMap<>();\r\n        params.put(\"username\", userName);\r\n        params.put(\"password\", password);\r\n        XRetrofit.post(buildUrl(Constant.LOGIN_URI), params, new MyGsonCallback<ResponseItem<JsonObject>>(baseView) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<JsonObject> response, BaseView baseView) {\r\n                ((LoginActivity)baseView).updateUI(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void requestRegister(BaseView baseView, String userName, String password, String repassword) {\r\n        Map<String, String> params = new HashMap<>();\r\n        params.put(\"username\", userName);\r\n        params.put(\"password\", password);\r\n        params.put(\"repassword\", repassword);\r\n        XRetrofit.post(buildUrl(Constant.REGISTER_URI), params, new MyGsonCallback<ResponseItem<JsonObject>>(baseView) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<JsonObject> response, BaseView baseView) {\r\n                ((RegisterActivity)baseView).updateUI(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void requestAddTodoData(BaseView baseView, String todoName, String todoDes, String todoDate) {\r\n        Map<String, String> params = new HashMap<>();\r\n        params.put(\"title\", todoName);\r\n        if (!TextUtils.isEmpty(todoDes)) {\r\n            params.put(\"content\", todoDes);\r\n        }\r\n        params.put(\"date\", todoDate);\r\n        params.put(\"type\", \"0\");\r\n\r\n        XRetrofit.post(buildUrl(Constant.ADD_TODO_URI), params, new MyGsonCallback<ResponseItem<TodoDesBean>>(baseView) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<TodoDesBean> response, BaseView baseView) {\r\n                ((AddTodoActivity)baseView).updateUI(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void requestTodoList(BaseView baseView, int page, boolean isDone) {\r\n        XRetrofit.post(isDone ? buildUrl(String.format(Constant.DONE_LIST_URI, page)) : buildUrl(String.format(Constant.TODO_LIST_URI, page)), new MyGsonCallback<ResponseItem<TodoListBean>>(baseView) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<TodoListBean> response, BaseView baseView) {\r\n                ((TodoFragment)baseView).updateUI(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void deleteTodoById(BaseView baseView, int todoId) {\r\n        XRetrofit.post(buildUrl(String.format(Constant.DELETE_TODO_URI, todoId)), new MyGsonCallback<ResponseItem>(baseView,1) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem response, BaseView baseView) {\r\n                ((TodoFragment)baseView).updateRemovedData(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void updateTodoById(BaseView baseView, int todoId, String title, String content, String date) {\r\n\r\n    }\r\n\r\n    public static void doneTodoById(BaseView baseView, int todoId, int status) {\r\n        Map<String, String> params = new HashMap<>();\r\n        params.put(\"status\", String.valueOf(status));\r\n\r\n        XRetrofit.post(buildUrl(String.format(Constant.DONE_TODO_URI, todoId)),params, new MyGsonCallback<ResponseItem<TodoDesBean>>(baseView,1) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<TodoDesBean> response, BaseView baseView) {\r\n                ((TodoFragment)baseView).updateDoneData(response);\r\n            }\r\n        });\r\n    }\r\n\r\n    public static void requestUpdateTodoData(BaseView baseView, int todoId, String todoName, String todoDes, String todoDate) {\r\n        Map<String, String> params = new HashMap<>();\r\n        params.put(\"title\", todoName);\r\n        if (!TextUtils.isEmpty(todoDes)) {\r\n            params.put(\"content\", todoDes);\r\n        }\r\n        params.put(\"date\", todoDate);\r\n\r\n        XRetrofit.post(buildUrl(String.format(Constant.UPDATE_TODO_URI, todoId)), params, new MyGsonCallback<ResponseItem<TodoDesBean>>(baseView) {\r\n            @Override\r\n            protected void onSuccess(ResponseItem<TodoDesBean> response, BaseView baseView) {\r\n                ((EditTodoActivity)baseView).updateUI(response);\r\n            }\r\n        });\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/http/MyGsonCallback.java",
    "content": "package com.wj.android.todo.http;\r\n\r\nimport com.google.gson.Gson;\r\nimport com.wj.android.http.BaseView;\r\nimport com.wj.android.http.GsonCallback;\r\nimport com.wj.android.todo.exception.ApiException;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 13:57\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic abstract class MyGsonCallback<T> extends GsonCallback<T> {\r\n\r\n    public MyGsonCallback(BaseView baseView) {\r\n        super(baseView);\r\n    }\r\n\r\n    public MyGsonCallback(BaseView baseView, int requestId) {\r\n        super(baseView, requestId);\r\n    }\r\n\r\n    @Override\r\n    protected String convertResponse(String response) {\r\n        ResponseItem responseItem = new Gson().fromJson(response, ResponseItem.class);\r\n        if (!responseItem.isSuccess()) {\r\n            throw new ApiException(responseItem.getErrorMsg(), responseItem.getErrorCode());\r\n        }\r\n        return super.convertResponse(response);\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/http/ResponseItem.java",
    "content": "package com.wj.android.todo.http;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n* 响应类型\r\n* @author wangwenjie\r\n*泛型T是实际的响应类型\r\n*响应类型T为Object\r\n*/\r\npublic class ResponseItem<T> implements Serializable {\r\n\t/**\r\n\t * 错误的内部编号,success表示成功\r\n\t */\r\n\tprivate int errorCode;\r\n\t/**\r\n\t * 错误描述\r\n\t */\r\n\tprivate String errorMsg;\r\n\t/**\r\n\t * 返回值\r\n\t */\r\n\tprivate T data;\r\n\r\n\tpublic int getErrorCode() {\r\n\t\treturn errorCode;\r\n\t}\r\n\r\n\tpublic void setErrorCode(int errorCode) {\r\n\t\tthis.errorCode = errorCode;\r\n\t}\r\n\r\n\tpublic String getErrorMsg() {\r\n\t\treturn errorMsg;\r\n\t}\r\n\r\n\tpublic void setErrorMsg(String errorMsg) {\r\n\t\tthis.errorMsg = errorMsg;\r\n\t}\r\n\r\n\tpublic T getData() {\r\n\t\treturn data;\r\n\t}\r\n\r\n\tpublic void setData(T data) {\r\n\t\tthis.data = data;\r\n\t}\r\n\r\n\tpublic boolean isSuccess() {\r\n\t\treturn errorCode == 0;\r\n\t}\r\n\r\n}"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/manager/PersistentCookieJarManager.java",
    "content": "package com.wj.android.todo.manager;\r\n\r\n\r\nimport android.content.Context;\r\n\r\nimport com.franmontiel.persistentcookiejar.PersistentCookieJar;\r\nimport com.franmontiel.persistentcookiejar.cache.SetCookieCache;\r\nimport com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;\r\nimport com.wj.android.http.RetrofitHttpManager;\r\n\r\n/**\r\n * 作者：wangwnejie on 2018/8/8 16:34\r\n * 邮箱：wang20080990@163.com\r\n */\r\npublic class PersistentCookieJarManager {\r\n\r\n    private volatile static PersistentCookieJarManager sInstance;\r\n    private PersistentCookieJar mPersistentCookieJar;\r\n\r\n    private PersistentCookieJarManager(Context context) {\r\n        mPersistentCookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));\r\n    }\r\n\r\n    public static PersistentCookieJarManager getInstance(Context context) {\r\n        if (sInstance == null) {\r\n            synchronized (RetrofitHttpManager.class) {\r\n                if (sInstance == null) {\r\n                    sInstance = new PersistentCookieJarManager(context.getApplicationContext());\r\n                }\r\n            }\r\n        }\r\n        return sInstance;\r\n    }\r\n\r\n    public PersistentCookieJar getPersistentCookieJar() {\r\n        return mPersistentCookieJar;\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/manager/PreferenceHelper.java",
    "content": "/*******************************************************************************\r\n * Copyright (c) Baina Info Tech Co. Ltd\r\n * <p/>\r\n * DolphinCoreLibrary_Webzine\r\n * <p/>\r\n * PreferenceHelper\r\n * TODO File description or class description.\r\n *\r\n * @author: dhu\r\n * @since: 2011-8-6\r\n * @version: 1.0\r\n ******************************************************************************/\r\npackage com.wj.android.todo.manager;\r\n\r\nimport android.annotation.SuppressLint;\r\nimport android.content.SharedPreferences.Editor;\r\nimport android.os.Build;\r\n\r\n/**\r\n * PreferenceHelper of DolphinCoreLibrary_Webzine.\r\n *\r\n * @author dhu\r\n */\r\npublic abstract class PreferenceHelper {\r\n\r\n    public abstract void save(Editor editor);\r\n\r\n    private static PreferenceHelper sHelper;\r\n\r\n    public static PreferenceHelper getInstance() {\r\n        if (null == sHelper) {\r\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {\r\n                sHelper = new NewPreferenceHelper();\r\n            } else {\r\n                sHelper = new OlderPreferenceHelper();\r\n            }\r\n        }\r\n        return sHelper;\r\n    }\r\n\r\n    private static class OlderPreferenceHelper extends PreferenceHelper {\r\n\r\n        @Override\r\n        public void save(Editor editor) {\r\n            editor.commit();\r\n        }\r\n\r\n    }\r\n\r\n    private static class NewPreferenceHelper extends PreferenceHelper {\r\n\r\n        @SuppressLint(\"NewApi\")\r\n        @Override\r\n        public void save(Editor editor) {\r\n            editor.apply();\r\n        }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/manager/SharePreferenceManager.java",
    "content": "package com.wj.android.todo.manager;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\n/**\n * Created by Administrator on 2017/5/25.\n */\n\npublic class SharePreferenceManager {\n\n    private static final String PREF_FILE = \"wj_todo\";\n    private static final String KEY_USER_NAME = \"user_name\";\n\n    private volatile static SharePreferenceManager sInstance;\n    private SharedPreferences mSharedPreferences;\n\n    private SharePreferenceManager(Context context) {\n        mSharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);\n    }\n\n    public static SharePreferenceManager getInstance(Context context) {\n        if (sInstance == null) {\n            synchronized (SharePreferenceManager.class) {\n                if (sInstance == null) {\n                    sInstance = new SharePreferenceManager(context.getApplicationContext());\n                }\n            }\n        }\n        return sInstance;\n    }\n\n    public void setUserName(String userName) {\n        SharedPreferences.Editor editor = mSharedPreferences.edit();\n        editor.putString(KEY_USER_NAME, userName);\n        PreferenceHelper.getInstance().save(editor);\n    }\n\n    public String getUserName() {\n        return mSharedPreferences.getString(KEY_USER_NAME, null);\n    }\n\n    public void clear() {\n        mSharedPreferences.edit().clear();\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/util/TimeUtils.java",
    "content": "package com.wj.android.todo.util;\n\nimport android.support.annotation.NonNull;\n\n\nimport com.wj.android.todo.constant.TimeConstants;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Locale;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about time\n * </pre>\n */\npublic final class TimeUtils {\n\n    private static final ThreadLocal<SimpleDateFormat> SDF_THREAD_LOCAL = new ThreadLocal<>();\n\n    private static SimpleDateFormat getDefaultFormat() {\n        SimpleDateFormat simpleDateFormat = SDF_THREAD_LOCAL.get();\n        if (simpleDateFormat == null) {\n            simpleDateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.getDefault());\n            SDF_THREAD_LOCAL.set(simpleDateFormat);\n        }\n        return simpleDateFormat;\n    }\n\n    private static SimpleDateFormat getDateFormat(String pattern) {\n        return new SimpleDateFormat(pattern, Locale.getDefault());\n    }\n\n    private TimeUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Milliseconds to the formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param millis The milliseconds.\n     * @return the formatted time string\n     */\n    public static String millis2String(final long millis) {\n        return millis2String(millis, getDefaultFormat());\n    }\n\n    /**\n     * Milliseconds to the formatted time string.\n     *\n     * @param millis The milliseconds.\n     * @param format The format.\n     * @return the formatted time string\n     */\n    public static String millis2String(final long millis, @NonNull final DateFormat format) {\n        return format.format(new Date(millis));\n    }\n\n    /**\n     * Formatted time string to the milliseconds.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the milliseconds\n     */\n    public static long string2Millis(final String time) {\n        return string2Millis(time, getDefaultFormat());\n    }\n\n    /**\n     * Formatted time string to the milliseconds.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the milliseconds\n     */\n    public static long string2Millis(final String time, @NonNull final DateFormat format) {\n        try {\n            return format.parse(time).getTime();\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return -1;\n    }\n\n    /**\n     * Formatted time string to the date.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the date\n     */\n    public static Date string2Date(final String time) {\n        return string2Date(time, getDefaultFormat());\n    }\n\n    /**\n     * Formatted time string to the date.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the date\n     */\n    public static Date string2Date(final String time, @NonNull final DateFormat format) {\n        try {\n            return format.parse(time);\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Date to the formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param date The date.\n     * @return the formatted time string\n     */\n    public static String date2String(final Date date) {\n        return date2String(date, getDefaultFormat());\n    }\n\n    /**\n     * Date to the formatted time string.\n     *\n     * @param date   The date.\n     * @param format The format.\n     * @return the formatted time string\n     */\n    public static String date2String(final Date date, @NonNull final DateFormat format) {\n        return format.format(date);\n    }\n\n\n    public static String date2String(final Date date, @NonNull final String pattern) {\n        return date2String(date, getDateFormat(pattern));\n    }\n    /**\n     * Date to the milliseconds.\n     *\n     * @param date The date.\n     * @return the milliseconds\n     */\n    public static long date2Millis(final Date date) {\n        return date.getTime();\n    }\n\n    /**\n     * Milliseconds to the date.\n     *\n     * @param millis The milliseconds.\n     * @return the date\n     */\n    public static Date millis2Date(final long millis) {\n        return new Date(millis);\n    }\n\n    /**\n     * Return the time span, in unit.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time1 The first formatted time string.\n     * @param time2 The second formatted time string.\n     * @param unit  The unit of time span.\n     *              <ul>\n     *              <li>{@link TimeConstants#MSEC}</li>\n     *              <li>{@link TimeConstants#SEC }</li>\n     *              <li>{@link TimeConstants#MIN }</li>\n     *              <li>{@link TimeConstants#HOUR}</li>\n     *              <li>{@link TimeConstants#DAY }</li>\n     *              </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final String time1,\n                                   final String time2,\n                                   @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time1, time2, getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param time1  The first formatted time string.\n     * @param time2  The second formatted time string.\n     * @param format The format.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final String time1,\n                                   final String time2,\n                                   @NonNull final DateFormat format,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(string2Millis(time1, format) - string2Millis(time2, format), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param date1 The first date.\n     * @param date2 The second date.\n     * @param unit  The unit of time span.\n     *              <ul>\n     *              <li>{@link TimeConstants#MSEC}</li>\n     *              <li>{@link TimeConstants#SEC }</li>\n     *              <li>{@link TimeConstants#MIN }</li>\n     *              <li>{@link TimeConstants#HOUR}</li>\n     *              <li>{@link TimeConstants#DAY }</li>\n     *              </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final Date date1,\n                                   final Date date2,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(date2Millis(date1) - date2Millis(date2), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param millis1 The first milliseconds.\n     * @param millis2 The second milliseconds.\n     * @param unit    The unit of time span.\n     *                <ul>\n     *                <li>{@link TimeConstants#MSEC}</li>\n     *                <li>{@link TimeConstants#SEC }</li>\n     *                <li>{@link TimeConstants#MIN }</li>\n     *                <li>{@link TimeConstants#HOUR}</li>\n     *                <li>{@link TimeConstants#DAY }</li>\n     *                </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final long millis1,\n                                   final long millis2,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(millis1 - millis2, unit);\n    }\n\n    /**\n     * Return the fit time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time1     The first formatted time string.\n     * @param time2     The second formatted time string.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final String time1,\n                                        final String time2,\n                                        final int precision) {\n        long delta = string2Millis(time1, getDefaultFormat()) - string2Millis(time2, getDefaultFormat());\n        return millis2FitTimeSpan(delta, precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param time1     The first formatted time string.\n     * @param time2     The second formatted time string.\n     * @param format    The format.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final String time1,\n                                        final String time2,\n                                        @NonNull final DateFormat format,\n                                        final int precision) {\n        long delta = string2Millis(time1, format) - string2Millis(time2, format);\n        return millis2FitTimeSpan(delta, precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param date1     The first date.\n     * @param date2     The second date.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final Date date1, final Date date2, final int precision) {\n        return millis2FitTimeSpan(date2Millis(date1) - date2Millis(date2), precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param millis1   The first milliseconds.\n     * @param millis2   The second milliseconds.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final long millis1,\n                                        final long millis2,\n                                        final int precision) {\n        return millis2FitTimeSpan(millis1 - millis2, precision);\n    }\n\n    /**\n     * Return the current time in milliseconds.\n     *\n     * @return the current time in milliseconds\n     */\n    public static long getNowMills() {\n        return System.currentTimeMillis();\n    }\n\n    /**\n     * Return the current formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @return the current formatted time string\n     */\n    public static String getNowString() {\n        return millis2String(System.currentTimeMillis(), getDefaultFormat());\n    }\n\n    /**\n     * Return the current formatted time string.\n     *\n     * @param format The format.\n     * @return the current formatted time string\n     */\n    public static String getNowString(@NonNull final DateFormat format) {\n        return millis2String(System.currentTimeMillis(), format);\n    }\n\n    /**\n     * Return the current date.\n     *\n     * @return the current date\n     */\n    public static Date getNowDate() {\n        return new Date();\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @param unit The unit of time span.\n     *             <ul>\n     *             <li>{@link TimeConstants#MSEC}</li>\n     *             <li>{@link TimeConstants#SEC }</li>\n     *             <li>{@link TimeConstants#MIN }</li>\n     *             <li>{@link TimeConstants#HOUR}</li>\n     *             <li>{@link TimeConstants#DAY }</li>\n     *             </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final String time, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time, getNowString(), getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final String time,\n                                        @NonNull final DateFormat format,\n                                        @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time, getNowString(format), format, unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param date The date.\n     * @param unit The unit of time span.\n     *             <ul>\n     *             <li>{@link TimeConstants#MSEC}</li>\n     *             <li>{@link TimeConstants#SEC }</li>\n     *             <li>{@link TimeConstants#MIN }</li>\n     *             <li>{@link TimeConstants#HOUR}</li>\n     *             <li>{@link TimeConstants#DAY }</li>\n     *             </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final Date date, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(date, new Date(), unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param millis The milliseconds.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final long millis, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(millis, System.currentTimeMillis(), unit);\n    }\n\n    /**\n     * Return the fit time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time      The formatted time string.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final String time, final int precision) {\n        return getFitTimeSpan(time, getNowString(), getDefaultFormat(), precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param time      The formatted time string.\n     * @param format    The format.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final String time,\n                                             @NonNull final DateFormat format,\n                                             final int precision) {\n        return getFitTimeSpan(time, getNowString(format), format, precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param date      The date.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final Date date, final int precision) {\n        return getFitTimeSpan(date, getNowDate(), precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param millis    The milliseconds.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final long millis, final int precision) {\n        return getFitTimeSpan(millis, System.currentTimeMillis(), precision);\n    }\n\n    /**\n     * Return the friendly time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final String time) {\n        return getFriendlyTimeSpanByNow(time, getDefaultFormat());\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final String time,\n                                                  @NonNull final DateFormat format) {\n        return getFriendlyTimeSpanByNow(string2Millis(time, format));\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param date The date.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final Date date) {\n        return getFriendlyTimeSpanByNow(date.getTime());\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param millis The milliseconds.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final long millis) {\n        long now = System.currentTimeMillis();\n        long span = now - millis;\n        if (span < 0)\n            // U can read http://www.apihome.cn/api/java/Formatter.html to understand it.\n            return String.format(\"%tc\", millis);\n        if (span < 1000) {\n            return \"刚刚\";\n        } else if (span < TimeConstants.MIN) {\n            return String.format(Locale.getDefault(), \"%d秒前\", span / TimeConstants.SEC);\n        } else if (span < TimeConstants.HOUR) {\n            return String.format(Locale.getDefault(), \"%d分钟前\", span / TimeConstants.MIN);\n        }\n        // 获取当天 00:00\n        long wee = getWeeOfToday();\n        if (millis >= wee) {\n            return String.format(\"今天%tR\", millis);\n        } else if (millis >= wee - TimeConstants.DAY) {\n            return String.format(\"昨天%tR\", millis);\n        } else {\n            return String.format(\"%tF\", millis);\n        }\n    }\n\n    private static long getWeeOfToday() {\n        Calendar cal = Calendar.getInstance();\n        cal.set(Calendar.HOUR_OF_DAY, 0);\n        cal.set(Calendar.SECOND, 0);\n        cal.set(Calendar.MINUTE, 0);\n        cal.set(Calendar.MILLISECOND, 0);\n        return cal.getTimeInMillis();\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span\n     */\n    public static long getMillis(final long millis,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return millis + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span\n     */\n    public static long getMillis(final String time,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return getMillis(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span.\n     */\n    public static long getMillis(final String time,\n                                 @NonNull final DateFormat format,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return string2Millis(time, format) + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span.\n     */\n    public static long getMillis(final Date date,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return date2Millis(date) + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final long millis,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(millis, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final long millis,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(millis + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final String time,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final String time,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final Date date,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(date, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param date     The date.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final Date date,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(date2Millis(date) + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final long millis,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(millis + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the date differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final String time,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return getDate(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final String time,\n                               @NonNull final DateFormat format,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final Date date,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(date2Millis(date) + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the milliseconds differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span by now\n     */\n    public static long getMillisByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getMillis(getNowMills(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span by now\n     */\n    public static String getStringByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getStringByNow(timeSpan, getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param format   The format.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span by now\n     */\n    public static String getStringByNow(final long timeSpan,\n                                        @NonNull final DateFormat format,\n                                        @TimeConstants.Unit final int unit) {\n        return getString(getNowMills(), format, timeSpan, unit);\n    }\n\n    /**\n     * Return the date differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span by now\n     */\n    public static Date getDateByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getDate(getNowMills(), timeSpan, unit);\n    }\n\n    /**\n     * Return whether it is today.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final String time) {\n        return isToday(string2Millis(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final String time, @NonNull final DateFormat format) {\n        return isToday(string2Millis(time, format));\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final Date date) {\n        return isToday(date.getTime());\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final long millis) {\n        long wee = getWeeOfToday();\n        return millis >= wee && millis < wee + TimeConstants.DAY;\n    }\n\n    /**\n     * Return whether it is leap year.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final String time) {\n        return isLeapYear(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final String time, @NonNull final DateFormat format) {\n        return isLeapYear(string2Date(time, format));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        int year = cal.get(Calendar.YEAR);\n        return isLeapYear(year);\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final long millis) {\n        return isLeapYear(millis2Date(millis));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param year The year.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final int year) {\n        return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final String time) {\n        return getChineseWeek(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final String time, @NonNull final DateFormat format) {\n        return getChineseWeek(string2Date(time, format));\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param date The date.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final Date date) {\n        return new SimpleDateFormat(\"E\", Locale.CHINA).format(date);\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param millis The milliseconds.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final long millis) {\n        return getChineseWeek(new Date(millis));\n    }\n\n    /**\n     * Return the day of week in US.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final String time) {\n        return getUSWeek(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final String time, @NonNull final DateFormat format) {\n        return getUSWeek(string2Date(time, format));\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param date The date.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final Date date) {\n        return new SimpleDateFormat(\"EEEE\", Locale.US).format(date);\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param millis The milliseconds.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final long millis) {\n        return getUSWeek(new Date(millis));\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time  The formatted time string.\n     * @param field The given calendar field.\n     *              <ul>\n     *              <li>{@link Calendar#ERA}</li>\n     *              <li>{@link Calendar#YEAR}</li>\n     *              <li>{@link Calendar#MONTH}</li>\n     *              <li>...</li>\n     *              <li>{@link Calendar#DST_OFFSET}</li>\n     *              </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final String time, final int field) {\n        return getValueByCalendarField(string2Date(time, getDefaultFormat()), field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @param field  The given calendar field.\n     *               <ul>\n     *               <li>{@link Calendar#ERA}</li>\n     *               <li>{@link Calendar#YEAR}</li>\n     *               <li>{@link Calendar#MONTH}</li>\n     *               <li>...</li>\n     *               <li>{@link Calendar#DST_OFFSET}</li>\n     *               </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final String time,\n                                              @NonNull final DateFormat format,\n                                              final int field) {\n        return getValueByCalendarField(string2Date(time, format), field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param date  The date.\n     * @param field The given calendar field.\n     *              <ul>\n     *              <li>{@link Calendar#ERA}</li>\n     *              <li>{@link Calendar#YEAR}</li>\n     *              <li>{@link Calendar#MONTH}</li>\n     *              <li>...</li>\n     *              <li>{@link Calendar#DST_OFFSET}</li>\n     *              </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final Date date, final int field) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        return cal.get(field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param millis The milliseconds.\n     * @param field  The given calendar field.\n     *               <ul>\n     *               <li>{@link Calendar#ERA}</li>\n     *               <li>{@link Calendar#YEAR}</li>\n     *               <li>{@link Calendar#MONTH}</li>\n     *               <li>...</li>\n     *               <li>{@link Calendar#DST_OFFSET}</li>\n     *               </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final long millis, final int field) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTimeInMillis(millis);\n        return cal.get(field);\n    }\n\n    private static final String[] CHINESE_ZODIAC =\n            {\"猴\", \"鸡\", \"狗\", \"猪\", \"鼠\", \"牛\", \"虎\", \"兔\", \"龙\", \"蛇\", \"马\", \"羊\"};\n\n    /**\n     * Return the Chinese zodiac.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final String time) {\n        return getChineseZodiac(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final String time, @NonNull final DateFormat format) {\n        return getChineseZodiac(string2Date(time, format));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param date The date.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        return CHINESE_ZODIAC[cal.get(Calendar.YEAR) % 12];\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param millis The milliseconds.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final long millis) {\n        return getChineseZodiac(millis2Date(millis));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param year The year.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final int year) {\n        return CHINESE_ZODIAC[year % 12];\n    }\n\n    private static final int[]    ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};\n    private static final String[] ZODIAC       = {\n            \"水瓶座\", \"双鱼座\", \"白羊座\", \"金牛座\", \"双子座\", \"巨蟹座\",\n            \"狮子座\", \"处女座\", \"天秤座\", \"天蝎座\", \"射手座\", \"魔羯座\"\n    };\n\n    /**\n     * Return the zodiac.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the zodiac\n     */\n    public static String getZodiac(final String time) {\n        return getZodiac(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the zodiac\n     */\n    public static String getZodiac(final String time, @NonNull final DateFormat format) {\n        return getZodiac(string2Date(time, format));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param date The date.\n     * @return the zodiac\n     */\n    public static String getZodiac(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        int month = cal.get(Calendar.MONTH) + 1;\n        int day = cal.get(Calendar.DAY_OF_MONTH);\n        return getZodiac(month, day);\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param millis The milliseconds.\n     * @return the zodiac\n     */\n    public static String getZodiac(final long millis) {\n        return getZodiac(millis2Date(millis));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param month The month.\n     * @param day   The day.\n     * @return the zodiac\n     */\n    public static String getZodiac(final int month, final int day) {\n        return ZODIAC[day >= ZODIAC_FLAGS[month - 1]\n                ? month - 1\n                : (month + 10) % 12];\n    }\n\n    private static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return timeSpan * unit;\n    }\n\n    private static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {\n        return millis / unit;\n    }\n\n    private static String millis2FitTimeSpan(long millis, int precision) {\n        if (precision <= 0) return null;\n        precision = Math.min(precision, 5);\n        String[] units = {\"天\", \"小时\", \"分钟\", \"秒\", \"毫秒\"};\n        if (millis == 0) return 0 + units[precision - 1];\n        StringBuilder sb = new StringBuilder();\n        if (millis < 0) {\n            sb.append(\"-\");\n            millis = -millis;\n        }\n        int[] unitLen = {86400000, 3600000, 60000, 1000, 1};\n        for (int i = 0; i < precision; i++) {\n            if (millis >= unitLen[i]) {\n                long mode = millis / unitLen[i];\n                millis -= mode * unitLen[i];\n                sb.append(mode).append(units[i]);\n            }\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wj/android/todo/widget/BottomNavigationViewEx.java",
    "content": "package com.wj.android.todo.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.design.internal.BottomNavigationItemView;\nimport android.support.design.internal.BottomNavigationMenuView;\nimport android.support.design.widget.BottomNavigationView;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.util.TypedValue;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Field;\n\n/**\n * Created by yu on 2016/11/10.\n */\npublic class BottomNavigationViewEx extends BottomNavigationView {\n    // used for animation\n    private int mShiftAmount;\n    private float mScaleUpFactor;\n    private float mScaleDownFactor;\n    private boolean animationRecord;\n    private float mLargeLabelSize;\n    private float mSmallLabelSize;\n    private boolean visibilityTextSizeRecord;\n    private boolean visibilityHeightRecord;\n    private int mItemHeight;\n    private boolean textVisibility = true;\n    // used for animation end\n\n    // used for setupWithViewPager\n    private ViewPager mViewPager;\n    private MyOnNavigationItemSelectedListener mMyOnNavigationItemSelectedListener;\n    private BottomNavigationViewExOnPageChangeListener mPageChangeListener;\n    private BottomNavigationMenuView mMenuView;\n    private BottomNavigationItemView[] mButtons;\n    // used for setupWithViewPager end\n\n    // detect navigation tab changes when the user clicking on navigation item\n    private static boolean isNavigationItemClicking = false;\n\n    public BottomNavigationViewEx(Context context) {\n        super(context);\n    }\n\n    public BottomNavigationViewEx(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public BottomNavigationViewEx(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    private void refreshTextViewVisibility() {\n        if (!textVisibility)\n            return;\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n\n        int currentItem = getCurrentItem();\n\n        // 3. get field mShiftingMode and TextView in mButtons\n        for (BottomNavigationItemView button : mButtons) {\n            TextView mLargeLabel = getField(button.getClass(), button, \"mLargeLabel\");\n            TextView mSmallLabel = getField(button.getClass(), button, \"mSmallLabel\");\n\n            mLargeLabel.clearAnimation();\n            mSmallLabel.clearAnimation();\n\n            // mShiftingMode\n            boolean mShiftingMode = getField(button.getClass(), button, \"mShiftingMode\");\n            boolean selected = button.getItemPosition() == currentItem;\n            if (mShiftingMode) {\n                if (selected) {\n                    mLargeLabel.setVisibility(VISIBLE);\n                } else {\n                    mLargeLabel.setVisibility(INVISIBLE);\n                }\n                mSmallLabel.setVisibility(INVISIBLE);\n            } else {\n                if (selected) {\n                    mLargeLabel.setVisibility(VISIBLE);\n                    mSmallLabel.setVisibility(INVISIBLE);\n                } else {\n                    mLargeLabel.setVisibility(INVISIBLE);\n                    mSmallLabel.setVisibility(VISIBLE);\n                }\n            }\n        }\n    }\n\n\n    /**\n     * change the visibility of icon\n     *\n     * @param visibility\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setIconVisibility(boolean visibility) {\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in mButtons\n        private BottomNavigationItemView[] mButtons;\n\n        3. get mIcon in mButtons\n        private ImageView mIcon\n\n        4. set mIcon visibility gone\n\n        5. change mItemHeight to only text size in mMenuView\n         */\n        // 1. get mMenuView\n        final BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n        // 3. get mIcon in mButtons\n        for (BottomNavigationItemView button : mButtons) {\n            ImageView mIcon = getField(button.getClass(), button, \"mIcon\");\n            // 4. set mIcon visibility gone\n            mIcon.setVisibility(visibility ? View.VISIBLE : View.INVISIBLE);\n        }\n\n        // 5. change mItemHeight to only text size in mMenuView\n        if (!visibility) {\n            // if not record mItemHeight\n            if (!visibilityHeightRecord) {\n                visibilityHeightRecord = true;\n                mItemHeight = getItemHeight();\n            }\n\n            // change mItemHeight\n            BottomNavigationItemView button = mButtons[0];\n            if (null != button) {\n                final ImageView mIcon = getField(button.getClass(), button, \"mIcon\");\n//                System.out.println(\"mIcon.getMeasuredHeight():\" + mIcon.getMeasuredHeight());\n                if (null != mIcon) {\n                    mIcon.post(new Runnable() {\n                        @Override\n                        public void run() {\n//                            System.out.println(\"mIcon.getMeasuredHeight():\" + mIcon.getMeasuredHeight());\n                            setItemHeight(mItemHeight - mIcon.getMeasuredHeight());\n                        }\n                    });\n                }\n            }\n        } else {\n            // if not record the mItemHeight, we need do nothing.\n            if (!visibilityHeightRecord)\n                return;\n\n            // restore it\n            setItemHeight(mItemHeight);\n        }\n\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * change the visibility of text\n     *\n     * @param visibility\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setTextVisibility(boolean visibility) {\n        this.textVisibility = visibility;\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in mButtons\n        private BottomNavigationItemView[] mButtons;\n\n        3. set text size in mButtons\n        private final TextView mLargeLabel\n        private final TextView mSmallLabel\n\n        4. change mItemHeight to only icon size in mMenuView\n         */\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n\n        // 3. change field mShiftingMode value in mButtons\n        for (BottomNavigationItemView button : mButtons) {\n            TextView mLargeLabel = getField(button.getClass(), button, \"mLargeLabel\");\n            TextView mSmallLabel = getField(button.getClass(), button, \"mSmallLabel\");\n\n            if (!visibility) {\n                // if not record the font size, record it\n                if (!visibilityTextSizeRecord && !animationRecord) {\n                    visibilityTextSizeRecord = true;\n                    mLargeLabelSize = mLargeLabel.getTextSize();\n                    mSmallLabelSize = mSmallLabel.getTextSize();\n                }\n\n                // if not visitable, set font size to 0\n                mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, 0);\n                mSmallLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, 0);\n\n            } else {\n                // if not record the font size, we need do nothing.\n                if (!visibilityTextSizeRecord)\n                    break;\n\n                // restore it\n                mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mLargeLabelSize);\n                mSmallLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSmallLabelSize);\n            }\n        }\n\n        // 4 change mItemHeight to only icon size in mMenuView\n        if (!visibility) {\n            // if not record mItemHeight\n            if (!visibilityHeightRecord) {\n                visibilityHeightRecord = true;\n                mItemHeight = getItemHeight();\n            }\n\n            // change mItemHeight to only icon size in mMenuView\n            // private final int mItemHeight;\n\n            // change mItemHeight\n//            System.out.println(\"mLargeLabel.getMeasuredHeight():\" + getFontHeight(mSmallLabelSize));\n            setItemHeight(mItemHeight - getFontHeight(mSmallLabelSize));\n\n        } else {\n            // if not record the mItemHeight, we need do nothing.\n            if (!visibilityHeightRecord)\n                return;\n            // restore mItemHeight\n            setItemHeight(mItemHeight);\n        }\n\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * get text height by font size\n     *\n     * @param fontSize\n     * @return\n     */\n    private static int getFontHeight(float fontSize) {\n        Paint paint = new Paint();\n        paint.setTextSize(fontSize);\n        Paint.FontMetrics fm = paint.getFontMetrics();\n        return (int) Math.ceil(fm.descent - fm.top) + 2;\n    }\n\n    /**\n     * enable or disable click item animation(text scale and icon move animation in no item shifting mode)\n     *\n     * @param enable It means the text won't scale and icon won't move when active it in no item shifting mode if false.\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void enableAnimation(boolean enable) {\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in mButtons\n        private BottomNavigationItemView[] mButtons;\n\n        3. chang mShiftAmount to 0 in mButtons\n        private final int mShiftAmount\n\n        change mScaleUpFactor and mScaleDownFactor to 1f in mButtons\n        private final float mScaleUpFactor\n        private final float mScaleDownFactor\n\n        4. change label font size in mButtons\n        private final TextView mLargeLabel\n        private final TextView mSmallLabel\n         */\n\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n        // 3. change field mShiftingMode value in mButtons\n        for (BottomNavigationItemView button : mButtons) {\n            TextView mLargeLabel = getField(button.getClass(), button, \"mLargeLabel\");\n            TextView mSmallLabel = getField(button.getClass(), button, \"mSmallLabel\");\n\n            // if disable animation, need animationRecord the source value\n            if (!enable) {\n                if (!animationRecord) {\n                    animationRecord = true;\n                    mShiftAmount = getField(button.getClass(), button, \"mShiftAmount\");\n                    mScaleUpFactor = getField(button.getClass(), button, \"mScaleUpFactor\");\n                    mScaleDownFactor = getField(button.getClass(), button, \"mScaleDownFactor\");\n\n                    mLargeLabelSize = mLargeLabel.getTextSize();\n                    mSmallLabelSize = mSmallLabel.getTextSize();\n\n//                    System.out.println(\"mShiftAmount:\" + mShiftAmount + \" mScaleUpFactor:\"\n//                            + mScaleUpFactor + \" mScaleDownFactor:\" + mScaleDownFactor\n//                            + \" mLargeLabel:\" + mLargeLabelSize + \" mSmallLabel:\" + mSmallLabelSize);\n                }\n                // disable\n                setField(button.getClass(), button, \"mShiftAmount\", 0);\n                setField(button.getClass(), button, \"mScaleUpFactor\", 1);\n                setField(button.getClass(), button, \"mScaleDownFactor\", 1);\n\n                // let the mLargeLabel font size equal to mSmallLabel\n                mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSmallLabelSize);\n\n                // debug start\n//                mLargeLabelSize = mLargeLabel.getTextSize();\n//                System.out.println(\"mLargeLabel:\" + mLargeLabelSize);\n                // debug end\n\n            } else {\n                // haven't change the value. It means it was the first call this method. So nothing need to do.\n                if (!animationRecord)\n                    return;\n                // enable animation\n                setField(button.getClass(), button, \"mShiftAmount\", mShiftAmount);\n                setField(button.getClass(), button, \"mScaleUpFactor\", mScaleUpFactor);\n                setField(button.getClass(), button, \"mScaleDownFactor\", mScaleDownFactor);\n                // restore\n                mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mLargeLabelSize);\n            }\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * enable the shifting mode for navigation\n     *\n     * @param enable It will has a shift animation if true. Otherwise all items are the same width.\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void enableShiftingMode(boolean enable) {\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. change field mShiftingMode value in mMenuView\n        private boolean mShiftingMode = true;\n         */\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. change field mShiftingMode value in mMenuView\n        setField(mMenuView.getClass(), mMenuView, \"mShiftingMode\", enable);\n\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * enable the shifting mode for each item\n     *\n     * @param enable It will has a shift animation for item if true. Otherwise the item text always be shown.\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void enableItemShiftingMode(boolean enable) {\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in this mMenuView\n        private BottomNavigationItemView[] mButtons;\n\n        3. change field mShiftingMode value in mButtons\n        private boolean mShiftingMode = true;\n         */\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n        // 3. change field mShiftingMode value in mButtons\n        for (BottomNavigationItemView button : mButtons) {\n            setField(button.getClass(), button, \"mShiftingMode\", enable);\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * get the current checked item position\n     *\n     * @return index of item, start from 0.\n     */\n    public int getCurrentItem() {\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in mMenuView\n        private BottomNavigationItemView[] mButtons;\n\n        3. get menu and traverse it to get the checked one\n         */\n\n        // 1. get mMenuView\n//        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n        // 3. get menu and traverse it to get the checked one\n        Menu menu = getMenu();\n        for (int i = 0; i < mButtons.length; i++) {\n            if (menu.getItem(i).isChecked()) {\n                return i;\n            }\n        }\n        return 0;\n    }\n\n    /**\n     * get menu item position in menu\n     *\n     * @param item\n     * @return position if success, -1 otherwise\n     */\n    public int getMenuItemPosition(MenuItem item) {\n        // get item id\n        int itemId = item.getItemId();\n        // get meunu\n        Menu menu = getMenu();\n        int size = menu.size();\n        for (int i = 0; i < size; i++) {\n            if (menu.getItem(i).getItemId() == itemId) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    /**\n     * set the current checked item\n     *\n     * @param item start from 0.\n     */\n    public void setCurrentItem(int item) {\n        // check bounds\n        if (item < 0 || item >= getMaxItemCount()) {\n            throw new ArrayIndexOutOfBoundsException(\"item is out of bounds, we expected 0 - \"\n                    + (getMaxItemCount() - 1) + \". Actually \" + item);\n        }\n\n        /*\n        1. get field in this class\n        private final BottomNavigationMenuView mMenuView;\n\n        2. get field in mMenuView\n        private BottomNavigationItemView[] mButtons;\n        private final OnClickListener mOnClickListener;\n\n        3. call mOnClickListener.onClick();\n         */\n        // 1. get mMenuView\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get mButtons\n        BottomNavigationItemView[] mButtons = getBottomNavigationItemViews();\n        // get mOnClickListener\n        OnClickListener mOnClickListener = getField(mMenuView.getClass(), mMenuView, \"onClickListener\");\n\n//        System.out.println(\"mMenuView:\" + mMenuView + \" mButtons:\" + mButtons + \" mOnClickListener\" + mOnClickListener);\n        // 3. call mOnClickListener.onClick();\n        mOnClickListener.onClick(mButtons[item]);\n\n    }\n\n    /**\n     * get OnNavigationItemSelectedListener\n     *\n     * @return\n     */\n    public OnNavigationItemSelectedListener getOnNavigationItemSelectedListener() {\n        // private OnNavigationItemSelectedListener mListener;\n        OnNavigationItemSelectedListener mListener = getField(BottomNavigationView.class, this, \"selectedListener\");\n        return mListener;\n    }\n\n    @Override\n    public void setOnNavigationItemSelectedListener(@Nullable OnNavigationItemSelectedListener listener) {\n        // if not set up with view pager, the same with father\n        if (null == mMyOnNavigationItemSelectedListener) {\n            super.setOnNavigationItemSelectedListener(listener);\n            return;\n        }\n\n        mMyOnNavigationItemSelectedListener.setOnNavigationItemSelectedListener(listener);\n    }\n\n    /**\n     * get private mMenuView\n     *\n     * @return\n     */\n    private BottomNavigationMenuView getBottomNavigationMenuView() {\n        if (null == mMenuView)\n            mMenuView = getField(BottomNavigationView.class, this, \"menuView\");\n        return mMenuView;\n    }\n\n    /**\n     * get private mButtons in mMenuView\n     *\n     * @return\n     */\n    public BottomNavigationItemView[] getBottomNavigationItemViews() {\n        if (null != mButtons)\n            return mButtons;\n        /*\n         * 1 private final BottomNavigationMenuView mMenuView;\n         * 2 private BottomNavigationItemView[] mButtons;\n         */\n        BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        mButtons = getField(mMenuView.getClass(), mMenuView, \"buttons\");\n        return mButtons;\n    }\n\n    /**\n     * get private mButton in mMenuView at position\n     *\n     * @param position\n     * @return\n     */\n    public BottomNavigationItemView getBottomNavigationItemView(int position) {\n        return getBottomNavigationItemViews()[position];\n    }\n\n    /**\n     * get icon at position\n     *\n     * @param position\n     * @return\n     */\n    public ImageView getIconAt(int position) {\n        /*\n         * 1 private final BottomNavigationMenuView mMenuView;\n         * 2 private BottomNavigationItemView[] mButtons;\n         * 3 private ImageView mIcon;\n         */\n        BottomNavigationItemView mButtons = getBottomNavigationItemView(position);\n        ImageView mIcon = getField(BottomNavigationItemView.class, mButtons, \"icon\");\n        return mIcon;\n    }\n\n    /**\n     * get small label at position\n     * Each item has tow label, one is large, another is small.\n     *\n     * @param position\n     * @return\n     */\n    public TextView getSmallLabelAt(int position) {\n        /*\n         * 1 private final BottomNavigationMenuView mMenuView;\n         * 2 private BottomNavigationItemView[] mButtons;\n         * 3 private final TextView mSmallLabel;\n         */\n        BottomNavigationItemView mButtons = getBottomNavigationItemView(position);\n        TextView mSmallLabel = getField(BottomNavigationItemView.class, mButtons, \"smallLabel\");\n        return mSmallLabel;\n    }\n\n    /**\n     * get large label at position\n     * Each item has tow label, one is large, another is small.\n     *\n     * @param position\n     * @return\n     */\n    public TextView getLargeLabelAt(int position) {\n        /*\n         * 1 private final BottomNavigationMenuView mMenuView;\n         * 2 private BottomNavigationItemView[] mButtons;\n         * 3 private final TextView mLargeLabel;\n         */\n        BottomNavigationItemView mButtons = getBottomNavigationItemView(position);\n        TextView mLargeLabel = getField(BottomNavigationItemView.class, mButtons, \"largeLabel\");\n        return mLargeLabel;\n    }\n\n    /**\n     * return item count\n     *\n     * @return\n     */\n    public int getItemCount() {\n        BottomNavigationItemView[] bottomNavigationItemViews = getBottomNavigationItemViews();\n        if (null == bottomNavigationItemViews)\n            return 0;\n        return bottomNavigationItemViews.length;\n    }\n\n    /**\n     * set all item small TextView size\n     * Each item has tow label, one is large, another is small.\n     * Small one will be shown when item state is normal\n     * Large one will be shown when item checked.\n     *\n     * @param sp\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setSmallTextSize(float sp) {\n        int count = getItemCount();\n        for (int i = 0; i < count; i++) {\n            getSmallLabelAt(i).setTextSize(sp);\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * set all item large TextView size\n     * Each item has tow label, one is large, another is small.\n     * Small one will be shown when item state is normal.\n     * Large one will be shown when item checked.\n     *\n     * @param sp\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setLargeTextSize(float sp) {\n        int count = getItemCount();\n        for (int i = 0; i < count; i++) {\n            getLargeLabelAt(i).setTextSize(sp);\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * set all item large and small TextView size\n     * Each item has tow label, one is large, another is small.\n     * Small one will be shown when item state is normal\n     * Large one will be shown when item checked.\n     *\n     * @param sp\n     */\n    public void setTextSize(float sp) {\n        setLargeTextSize(sp);\n        setSmallTextSize(sp);\n    }\n\n    /**\n     * set item ImageView size which at position\n     *\n     * @param position position start from 0\n     * @param width    in dp\n     * @param height   in dp\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setIconSizeAt(int position, float width, float height) {\n        ImageView icon = getIconAt(position);\n        // update size\n        ViewGroup.LayoutParams layoutParams = icon.getLayoutParams();\n        layoutParams.width = dp2px(getContext(), width);\n        layoutParams.height = dp2px(getContext(), height);\n        icon.setLayoutParams(layoutParams);\n\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * set all item ImageView size\n     *\n     * @param width  in dp\n     * @param height in dp\n     */\n    public void setIconSize(float width, float height) {\n        int count = getItemCount();\n        for (int i = 0; i < count; i++) {\n            setIconSizeAt(i, width, height);\n        }\n    }\n\n    /**\n     * set menu item height\n     *\n     * @param height in px\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setItemHeight(int height) {\n        // 1. get mMenuView\n        final BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. set private final int mItemHeight in mMenuView\n        setField(mMenuView.getClass(), mMenuView, \"itemHeight\", height);\n\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * get menu item height\n     *\n     * @return in px\n     */\n    public int getItemHeight() {\n        // 1. get mMenuView\n        final BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();\n        // 2. get private final int mItemHeight in mMenuView\n        return getField(mMenuView.getClass(), mMenuView, \"itemHeight\");\n    }\n\n    /**\n     * dp to px\n     *\n     * @param context\n     * @param dpValue dp\n     * @return px\n     */\n    public static int dp2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n    /**\n     * set Typeface for all item TextView\n     *\n     * @attr ref android.R.styleable#TextView_typeface\n     * @attr ref android.R.styleable#TextView_textStyle\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setTypeface(Typeface typeface, int style) {\n        int count = getItemCount();\n        for (int i = 0; i < count; i++) {\n            getLargeLabelAt(i).setTypeface(typeface, style);\n            getSmallLabelAt(i).setTypeface(typeface, style);\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * set Typeface for all item TextView\n     *\n     * @attr ref android.R.styleable#TextView_typeface\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setTypeface(Typeface typeface) {\n        int count = getItemCount();\n        for (int i = 0; i < count; i++) {\n            getLargeLabelAt(i).setTypeface(typeface);\n            getSmallLabelAt(i).setTypeface(typeface);\n        }\n        mMenuView.updateMenuView();\n    }\n\n    /**\n     * get private filed in this specific object\n     *\n     * @param targetClass\n     * @param instance    the filed owner\n     * @param fieldName\n     * @param <T>\n     * @return field if success, null otherwise.\n     */\n    private <T> T getField(Class targetClass, Object instance, String fieldName) {\n        try {\n            Field field = targetClass.getDeclaredField(fieldName);\n            field.setAccessible(true);\n            return (T) field.get(instance);\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * change the field value\n     *\n     * @param targetClass\n     * @param instance    the filed owner\n     * @param fieldName\n     * @param value\n     */\n    private void setField(Class targetClass, Object instance, String fieldName, Object value) {\n        try {\n            Field field = targetClass.getDeclaredField(fieldName);\n            field.setAccessible(true);\n            field.set(instance, value);\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * This method will link the given ViewPager and this BottomNavigationViewEx together so that\n     * changes in one are automatically reflected in the other. This includes scroll state changes\n     * and clicks.\n     *\n     * @param viewPager\n     */\n    public void setupWithViewPager(@Nullable final ViewPager viewPager) {\n        setupWithViewPager(viewPager, false);\n    }\n\n    /**\n     * This method will link the given ViewPager and this BottomNavigationViewEx together so that\n     * changes in one are automatically reflected in the other. This includes scroll state changes\n     * and clicks.\n     *\n     * @param viewPager\n     * @param smoothScroll whether ViewPager changed with smooth scroll animation\n     */\n    public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean smoothScroll) {\n        if (mViewPager != null) {\n            // If we've already been setup with a ViewPager, remove us from it\n            if (mPageChangeListener != null) {\n                mViewPager.removeOnPageChangeListener(mPageChangeListener);\n            }\n        }\n\n        if (null == viewPager) {\n            mViewPager = null;\n            super.setOnNavigationItemSelectedListener(null);\n            return;\n        }\n\n        mViewPager = viewPager;\n\n        // Add our custom OnPageChangeListener to the ViewPager\n        if (mPageChangeListener == null) {\n            mPageChangeListener = new BottomNavigationViewExOnPageChangeListener(this);\n        }\n        viewPager.addOnPageChangeListener(mPageChangeListener);\n\n        // Now we'll add a navigation item selected listener to set ViewPager's current item\n        OnNavigationItemSelectedListener listener = getOnNavigationItemSelectedListener();\n        mMyOnNavigationItemSelectedListener = new MyOnNavigationItemSelectedListener(viewPager, this, smoothScroll, listener);\n        super.setOnNavigationItemSelectedListener(mMyOnNavigationItemSelectedListener);\n    }\n\n    /**\n     * A {@link ViewPager.OnPageChangeListener} class which contains the\n     * necessary calls back to the provided {@link BottomNavigationViewEx} so that the tab position is\n     * kept in sync.\n     * <p>\n     * <p>This class stores the provided BottomNavigationViewEx weakly, meaning that you can use\n     * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)\n     * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and\n     * not cause a leak.\n     */\n    private static class BottomNavigationViewExOnPageChangeListener implements ViewPager.OnPageChangeListener {\n        private final WeakReference<BottomNavigationViewEx> mBnveRef;\n\n        public BottomNavigationViewExOnPageChangeListener(BottomNavigationViewEx bnve) {\n            mBnveRef = new WeakReference<>(bnve);\n        }\n\n        @Override\n        public void onPageScrollStateChanged(final int state) {\n        }\n\n        @Override\n        public void onPageScrolled(final int position, final float positionOffset,\n                                   final int positionOffsetPixels) {\n        }\n\n        @Override\n        public void onPageSelected(final int position) {\n            final BottomNavigationViewEx bnve = mBnveRef.get();\n            if (null != bnve && !isNavigationItemClicking)\n                bnve.setCurrentItem(position);\n//            Log.d(\"onPageSelected\", \"--------- position \" + position + \" ------------\");\n        }\n    }\n\n    /**\n     * Decorate OnNavigationItemSelectedListener for setupWithViewPager\n     */\n    private static class MyOnNavigationItemSelectedListener implements OnNavigationItemSelectedListener {\n        private OnNavigationItemSelectedListener listener;\n        private final WeakReference<ViewPager> viewPagerRef;\n        private boolean smoothScroll;\n        private SparseIntArray items;// used for change ViewPager selected item\n        private int previousPosition = -1;\n\n\n        MyOnNavigationItemSelectedListener(ViewPager viewPager, BottomNavigationViewEx bnve, boolean smoothScroll, OnNavigationItemSelectedListener listener) {\n            this.viewPagerRef = new WeakReference<>(viewPager);\n            this.listener = listener;\n            this.smoothScroll = smoothScroll;\n\n            // create items\n            Menu menu = bnve.getMenu();\n            int size = menu.size();\n            items = new SparseIntArray(size);\n            for (int i = 0; i < size; i++) {\n                int itemId = menu.getItem(i).getItemId();\n                items.put(itemId, i);\n            }\n        }\n\n        public void setOnNavigationItemSelectedListener(OnNavigationItemSelectedListener listener) {\n            this.listener = listener;\n        }\n\n        @Override\n        public boolean onNavigationItemSelected(@NonNull MenuItem item) {\n            int position = items.get(item.getItemId());\n            // only set item when item changed\n            if (previousPosition == position) {\n                return true;\n            }\n//            Log.d(\"onNavigationItemSelecte\", \"position:\"  + position);\n            // user listener\n            if (null != listener) {\n                boolean bool = listener.onNavigationItemSelected(item);\n                // if the selected is invalid, no need change the view pager\n                if (!bool)\n                    return false;\n            }\n\n            // change view pager\n            ViewPager viewPager = viewPagerRef.get();\n            if (null == viewPager)\n                return false;\n\n            // use isNavigationItemClicking flag to avoid `ViewPager.OnPageChangeListener` trigger\n            isNavigationItemClicking = true;\n            viewPager.setCurrentItem(items.get(item.getItemId()), smoothScroll);\n            isNavigationItemClicking = false;\n\n            // update previous position\n            previousPosition = position;\n\n            return true;\n        }\n\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    public void enableShiftingMode(int position, boolean enable) {\n        getBottomNavigationItemView(position).setShifting(enable);\n    }\n    @SuppressLint(\"RestrictedApi\")\n    public void setItemBackground(int position, int background) {\n        getBottomNavigationItemView(position).setItemBackground(background);\n    }\n    @SuppressLint(\"RestrictedApi\")\n    public void setIconTintList(int position, ColorStateList tint) {\n        getBottomNavigationItemView(position).setIconTintList(tint);\n    }\n    @SuppressLint(\"RestrictedApi\")\n    public void setTextTintList(int position, ColorStateList tint) {\n        getBottomNavigationItemView(position).setTextColor(tint);\n    }\n\n    /**\n     * set margin top for all icons\n     *\n     * @param marginTop in px\n     */\n    public void setIconsMarginTop(int marginTop) {\n        for (int i = 0; i < getItemCount(); i++) {\n            setIconMarginTop(i, marginTop);\n        }\n    }\n\n    /**\n     * set margin top for icon\n     *\n     * @param position\n     * @param marginTop in px\n     */\n    @SuppressLint(\"RestrictedApi\")\n    public void setIconMarginTop(int position, int marginTop) {\n        /*\n        1. BottomNavigationItemView\n        2. private final int mDefaultMargin;\n         */\n        BottomNavigationItemView itemView = getBottomNavigationItemView(position);\n        setField(BottomNavigationItemView.class, itemView, \"defaultMargin\", marginTop);\n        mMenuView.updateMenuView();\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/res/color/selector_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n    <item android:state_pressed=\"true\" android:color=\"@color/button_pressed\"/>\r\n    <item android:state_focused=\"true\" android:color=\"@color/button_pressed\"/>\r\n    <item android:state_selected=\"true\" android:color=\"@color/button_pressed\"/>\r\n    <item android:color=\"@color/colorPrimary\"/>\r\n</selector>"
  },
  {
    "path": "app/src/main/res/color/selector_nav_item_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/colorPrimary\" android:state_checked=\"true\"/>\n    <item android:color=\"@color/colorPrimary\" android:state_pressed=\"true\"/>\n    <item android:color=\"@color/colorNavText\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_complete.xml",
    "content": "<vector android:height=\"24dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1024\" android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M511.96,0C229.64,0 0,229.7 0,512c0,282.3 229.64,512 511.96,512 282.32,0 512.04,-229.7 512.04,-512C1024,229.7 794.28,0 511.96,0zM511.97,932.45c-231.99,0 -420.77,-188.61 -420.77,-420.45 0,-231.84 188.78,-420.45 420.77,-420.45 232.05,0 420.84,188.61 420.84,420.45 0,231.84 -188.79,420.45 -420.84,420.45z\"/>\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M443.73,565.2L303.48,437.51l-80.25,86.28 242.4,245.4c41.7,-106.38 173.83,-314.25 335.15,-461.94l-33.16,-52.43C591.94,362.03 501.02,497.3 443.73,565.2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_setting.xml",
    "content": "<vector android:height=\"24dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1024\" android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M512,0C229.22,0 0,229.22 0,512c0,282.77 229.22,512 512,512 282.75,0 512,-229.23 512,-512C1024,229.22 794.75,0 512,0zM512,992C246.9,992 32,777.09 32,512 32,246.9 246.9,32 512,32c265.06,0 480,214.9 480,480C992,777.09 777.06,992 512,992z\"/>\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M512,416c-53.02,0 -96,42.98 -96,96 0,53.01 42.98,96 96,96 53.01,0 96,-42.99 96,-96C608,458.98 565.01,416 512,416zM512,576c-35.36,0 -64,-28.66 -64,-64 0,-35.34 28.64,-64 64,-64 35.34,0 64,28.66 64,64C576,547.34 547.34,576 512,576z\"/>\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M810.24,405.12c1.74,-8.16 0.32,-16.16 -3.54,-22.72l-35.02,-60.8c-3.84,-6.56 -10.08,-11.84 -17.92,-14.4 -7.84,-2.56 -16,-1.92 -23.04,1.28l-41.44,18.72c-29.44,-28.16 -65.6,-49.44 -105.92,-61.12L578.88,220.8c-0.8,-7.68 -4.18,-15.04 -10.4,-20.48 -6.08,-5.6 -13.76,-8.32 -21.44,-8.32l-70.08,0c-7.68,0 -15.36,2.72 -21.44,8.32 -6.08,5.44 -9.62,12.8 -10.4,20.48l-4.48,45.28c-40.32,11.68 -76.46,32.96 -105.92,61.12l-41.44,-18.72c-6.88,-3.2 -15.02,-3.84 -22.88,-1.28 -8,2.56 -14.08,7.84 -17.92,14.4l-35.02,60.8c-3.84,6.56 -5.46,14.56 -3.68,22.72 1.76,8 6.4,14.72 12.64,19.2l36.96,26.56c-4.8,19.52 -7.36,40 -7.36,61.12 0,21.12 2.56,41.6 7.36,61.12l-36.96,26.56c-6.24,4.46 -10.88,11.18 -12.64,19.18 -1.76,8.18 -0.16,16.18 3.68,22.72l35.02,60.82c3.84,6.54 9.92,11.82 17.92,14.38 7.84,2.56 15.84,1.92 22.88,-1.28l41.44,-18.72c29.46,28.16 65.62,49.44 105.92,61.12l4.48,45.28c0.8,7.66 4.34,15.02 10.4,20.48 6.08,5.6 13.74,8.32 21.44,8.32l70.08,0c7.66,0 15.36,-2.72 21.44,-8.32 6.24,-5.46 9.62,-12.82 10.4,-20.48l4.46,-45.28c40.34,-11.68 76.48,-32.98 105.92,-61.12l41.44,18.72c7.04,3.2 15.2,3.84 23.04,1.28s14.08,-7.84 17.92,-14.38l35.02,-60.82c3.84,-6.54 5.28,-14.54 3.54,-22.72 -1.76,-8 -6.4,-14.72 -12.64,-19.18l-36.98,-26.56c4.82,-19.54 7.36,-40 7.36,-61.12 0,-21.12 -2.54,-41.6 -7.36,-61.12l36.98,-26.56C803.82,419.84 808.46,413.12 810.24,405.12zM771.98,394.56c-0.8,4 -3.02,7.36 -6.08,9.6l-16.46,12.16 -26.74,19.52c4.18,11.36 7.38,23.2 9.62,35.36 2.38,13.28 3.66,26.88 3.66,40.8s-1.28,27.52 -3.66,40.8c-2.24,12.16 -5.44,24 -9.62,35.36l26.74,19.54 16.46,12.16c3.04,2.24 5.28,5.6 6.08,9.6 0.98,4 0.18,8 -1.74,11.2l-17.62,30.72c-1.92,3.2 -4.96,5.92 -8.96,7.2 -3.84,1.28 -7.84,0.96 -11.36,-0.64l-18.56,-8.16 -30.56,-13.46c-0.16,0.34 -0.48,0.64 -0.96,0.98 -0.34,0.32 -0.64,0.8 -0.98,1.12l-0.16,0.16c-0.16,0.16 -0.32,0.32 -0.32,0.48 -3.2,3.82 -6.4,7.36 -10.1,10.88 -3.82,4 -7.82,7.84 -12.16,11.36 -0.46,0.48 -0.8,0.8 -1.28,1.12 -20.64,17.6 -44.46,31.52 -70.38,40.64 -11.36,4.16 -23.2,7.36 -35.36,9.44l-3.68,32.96L545.6,785.76c-0.48,3.82 -2.26,7.36 -5.28,10.24 -3.04,2.72 -6.88,4 -10.56,4l-35.36,0c-3.84,0 -7.68,-1.28 -10.72,-4 -3.04,-2.88 -4.8,-6.4 -5.28,-10.24l-2.24,-20.34 -3.68,-32.96c-12.16,-2.08 -24,-5.28 -35.36,-9.44 -25.92,-9.12 -49.76,-23.04 -70.4,-40.64 -0.46,-0.34 -0.8,-0.64 -1.26,-1.12 -4.34,-3.52 -8.34,-7.36 -12.18,-11.36 -4.46,-4.34 -8.64,-8.98 -12.32,-13.62l-30.72,13.46 -18.56,8.16c-3.36,1.6 -7.52,1.92 -11.36,0.64s-7.04,-4 -8.8,-7.2l-17.76,-30.72c-1.92,-3.2 -2.56,-7.2 -1.76,-11.2 0.82,-4 3.04,-7.36 6.1,-9.6l16.46,-12.16 26.72,-19.54c-4.16,-11.36 -7.36,-23.2 -9.6,-35.36 -2.4,-13.28 -3.68,-26.88 -3.68,-40.8s1.28,-27.52 3.68,-40.8c2.24,-12.16 5.44,-24 9.6,-35.36l-26.72,-19.68 -16.32,-12c-3.2,-2.24 -5.44,-5.6 -6.26,-9.6 -0.8,-4 -0.16,-8 1.76,-11.2l17.76,-30.72c1.74,-3.2 4.96,-5.92 8.8,-7.2s8,-0.96 11.36,0.64l18.72,8.16 30.56,13.28c0,-0.16 0.32,-0.48 0.64,-0.64 0.16,-0.32 0.48,-0.48 0.8,-0.8 0.48,-0.48 0.8,-1.12 1.28,-1.6 2.88,-3.68 6.24,-7.04 9.6,-10.4 3.84,-4 7.84,-7.84 12.18,-11.36 0.46,-0.48 0.8,-0.8 1.26,-1.12 20.64,-17.6 44.48,-31.52 70.4,-40.64 11.36,-4.16 23.2,-7.36 35.36,-9.44l3.68,-32.96 2.24,-20.32c0.48,-3.84 2.24,-7.36 5.28,-10.24 3.02,-2.72 6.88,-4 10.72,-4l35.36,0c3.68,0 7.52,1.28 10.56,4 3.02,2.88 4.8,6.4 5.28,10.24l2.24,20.32 3.68,32.96c12.16,2.08 24,5.28 35.36,9.44 25.92,9.12 49.74,23.04 70.38,40.64 0.48,0.32 0.82,0.64 1.28,1.12 2.26,1.76 4.16,3.52 6.26,5.6 2.08,1.76 4,3.68 5.9,5.76 3.38,3.2 6.56,6.4 9.28,9.92 0.18,0.16 0.34,0.48 0.48,0.64 1.12,0.96 1.92,1.92 2.72,2.88l30.4,-13.28 18.72,-8.16c3.52,-1.6 7.52,-1.92 11.36,-0.64 4,1.28 7.02,4 8.96,7.2l17.62,30.72C772.16,386.56 772.96,390.56 771.98,394.56z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_todo.xml",
    "content": "<vector android:height=\"24dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1024\" android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M512,32C246.91,32 32,246.91 32,512c0,265.09 214.91,480 480,480s480,-214.91 480,-480c0,-265.09 -214.91,-480 -480,-480zM512,928C284.03,928 96,739.97 96,512S284.03,96 512,96s416,188.03 416,416 -188.03,416 -416,416z\"/>\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M631.9,259.1l-33.22,33.25 127.1,127.14 33.22,-33.25s50.3,-50.34 -13.25,-113.89c-63.55,-63.58 -113.86,-13.25 -113.86,-13.25zM303.58,587.55l-42.37,169.5 169.47,-42.37 254.75,-254.85 -127.1,-127.14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/selector_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n    <item android:state_pressed=\"true\">\r\n        <shape android:shape=\"rectangle\">\r\n            <corners android:radius=\"4dp\"/>\r\n            <solid android:color=\"@color/button_pressed\"/>\r\n        </shape>\r\n    </item>\r\n    <item android:state_focused=\"true\">\r\n        <shape android:shape=\"rectangle\">\r\n            <corners android:radius=\"4dp\"/>\r\n            <solid android:color=\"@color/button_pressed\"/>\r\n        </shape>\r\n    </item>\r\n    <item>\r\n        <shape android:shape=\"rectangle\">\r\n            <corners android:radius=\"4dp\"/>\r\n            <solid android:color=\"@color/colorPrimary\"/>\r\n        </shape>\r\n    </item>\r\n</selector>"
  },
  {
    "path": "app/src/main/res/layout/activity_add_todo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <include layout=\"@layout/title_bar_layout\"/>\r\n\r\n\r\n    <android.support.design.widget.TextInputEditText\r\n        android:id=\"@+id/todo_name\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textColorHint=\"#B5B3B5\"\r\n        android:textColor=\"@android:color/black\"\r\n        android:hint=\"@string/input_todo_name\"\r\n        android:layout_marginTop=\"@dimen/dp_10\"\r\n        android:layout_marginLeft=\"@dimen/dp_10\"\r\n        android:layout_marginRight=\"@dimen/dp_10\"\r\n        android:textSize=\"16sp\"\r\n        android:background=\"@null\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\"\r\n        android:layout_marginTop=\"5dp\"/>\r\n    <android.support.design.widget.TextInputEditText\r\n        android:id=\"@+id/todo_des\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textColorHint=\"#B5B3B5\"\r\n        android:textColor=\"@android:color/black\"\r\n        android:hint=\"@string/input_todo_des\"\r\n        android:layout_marginTop=\"@dimen/dp_10\"\r\n        android:layout_marginLeft=\"@dimen/dp_10\"\r\n        android:layout_marginRight=\"@dimen/dp_10\"\r\n        android:background=\"@null\"\r\n        android:textSize=\"16sp\"\r\n        android:gravity=\"left|top\"\r\n        android:minLines=\"8\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\"\r\n        android:layout_marginTop=\"5dp\"/>\r\n    <TextView\r\n        android:id=\"@+id/todo_date\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:padding=\"@dimen/dp_10\"\r\n        android:drawableLeft=\"@drawable/date\"\r\n        android:drawablePadding=\"5dp\"\r\n        android:textSize=\"16sp\"\r\n        android:textColor=\"@android:color/black\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\"\r\n        android:layout_marginTop=\"1dp\"/>\r\n\r\n    <Button\r\n        android:id=\"@+id/save_todo\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textColor=\"@android:color/white\"\r\n        android:textSize=\"16sp\"\r\n        android:text=\"@string/save\"\r\n        android:background=\"@drawable/selector_button\"\r\n        android:layout_marginTop=\"30dp\"\r\n        android:layout_marginLeft=\"10dp\"\r\n        android:layout_marginRight=\"10dp\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_edit_todo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <include layout=\"@layout/title_bar_layout\"/>\r\n\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:orientation=\"horizontal\"\r\n        android:padding=\"10dp\">\r\n        <TextView\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:textSize=\"16sp\"\r\n            android:textColor=\"#B5B3B5\"\r\n            android:text=\"@string/todo_title\"/>\r\n        <android.support.design.widget.TextInputEditText\r\n            android:id=\"@+id/todo_name\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_weight=\"1\"\r\n            android:textColorHint=\"#B5B3B5\"\r\n            android:textColor=\"@android:color/black\"\r\n            android:hint=\"@string/input_todo_name\"\r\n            android:layout_marginLeft=\"5dp\"\r\n            android:textSize=\"16sp\"\r\n            android:background=\"@null\" />\r\n    </LinearLayout>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\" />\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:orientation=\"horizontal\"\r\n        android:padding=\"10dp\">\r\n        <TextView\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:textSize=\"16sp\"\r\n            android:textColor=\"#B5B3B5\"\r\n            android:gravity=\"top|left\"\r\n            android:text=\"@string/des\"/>\r\n        <android.support.design.widget.TextInputEditText\r\n            android:id=\"@+id/todo_des\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_weight=\"1\"\r\n            android:textColorHint=\"#B5B3B5\"\r\n            android:textColor=\"@android:color/black\"\r\n            android:hint=\"@string/input_todo_des\"\r\n            android:layout_marginLeft=\"5dp\"\r\n            android:background=\"@null\"\r\n            android:textSize=\"16sp\"\r\n            android:gravity=\"left|top\"\r\n            android:minLines=\"8\"/>\r\n    </LinearLayout>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\"\r\n        android:layout_marginTop=\"5dp\"/>\r\n    <TextView\r\n        android:id=\"@+id/todo_date\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:padding=\"@dimen/dp_10\"\r\n        android:drawableLeft=\"@drawable/date\"\r\n        android:drawablePadding=\"5dp\"\r\n        android:textSize=\"16sp\"\r\n        android:textColor=\"@android:color/black\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#B5B3B5\"\r\n        android:layout_marginTop=\"1dp\"/>\r\n\r\n    <Button\r\n        android:id=\"@+id/save_todo\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textColor=\"@android:color/white\"\r\n        android:textSize=\"16sp\"\r\n        android:text=\"@string/save\"\r\n        android:background=\"@drawable/selector_button\"\r\n        android:layout_marginTop=\"30dp\"\r\n        android:layout_marginLeft=\"10dp\"\r\n        android:layout_marginRight=\"10dp\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\r\n    android:gravity=\"center_horizontal\"\r\n    android:orientation=\"vertical\"\r\n    android:background=\"@android:color/white\">\r\n    <ScrollView\r\n        android:id=\"@+id/login_form\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"match_parent\"\r\n        android:padding=\"20dp\"\r\n        android:scrollbars=\"none\">\r\n\r\n        <LinearLayout\r\n            android:id=\"@+id/email_login_form\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"vertical\">\r\n\r\n            <ImageView\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_gravity=\"center_horizontal\"\r\n                android:src=\"@mipmap/ic_launcher\"\r\n                android:layout_margin=\"30dp\"/>\r\n\r\n            <android.support.design.widget.TextInputLayout\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_marginTop=\"5dp\">\r\n\r\n                <AutoCompleteTextView\r\n                    android:id=\"@+id/account\"\r\n                    android:layout_width=\"match_parent\"\r\n                    android:layout_height=\"wrap_content\"\r\n                    android:hint=\"@string/prompt_account\"\r\n                    android:textColorHint=\"#767676\"\r\n                    android:inputType=\"text|textAutoComplete\"\r\n                    android:maxLines=\"1\"\r\n                    android:singleLine=\"true\"/>\r\n\r\n            </android.support.design.widget.TextInputLayout>\r\n\r\n            <android.support.design.widget.TextInputLayout\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_marginTop=\"5dp\">\r\n\r\n                <EditText\r\n                    android:id=\"@+id/password\"\r\n                    android:layout_width=\"match_parent\"\r\n                    android:layout_height=\"wrap_content\"\r\n                    android:hint=\"@string/prompt_password\"\r\n                    android:textColorHint=\"#767676\"\r\n                    android:inputType=\"textPassword\"\r\n                    android:maxLines=\"1\"\r\n                    android:singleLine=\"true\" />\r\n\r\n            </android.support.design.widget.TextInputLayout>\r\n\r\n            <Button\r\n                android:id=\"@+id/login\"\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textColor=\"@android:color/white\"\r\n                android:textSize=\"16sp\"\r\n                android:text=\"@string/sign_in\"\r\n                android:background=\"@drawable/selector_button\"\r\n                android:layout_marginTop=\"30dp\"/>\r\n\r\n            <TextView\r\n                android:id=\"@+id/regitster\"\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textSize=\"14sp\"\r\n                android:textColor=\"@color/selector_color\"\r\n                android:layout_gravity=\"center_horizontal\"\r\n                android:text=\"@string/register_message\"\r\n                android:layout_marginTop=\"20dp\" />\r\n        </LinearLayout>\r\n    </ScrollView>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <include layout=\"@layout/title_bar_layout\"/>\r\n    <android.support.v4.view.ViewPager\r\n        android:id=\"@+id/view_pager\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0dp\"\r\n        android:layout_weight=\"1\"/>\r\n    <com.wj.android.todo.widget.BottomNavigationViewEx\r\n        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\r\n        android:id=\"@+id/navigation\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        app:itemBackground=\"@color/colorNavBg\"\r\n        app:itemIconTint=\"@color/selector_nav_item_color\"\r\n        app:itemTextColor=\"@color/selector_nav_item_color\"\r\n        app:menu=\"@menu/navigation\" />\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_register.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <include layout=\"@layout/title_bar_layout\"/>\r\n    <ScrollView\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"match_parent\"\r\n        android:padding=\"20dp\"\r\n        android:scrollbars=\"none\">\r\n\r\n        <LinearLayout\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"vertical\">\r\n            <android.support.design.widget.TextInputLayout\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_marginTop=\"5dp\">\r\n\r\n                <EditText\r\n                    android:id=\"@+id/account\"\r\n                    android:layout_width=\"match_parent\"\r\n                    android:layout_height=\"wrap_content\"\r\n                    android:hint=\"@string/input_account\"\r\n                    android:textColorHint=\"#767676\"\r\n                    android:inputType=\"text|textAutoComplete\"\r\n                    android:maxLines=\"1\"\r\n                    android:singleLine=\"true\"/>\r\n\r\n            </android.support.design.widget.TextInputLayout>\r\n\r\n            <android.support.design.widget.TextInputLayout\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_marginTop=\"5dp\">\r\n\r\n                <EditText\r\n                    android:id=\"@+id/password\"\r\n                    android:layout_width=\"match_parent\"\r\n                    android:layout_height=\"wrap_content\"\r\n                    android:hint=\"@string/input_password\"\r\n                    android:textColorHint=\"#767676\"\r\n                    android:inputType=\"textPassword\"\r\n                    android:maxLines=\"1\"\r\n                    android:singleLine=\"true\" />\r\n\r\n            </android.support.design.widget.TextInputLayout>\r\n\r\n            <android.support.design.widget.TextInputLayout\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_marginTop=\"5dp\">\r\n\r\n                <EditText\r\n                    android:id=\"@+id/repassword\"\r\n                    android:layout_width=\"match_parent\"\r\n                    android:layout_height=\"wrap_content\"\r\n                    android:hint=\"@string/input_repassword\"\r\n                    android:textColorHint=\"#767676\"\r\n                    android:inputType=\"textPassword\"\r\n                    android:maxLines=\"1\"\r\n                    android:singleLine=\"true\" />\r\n\r\n            </android.support.design.widget.TextInputLayout>\r\n\r\n            <Button\r\n                android:id=\"@+id/regitster\"\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textColor=\"@android:color/white\"\r\n                android:textSize=\"16sp\"\r\n                android:text=\"@string/register\"\r\n                android:background=\"@drawable/selector_button\"\r\n                android:layout_marginTop=\"30dp\"/>\r\n\r\n        </LinearLayout>\r\n    </ScrollView>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_splash.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:id=\"@+id/root_view\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\r\n    android:background=\"@color/colorPrimary\">\r\n    <TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_gravity=\"center\"\r\n        android:drawableTop=\"@mipmap/ic_launcher\"\r\n        android:textSize=\"20sp\"\r\n        android:textColor=\"@android:color/white\"\r\n        android:textStyle=\"bold\"\r\n        android:drawablePadding=\"20dp\"\r\n        android:text=\"@string/splash_message\"/>\r\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_todo_des_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"wrap_content\"\r\n    android:padding=\"10dp\">\r\n\r\n    <TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"16sp\"\r\n        android:textColor=\"#333333\"\r\n        android:text=\"@string/todo_name\"/>\r\n    <TextView\r\n        android:id=\"@+id/todo_name\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"14sp\"\r\n        android:textColor=\"#767676\"\r\n        android:layout_marginTop=\"5dp\"\r\n        android:text=\"@string/input_todo_name\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:layout_marginTop=\"5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"16sp\"\r\n        android:textColor=\"#333333\"\r\n        android:layout_marginTop=\"@dimen/dp_10\"\r\n        android:text=\"@string/todo_des\"/>\r\n    <TextView\r\n        android:id=\"@+id/todo_content\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"14sp\"\r\n        android:textColor=\"#767676\"\r\n        android:layout_marginTop=\"5dp\"\r\n        android:text=\"@string/input_todo_des\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:layout_marginTop=\"5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_complete.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <android.support.v4.widget.SwipeRefreshLayout\r\n        android:id=\"@+id/swipe_layout\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\">\r\n        <android.support.v7.widget.RecyclerView\r\n            android:id=\"@+id/done_rv\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"/>\r\n    </android.support.v4.widget.SwipeRefreshLayout>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:layout_marginTop=\"15dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:orientation=\"vertical\"\r\n        android:background=\"@android:color/white\"\r\n        android:paddingLeft=\"15dp\"\r\n        android:paddingRight=\"15dp\">\r\n        <LinearLayout\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"horizontal\"\r\n            android:paddingTop=\"10dp\"\r\n            android:paddingBottom=\"10dp\">\r\n            <TextView\r\n                android:layout_width=\"0dp\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_weight=\"1\"\r\n                android:textColor=\"#333333\"\r\n                android:textSize=\"16sp\"\r\n                android:text=\"@string/account_name\"/>\r\n            <TextView\r\n                android:id=\"@+id/user_name\"\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textColor=\"#767676\"\r\n                android:textSize=\"16sp\"\r\n                android:drawableRight=\"@drawable/arrow_right\"\r\n                android:gravity=\"center_vertical\"\r\n                android:drawablePadding=\"10dp\"\r\n                android:text=\"wangandroid\"/>\r\n        </LinearLayout>\r\n        <View\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"0.5dp\"\r\n            android:background=\"#E4E4E4\"/>\r\n        <LinearLayout\r\n            android:id=\"@+id/modify_layout\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"horizontal\"\r\n            android:paddingTop=\"10dp\"\r\n            android:paddingBottom=\"10dp\">\r\n            <TextView\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_weight=\"1\"\r\n                android:textColor=\"#333333\"\r\n                android:textSize=\"16sp\"\r\n                android:drawableRight=\"@drawable/arrow_right\"\r\n                android:gravity=\"center_vertical\"\r\n                android:text=\"@string/modify_password\"/>\r\n        </LinearLayout>\r\n    </LinearLayout>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:layout_marginTop=\"15dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:orientation=\"vertical\"\r\n        android:background=\"@android:color/white\"\r\n        android:paddingLeft=\"15dp\"\r\n        android:paddingRight=\"15dp\">\r\n        <LinearLayout\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"horizontal\"\r\n            android:paddingTop=\"10dp\"\r\n            android:paddingBottom=\"10dp\">\r\n            <TextView\r\n                android:layout_width=\"0dp\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_weight=\"1\"\r\n                android:textColor=\"#333333\"\r\n                android:textSize=\"16sp\"\r\n                android:text=\"@string/my_github_name\"/>\r\n            <TextView\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textColor=\"#767676\"\r\n                android:textSize=\"16sp\"\r\n                android:drawableRight=\"@drawable/arrow_right\"\r\n                android:gravity=\"center_vertical\"\r\n                android:drawablePadding=\"10dp\"\r\n                android:text=\"@string/my_github_url\"/>\r\n        </LinearLayout>\r\n        <View\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"0.5dp\"\r\n            android:background=\"#E4E4E4\"/>\r\n        <LinearLayout\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:orientation=\"horizontal\"\r\n            android:paddingTop=\"10dp\"\r\n            android:paddingBottom=\"10dp\">\r\n            <TextView\r\n                android:layout_width=\"0dp\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:layout_weight=\"1\"\r\n                android:textColor=\"#333333\"\r\n                android:textSize=\"16sp\"\r\n                android:text=\"@string/api_support_name\"/>\r\n            <TextView\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:textColor=\"#767676\"\r\n                android:textSize=\"16sp\"\r\n                android:drawableRight=\"@drawable/arrow_right\"\r\n                android:gravity=\"center_vertical\"\r\n                android:drawablePadding=\"10dp\"\r\n                android:text=\"@string/api_support_url\"/>\r\n        </LinearLayout>\r\n    </LinearLayout>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:layout_marginTop=\"30dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n    <TextView\r\n        android:id=\"@+id/logout\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:gravity=\"center\"\r\n        android:textColor=\"@android:color/holo_red_light\"\r\n        android:textSize=\"16sp\"\r\n        android:paddingTop=\"10dp\"\r\n        android:paddingBottom=\"10dp\"\r\n        android:background=\"@android:color/white\"\r\n        android:text=\"退出登录\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_todo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\">\r\n    <android.support.v4.widget.SwipeRefreshLayout\r\n        android:id=\"@+id/swipe_layout\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\">\r\n        <android.support.v7.widget.RecyclerView\r\n            android:id=\"@+id/todo_rv\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"/>\r\n    </android.support.v4.widget.SwipeRefreshLayout>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/title_bar_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"wrap_content\"\r\n    android:background=\"@color/colorPrimary\">\r\n    <ImageView\r\n        android:id=\"@+id/back\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"match_parent\"\r\n        android:padding=\"10dp\"\r\n        android:src=\"@drawable/back\"\r\n        android:layout_gravity=\"center_vertical|left\"\r\n        android:visibility=\"invisible\"/>\r\n    <TextView\r\n        android:id=\"@+id/title\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textColor=\"@android:color/white\"\r\n        android:textSize=\"18sp\"\r\n        android:layout_gravity=\"center\"/>\r\n    <ImageView\r\n        android:id=\"@+id/add_todo\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"match_parent\"\r\n        android:padding=\"10dp\"\r\n        android:src=\"@drawable/add_todo\"\r\n        android:layout_gravity=\"center_vertical|right\"\r\n        android:visibility=\"invisible\"/>\r\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/todo_item_head.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"wrap_content\"\r\n    android:orientation=\"vertical\">\r\n    <TextView\r\n        android:id=\"@+id/todo_head\"\r\n        android:orientation=\"vertical\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:background=\"#F7FFFD\"\r\n        android:padding=\"6dp\"\r\n        android:textSize=\"14sp\"\r\n        android:textColor=\"@color/colorPrimary\"\r\n        android:text=\"2018-08-09\"/>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/todo_item_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:orientation=\"vertical\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"wrap_content\">\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:orientation=\"horizontal\"\r\n        android:padding=\"@dimen/dp_10\">\r\n        <ImageView\r\n            android:id=\"@+id/item_complete\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:padding=\"2dp\"\r\n            android:src=\"@drawable/complete_todo\"/>\r\n        <LinearLayout\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_weight=\"1\"\r\n            android:orientation=\"vertical\"\r\n            android:layout_marginLeft=\"10dp\"\r\n            android:layout_marginRight=\"10dp\">\r\n            <TextView\r\n                android:id=\"@+id/item_name\"\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:singleLine=\"true\"\r\n                android:textSize=\"16sp\"\r\n                android:textColor=\"#333333\" />\r\n            <TextView\r\n                android:id=\"@+id/item_des\"\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:singleLine=\"true\"\r\n                android:textSize=\"12sp\"\r\n                android:textColor=\"#767676\"\r\n                android:layout_marginTop=\"3dp\"\r\n                android:visibility=\"gone\"/>\r\n            <TextView\r\n                android:id=\"@+id/item_done_time\"\r\n                android:layout_width=\"wrap_content\"\r\n                android:layout_height=\"wrap_content\"\r\n                android:singleLine=\"true\"\r\n                android:textSize=\"12sp\"\r\n                android:textColor=\"#767676\"\r\n                android:layout_marginTop=\"5dp\"\r\n                android:visibility=\"gone\"/>\r\n        </LinearLayout>\r\n        <ImageView\r\n            android:id=\"@+id/item_delete\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:padding=\"2dp\"\r\n            android:src=\"@drawable/delete_todo\"/>\r\n    </LinearLayout>\r\n    <View\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"0.5dp\"\r\n        android:background=\"#E4E4E4\"/>\r\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/menu/navigation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n    <item\r\n        android:id=\"@+id/navigation_todo\"\r\n        android:icon=\"@drawable/ic_todo\"\r\n        android:title=\"@string/to_do_list\" />\r\n    <item\r\n        android:id=\"@+id/navigation_complete\"\r\n        android:icon=\"@drawable/ic_complete\"\r\n        android:title=\"@string/complete_list\" />\r\n    <item\r\n        android:id=\"@+id/navigation_setting\"\r\n        android:icon=\"@drawable/ic_setting\"\r\n        android:title=\"@string/setting\" />\r\n</menu>\r\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n    <color name=\"colorPrimary\">#2AB27F</color>\r\n    <color name=\"colorPrimaryDark\">#2AB27F</color>\r\n    <color name=\"colorAccent\">#FF4081</color>\r\n    <color name=\"colorNavText\">#828282</color>\r\n    <color name=\"colorNavBg\">#f8f8f8</color>\r\n    <color name=\"button_pressed\">#449577</color>\r\n    <color name=\"border_gray\">#ADADAD</color>\r\n    <color name=\"done_todo_date\">#E4A340</color>\r\n</resources>\r\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\r\n    <!-- Default screen margins, per the Android Design guidelines. -->\r\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\r\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\r\n</resources>\r\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\r\n    <string name=\"app_name\">TODO</string>\r\n    <string name=\"loading\">正在加载中……</string>\r\n    <string name=\"service_error\">服务器开小差，请稍候重试</string>\r\n\r\n    <!-- Strings related to login -->\r\n    <string name=\"prompt_account\">账号</string>\r\n    <string name=\"prompt_password\">密码</string>\r\n    <string name=\"sign_in\">登录</string>\r\n    <string name=\"register_message\">还没账号，点击注册</string>\r\n    <string name=\"input_account\">请输入账号</string>\r\n    <string name=\"input_password\">请输入密码</string>\r\n    <string name=\"to_do_list\">待办清单</string>\r\n    <string name=\"complete_list\">完成清单</string>\r\n    <string name=\"splash_message\">高效率&#160;慢生活</string>\r\n    <string name=\"setting\">设置</string>\r\n    <string name=\"register\">注册</string>\r\n    <string name=\"input_repassword\">请输入确认密码</string>\r\n    <string name=\"valid_repassword\">两次输入的密码不一致</string>\r\n    <string name=\"register_success\">注册成功</string>\r\n    <string name=\"input_todo_name\">输入待办名称，如：准备演示PPT</string>\r\n    <string name=\"input_todo_des\">输入待办详情，非必填，如：\\n1、制作演示PPT\\n2、预定会议室\\n3、通知参加会议人员\\n4、提前10分钟准备会议环境</string>\r\n    <string name=\"save\">保存</string>\r\n    <string name=\"add_todo\">添加待办清单</string>\r\n    <string name=\"update_todo\">清单详细</string>\r\n    <string name=\"input_todo_name_toast\">请输入待办名称</string>\r\n    <string name=\"add_todo_success\">添加成功</string>\r\n    <string name=\"update_todo_success\">更新成功</string>\r\n    <string name=\"delete_todo_success\">删除成功</string>\r\n    <string name=\"done_todo_success\">标记完成</string>\r\n    <string name=\"notdo_todo_success\">撤销完成</string>\r\n    <string name=\"done_todo_date\">完成时间：%s</string>\r\n    <string name=\"account_name\">账号信息</string>\r\n    <string name=\"modify_password\">修改密码</string>\r\n    <string name=\"my_github_url\">https://github.com/wjwang0914</string>\r\n    <string name=\"my_github_name\">github</string>\r\n    <string name=\"api_support_name\">API</string>\r\n    <string name=\"api_support_url\">http://www.wanandroid.com/</string>\r\n    <string name=\"no_text\">本宝宝暂时还没有想到详细信息</string>\r\n    <string name=\"todo_name\">待办名称</string>\r\n    <string name=\"todo_des\">待办详细</string>\r\n    <string name=\"delete_todo\">删除待办清单</string>\r\n    <string name=\"sure_delete_todo\">请确认是否删除？</string>\r\n    <string name=\"cancel\">取消</string>\r\n    <string name=\"ok\">确定</string>\r\n    <string name=\"todo_title\">标题：</string>\r\n    <string name=\"des\">详情：</string>\r\n</resources>\r\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\r\n\r\n    <!-- Base application theme. -->\r\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\r\n        <!-- Customize your theme here. -->\r\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\r\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\r\n        <item name=\"colorAccent\">@color/colorAccent</item>\r\n    </style>\r\n\r\n</resources>\r\n"
  },
  {
    "path": "app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<network-security-config>\r\n    <base-config cleartextTrafficPermitted=\"true\" />\r\n</network-security-config>"
  },
  {
    "path": "app/src/test/java/com/wj/android/todo/ExampleUnitTest.java",
    "content": "package com.wj.android.todo;\r\n\r\nimport org.junit.Test;\r\n\r\nimport static org.junit.Assert.*;\r\n\r\n/**\r\n * Example local unit test, which will execute on the development machine (host).\r\n *\r\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\r\n */\r\npublic class ExampleUnitTest {\r\n    @Test\r\n    public void addition_isCorrect() {\r\n        assertEquals(4, 2 + 2);\r\n    }\r\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\r\n\r\nbuildscript {\r\n    \r\n    repositories {\r\n        google()\r\n        jcenter()\r\n    }\r\n    dependencies {\r\n        classpath 'com.android.tools.build:gradle:7.2.1'\r\n        \r\n\r\n        // NOTE: Do not place your application dependencies here; they belong\r\n        // in the individual module build.gradle files\r\n    }\r\n}\r\n\r\nallprojects {\r\n    repositories {\r\n        google()\r\n        jcenter()\r\n        maven { url \"https://jitpack.io\" }\r\n    }\r\n}\r\n\r\ntask clean(type: Delete) {\r\n    delete rootProject.buildDir\r\n}\r\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Aug 07 10:46:04 CST 2018\r\ndistributionBase=GRADLE_USER_HOME\r\ndistributionPath=wrapper/dists\r\nzipStoreBase=GRADLE_USER_HOME\r\nzipStorePath=wrapper/dists\r\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.3.3-all.zip\r\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\r\n# IDE (e.g. Android Studio) users:\r\n# Gradle settings configured through the IDE *will override*\r\n# any settings specified in this file.\r\n# For more details on how to configure your build environment visit\r\n# http://www.gradle.org/docs/current/userguide/build_environment.html\r\n# Specifies the JVM arguments used for the daemon process.\r\n# The setting is particularly useful for tweaking memory settings.\r\norg.gradle.jvmargs=-Xmx1536m\r\n# When configured, Gradle will run in incubating parallel mode.\r\n# This option should only be used with decoupled projects. More details, visit\r\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\r\n# org.gradle.parallel=true\r\n\r\n#ýӿڻ-ʽ\r\nFORMAL_BASE_URL = \"http://www.wanandroid.com\"\r\n\r\n#ýӿڻ-Ի\r\nTEST_BASE_URL = \"http://www.wanandroid.com\"\r\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\r\n\r\n##############################################################################\r\n##\r\n##  Gradle start up script for UN*X\r\n##\r\n##############################################################################\r\n\r\n# Attempt to set APP_HOME\r\n# Resolve links: $0 may be a link\r\nPRG=\"$0\"\r\n# Need this for relative symlinks.\r\nwhile [ -h \"$PRG\" ] ; do\r\n    ls=`ls -ld \"$PRG\"`\r\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\r\n    if expr \"$link\" : '/.*' > /dev/null; then\r\n        PRG=\"$link\"\r\n    else\r\n        PRG=`dirname \"$PRG\"`\"/$link\"\r\n    fi\r\ndone\r\nSAVED=\"`pwd`\"\r\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\r\nAPP_HOME=\"`pwd -P`\"\r\ncd \"$SAVED\" >/dev/null\r\n\r\nAPP_NAME=\"Gradle\"\r\nAPP_BASE_NAME=`basename \"$0\"`\r\n\r\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nDEFAULT_JVM_OPTS=\"\"\r\n\r\n# Use the maximum available, or set MAX_FD != -1 to use that value.\r\nMAX_FD=\"maximum\"\r\n\r\nwarn () {\r\n    echo \"$*\"\r\n}\r\n\r\ndie () {\r\n    echo\r\n    echo \"$*\"\r\n    echo\r\n    exit 1\r\n}\r\n\r\n# OS specific support (must be 'true' or 'false').\r\ncygwin=false\r\nmsys=false\r\ndarwin=false\r\nnonstop=false\r\ncase \"`uname`\" in\r\n  CYGWIN* )\r\n    cygwin=true\r\n    ;;\r\n  Darwin* )\r\n    darwin=true\r\n    ;;\r\n  MINGW* )\r\n    msys=true\r\n    ;;\r\n  NONSTOP* )\r\n    nonstop=true\r\n    ;;\r\nesac\r\n\r\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\r\n\r\n# Determine the Java command to use to start the JVM.\r\nif [ -n \"$JAVA_HOME\" ] ; then\r\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\r\n        # IBM's JDK on AIX uses strange locations for the executables\r\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\r\n    else\r\n        JAVACMD=\"$JAVA_HOME/bin/java\"\r\n    fi\r\n    if [ ! -x \"$JAVACMD\" ] ; then\r\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\r\n\r\nPlease set the JAVA_HOME variable in your environment to match the\r\nlocation of your Java installation.\"\r\n    fi\r\nelse\r\n    JAVACMD=\"java\"\r\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\n\r\nPlease set the JAVA_HOME variable in your environment to match the\r\nlocation of your Java installation.\"\r\nfi\r\n\r\n# Increase the maximum file descriptors if we can.\r\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\r\n    MAX_FD_LIMIT=`ulimit -H -n`\r\n    if [ $? -eq 0 ] ; then\r\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\r\n            MAX_FD=\"$MAX_FD_LIMIT\"\r\n        fi\r\n        ulimit -n $MAX_FD\r\n        if [ $? -ne 0 ] ; then\r\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\r\n        fi\r\n    else\r\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\r\n    fi\r\nfi\r\n\r\n# For Darwin, add options to specify how the application appears in the dock\r\nif $darwin; then\r\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\r\nfi\r\n\r\n# For Cygwin, switch paths to Windows format before running java\r\nif $cygwin ; then\r\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\r\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\r\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\r\n\r\n    # We build the pattern for arguments to be converted via cygpath\r\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\r\n    SEP=\"\"\r\n    for dir in $ROOTDIRSRAW ; do\r\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\r\n        SEP=\"|\"\r\n    done\r\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\r\n    # Add a user-defined pattern to the cygpath arguments\r\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\r\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\r\n    fi\r\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\r\n    i=0\r\n    for arg in \"$@\" ; do\r\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\r\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\r\n\r\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\r\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\r\n        else\r\n            eval `echo args$i`=\"\\\"$arg\\\"\"\r\n        fi\r\n        i=$((i+1))\r\n    done\r\n    case $i in\r\n        (0) set -- ;;\r\n        (1) set -- \"$args0\" ;;\r\n        (2) set -- \"$args0\" \"$args1\" ;;\r\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\r\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\r\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\r\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\r\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\r\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\r\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\r\n    esac\r\nfi\r\n\r\n# Escape application args\r\nsave () {\r\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\r\n    echo \" \"\r\n}\r\nAPP_ARGS=$(save \"$@\")\r\n\r\n# Collect all arguments for the java command, following the shell quoting and substitution rules\r\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\r\n\r\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\r\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\r\n  cd \"$(dirname \"$0\")\"\r\nfi\r\n\r\nexec \"$JAVACMD\" \"$@\"\r\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\r\ninclude ':wj-http'\r\n"
  }
]