Преглед на файлове

Merge branch 'tabbed' of APP_World/ShiBei into master

合并到主分支
Jason преди 5 години
родител
ревизия
456b99aac6
променени са 100 файла, в които са добавени 5208 реда и са изтрити 1210 реда
  1. 69 9
      app/build.gradle
  2. BIN
      app/libs/jiaozivideoplayer-7.4.2.aar
  3. 69 15
      app/src/main/AndroidManifest.xml
  4. 0 114
      app/src/main/java/com/itant/shibei/adapter/GoodsAdapter.java
  5. 0 70
      app/src/main/java/com/itant/shibei/adapter/TemplateAdapter.java
  6. 104 0
      app/src/main/java/com/itant/shibei/base/ApiService.java
  7. 2 4
      app/src/main/java/com/itant/shibei/base/BaseShiBeiActivity.java
  8. 11 0
      app/src/main/java/com/itant/shibei/base/ITopActionListener.java
  9. 5 0
      app/src/main/java/com/itant/shibei/base/ItemLongClickListener.java
  10. 25 143
      app/src/main/java/com/itant/shibei/bean/BeiUser.java
  11. 0 234
      app/src/main/java/com/itant/shibei/bean/GoodsBean.java
  12. 17 0
      app/src/main/java/com/itant/shibei/bean/SystemBean.java
  13. 0 88
      app/src/main/java/com/itant/shibei/bean/TemplateBean.java
  14. 17 0
      app/src/main/java/com/itant/shibei/bean/UpgradeBean.java
  15. 16 0
      app/src/main/java/com/itant/shibei/common/ICommonView.java
  16. 5 4
      app/src/main/java/com/itant/shibei/common/ConstantString.java
  17. 14 4
      app/src/main/java/com/itant/shibei/common/ConstantUrl.java
  18. 66 0
      app/src/main/java/com/itant/shibei/manager/PreferenceManager.java
  19. 21 3
      app/src/main/java/com/itant/shibei/ui/mine/login/UserInfoManager.java
  20. 173 0
      app/src/main/java/com/itant/shibei/net/RetrofitHelper.java
  21. 82 0
      app/src/main/java/com/itant/shibei/tool/Base64Tool.java
  22. 58 43
      app/src/main/java/com/itant/shibei/tool/DataTool.java
  23. 42 0
      app/src/main/java/com/itant/shibei/tool/MD5Tool.java
  24. 57 2
      app/src/main/java/com/itant/shibei/tool/StringTool.java
  25. 10 0
      app/src/main/java/com/itant/shibei/tool/SystemTool.java
  26. 51 0
      app/src/main/java/com/itant/shibei/tool/TimeTool.java
  27. 28 0
      app/src/main/java/com/itant/shibei/ui/BeiApplication.java
  28. 2 2
      app/src/main/java/com/itant/shibei/ui/home/HomeFragment.java
  29. 7 8
      app/src/main/java/com/itant/shibei/ui/MainActivity.java
  30. 23 1
      app/src/main/java/com/itant/shibei/ui/MainViewModel.java
  31. 165 0
      app/src/main/java/com/itant/shibei/ui/TabActivity.java
  32. 1 1
      app/src/main/java/com/itant/shibei/ui/function/FunctionFragment.java
  33. 0 17
      app/src/main/java/com/itant/shibei/ui/function/template/ITemplateView.java
  34. 0 65
      app/src/main/java/com/itant/shibei/ui/function/template/TemplateFragment.java
  35. 0 28
      app/src/main/java/com/itant/shibei/ui/function/template/TemplatePresenter.java
  36. 0 77
      app/src/main/java/com/itant/shibei/ui/home/GoodsFragment.java
  37. 0 25
      app/src/main/java/com/itant/shibei/ui/home/GoodsPresenter.java
  38. 0 17
      app/src/main/java/com/itant/shibei/ui/home/IGoodsView.java
  39. 97 0
      app/src/main/java/com/itant/shibei/ui/home/about/AboutFragment.java
  40. 49 0
      app/src/main/java/com/itant/shibei/ui/home/about/upgrade/UpgradePresenter.java
  41. 68 0
      app/src/main/java/com/itant/shibei/ui/home/coupon/CouponBean.java
  42. 17 0
      app/src/main/java/com/itant/shibei/ui/home/coupon/ITemplateView.java
  43. 129 0
      app/src/main/java/com/itant/shibei/ui/home/coupon/TemplateAdapter.java
  44. 238 0
      app/src/main/java/com/itant/shibei/ui/home/coupon/TemplateFragment.java
  45. 71 0
      app/src/main/java/com/itant/shibei/ui/home/coupon/TemplatePresenter.java
  46. 179 0
      app/src/main/java/com/itant/shibei/ui/home/goods/GoodsAdapter.java
  47. 136 0
      app/src/main/java/com/itant/shibei/ui/home/goods/GoodsBean.java
  48. 243 0
      app/src/main/java/com/itant/shibei/ui/home/goods/GoodsFragment.java
  49. 68 0
      app/src/main/java/com/itant/shibei/ui/home/goods/GoodsPresenter.java
  50. 17 0
      app/src/main/java/com/itant/shibei/ui/home/goods/IGoodsView.java
  51. 62 0
      app/src/main/java/com/itant/shibei/ui/home/goods/play/VideoPlayActivity.java
  52. 16 0
      app/src/main/java/com/itant/shibei/ui/home/search/ISearchView.java
  53. 267 0
      app/src/main/java/com/itant/shibei/ui/home/search/SearchActivity.java
  54. 47 0
      app/src/main/java/com/itant/shibei/ui/home/search/SearchPresenter.java
  55. 15 0
      app/src/main/java/com/itant/shibei/ui/home/tool/ISystemView.java
  56. 74 0
      app/src/main/java/com/itant/shibei/ui/home/tool/SystemPresenter.java
  57. 122 0
      app/src/main/java/com/itant/shibei/ui/home/tool/ToolFragment.java
  58. 15 0
      app/src/main/java/com/itant/shibei/ui/home/tool/json/IJsonView.java
  59. 106 0
      app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonActivity.java
  60. 14 0
      app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonBean.java
  61. 67 0
      app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonPresenter.java
  62. 69 0
      app/src/main/java/com/itant/shibei/ui/home/tool/weather/WeatherActivity.java
  63. 18 0
      app/src/main/java/com/itant/shibei/ui/home/tool/weather/WeatherBean.java
  64. 71 0
      app/src/main/java/com/itant/shibei/ui/home/tool/yiji/YijiActivity.java
  65. 18 0
      app/src/main/java/com/itant/shibei/ui/home/tool/yiji/YijiBean.java
  66. 0 37
      app/src/main/java/com/itant/shibei/ui/mine/AboutActivity.java
  67. 166 0
      app/src/main/java/com/itant/shibei/ui/mine/MineActivity.java
  68. 3 13
      app/src/main/java/com/itant/shibei/ui/mine/MineFragment.java
  69. 104 0
      app/src/main/java/com/itant/shibei/ui/mine/coupon/AddCouponActivity.java
  70. 43 0
      app/src/main/java/com/itant/shibei/ui/mine/coupon/AddCouponPresenter.java
  71. 18 0
      app/src/main/java/com/itant/shibei/ui/mine/coupon/IAddCouponView.java
  72. 130 0
      app/src/main/java/com/itant/shibei/ui/mine/forget/ForgetActivity.java
  73. 50 0
      app/src/main/java/com/itant/shibei/ui/mine/forget/ForgetPresenter.java
  74. 16 0
      app/src/main/java/com/itant/shibei/ui/mine/forget/IForgetView.java
  75. 193 0
      app/src/main/java/com/itant/shibei/ui/mine/goods/AddGoodsActivity.java
  76. 43 0
      app/src/main/java/com/itant/shibei/ui/mine/goods/AddGoodsPresenter.java
  77. 15 0
      app/src/main/java/com/itant/shibei/ui/mine/goods/IAddGoodsView.java
  78. 16 0
      app/src/main/java/com/itant/shibei/ui/mine/login/ILoginView.java
  79. 79 19
      app/src/main/java/com/itant/shibei/ui/mine/login/LoginActivity.java
  80. 43 0
      app/src/main/java/com/itant/shibei/ui/mine/login/LoginPresenter.java
  81. 0 63
      app/src/main/java/com/itant/shibei/ui/mine/login/forget/ForgetActivity.java
  82. 0 63
      app/src/main/java/com/itant/shibei/ui/mine/login/register/RegisterActivity.java
  83. 40 0
      app/src/main/java/com/itant/shibei/ui/mine/register/CodePresenter.java
  84. 18 0
      app/src/main/java/com/itant/shibei/ui/mine/register/ICodeView.java
  85. 159 0
      app/src/main/java/com/itant/shibei/ui/mine/register/RegisterActivity.java
  86. 94 0
      app/src/main/java/com/itant/shibei/ui/mine/register/fill/FillDataActivity.java
  87. 16 0
      app/src/main/java/com/itant/shibei/ui/mine/register/fill/IRegisterView.java
  88. 52 0
      app/src/main/java/com/itant/shibei/ui/mine/register/fill/RegisterPresenter.java
  89. 129 0
      app/src/main/java/com/itant/shibei/widget/AppbarTranslateListener.java
  90. 0 36
      app/src/main/java/com/itant/shibei/widget/DividerItemDecoration.java
  91. 50 0
      app/src/main/java/com/itant/shibei/widget/VideoPlayer.java
  92. 10 0
      app/src/main/java/com/itant/shibei/widget/bottomlistener/OnBottomListener.java
  93. 117 0
      app/src/main/java/com/itant/shibei/widget/bottomlistener/OnRcvScrollListener.java
  94. 44 0
      app/src/main/java/com/itant/shibei/widget/decoration/CardViewDividerItemDecoration.java
  95. 54 0
      app/src/main/java/com/itant/shibei/widget/decoration/NormalDividerItemDecoration.java
  96. 37 0
      app/src/main/java/com/itant/shibei/widget/decoration/SpacesItemDecoration.java
  97. 9 0
      app/src/main/res/drawable/ic_more_vert.xml
  98. 5 5
      app/src/main/res/drawable/selector_btn.xml
  99. 22 0
      app/src/main/res/drawable/selector_btn_exit.xml
  100. 0 0
      app/src/main/res/drawable/shape_gray_stroke_solid.xml

+ 69 - 9
app/build.gradle

@@ -10,15 +10,17 @@ android {
         minSdkVersion versions.minSdk
         targetSdkVersion versions.targetSdk
         applicationId "com.itant.shibei"
-        versionCode 1
-        versionName "1.0"
+        versionCode 5
+        versionName "1.5"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-    }
 
+        // 支持64位架构(上架谷歌市场必要条件),'x86_64','x86'
+        ndk.abiFilters 'armeabi-v7a','arm64-v8a'
+    }
 
     compileOptions {
-        sourceCompatibility 1.8
-        targetCompatibility 1.8
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
     }
 
 
@@ -31,17 +33,50 @@ android {
         }
     }
 
+    flavorDimensions "url"
+    productFlavors {
+        dev {
+            // 开发环境宿舍服务器
+            buildConfigField("String", "BASE_URL", '"http://192.168.0.190:8080/"')
+        }
+
+        rel {
+            // 正式上线
+            buildConfigField("String", "BASE_URL", '"http://app.jianjie.life:11111/"')
+        }
+
+        company {
+            // 开发2
+            buildConfigField("String", "BASE_URL", '"http://10.16.0.184:8080/"')
+        }
+    }
+
     buildTypes {
+        debug {
+
+        }
         release {
             signingConfig signingConfigs.config
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
-}
-
 
+    // 定义输出的APK名字
+    android.applicationVariants.all { variant ->
+        variant.outputs.all {
+            outputFileName = "Shibei_v${defaultConfig.versionName}.apk"
+        }
+    }
+}
 
+//implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+//增加'*.aar'就不用在android节点写下面的了:
+/* repositories {
+     flatDir {
+         dirs 'libs'
+     }
+ }*/
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
@@ -53,13 +88,38 @@ dependencies {
     api project(path: ':common')
     implementation project(path: ':network')
     implementation project(path: ':mvp')
-    implementation project(path: ':adapter')
     //compile "androidx.core:core-ktx:+"
     //compile "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
     //implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
 
+    // RxActivityResult
+    implementation 'com.github.VictorAlbertos:RxActivityResult:0.5.0-2.x'
+
+    // 调试过程中可以查看详细的网络请求与相应
+    debugImplementation 'com.readystatesoftware.chuck:library:1.1.0'
+    releaseImplementation 'com.readystatesoftware.chuck:library-no-op:1.1.0'
+
+    // 友盟
+    implementation "com.umeng.umsdk:common:9.1.0" //(必选)
+    implementation "com.umeng.umsdk:asms:1.1.3" // asms包依赖(必选)
+    implementation "com.umeng.umsdk:crash:0.0.4" // native crash包依赖(必选)
+
+    // Jsoup解析网页
+    //implementation 'org.jsoup:jsoup:1.13.1'
+
+    // 圆角图片
+    //implementation 'de.hdodenhof:circleimageview:3.1.0'
+    implementation 'com.makeramen:roundedimageview:2.3.0'
+
+    // 文字对齐
+    //implementation 'me.codeboy.android:align-text-view:2.3.2'
+
+    //饺子视频播放器
+    //implementation (name: 'jiaozivideoplayer-7.4.2', ext: 'aar')
+    implementation 'cn.jzvd:jiaozivideoplayer:7.4.2'
 }
 repositories {
     mavenCentral()
-}
+}
+

BIN
app/libs/jiaozivideoplayer-7.4.2.aar


+ 69 - 15
app/src/main/AndroidManifest.xml

@@ -3,19 +3,26 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.itant.shibei">
 
-    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 
     <application
+        android:allowBackup="false"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:supportsRtl="true"
         android:theme="@style/AppTheme"
         tools:ignore="GoogleAppIndexingWarning"
-        android:allowBackup="false">
+        android:requestLegacyExternalStorage="true"
+        android:networkSecurityConfig="@xml/network"
+        android:name=".ui.BeiApplication">
         <activity
-            android:name=".ui.MainActivity"
+            android:name=".ui.TabActivity"
             android:label="@string/app_name"
-            android:screenOrientation="portrait">
+            android:screenOrientation="portrait"
+            android:theme="@style/SplashTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
@@ -23,20 +30,67 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".ui.mine.AboutActivity"
-            android:theme="@style/BackTheme"
-            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".ui.mine.login.LoginActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle"/>
 
-        <activity android:name=".ui.mine.login.LoginActivity"
+        <activity
+            android:name=".ui.mine.register.RegisterActivity"
+            android:screenOrientation="portrait"
             android:theme="@style/TextInputStyle"
-            android:screenOrientation="portrait"/>
+            android:windowSoftInputMode="stateVisible|adjustResize"/>
 
-        <activity android:name=".ui.mine.login.register.RegisterActivity"
-            android:theme="@style/TextInputStyle"
-            android:screenOrientation="portrait"/>
-        <activity android:name=".ui.mine.login.forget.ForgetActivity"
-            android:theme="@style/TextInputStyle"
-            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".ui.mine.register.fill.FillDataActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.mine.forget.ForgetActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.mine.MineActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.mine.goods.AddGoodsActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.mine.coupon.AddCouponActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.home.search.SearchActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.home.tool.json.JsonActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.home.tool.weather.WeatherActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.home.tool.yiji.YijiActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/TextInputStyle" />
+
+        <activity
+            android:name=".ui.home.goods.play.VideoPlayActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen" />
     </application>
 
 </manifest>

+ 0 - 114
app/src/main/java/com/itant/shibei/adapter/GoodsAdapter.java

@@ -1,114 +0,0 @@
-package com.itant.shibei.adapter;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.widget.AppCompatImageView;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
-import com.bumptech.glide.request.RequestOptions;
-import com.bumptech.glide.request.target.SimpleTarget;
-import com.bumptech.glide.request.transition.Transition;
-import com.itant.library.recyclerview.CommonAdapter;
-import com.itant.library.recyclerview.base.ViewHolder;
-import com.itant.shibei.R;
-import com.itant.shibei.bean.GoodsBean;
-import com.itant.shibei.common.ConstantString;
-import com.itant.shibei.tool.StringTool;
-import com.miekir.common.utils.ActivityTool;
-
-import java.util.List;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/6 20:08
- * Description: 首页商品适配器
- */
-public class GoodsAdapter extends CommonAdapter<GoodsBean> {
-    private Context mContext;
-    private int mRadius;
-
-    public GoodsAdapter(Context context, int layoutId, List<GoodsBean> goodsList) {
-        super(context, layoutId, goodsList);
-        mContext = context;
-        mRadius = (int) context.getResources().getDimension(R.dimen.margin_ss);
-    }
-
-
-    @Override
-    protected void convert(ViewHolder holder, GoodsBean goodsBean, int position) {
-        holder.setText(R.id.tv_title, goodsBean.getTitle());
-        holder.setText(R.id.tv_shop_name, goodsBean.getShopName());
-
-        // 好评
-        holder.setText(R.id.tv_good_comment,
-                String.format(ConstantString.GOOD_COMMENT, Math.round(goodsBean.getGoodCommentPercent()*100)));
-
-        // 月销
-        holder.setText(R.id.tv_sales_per_month,
-                String.format(ConstantString.SALES_PER_MONTH, String.valueOf(goodsBean.getSalesPerMonth())));
-
-        // 返现
-        TextView tv_rebate = holder.getView(R.id.tv_rebate);
-        tv_rebate.setText(String.format(ConstantString.MONEY_GAME,
-                StringTool.getShowMoney(goodsBean.getOldPrice()-goodsBean.getNowPrice())));
-        // 走马灯
-        tv_rebate.setSelected(true);
-
-        // 自营
-        TextView tv_self = holder.getView(R.id.tv_self);
-        if (goodsBean.isSelfBusiness()) {
-            tv_self.setVisibility(View.VISIBLE);
-        } else {
-            tv_self.setVisibility(View.GONE);
-        }
-
-        // 原价
-        TextView tv_old_price = holder.getView(R.id.tv_old_price);
-        tv_old_price.setText(String.format(ConstantString.MONEY_RMB, StringTool.getShowMoney(goodsBean.getOldPrice())));
-        // 增加删除线
-        tv_old_price.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
-
-        // 现价
-        holder.setText(R.id.tv_now_price, String.format(ConstantString.MONEY_RMB, StringTool.getShowMoney(goodsBean.getNowPrice())));
-
-        // 一句话推荐
-        TextView tv_reason = holder.getView(R.id.tv_reason);
-        tv_reason.setText(goodsBean.getReason());
-
-        holder.setOnClickListener(R.id.cv_goods, new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                String goodsUrl = goodsBean.getGoodsUrl();
-                ActivityTool.openUrl((Activity) mContext, goodsUrl);
-            }
-        });
-
-        AppCompatImageView aciv_goods = holder.getView(R.id.aciv_goods);
-        Glide.with(mContext).load(goodsBean.getCoverImageUrl())
-                .apply(RequestOptions.bitmapTransform(new RoundedCorners(mRadius)))
-                .apply(new RequestOptions()
-                        .skipMemoryCache(true)
-                        .diskCacheStrategy(DiskCacheStrategy.NONE))
-                //先加载原图大小的十分之一
-                .thumbnail(0.1f)
-                .into(new SimpleTarget<Drawable>() {
-                    @Override
-                    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
-                        aciv_goods.setScaleType(ImageView.ScaleType.CENTER_CROP);
-                        aciv_goods.setImageDrawable(resource);
-                    }
-                });
-    }
-}

+ 0 - 70
app/src/main/java/com/itant/shibei/adapter/TemplateAdapter.java

@@ -1,70 +0,0 @@
-package com.itant.shibei.adapter;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.bumptech.glide.request.RequestOptions;
-import com.bumptech.glide.request.target.SimpleTarget;
-import com.bumptech.glide.request.transition.Transition;
-import com.itant.library.recyclerview.CommonAdapter;
-import com.itant.library.recyclerview.base.ViewHolder;
-import com.itant.shibei.R;
-import com.itant.shibei.bean.TemplateBean;
-import com.miekir.common.utils.ActivityTool;
-import com.miekir.common.utils.ToastTool;
-
-import java.util.List;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/6 20:08
- * Description: 首页商品适配器
- */
-public class TemplateAdapter extends CommonAdapter<TemplateBean> {
-    private List<TemplateBean> mTemplateList;
-
-    public TemplateAdapter(Context context, int layoutId, List<TemplateBean> templateBeanList) {
-        super(context, layoutId, templateBeanList);
-        mTemplateList = templateBeanList;
-    }
-
-
-    @Override
-    protected void convert(ViewHolder holder, TemplateBean templateBean, int position) {
-        ImageView iv_template = holder.getView(R.id.iv_template);
-        // 圆角
-        Glide.with(mContext).load(templateBean.getCoverUrl())
-                //.apply(RequestOptions.bitmapTransform(new RoundedCorners(48))
-                .apply(new RequestOptions()
-                        .skipMemoryCache(true)
-                        .diskCacheStrategy(DiskCacheStrategy.NONE))
-                //先加载原图大小的十分之一
-                .thumbnail(0.1f)
-                .into(new SimpleTarget<Drawable>() {
-                    @Override
-                    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
-                        iv_template.setScaleType(ImageView.ScaleType.CENTER_CROP);
-                        iv_template.setImageDrawable(resource);
-                    }
-                });
-
-        holder.setText(R.id.tv_template, templateBean.getTemplateName());
-        holder.setOnClickListener(R.id.rl_template, new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (templateBean.isActionEnable()) {
-                    ActivityTool.openUrl((Activity) mContext, templateBean.getJumpUrl());
-                } else {
-                    ToastTool.showShort("敬请期待");
-                }
-            }
-        });
-    }
-}

+ 104 - 0
app/src/main/java/com/itant/shibei/base/ApiService.java

@@ -0,0 +1,104 @@
+package com.itant.shibei.base;
+
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.bean.SystemBean;
+import com.itant.shibei.bean.UpgradeBean;
+import com.itant.shibei.ui.home.coupon.CouponBean;
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.itant.shibei.ui.home.tool.json.JsonBean;
+import com.miekir.network.core.base.BaseResponse;
+
+import java.util.List;
+import java.util.Map;
+
+import io.reactivex.Observable;
+import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+import retrofit2.http.Query;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:38
+ * Description: 请求接口
+ */
+public interface ApiService {
+
+    /**请求验证码*/
+    @GET("/shibei/api/code")
+    Observable<BaseResponse<Boolean>> getCode(@Query("email") String email);
+
+    /**注册*/
+    //@Headers({ "Content-Type: application/json;charset=UTF-8"})
+    @POST("/shibei/api/register")
+    Observable<BaseResponse<BeiUser>> submitRegister(@Body Map<String, Object> body);
+
+    /**邮箱+加密的密码 登录*/
+    @POST("/shibei/api/login/normal")
+    Observable<BaseResponse<BeiUser>> submitLogin(@Query("email") String email, @Query("password") String password);
+
+    /**忘记密码*/
+    @POST("/shibei/api/password")
+    Observable<BaseResponse<BeiUser>> resetPassword(@Body Map<String, Object> body);
+
+    /*----------------------------------------京东商品开始----------------------------------------*/
+    /**新增京东商品*/
+    @POST("/shibei/api/addGoods")
+    Observable<BaseResponse<String>> addGoods(@Body GoodsBean body);
+
+    /**删除京东商品*/
+    @GET("/shibei/api/deleteGoodsById")
+    Observable<BaseResponse<String>> deleteGoodsById(@Query("goodsId") long goodsId);
+
+    /**分页查询京东商品*/
+    @GET("/shibei/api/getGoodsList")
+    Observable<BaseResponse<List<GoodsBean>>> getGoodsList(@Query("pageNum") int pageNum, @Query("pageSize") int pageSize);
+
+    /**根据关键字分页查询京东商品*/
+    @GET("/shibei/api/getGoodsListByKeyword")
+    Observable<BaseResponse<List<GoodsBean>>> getGoodsListByKeyword(@Query("keywords") String keyword, @Query("pageNum") int pageNum, @Query("pageSize") int pageSize);
+
+    /**根据链接查询京东商品*/
+    //@GET("/shibei/api/getGoodsByUrl")
+    //Observable<BaseResponse<GoodsBean>> getGoodsByUrl(@Query("url") String url, @Query("pageNum") int pageNum, @Query("pageSize") int pageSize);
+
+    /*----------------------------------------优惠券开始----------------------------------------*/
+    /**新增优惠券*/
+    @POST("/shibei/api/addCoupon")
+    Observable<BaseResponse<String>> addCoupon(@Body CouponBean body);
+
+    /**删除优惠券*/
+    @GET("/shibei/api/deleteCouponById")
+    Observable<BaseResponse<String>> deleteCouponById(@Query("couponId") long goodsId);
+
+    /**分页查询优惠券*/
+    @GET("/shibei/api/getCouponList")
+    Observable<BaseResponse<List<CouponBean>>> getCouponList(@Query("pageNum") int pageNum, @Query("pageSize") int pageSize);
+
+    /*----------------------------------------JSON开始----------------------------------------*/
+    /**保存和更新JSON*/
+    @POST("/shibei/api/saveJson")
+    Observable<BaseResponse<JsonBean>> saveJson(@Body JsonBean body);
+
+    /**获取JSON bean*/
+    @GET("/shibei/api/getJsonBean")
+    Observable<BaseResponse<JsonBean>> getJsonBean();
+
+    /*----------------------------------------升级信息----------------------------------------*/
+    /**获取版本更新信息*/
+    @GET("/shibei/api/getUpgradeInfo")
+    Observable<BaseResponse<UpgradeBean>> getUpgradeInfo();
+
+    /*----------------------------------------系统配置信息----------------------------------------*/
+    /**获取系统配置信息*/
+    @GET("/shibei/api/getSystemConfig")
+    Observable<BaseResponse<SystemBean>> getSystemConfig();
+
+    /**设置是否VIP才能用API*/
+    @POST("/shibei/api/setApiConfig")
+    Observable<BaseResponse<String>> setApiConfig(@Query("isApiVipOnly") boolean isApiVipOnly);
+
+
+}

+ 2 - 4
app/src/main/java/com/itant/shibei/base/BaseShiBeiActivity.java

@@ -3,10 +3,8 @@ package com.itant.shibei.base;
 import android.os.Build;
 import android.os.Bundle;
 import android.view.View;
-import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 
 import com.itant.shibei.R;
@@ -15,11 +13,11 @@ import com.miekir.mvp.view.BaseMVPActivity;
 /**
  *
  *
- * @author 詹子聪
+ * @author Miekir
  * @date 2020/6/18 16:47
  * Description: 界面基类
  */
-public abstract class BaseShiBeiActivity extends BaseMVPActivity {
+public abstract class BaseBeiActivity extends BaseMVPActivity {
     private View iv_done;
 
     @Override

+ 11 - 0
app/src/main/java/com/itant/shibei/base/ITopActionListener.java

@@ -0,0 +1,11 @@
+package com.itant.shibei.base;
+
+/**
+ *
+ * @author Miekir
+ * @date 2020/8/2 9:19
+ * Description: 监听滚动到顶部的动作
+ */
+public interface ITopActionListener {
+    void onTopAction();
+}

+ 5 - 0
app/src/main/java/com/itant/shibei/base/ItemLongClickListener.java

@@ -0,0 +1,5 @@
+package com.itant.shibei.base;
+
+public interface ItemLongClickListener {
+    void onItemLongClick(int position);
+}

+ 25 - 143
app/src/main/java/com/itant/shibei/bean/BeiUser.java

@@ -1,151 +1,33 @@
 package com.itant.shibei.bean;
 
+import java.io.Serializable;
+
 /**
  * Created by Jason on 2018/8/26.
  */
 
-public class BeiUser {
-    private String email;
-    private String nickName;
-    private String autograph;
-    private String password;
-    private String headIcon;
-    private int sex;
-    private String token;
-    private long registerTimeMillis;
-    private long lastLoginTimeMillis;
-    private boolean isVip;
-    private String deviceId;
-    private String lastIp;
-    private int themeMode;
-
-    /**
-     * 提现账号
-     */
-    private String cashAccount;
-
-    /**
-     * 余额
-     */
-    private double money;
-
-    public String getAutograph() {
-        return autograph;
-    }
-
-    public void setAutograph(String autograph) {
-        this.autograph = autograph;
-    }
-
-    public String getEmail() {
-        return email;
-    }
-
-    public void setEmail(String email) {
-        this.email = email;
-    }
-
-    public String getNickName() {
-        return nickName;
-    }
-
-    public void setNickName(String nickName) {
-        this.nickName = nickName;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public String getHeadIcon() {
-        return headIcon;
-    }
-
-    public void setHeadIcon(String headIcon) {
-        this.headIcon = headIcon;
-    }
-
-    public int getSex() {
-        return sex;
-    }
-
-    public void setSex(int sex) {
-        this.sex = sex;
-    }
-
-    public String getToken() {
-        return token;
-    }
-
-    public void setToken(String token) {
-        this.token = token;
-    }
-
-    public long getRegisterTimeMillis() {
-        return registerTimeMillis;
-    }
-
-    public void setRegisterTimeMillis(long registerTimeMillis) {
-        this.registerTimeMillis = registerTimeMillis;
-    }
-
-    public long getLastLoginTimeMillis() {
-        return lastLoginTimeMillis;
-    }
-
-    public void setLastLoginTimeMillis(long lastLoginTimeMillis) {
-        this.lastLoginTimeMillis = lastLoginTimeMillis;
-    }
-
-    public boolean isVip() {
-        return isVip;
-    }
-
-    public void setVip(boolean vip) {
-        isVip = vip;
-    }
-
-    public String getDeviceId() {
-        return deviceId;
-    }
-
-    public void setDeviceId(String deviceId) {
-        this.deviceId = deviceId;
-    }
-
-    public String getLastIp() {
-        return lastIp;
-    }
-
-    public void setLastIp(String lastIp) {
-        this.lastIp = lastIp;
-    }
-
-    public int getThemeMode() {
-        return themeMode;
-    }
-
-    public void setThemeMode(int themeMode) {
-        this.themeMode = themeMode;
-    }
-
-    public double getMoney() {
-        return money;
-    }
-
-    public void setMoney(double money) {
-        this.money = money;
-    }
-
-    public String getCashAccount() {
-        return cashAccount;
-    }
+public class BeiUser implements Serializable {
+    public String email;
+    public String nickName;
+    public String autograph;
+    public String password;
+    public String headIcon;
+    public int sex;
+    public String token;
+    public long registerTimeMillis;
+    public long lastLoginTimeMillis;
+    public boolean isVip;
+    public String deviceId;
+    public String lastIp;
+    public int themeMode;
+    /**当前余额,单位:分*/
+    public long currentMoney;
+    /**历史提现,单位:分*/
+    public long historyCash;
+
+    /**提现支付宝账号*/
+    public String cashAccount;
+    /**是否被禁用*/
+    public boolean isLimit;
 
-    public void setCashAccount(String cashAccount) {
-        this.cashAccount = cashAccount;
-    }
 }

+ 0 - 234
app/src/main/java/com/itant/shibei/bean/GoodsBean.java

@@ -1,234 +0,0 @@
-package com.itant.shibei.bean;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/5 21:50
- * Description: 商品实体
- */
-public class GoodsBean {
-    public static final int TYPE_TECHNOLOGY = 1;
-    public static final int TYPE_LIFE = 2;
-    public static final int TYPE_NETWORK = 3;
-
-
-    /**
-     * 商品id
-     */
-    private String goodsId;
-    /**
-     * 封面图片地址
-     */
-    private String coverImageUrl;
-    /**
-     * 标题
-     */
-    private String title;
-    /**
-     * 商品描述
-     */
-    private String description;
-    /**
-     * 商品推荐理由(一句话推荐、推荐者心声)
-     */
-    private String reason;
-    /**
-     * 原价
-     */
-    private double oldPrice;
-    /**
-     * 现价
-     */
-    private double nowPrice;
-    /**
-     * 返利
-     */
-    private String rebate;
-    /**
-     * 店名
-     */
-    private String shopName;
-    /**
-     * 商店所属省份
-     */
-    private String province;
-    /**
-     * 是否自营
-     */
-    private boolean isSelfBusiness;
-    /**
-     * 是否有券
-     */
-    private boolean hasCoupon;
-    /**
-     * 优惠券金额
-     */
-    private String couponMoney;
-    /**
-     * 所属类型
-     */
-    private int goodsType;
-    /**
-     * 商品链接
-     */
-    private String goodsUrl;
-    /**
-     * 月销量
-     */
-    private int salesPerMonth;
-
-    /**
-     * 评论条数
-     */
-    private int commentNum;
-
-    /**
-     * 好评率
-     */
-    private double goodCommentPercent;
-
-    public String getGoodsId() {
-        return goodsId;
-    }
-
-    public void setGoodsId(String goodsId) {
-        this.goodsId = goodsId;
-    }
-
-    public String getCoverImageUrl() {
-        return coverImageUrl;
-    }
-
-    public void setCoverImageUrl(String coverImageUrl) {
-        this.coverImageUrl = coverImageUrl;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public double getOldPrice() {
-        return oldPrice;
-    }
-
-    public void setOldPrice(double oldPrice) {
-        this.oldPrice = oldPrice;
-    }
-
-    public double getNowPrice() {
-        return nowPrice;
-    }
-
-    public void setNowPrice(double nowPrice) {
-        this.nowPrice = nowPrice;
-    }
-
-    public String getRebate() {
-        return rebate;
-    }
-
-    public void setRebate(String rebate) {
-        this.rebate = rebate;
-    }
-
-    public String getShopName() {
-        return shopName;
-    }
-
-    public void setShopName(String shopName) {
-        this.shopName = shopName;
-    }
-
-    public String getProvince() {
-        return province;
-    }
-
-    public void setProvince(String province) {
-        this.province = province;
-    }
-
-    public boolean isSelfBusiness() {
-        return isSelfBusiness;
-    }
-
-    public void setSelfBusiness(boolean selfBusiness) {
-        isSelfBusiness = selfBusiness;
-    }
-
-    public boolean isHasCoupon() {
-        return hasCoupon;
-    }
-
-    public void setHasCoupon(boolean hasCoupon) {
-        this.hasCoupon = hasCoupon;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getReason() {
-        return reason;
-    }
-
-    public void setReason(String reason) {
-        this.reason = reason;
-    }
-
-    public int getGoodsType() {
-        return goodsType;
-    }
-
-    public void setGoodsType(int goodsType) {
-        this.goodsType = goodsType;
-    }
-
-    public String getCouponMoney() {
-        return couponMoney;
-    }
-
-    public void setCouponMoney(String couponMoney) {
-        this.couponMoney = couponMoney;
-    }
-
-    public String getGoodsUrl() {
-        return goodsUrl;
-    }
-
-    public void setGoodsUrl(String goodsUrl) {
-        this.goodsUrl = goodsUrl;
-    }
-
-    public int getSalesPerMonth() {
-        return salesPerMonth;
-    }
-
-    public void setSalesPerMonth(int salesPerMonth) {
-        this.salesPerMonth = salesPerMonth;
-    }
-
-    public int getCommentNum() {
-        return commentNum;
-    }
-
-    public void setCommentNum(int commentNum) {
-        this.commentNum = commentNum;
-    }
-
-    public double getGoodCommentPercent() {
-        return goodCommentPercent;
-    }
-
-    public void setGoodCommentPercent(double goodCommentPercent) {
-        this.goodCommentPercent = goodCommentPercent;
-    }
-}

+ 17 - 0
app/src/main/java/com/itant/shibei/bean/SystemBean.java

@@ -0,0 +1,17 @@
+package com.itant.shibei.bean;
+
+
+/**
+ * 系统控制类
+ */
+public class SystemBean {
+    /**
+     * 模板Id
+     */
+    public long id;
+
+    /**
+     * 是否有VIP限制,true表示必须要VIP才能使用特殊服务,false表示免费开放中...
+     */
+    public boolean isVipLimit;
+}

+ 0 - 88
app/src/main/java/com/itant/shibei/bean/TemplateBean.java

@@ -1,88 +0,0 @@
-package com.itant.shibei.bean;
-
-import com.itant.shibei.common.ConstantUrl;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/7 19:37
- * Description: 模板实体
- */
-public class TemplateBean {
-    /**
-     * 模板Id
-     */
-    private int templateId;
-    /**
-     * 模板类型
-     */
-    private int templateType;
-
-    /**
-     * 模板名称
-     */
-    private String templateName;
-    /**
-     * 点击之后跳转的链接
-     */
-    private String jumpUrl;
-    /**
-     * 封面图片URL
-     */
-    private String coverUrl = ConstantUrl.URL_RANDOM_PHOTO_WALLPAPER;
-
-
-    /**
-     * 是否可以响应点击
-     */
-    private boolean actionEnable;
-
-    public int getTemplateId() {
-        return templateId;
-    }
-
-    public void setTemplateId(int templateId) {
-        this.templateId = templateId;
-    }
-
-    public String getTemplateName() {
-        return templateName;
-    }
-
-    public void setTemplateName(String templateName) {
-        this.templateName = templateName;
-    }
-
-    public String getJumpUrl() {
-        return jumpUrl;
-    }
-
-    public void setJumpUrl(String jumpUrl) {
-        this.jumpUrl = jumpUrl;
-    }
-
-    public String getCoverUrl() {
-        return coverUrl;
-    }
-
-    public void setCoverUrl(String coverUrl) {
-        this.coverUrl = coverUrl;
-    }
-
-    public boolean isActionEnable() {
-        return actionEnable;
-    }
-
-    public void setActionEnable(boolean actionEnable) {
-        this.actionEnable = actionEnable;
-    }
-
-    public int getTemplateType() {
-        return templateType;
-    }
-
-    public void setTemplateType(int templateType) {
-        this.templateType = templateType;
-    }
-}

+ 17 - 0
app/src/main/java/com/itant/shibei/bean/UpgradeBean.java

@@ -0,0 +1,17 @@
+package com.itant.shibei.bean;
+
+/**
+ * Created by Jason on 2019/09/29.
+ */
+public class UpgradeBean {
+    // Id自增,注意,一个表只能有一个主键,否则服务器会报500错误,启动失败
+    public int id;
+
+    public int versionCode;
+
+    public String versionName;
+
+    public String content;
+
+    public String url;
+}

+ 16 - 0
app/src/main/java/com/itant/shibei/common/ICommonView.java

@@ -0,0 +1,16 @@
+package com.itant.shibei.common;
+
+import com.miekir.mvp.view.IView;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:44
+ */
+public interface ICommonView<T> extends IView {
+    default void onCommonResult(boolean success, String message, T resultBean) {
+        //要在实现类显示调用ICommonView.super.onCommonResult(success, message, resultBean);才会生效
+        //dismissLoading();
+    }
+}

+ 5 - 4
app/src/main/java/com/itant/shibei/common/ConstantString.java

@@ -1,15 +1,16 @@
-package com.itant.shibei.common;
+package com.itant.shibei.constant;
 
 /**
- * Copyright (C), 2019-2020, Genlot
  *
- * @author 詹子聪
+ *
+ * @author Miekir
  * @date 2020/7/11 16:35
  * Description: 常量字符串
  */
 public interface ConstantString {
     String MONEY_RMB = "¥%s";
-    String SALES_PER_MONTH = "月销 %s";
+    String COMMENT_NUM = "评论 %s";
     String GOOD_COMMENT = "%s%%";
     String MONEY_GAME = "¥%s";
+    String WELCOME_HELLO = "%s好,%s";
 }

+ 14 - 4
app/src/main/java/com/itant/shibei/common/ConstantUrl.java

@@ -1,9 +1,9 @@
-package com.itant.shibei.common;
+package com.itant.shibei.constant;
 
 /**
  *
  *
- * @author 詹子聪
+ * @author Miekir
  * @date 2020/7/5 11:27
  * Description: 常量
  */
@@ -20,9 +20,19 @@ public interface ConstantUrl {
     String URL_JD_TEMPLATE_COMPUTER = "https://diy.m.jd.com/?utm_user=plusmember&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=CopyURL";
 
     /**
-     * 美团优惠
+     * 表情包制作
      */
-    String URL_MEITUAN_BONUS = "http://p.yiqifa.com/l?l=Cl7SYKqBR9et6lMdUJqSpNM2W9sWgQ446wbQNcUJWKe6pBU1C9qQYcDm696WNsUdUtqQN9U25KV6KlB7Y7yS5cy2W9sWgEM1UmBQgPUVNPAWgpMEUQ4SM5e7KOosR96y!5F9UnsuUZgLRlu_fOwdY5PAUZPbY8eEYO7_3QULfOb9Mpo8YJoECSo8MJMw6O4w6NUy6O3_6lKSWNK_C5ewMQzdCZgVYj--";
+    String URL_BIAO_QING = "https://app.xuty.tk/static/app/index.html";
+
+    /**京东联盟地址*/
+    String URL_JD_UNION = "https://union.jd.com/proManager/index?pageNo=1";
+
+
+    /**
+     * 美团优惠1
+     */
+    String URL_MEITUAN_BONUS_1 = "https://tb.jiuxinban.com/6Fq9k9";
+    String URL_MEITUAN_BONUS_2 = "https://tb.jiuxinban.com/6Fq9mM";
 
     /**
      * 二次元

+ 66 - 0
app/src/main/java/com/itant/shibei/manager/PreferenceManager.java

@@ -0,0 +1,66 @@
+package com.itant.shibei.manager;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.google.gson.Gson;
+
+import java.io.Serializable;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 16:44
+ * Description: 用户信息保存
+ */
+public class PreferenceManager {
+    public static final String KEY_USER = "user_info";
+
+    private static volatile PreferenceManager instance;
+    private PreferenceManager() {}
+
+    private SharedPreferences preferences;
+
+    public static PreferenceManager getInstance() {
+        if (instance == null) {
+            init();
+        }
+        return instance;
+    }
+
+    private static synchronized void init() {
+        if (instance == null) {
+            instance = new PreferenceManager();
+        }
+    }
+
+    public void initPreference(Context context) {
+        preferences = context.getSharedPreferences("current", Context.MODE_PRIVATE);
+    }
+
+    public void putSerializable(String key, Serializable value) {
+        if (value == null) {
+            preferences.edit().putString(key, "").commit();
+        } else {
+            preferences.edit().putString(key, new Gson().toJson(value)).commit();
+        }
+    }
+
+    /**
+     * 这里不能使用TypeToken<T>,因为要明确地具体到某一个类才能解析
+     * @param key
+     * @param clazz
+     * @param <T>
+     * @return
+     */
+    public <T> T getObject(String key, Class<T> clazz){
+        String jsonString = preferences.getString(key, null);
+        try {
+            return new Gson().fromJson(jsonString, clazz);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 21 - 3
app/src/main/java/com/itant/shibei/ui/mine/login/UserInfoManager.java

@@ -1,15 +1,18 @@
-package com.itant.shibei.ui.mine.login;
+package com.itant.shibei.manager;
+
+import com.itant.shibei.bean.BeiUser;
 
 /**
  *
  *
- * @author 詹子聪
+ * @author Miekir
  * @date 2020/6/27 20:40
  * Description: 用户信息
  */
 public class UserInfoManager {
     private static volatile UserInfoManager userInfoManager;
     private UserInfoManager() {}
+    private BeiUser mBeiUser;
 
     private boolean isLogin;
 
@@ -27,8 +30,23 @@ public class UserInfoManager {
         }
     }
 
+    public BeiUser getBeiUser() {
+        if (mBeiUser == null) {
+            // 从本地拿
+            mBeiUser = PreferenceManager.getInstance().getObject(PreferenceManager.KEY_USER, BeiUser.class);
+        }
+
+        return mBeiUser;
+    }
+
+    public void setBeiUser(BeiUser beiUser) {
+        this.mBeiUser = beiUser;
+        // 保存到本地
+        PreferenceManager.getInstance().putSerializable(PreferenceManager.KEY_USER, beiUser);
+    }
+
     public boolean isLogin() {
-        return isLogin;
+        return getBeiUser() != null;
     }
 
     public void setLogin(boolean login) {

+ 173 - 0
app/src/main/java/com/itant/shibei/net/RetrofitHelper.java

@@ -0,0 +1,173 @@
+package com.itant.shibei.net;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.itant.shibei.manager.UserInfoManager;
+import com.miekir.network.BuildConfig;
+import com.miekir.network.core.RetrofitInstaller;
+import com.readystatesoftware.chuck.ChuckInterceptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.logging.HttpLoggingInterceptor;
+import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/1/19 10:58
+ * Description: 请求类,适用于请求链接和配置随时变动
+ */
+public class RetrofitHelper {
+    private static volatile RetrofitHelper mInstance;
+
+    private Context mContext;
+    private Retrofit mRetrofit;
+    private Retrofit mOtherRetrofit;
+    private OkHttpClient mOkHttpClient;
+    private OkHttpClient mOtherClient;
+
+    private Interceptor mHeaderInterceptor = new Interceptor() {
+        @Override
+        public Response intercept(Chain chain) throws IOException {
+            String token = "";
+            String email = "";
+            if (UserInfoManager.getInstance().isLogin()) {
+                token = UserInfoManager.getInstance().getBeiUser().token;
+                email = UserInfoManager.getInstance().getBeiUser().email;
+            }
+            Request request = chain.request()
+                    .newBuilder()
+                    .addHeader("token", token)
+                    .addHeader("email", email)
+                    .build();
+            return chain.proceed(request);
+        }
+    };
+
+    private static SSLSocketFactory getSSLContext (Context context){
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            //privatekey.crt证书文件,将它放在Assets目录下
+            InputStream is =  context.getAssets().open("lomsxj.crt");
+
+            Certificate ca = cf.generateCertificate(is);
+            Log.i("getSSLContext ", "ca=" + ((X509Certificate) ca).getSubjectDN());
+
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            keyStore.load(null,null);
+            keyStore.setCertificateEntry("ca",ca);
+
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            tmf.init(keyStore);
+
+            SSLContext ssContext = SSLContext.getInstance("TLSv1", "AndroidOpenSSL");
+            ssContext.init(null,tmf.getTrustManagers(),null);
+
+            return ssContext.getSocketFactory();
+
+        } catch (CertificateException | KeyManagementException | NoSuchProviderException | NoSuchAlgorithmException | KeyStoreException | IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private RetrofitHelper() {
+        mContext = RetrofitInstaller.mInstallerContext;
+
+        // 默认请求的超时等配置
+        mOkHttpClient = getDefaultOkHttpClient();
+        mOtherClient = getDefaultOkHttpClient();
+
+        // 默认请求的地址、转换规则等配置,Base URL以不能包含路径,只能是http://加上IP或域名
+        mRetrofit = new Retrofit.Builder()
+                .baseUrl(BuildConfig.BASE_URL)
+                .client(mOkHttpClient)
+                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+                .addConverterFactory(GsonConverterFactory.create())
+                .build();
+    }
+
+    public static RetrofitHelper getInstance() {
+        if (mInstance == null) {
+            init();
+        }
+        return mInstance;
+    }
+
+    private static synchronized void init() {
+        if (mInstance == null) {
+            mInstance = new RetrofitHelper();
+        }
+    }
+
+    /**
+     *
+     * @return 默认的请求设置
+     */
+    public OkHttpClient getDefaultOkHttpClient() {
+        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
+            @Override
+            public void log(String message) {
+                //LogTool.logInfo("Retrofit:", message);
+            }
+        });
+        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
+
+        return new OkHttpClient.Builder()
+                .addInterceptor(mHeaderInterceptor)
+                //.addInterceptor(interceptor)
+                //.sslSocketFactory(getSSLContext(context))
+                .addInterceptor(new ChuckInterceptor(mContext))
+                .retryOnConnectionFailure(true)
+                .connectTimeout(20, TimeUnit.SECONDS)
+                .writeTimeout(20, TimeUnit.SECONDS)
+                .readTimeout(60, TimeUnit.SECONDS)
+                .build();
+    }
+
+    /**
+     *
+     * @return 请求接口
+     */
+    public <T> T getRequestApi(Class<T> apiClass) {
+        return mRetrofit.create(apiClass);
+    }
+
+    /**
+     *
+     * @return 请求接口
+     */
+    public <T> T getRequestApi(Class<T> apiClass, String baseUrl) {
+        mOtherRetrofit = new Retrofit.Builder()
+                .baseUrl(baseUrl)
+                .client(mOtherClient)
+                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+                .addConverterFactory(GsonConverterFactory.create())
+                .build();
+        return mOtherRetrofit.create(apiClass);
+    }
+}

+ 82 - 0
app/src/main/java/com/itant/shibei/tool/Base64Tool.java

@@ -0,0 +1,82 @@
+package com.itant.shibei.tool;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.util.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+/**
+ * @author 詹子聪
+ * @date 2020/7/27 15:40
+ * Description: 图片转Base64字符串
+ */
+public class Base64Tool {
+    private Base64Tool() {}
+    /**
+     * 图片Uri转Base64
+     *
+     * @param context
+     * @param imageUri
+     * @return
+     */
+    public static String getBase64FromUri(Context context, Uri imageUri) {
+        String encodedString = "";
+        if (imageUri == null) {
+            return encodedString;
+        }
+
+        InputStream imageStream = null;
+        try {
+            imageStream = context.getContentResolver().openInputStream(imageUri);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            return encodedString;
+        }
+
+        final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
+        encodedString = bitmap2Base64(selectedImage);
+
+        return encodedString;
+    }
+
+    private static String bitmap2Base64(Bitmap bitmap) {
+        if (bitmap == null) {
+            return "";
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
+        byte[] b = baos.toByteArray();
+
+        //return Base64.encodeToString(b, Base64.DEFAULT);
+        return Base64.encodeToString(b, Base64.NO_WRAP);
+    }
+
+    /**
+     * 根据路径,图片转Base64
+     * @param path
+     * @return
+     */
+    public static String getBase64FromFilePath(String path) {
+        File imageFile = new File(path);
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(imageFile);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            return "";
+        }
+        Bitmap bm = BitmapFactory.decodeStream(fis);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
+        byte[] b = baos.toByteArray();
+
+        return Base64.encodeToString(b, Base64.NO_WRAP);
+    }
+}

+ 58 - 43
app/src/main/java/com/itant/shibei/tool/DataTool.java

@@ -1,17 +1,16 @@
 package com.itant.shibei.tool;
 
-import com.itant.shibei.bean.GoodsBean;
-import com.itant.shibei.bean.TemplateBean;
-import com.itant.shibei.common.ConstantUrl;
+import com.itant.shibei.R;
+import com.itant.shibei.constant.ConstantUrl;
 import com.itant.shibei.ui.function.FunctionFragment;
+import com.itant.shibei.ui.home.coupon.CouponBean;
+import com.itant.shibei.ui.home.goods.GoodsBean;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- *
- *
- * @author 詹子聪
+ * @author Miekir
  * @date 2020/7/8 19:36
  * Description: 模拟数据提供者
  */
@@ -19,61 +18,77 @@ public class DataTool {
     private DataTool() {
     }
 
-    public static List<TemplateBean> getTemplateList(int functionTye) {
-        TemplateBean templateBean = new TemplateBean();
+    public static List<CouponBean> getTemplateList(int functionTye) {
 
+        List<CouponBean> couponBeanList = new ArrayList<>();
         switch (functionTye) {
             case FunctionFragment.TEMPLATE_TYPE_BONUS:
-                templateBean.setCoverUrl(ConstantUrl.URL_RANDOM_PHOTO_WALLPAPER);
-                templateBean.setTemplateName("美团优惠券");
-                templateBean.setJumpUrl(ConstantUrl.URL_MEITUAN_BONUS);
-                templateBean.setActionEnable(true);
+                CouponBean meituan1 = new CouponBean();
+                //meituan1.coverUrl = ConstantUrl.URL_RANDOM_PHOTO_WALLPAPER;
+                meituan1.coverUrl = String.valueOf(R.mipmap.meituan1);
+                meituan1.isCoverUrlLocal = true;
+                meituan1.couponName = "美团优惠券最多可领48元大礼包";
+                meituan1.jumpUrl = ConstantUrl.URL_MEITUAN_BONUS_1;
+                meituan1.actionEnable = true;
+                meituan1.isLocal = true;
+
+                CouponBean meituan2 = new CouponBean();
+                //meituan2.coverUrl = ConstantUrl.URL_RANDOM_PHOTO_WALLPAPER;
+                meituan2.coverUrl = String.valueOf(R.mipmap.meituan2);
+                meituan2.isCoverUrlLocal = true;
+                meituan2.couponName = "美团优惠券至少2元起(新老用户都可领)";
+                meituan2.jumpUrl = ConstantUrl.URL_MEITUAN_BONUS_2;
+                meituan2.actionEnable = true;
+                meituan2.isLocal = true;
+
+                couponBeanList.add(meituan2);
+                couponBeanList.add(meituan1);
                 break;
             case FunctionFragment.TEMPLATE_TYPE_TEMPLATE:
-                templateBean.setCoverUrl(ConstantUrl.URL_RANDOM_PHOTO_ECY);
-                templateBean.setTemplateName("DIY装机");
-                templateBean.setJumpUrl(ConstantUrl.URL_JD_TEMPLATE_COMPUTER);
-                templateBean.setActionEnable(true);
+                CouponBean template = new CouponBean();
+                template.coverUrl = ConstantUrl.URL_RANDOM_PHOTO_ECY;
+                template.couponName = "DIY装机";
+                template.jumpUrl = ConstantUrl.URL_JD_TEMPLATE_COMPUTER;
+                template.actionEnable = true;
+                template.isLocal = true;
+                couponBeanList.add(template);
                 break;
 
             case FunctionFragment.TEMPLATE_TYPE_GAME:
-                templateBean.setCoverUrl(ConstantUrl.URL_RANDOM_PHOTO_ECY);
-                templateBean.setTemplateName("欢乐猜猜");
-                templateBean.setActionEnable(false);
+//                CouponBean game = new CouponBean();
+//                game.setCoverUrl(ConstantUrl.URL_RANDOM_PHOTO_ECY);
+//                game.setTemplateName("欢乐猜猜");
+//                game.setActionEnable(false);
                 break;
             default:
                 break;
         }
 
-        List<TemplateBean> templateBeanList = new ArrayList<>();
-        templateBeanList.add(templateBean);
-        return templateBeanList;
+        return couponBeanList;
     }
 
     public static List<GoodsBean> getGoodsList() {
         List<GoodsBean> goodsList = new ArrayList<>();
         GoodsBean goodsBean = new GoodsBean();
-        goodsBean.setCoverImageUrl("https://img14.360buyimg.com/n0/jfs/t1/97822/29/10664/255856/5e1ebf26Eea1a28ec/ac2c298127bae9a4.jpg");
-        goodsBean.setTitle("华硕(ASUS) PN60 商用办公家用教育 台式机电脑主机 (i7-8550U 256G SSD 8G 正版Win10 三年上门)迷你台式");
-        goodsBean.setDescription("远程教育,在家办公,顺畅不卡顿!酷睿i7-8550U处理器、256GSSD,8G内存,便携电脑移动办公更强性能购买PN61-i7,高效稳定");
-        goodsBean.setReason("小巧便携且高效稳定");
-        goodsBean.setOldPrice(3899);
-        goodsBean.setNowPrice(3849);
-        goodsBean.setRebate("50");
-        goodsBean.setShopName("华硕京东自营旗舰店");
-        goodsBean.setProvince("广东");
-        goodsBean.setSelfBusiness(true);
-        goodsBean.setHasCoupon(true);
-        goodsBean.setCouponMoney("30");
-        goodsBean.setGoodsType(GoodsBean.TYPE_TECHNOLOGY);
-        goodsBean.setGoodsUrl("https://union-click.jd.com/jdc?e=&p=AyIGZRtcEgAXA1QfWhIyEgZUGlocChIFVBJeJUZNXwtEa0xHV0YXEEULWldTCQQHCllHGAdFBwtEQkQBBRxNVlQYBUkeTVxNCRNLGEF6RwtVGloUAxsPVRlaHAciVR1hWBNYZl42cCVPUVF6V1skdFxJZ1kXaxQyEgZUGFMcBREAUitrFQUiVDtADnsGQQBWH1JHCxMCUksOJQMiB1ETXhYKGwFdHVsQByIAVRJrU1dTWhNNBEtnbFMSRQYlMiIEZStrFTIRNxd1WBYLFVcAEg8dUkcFUE9THAsbBl0aWRBSFwIFTl8VChI3VxpaEQs%3D");
-        goodsBean.setSalesPerMonth(1022);
-        goodsBean.setGoodCommentPercent(0.98d);
-        goodsList.add(goodsBean);
-        goodsList.add(goodsBean);
-        goodsList.add(goodsBean);
-        goodsList.add(goodsBean);
-        goodsList.add(goodsBean);
+        goodsBean.coverImageUrl = "https://img14.360buyimg.com/n12/jfs/t1/97822/29/10664/255856/5e1ebf26Eea1a28ec/ac2c298127bae9a4.jpg";
+        goodsBean.title = "华硕(ASUS) PN60 商用办公家用教育 台式机电脑主机 (i3-8130U 128G SSD 4G 正版Win10 三年上门)迷你主机";
+        goodsBean.description = "远程教育,在家办公,顺畅不卡顿!";
+        goodsBean.reason = "小巧稳定的个人服务器";
+        goodsBean.oldPrice = 229900;
+        goodsBean.nowPrice = 229900;
+        goodsBean.rebate = 0;
+        goodsBean.shopName = "华硕京东自营旗舰店";
+        goodsBean.province = "广东";
+        goodsBean.isSelfBusiness = true;
+        goodsBean.hasCoupon = true;
+        goodsBean.couponInfo = "30";
+        goodsBean.goodsType = GoodsBean.TYPE_TECHNOLOGY;
+        goodsBean.goodsUrl = "https://union-click.jd.com/jdc?e=&p=AyIGZRtcEgAXA1QfWhIyEgZUGloRAxYEXRpfJUZNXwtEa0xHV0YXEEULWldTCQQHCllHGAdFBwtEQkQBBRxNVlQYBUkeTVxNCRNLGEF6RwtVGloUAxYGURhTFAYichZtC3VcTl8wREFHCkp9MRwcSkBbZ1kXaxQyEgZUGFMcBREAUitrFQUiVDtADnsGQQBWH1JHCxMCUksOJQMiB1ETXR0CEANQG10cASIAVRJrU1dTWhNNBEtnbFMSRQYlMiIEZStrFTIRNxd1CxNSEwUFHggXBRFQUB0JFlFAUFcaXhUBGwVXT14UBEU3VxpaEQs%3D";
+        goodsBean.salesPerMonth = 1022;
+        goodsBean.commentNum = 1022;
+        goodsBean.goodCommentPercent = 0.97d;
+        goodsBean.isLocal = true;
+        goodsBean.enable = true;
         goodsList.add(goodsBean);
         return goodsList;
     }

+ 42 - 0
app/src/main/java/com/itant/shibei/tool/MD5Tool.java

@@ -0,0 +1,42 @@
+package com.itant.shibei.tool;
+
+
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/7 18:37
+ * Description: md5工具
+ */
+public class MD5Tool {
+    private MD5Tool(){}
+
+    /**
+     * 将字符串转成MD5值
+     * @param string
+     * @return
+     */
+    public static String stringToMD5(String string) {
+        byte[] hash;
+        try {
+            hash = MessageDigest.getInstance("MD5").digest(string.getBytes(Charset.forName("UTF-8")));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        StringBuilder hex = new StringBuilder(hash.length * 2);
+        for (byte b : hash) {
+            if ((b & 0xFF) < 0x10) {
+                hex.append(0);
+            }
+            hex.append(Integer.toHexString(b & 0xFF));
+        }
+        return hex.toString();
+    }
+
+}

+ 57 - 2
app/src/main/java/com/itant/shibei/tool/StringTool.java

@@ -1,11 +1,17 @@
 package com.itant.shibei.tool;
 
+import android.text.TextUtils;
+import android.util.Base64;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.nio.charset.StandardCharsets;
 import java.text.DecimalFormat;
 
 /**
- * Copyright (C), 2019-2020, Genlot
  *
- * @author 詹子聪
+ *
+ * @author Miekir
  * @date 2020/7/11 21:59
  * Description: 字符串相关工具
  */
@@ -16,4 +22,53 @@ public class StringTool {
     public static String getShowMoney(double number) {
         return FORMAT_TWO.format(number);
     }
+
+    public static String getEncodeString(String rawString) {
+        String base64String = Base64.encodeToString(rawString.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
+        StringBuilder stringBuffer = new StringBuilder(base64String);
+        if (base64String.length() > 0) {
+            stringBuffer.setCharAt(base64String.length()/2, 's');
+        }
+        String base64Reverse = stringBuffer.reverse().toString();
+        return MD5Tool.stringToMD5(base64Reverse);
+    }
+
+    /**
+     * 分转元,保留2位小数
+     * @return
+     */
+    public static String longCent2Yuan(long cent) {
+        BigDecimal centDecimal = BigDecimal.valueOf(cent);
+        BigDecimal exchangeNum = BigDecimal.valueOf(100);
+        BigDecimal yuanDecimal = centDecimal.divide(exchangeNum, 2, RoundingMode.UNNECESSARY);
+        return yuanDecimal.toPlainString();
+    }
+
+    public static String getNumberString(long number) {
+        if (number > 100000) {
+            return "10w+";
+        }
+
+        if (number > 10000) {
+            return "1w+";
+        }
+
+        if (number > 1000) {
+            return "1k+";
+        }
+
+        return String.valueOf(number);
+    }
+
+    public static String getMixToken(String token) {
+        if (TextUtils.isEmpty(token) || token.length() < 2) {
+            return token;
+        }
+        int len = token.length();
+        char[] tokenArray = token.toCharArray();
+        char temp = tokenArray[len-2];
+        tokenArray[len-2] = tokenArray[len-1];
+        tokenArray[len-1] = temp;
+        return new String(tokenArray);
+    }
 }

+ 10 - 0
app/src/main/java/com/itant/shibei/tool/SystemTool.java

@@ -28,6 +28,11 @@ public class SystemTool {
         return false;
     }
 
+    /**
+     * 获取版本code
+     * @param context
+     * @return
+     */
     public static int getVersionCode(Context context) {
         int versionCode = -1;
         PackageManager packageManager = context.getPackageManager();
@@ -43,6 +48,11 @@ public class SystemTool {
         return versionCode;
     }
 
+    /**
+     * 获取版本名字
+     * @param context
+     * @return
+     */
     public static String getVersionName(Context context) {
         PackageManager manager = context.getPackageManager();
         if (manager != null) {

+ 51 - 0
app/src/main/java/com/itant/shibei/tool/TimeTool.java

@@ -0,0 +1,51 @@
+package com.itant.shibei.tool;
+
+import java.util.Calendar;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/24 9:15
+ * Description: 时间相关工具类
+ */
+public class TimeTool {
+    /**
+     *
+     * @return 返回当前是上午、中午、下午
+     */
+    public static String getCurrentTimePeriod() {
+        Calendar cal = Calendar.getInstance();
+        int hour = cal.get(Calendar.HOUR_OF_DAY);
+
+        if (hour >= 3 && hour < 6) {
+            return "凌晨";
+        }
+
+        if (hour >= 6 && hour < 8) {
+            return "早上";
+        }
+
+        if (hour >= 8 && hour < 11) {
+            return "上午";
+        }
+
+        if (hour >= 11 && hour < 13) {
+            return "中午";
+        }
+
+        if (hour >= 13 && hour < 17) {
+            return "下午";
+        }
+
+        if (hour >= 17 && hour < 19) {
+            return "傍晚";
+        }
+
+        if (hour >= 19 && hour < 23) {
+            return "晚上";
+        }
+
+        return "深夜";
+    }
+}

+ 28 - 0
app/src/main/java/com/itant/shibei/ui/BeiApplication.java

@@ -0,0 +1,28 @@
+package com.itant.shibei.ui;
+
+import android.app.Application;
+
+import com.itant.shibei.manager.PreferenceManager;
+import com.umeng.analytics.MobclickAgent;
+import com.umeng.commonsdk.UMConfigure;
+
+import rx_activity_result2.RxActivityResult;
+
+/**
+ *
+ * @author Miekir
+ * @date 2020/8/2 9:34
+ * Description:
+ */
+public class BeiApplication extends Application {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        PreferenceManager.getInstance().initPreference(this);
+        RxActivityResult.register(this);
+        UMConfigure.init(this, "5f35e7d9d3093221547834b6", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, "");
+        //选择AUTO页面采集模式,统计SDK基础指标无需手动埋点可自动采集。
+        MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO);
+    }
+}

+ 2 - 2
app/src/main/java/com/itant/shibei/ui/home/HomeFragment.java

@@ -1,4 +1,4 @@
-package com.itant.shibei.ui.home;
+package com.itant.shibei.ui;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -7,11 +7,11 @@ import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProviders;
 import androidx.viewpager.widget.ViewPager;
 
 import com.flyco.tablayout.SlidingTabLayout;
 import com.itant.shibei.R;
+import com.itant.shibei.ui.home.goods.GoodsFragment;
 import com.miekir.common.adapter.TabFragmentAdapter;
 
 import java.util.ArrayList;

+ 7 - 8
app/src/main/java/com/itant/shibei/ui/MainActivity.java

@@ -4,24 +4,23 @@ import android.os.Bundle;
 import android.view.MenuItem;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.viewpager.widget.ViewPager;
+
 import com.google.android.material.bottomnavigation.BottomNavigationView;
 import com.google.android.material.bottomnavigation.LabelVisibilityMode;
 import com.itant.shibei.R;
 import com.itant.shibei.adapter.FragmentPagerItemAdapter;
-import com.itant.shibei.base.BaseShiBeiActivity;
+import com.itant.shibei.base.BaseBeiActivity;
 import com.itant.shibei.ui.function.FunctionFragment;
-import com.itant.shibei.ui.home.HomeFragment;
 import com.itant.shibei.ui.mine.MineFragment;
 
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.NavController;
-import androidx.viewpager.widget.ViewPager;
-
 import java.util.ArrayList;
 import java.util.List;
 
-public class MainActivity extends BaseShiBeiActivity implements View.OnClickListener {
+public class MainActivity extends BaseBeiActivity implements View.OnClickListener {
 
     private NavController navController;
     private ViewPager vp_main;

+ 23 - 1
app/src/main/java/com/itant/shibei/ui/MainViewModel.java

@@ -7,8 +7,30 @@ import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModel;
 
 /**
- * 使用ViewModel保存Fragment的数据,阻止Fragment在配合BottomNavigationView切换时总被销毁的问题
+ * 使用ViewModel + LiveData保存Fragment的数据,阻止Fragment在配合BottomNavigationView切换时总被销毁的问题
+ *
  */
+/*public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+    mainViewModel=new ViewModelProvider(getActivity()).get(MainViewModel.class);
+    root=mainViewModel.getHomeView().getValue();
+    if(root==null){
+    //正常初始化View
+    root = inflater.inflate(R.layout.fragment_home, container, false);
+    //...
+    }
+    //...
+    return root;
+}*/
+/*@Override
+public void onDestroyView() {
+    super.onDestroyView();
+    //其实以下代码不一定写在onDestroyView(),可以获取指定后root的地方都可以
+    ViewGroup parent = (ViewGroup) root.getParent();
+    if(parent!=null){
+        parent.removeView(root);
+    }
+    mainViewModel.setHomeView(root);
+}*/
 public class MainViewModel extends ViewModel {
     private MutableLiveData<View> mTemplateView;
     private MutableLiveData<View> mHomeView;

+ 165 - 0
app/src/main/java/com/itant/shibei/ui/TabActivity.java

@@ -0,0 +1,165 @@
+package com.itant.shibei.ui;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager.widget.ViewPager;
+
+import com.flyco.tablayout.SlidingTabLayout;
+import com.flyco.tablayout.listener.OnTabSelectListener;
+import com.google.android.material.appbar.AppBarLayout;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.base.ITopActionListener;
+import com.itant.shibei.bean.UpgradeBean;
+import com.itant.shibei.common.ICommonView;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.SystemTool;
+import com.itant.shibei.ui.function.FunctionFragment;
+import com.itant.shibei.ui.home.about.AboutFragment;
+import com.itant.shibei.ui.home.about.upgrade.UpgradePresenter;
+import com.itant.shibei.ui.home.coupon.TemplateFragment;
+import com.itant.shibei.ui.home.goods.GoodsFragment;
+import com.itant.shibei.ui.home.search.SearchActivity;
+import com.itant.shibei.ui.home.tool.ToolFragment;
+import com.itant.shibei.ui.mine.MineActivity;
+import com.itant.shibei.ui.mine.login.LoginActivity;
+import com.itant.shibei.widget.AppbarTranslateListener;
+import com.miekir.common.adapter.TabFragmentAdapter;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TabActivity extends BaseBeiActivity implements View.OnClickListener, ICommonView<UpgradeBean> {
+
+    @InjectPresenter
+    UpgradePresenter upgradePresenter;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // 获取版本更新
+        upgradePresenter.getUpgradeInfo();
+    }
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_tab;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        String[] titles = {"推荐", "好券", "工具", "关于"};
+        List<Fragment> fragments = new ArrayList<>();
+        fragments.add(new GoodsFragment());
+        fragments.add(new TemplateFragment(FunctionFragment.TEMPLATE_TYPE_BONUS));
+        //fragments.add(new TemplateFragment(FunctionFragment.TEMPLATE_TYPE_TEMPLATE));
+        fragments.add(new ToolFragment());
+        fragments.add(new AboutFragment());
+
+        ViewPager vp_main = findViewById(R.id.vp_main);
+        TabFragmentAdapter adapter = new TabFragmentAdapter(getSupportFragmentManager(), fragments);
+        vp_main.setAdapter(adapter);
+        vp_main.setOffscreenPageLimit(titles.length);
+        //vp_home.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+
+        SlidingTabLayout stl_home = findViewById(R.id.stl_home);
+        stl_home.setViewPager(vp_main, titles);
+        // 默认选中返利商品列表
+        stl_home.setCurrentTab(0);
+        stl_home.setOnTabSelectListener(new OnTabSelectListener() {
+            @Override
+            public void onTabSelect(int position) {
+            }
+
+            @Override
+            public void onTabReselect(int position) {
+                // 重复选中Tab可以让该Tab对应的列表滚动到顶部
+                Fragment fragment = fragments.get(position);
+                if (fragment instanceof ITopActionListener) {
+                    ((ITopActionListener) fragment).onTopAction();
+                }
+            }
+        });
+
+        ViewTool.setOnClickListener(this, this,
+                new int[]{R.id.fl_search, R.id.fl_search_top, R.id.fl_more});
+
+        double defaultHeight = getResources().getDimension(R.dimen.margin_default);
+        double defaultWidth = getResources().getDimension(R.dimen.height_edit_text);
+
+        View rl_search_top = findViewById(R.id.rl_search_top);
+        View rl_search_top_shadow = findViewById(R.id.rl_search_top_shadow);
+        View rl_search = findViewById(R.id.rl_search);
+        // 搜索图标动画
+        AppBarLayout abl_main = findViewById(R.id.abl_main);
+        AppbarTranslateListener listener = new AppbarTranslateListener(
+                this,
+                defaultWidth, defaultHeight,
+                rl_search_top, rl_search_top_shadow, rl_search);
+        abl_main.addOnOffsetChangedListener(listener);
+    }
+
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.fl_more:
+                // 点击更多
+                if (UserInfoManager.getInstance().isLogin()) {
+                    startActivity(new Intent(this, MineActivity.class));
+                } else {
+                    // 登录
+                    Intent intent = new Intent(this, LoginActivity.class);
+                    startActivity(intent);
+                }
+                break;
+            case R.id.fl_search_top:
+            case R.id.fl_search:
+                // 点击搜索
+                startActivity(new Intent(this, SearchActivity.class));
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onCommonResult(boolean success, String message, UpgradeBean resultBean) {
+        // 获取到了版本信息
+        if (success && resultBean != null && SystemTool.getVersionCode(this) < resultBean.versionCode) {
+            AlertDialog alertDialog = new AlertDialog.Builder(this)
+                    .setMessage(resultBean.content)
+                    .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+                    .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+                        dialog.dismiss();
+                        if (!TextUtils.isEmpty(resultBean.url)) {
+                            ActivityTool.openUrl(TabActivity.this, resultBean.url);
+                        }
+                    }).create();
+            alertDialog.show();
+        }
+    }
+
+    private long mLastBackMillis;
+    @Override
+    public void onBackPressed() {
+        if (System.currentTimeMillis() - mLastBackMillis > 3000) {
+            ToastTool.showShort("再按一次退出");
+            mLastBackMillis = System.currentTimeMillis();
+        } else {
+            super.onBackPressed();
+        }
+    }
+}

+ 1 - 1
app/src/main/java/com/itant/shibei/ui/function/FunctionFragment.java

@@ -11,7 +11,7 @@ import androidx.viewpager.widget.ViewPager;
 
 import com.flyco.tablayout.SlidingTabLayout;
 import com.itant.shibei.R;
-import com.itant.shibei.ui.function.template.TemplateFragment;
+import com.itant.shibei.ui.home.coupon.TemplateFragment;
 import com.miekir.common.adapter.TabFragmentAdapter;
 
 import java.util.ArrayList;

+ 0 - 17
app/src/main/java/com/itant/shibei/ui/function/template/ITemplateView.java

@@ -1,17 +0,0 @@
-package com.itant.shibei.ui.function.template;
-
-import com.itant.shibei.bean.TemplateBean;
-import com.miekir.mvp.view.IView;
-
-import java.util.List;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/8 19:44
- * Description: Template的View
- */
-public interface ITemplateView extends IView {
-    void onTemplateDataCome(List<TemplateBean> templateBeanList);
-}

+ 0 - 65
app/src/main/java/com/itant/shibei/ui/function/template/TemplateFragment.java

@@ -1,65 +0,0 @@
-package com.itant.shibei.ui.function.template;
-
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.itant.library.recyclerview.CommonAdapter;
-import com.itant.shibei.R;
-import com.itant.shibei.adapter.TemplateAdapter;
-import com.itant.shibei.bean.TemplateBean;
-import com.itant.shibei.ui.function.FunctionFragment;
-import com.itant.shibei.widget.DividerItemDecoration;
-import com.miekir.mvp.presenter.InjectPresenter;
-import com.miekir.mvp.view.BaseMVPFragment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 模板Fragment
- */
-public class TemplateFragment extends BaseMVPFragment implements ITemplateView {
-    private int mTemplateType = FunctionFragment.TEMPLATE_TYPE_BONUS;
-    private List<TemplateBean> mTemplateBeanList = new ArrayList<>();
-    private CommonAdapter<TemplateBean> mAdapter;
-
-    @InjectPresenter
-    TemplatePresenter mPresenter;
-
-    public TemplateFragment(int templateType) {
-        mTemplateType = templateType;
-    }
-
-    @Override
-    public int getLayoutResId() {
-        return R.layout.fragment_function_template;
-    }
-
-    @Override
-    public void onCreateViewFinished(@Nullable Bundle savedInstanceState) {
-        super.onCreateViewFinished(savedInstanceState);
-
-        RecyclerView rv_template = rootView.findViewById(R.id.rv_template);
-        // 必须要设置LayoutManager,否则RecyclerView不知道要使用什么布局,从而在界面上不显示
-        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
-        rv_template.setLayoutManager(layoutManager);
-        int dividerWidth = (int) getResources().getDimension(R.dimen.margin_s);
-        DividerItemDecoration decoration = new DividerItemDecoration(dividerWidth);
-        rv_template.addItemDecoration(decoration);
-        mAdapter = new TemplateAdapter(getActivity(), R.layout.item_template, mTemplateBeanList);
-        rv_template.setAdapter(mAdapter);
-
-        // 从服务器获取数据
-        mPresenter.getTemplateData(mTemplateType);
-    }
-
-    @Override
-    public void onTemplateDataCome(List<TemplateBean> templateBeanList) {
-        // todo 分页,判断是否第0页(下拉刷新)
-        mTemplateBeanList.addAll(templateBeanList);
-        mAdapter.notifyDataSetChanged();
-    }
-}

+ 0 - 28
app/src/main/java/com/itant/shibei/ui/function/template/TemplatePresenter.java

@@ -1,28 +0,0 @@
-package com.itant.shibei.ui.function.template;
-
-import com.itant.shibei.bean.TemplateBean;
-import com.itant.shibei.tool.DataTool;
-import com.miekir.mvp.presenter.BasePresenter;
-
-import java.util.List;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/8 19:43
- * Description: 模板的Presenter
- */
-public class TemplatePresenter extends BasePresenter<ITemplateView> {
-
-    /**
-     * todo 从服务器获取数据,切换线程
-     * @param functionType
-     */
-    public void getTemplateData(int functionType) {
-        if (getView() != null) {
-            List<TemplateBean> templateBeanList = DataTool.getTemplateList(functionType);
-            getView().onTemplateDataCome(templateBeanList);
-        }
-    }
-}

+ 0 - 77
app/src/main/java/com/itant/shibei/ui/home/GoodsFragment.java

@@ -1,77 +0,0 @@
-package com.itant.shibei.ui.home;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.itant.shibei.R;
-import com.itant.shibei.adapter.GoodsAdapter;
-import com.itant.shibei.bean.GoodsBean;
-import com.itant.shibei.widget.DividerItemDecoration;
-import com.miekir.mvp.presenter.InjectPresenter;
-import com.miekir.mvp.view.BaseMVPFragment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/7/5 22:07
- * Description: 商品列表Fragment
- */
-public class GoodsFragment extends BaseMVPFragment implements IGoodsView {
-
-    @InjectPresenter
-    GoodsPresenter mPresenter;
-
-    private GoodsAdapter mAdapter;
-    private RecyclerView rv_goods;
-    private List<GoodsBean> mGoodsList = new ArrayList<>();
-
-    private ImageView iv_empty;
-
-    @Override
-    public int getLayoutResId() {
-        return R.layout.fragment_home_goods;
-    }
-
-    @Override
-    public void onCreateViewFinished(@Nullable Bundle savedInstanceState) {
-        super.onCreateViewFinished(savedInstanceState);
-
-        iv_empty = rootView.findViewById(R.id.iv_empty);
-        rv_goods = rootView.findViewById(R.id.rv_goods);
-        // 必须要设置LayoutManager,否则RecyclerView不知道要使用什么布局,从而在界面上不显示
-        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
-        rv_goods.setLayoutManager(layoutManager);
-
-        int dividerWidth = (int) getResources().getDimension(R.dimen.margin_s);
-        DividerItemDecoration decoration = new DividerItemDecoration(dividerWidth);
-        rv_goods.addItemDecoration(decoration);
-        mAdapter = new GoodsAdapter(getActivity(), R.layout.item_goods, mGoodsList);
-        rv_goods.setAdapter(mAdapter);
-
-        // 获取数据
-        mPresenter.getGoodsData();
-    }
-
-    @Override
-    public void onGoodsDataCome(List<GoodsBean> goodsList) {
-        // todo 分页或者刷新
-        mGoodsList.clear();
-        mGoodsList.addAll(goodsList);
-        mAdapter.notifyDataSetChanged();
-        if (mGoodsList.size() == 0) {
-            iv_empty.setVisibility(View.VISIBLE);
-        } else {
-            iv_empty.setVisibility(View.GONE);
-        }
-
-    }
-}

+ 0 - 25
app/src/main/java/com/itant/shibei/ui/home/GoodsPresenter.java

@@ -1,25 +0,0 @@
-package com.itant.shibei.ui.home;
-
-import com.itant.shibei.bean.GoodsBean;
-import com.itant.shibei.tool.DataTool;
-import com.miekir.mvp.presenter.BasePresenter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Copyright (C), 2019-2020, Genlot
- *
- * @author 詹子聪
- * @date 2020/7/9 20:54
- * Description: 商品的相关数据接口处理
- */
-public class GoodsPresenter extends BasePresenter<IGoodsView> {
-
-    public void getGoodsData() {
-        List<GoodsBean> goodsList = DataTool.getGoodsList();
-        if (getView() != null) {
-            getView().onGoodsDataCome(goodsList);
-        }
-    }
-}

+ 0 - 17
app/src/main/java/com/itant/shibei/ui/home/IGoodsView.java

@@ -1,17 +0,0 @@
-package com.itant.shibei.ui.home;
-
-import com.itant.shibei.bean.GoodsBean;
-import com.miekir.mvp.view.IView;
-
-import java.util.List;
-
-/**
- * Copyright (C), 2019-2020, Genlot
- *
- * @author 詹子聪
- * @date 2020/7/9 20:52
- * Description: 商品的View回调
- */
-public interface IGoodsView extends IView {
-    void onGoodsDataCome(List<GoodsBean> goodsList);
-}

+ 97 - 0
app/src/main/java/com/itant/shibei/ui/home/about/AboutFragment.java

@@ -0,0 +1,97 @@
+package com.itant.shibei.ui.home.about;
+
+import android.content.DialogInterface;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.itant.shibei.R;
+import com.itant.shibei.bean.UpgradeBean;
+import com.itant.shibei.common.ICommonView;
+import com.itant.shibei.tool.SystemTool;
+import com.itant.shibei.ui.home.about.upgrade.UpgradePresenter;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+import com.miekir.mvp.view.BaseMVPFragment;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 关于界面
+ * todo 点击版本号检查更新
+ */
+public class AboutFragment extends BaseMVPFragment implements View.OnClickListener, ICommonView<UpgradeBean> {
+
+    @InjectPresenter
+    UpgradePresenter upgradePresenter;
+
+    @Override
+    protected void onViewInit() {
+        TextView tv_title = rootView.findViewById(R.id.tv_title);
+        tv_title.setText(" 拾贝 v" + SystemTool.getVersionName(getActivity()));
+        rootView.findViewById(R.id.tv_feedback).setOnClickListener(this);
+        rootView.findViewById(R.id.tv_update).setOnClickListener(this);
+        rootView.findViewById(R.id.tv_more).setOnClickListener(this);
+        rootView.findViewById(R.id.tv_protocol).setOnClickListener(this);
+    }
+
+    @Override
+    protected void onLazyLoad() {
+
+    }
+
+    @Override
+    public int getLayoutResId() {
+        return R.layout.fragment_about;
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.tv_feedback:
+                SystemTool.sendEmail(getActivity());
+                break;
+
+            case R.id.tv_update:
+                showLoading();
+                upgradePresenter.getUpgradeInfo();
+                break;
+
+            case R.id.tv_more:
+                SystemTool.copyText(getActivity(), "http://jianjie.life");
+                ToastTool.showShort("已拷贝主页链接");
+                break;
+            case R.id.tv_protocol:
+                ActivityTool.openUrl(getActivity(), "http://note.youdao.com/s/KTiCW1qH");
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onCommonResult(boolean success, String message, UpgradeBean resultBean) {
+        dismissLoading();
+        // 获取到了版本信息
+        if (success &&
+                resultBean != null &&
+                SystemTool.getVersionCode(getActivity()) < resultBean.versionCode &&
+                !TextUtils.isEmpty(resultBean.url)) {
+            AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                    .setMessage(resultBean.content)
+                    .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+                    .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+                        dialog.dismiss();
+                        ActivityTool.openUrl(getActivity(), resultBean.url);
+                    }).create();
+            alertDialog.setCancelable(false);
+            alertDialog.setCanceledOnTouchOutside(false);
+            alertDialog.show();
+        } else {
+            ToastTool.showShort("当前已经是最新版本啦");
+        }
+    }
+}

+ 49 - 0
app/src/main/java/com/itant/shibei/ui/home/about/upgrade/UpgradePresenter.java

@@ -0,0 +1,49 @@
+package com.itant.shibei.ui.home.about.upgrade;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.bean.UpgradeBean;
+import com.itant.shibei.common.ICommonView;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:43
+ * Description: 模板的Presenter
+ */
+public class UpgradePresenter extends BasePresenter<ICommonView<UpgradeBean>> {
+    /**
+     * 获取版本信息
+     */
+    public void getUpgradeInfo() {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getUpgradeInfo()
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<UpgradeBean>() {
+                    @Override
+                    public void onSuccess(UpgradeBean result) {
+                        if (getView() != null) {
+                            getView().onCommonResult(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onCommonResult(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+
+}

+ 68 - 0
app/src/main/java/com/itant/shibei/ui/home/coupon/CouponBean.java

@@ -0,0 +1,68 @@
+package com.itant.shibei.ui.home.coupon;
+
+import com.itant.shibei.constant.ConstantUrl;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/7 19:37
+ * Description: 优惠券实体
+ */
+public class CouponBean implements Serializable {
+    /**
+     * 模板Id
+     */
+    public long id;
+    /**
+     * 模板类型
+     */
+    public int couponType;
+
+    /**
+     * 模板名称
+     */
+    public String couponName;
+    /**
+     * 点击之后跳转的链接
+     */
+    public String jumpUrl;
+    /**
+     * 封面图片URL
+     */
+    public String coverUrl = ConstantUrl.URL_RANDOM_PHOTO_WALLPAPER;
+
+
+    /**
+     * 是否可以响应点击
+     */
+    public boolean actionEnable;
+
+    /**是否是本地写死的数据*/
+    public boolean isLocal;
+
+    /**
+     * 创建时间
+     */
+    public long createTimeMillis;
+
+    /**
+     * 更新时间
+     */
+    public long updateTimeMillis;
+
+    public boolean isCoverUrlLocal;
+
+    public void updateData(CouponBean couponBean) {
+        this.couponType = couponBean.couponType;
+        this.couponName = couponBean.couponName;
+        this.coverUrl = couponBean.coverUrl;
+        this.jumpUrl = couponBean.jumpUrl;
+        this.actionEnable = couponBean.actionEnable;
+        this.isLocal = couponBean.isLocal;
+        this.createTimeMillis = couponBean.createTimeMillis;
+        this.updateTimeMillis = couponBean.updateTimeMillis;
+    }
+}

+ 17 - 0
app/src/main/java/com/itant/shibei/ui/home/coupon/ITemplateView.java

@@ -0,0 +1,17 @@
+package com.itant.shibei.ui.home.coupon;
+
+import com.miekir.mvp.view.IView;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:44
+ * Description: Template的View
+ */
+public interface ITemplateView<T> extends IView {
+    void onTemplateDataCome(boolean success, String message, List<T> couponBeanList);
+    void onDeleteCouponResult(boolean success, String message, int position);
+}

+ 129 - 0
app/src/main/java/com/itant/shibei/ui/home/coupon/TemplateAdapter.java

@@ -0,0 +1,129 @@
+package com.itant.shibei.ui.home.coupon;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.bumptech.glide.request.transition.Transition;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.itant.shibei.R;
+import com.itant.shibei.base.ItemLongClickListener;
+import com.itant.shibei.manager.UserInfoManager;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+
+import java.util.List;
+
+
+/**
+ * @author Miekir
+ * @date 2020/7/6 20:08
+ * Description: 首页商品适配器
+ */
+public class TemplateAdapter extends BaseQuickAdapter<CouponBean, BaseViewHolder> {
+    private int mRadius = 8;
+    private Context mContext;
+
+    public TemplateAdapter(Context context, @Nullable List<CouponBean> data) {
+        super(R.layout.item_template, data);
+        mRadius = (int) context.getResources().getDimension(R.dimen.margin_s);
+        mContext = context;
+    }
+
+    @Override
+    protected void convert(@NonNull BaseViewHolder holder, CouponBean couponBean) {
+        ImageView iv_template = holder.getView(R.id.iv_template);
+        // 解决图片错乱
+        iv_template.setTag(R.id.aciv_goods, couponBean.coverUrl);
+        if (couponBean.isCoverUrlLocal) {
+            int resourceId = Integer.parseInt(couponBean.coverUrl);
+            iv_template.setScaleType(ImageView.ScaleType.CENTER_CROP);
+            iv_template.setImageResource(resourceId);
+            //Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId);
+            // 圆角
+            //RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
+            //roundDrawable.setCornerRadius(48);
+            //iv_template.setImageDrawable(roundDrawable);
+            // 这些处理都是把drawable圆角了,如果drawable本身尺寸比较小,就起不到圆角效果
+        } else {
+            // 圆角
+            Glide.with(mContext).load(couponBean.coverUrl)
+                    //.transform(new CenterCrop(), new RoundedCorners(48))
+                    //.apply(RequestOptions.bitmapTransform(new RoundedCorners(mRadius)))
+                    //.apply(new RequestOptions()
+                    //        .skipMemoryCache(true)
+                    //        .diskCacheStrategy(DiskCacheStrategy.NONE))
+                    //.centerCrop() 千万不要加,加了就没有圆角效果了
+                    //先加载原图大小的十分之一
+                    .thumbnail(0.1f)
+                    .into(new SimpleTarget<Drawable>() {
+                        @Override
+                        public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
+                            //iv_template.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                            //iv_template.setImageDrawable(resource);
+                            if (resource == null) {
+                                iv_template.setImageResource(R.mipmap.logo_gray);
+                                return;
+                            }
+
+                            String coverUrl = null;
+                            try {
+                                coverUrl = (String) iv_template.getTag(R.id.aciv_goods);
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                            if (TextUtils.isEmpty(coverUrl) || !TextUtils.equals(coverUrl, couponBean.coverUrl)) {
+                                iv_template.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+                                iv_template.setImageResource(R.mipmap.logo_gray);
+                            } else {
+                                iv_template.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                                iv_template.setImageDrawable(resource);
+                            }
+                        }
+                    });
+        }
+
+
+        holder.setText(R.id.tv_template, couponBean.couponName);
+        holder.setOnClickListener(R.id.rl_template, new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (!couponBean.actionEnable) {
+                    ToastTool.showShort("敬请期待");
+                    return;
+                }
+
+                ActivityTool.openUrl((Activity) mContext, couponBean.jumpUrl);
+            }
+        });
+
+        if (UserInfoManager.getInstance().isLogin() &&
+                TextUtils.equals("[email protected]", UserInfoManager.getInstance().getBeiUser().email)) {
+            View view = holder.getView(R.id.rl_template);
+            view.setOnLongClickListener(v -> {
+                if (couponLongClickListener != null) {
+                    couponLongClickListener.onItemLongClick(holder.getLayoutPosition());
+                }
+                return false;
+            });
+        }
+    }
+
+    private ItemLongClickListener couponLongClickListener;
+    public ItemLongClickListener getCouponLongClickListener() {
+        return couponLongClickListener;
+    }
+
+    public void setCouponLongClickListener(ItemLongClickListener couponListener) {
+        this.couponLongClickListener = couponListener;
+    }
+}

+ 238 - 0
app/src/main/java/com/itant/shibei/ui/home/coupon/TemplateFragment.java

@@ -0,0 +1,238 @@
+package com.itant.shibei.ui.home.coupon;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.itant.shibei.R;
+import com.itant.shibei.base.ITopActionListener;
+import com.itant.shibei.tool.DataTool;
+import com.itant.shibei.ui.function.FunctionFragment;
+import com.itant.shibei.ui.mine.coupon.AddCouponActivity;
+import com.itant.shibei.widget.bottomlistener.OnRcvScrollListener;
+import com.itant.shibei.widget.decoration.NormalDividerItemDecoration;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+import com.miekir.mvp.view.BaseMVPFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import rx_activity_result2.RxActivityResult;
+
+import static android.app.Activity.RESULT_OK;
+
+/**
+ * 模板Fragment
+ */
+public class TemplateFragment extends BaseMVPFragment implements ITemplateView<CouponBean>, ITopActionListener {
+    private static final int PAGE_START = 0;
+    private static final int PAGE_SIZE = 20;
+    
+    private int mTemplateType = FunctionFragment.TEMPLATE_TYPE_BONUS;
+
+    @InjectPresenter
+    TemplatePresenter mPresenter;
+    
+    private SwipeRefreshLayout srl_template;
+    private List<CouponBean> mCouponBeanList = new ArrayList<>();
+    private TemplateAdapter mAdapter;
+    private RecyclerView rv_template;
+
+    /**当前页*/
+    private int mCurrentPage = PAGE_START;
+    /**是否下拉获取数据*/
+    private boolean mIsRefresh = true;
+
+    public TemplateFragment(int templateType) {
+        mTemplateType = templateType;
+    }
+
+    @Override
+    public int getLayoutResId() {
+        return R.layout.fragment_function_template;
+    }
+
+    @Override
+    protected void onViewInit() {
+        rv_template = rootView.findViewById(R.id.rv_template);
+        // 必须要设置LayoutManager,否则RecyclerView不知道要使用什么布局,从而在界面上不显示
+        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
+        rv_template.setLayoutManager(layoutManager);
+
+        int dividerWidth = (int) getResources().getDimension(R.dimen.margin_s);
+        NormalDividerItemDecoration decoration = new NormalDividerItemDecoration(dividerWidth);
+        rv_template.addItemDecoration(decoration);
+        mAdapter = new TemplateAdapter(getActivity(), mCouponBeanList);
+        mAdapter.setCouponLongClickListener(this::showAdminDialog);
+        rv_template.setAdapter(mAdapter);
+        // 必须先绑定RecyclerView
+        mAdapter.setEmptyView(R.layout.view_empty, rv_template);
+
+        // 不要滑动一段距离
+        rv_template.smoothScrollToPosition(0);
+        srl_template = rootView.findViewById(R.id.srl_template);
+        srl_template.setColorSchemeResources(
+                R.color.black_theme,
+                R.color.blue_droid,
+                R.color.yellow_droid,
+                R.color.red_droid);
+
+        // 下拉刷新
+        srl_template.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                mIsRefresh = true;
+                mCurrentPage = PAGE_START;
+                mPresenter.getTemplateData(mCurrentPage, PAGE_SIZE);
+            }
+        });
+
+        // 加载更多
+        rv_template.addOnScrollListener(new OnRcvScrollListener(){
+            @Override
+            public void onBottom() {
+                super.onBottom();
+                // 如果到底部了,而且不是正在加载状态,就变为正在加载状态,并及时去加载数据
+                if (!srl_template.isRefreshing()) {
+                    mIsRefresh = false;
+                    srl_template.setRefreshing(true);
+                    mPresenter.getTemplateData(mCurrentPage, PAGE_SIZE);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onLazyLoad() {
+        // 获取数据
+        srl_template.setRefreshing(true);
+        mPresenter.getTemplateData(mCurrentPage, PAGE_SIZE);
+    }
+
+    /**
+     * 修改商品或者删除商品
+     */
+    private void showAdminDialog(int position) {
+        CouponBean couponBean = mCouponBeanList.get(position);
+        if (couponBean.isLocal) {
+            // 本地写死的不能删除
+            return;
+        }
+        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                .setMessage("选择操作")
+                .setPositiveButton("修改", (dialog, which) -> {
+                    dialog.dismiss();
+                    Intent modifyIntent = new Intent(getActivity(), AddCouponActivity.class);
+                    modifyIntent.putExtra(AddCouponActivity.KEY_MODIFY_COUPON, couponBean);
+                    RxActivityResult.on(getActivity())
+                            .startIntent(modifyIntent)
+                            .filter(result -> result.resultCode() == RESULT_OK)
+                            .doOnNext(result -> {
+                                // 修改商品成功之后刷新item
+                                CouponBean modifiedBean = (CouponBean) result.data().getSerializableExtra(AddCouponActivity.KEY_MODIFY_COUPON);
+                                if (modifiedBean == null) {
+                                    return;
+                                }
+                                couponBean.updateData(modifiedBean);
+                                mAdapter.notifyItemChanged(position);
+                            })
+                            .subscribe();
+                })
+                .setNeutralButton("取消", (dialog, which) -> {
+                    dialog.dismiss();
+                })
+                .setNegativeButton("删除", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    showDeleteDialog(position);
+                }).create();
+        alertDialog.setCanceledOnTouchOutside(false);
+        alertDialog.setCancelable(false);
+        alertDialog.show();
+    }
+
+    /**
+     * 是否删除商品
+     */
+    private void showDeleteDialog(int position) {
+        CouponBean couponBean = mCouponBeanList.get(position);
+        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                .setMessage("确定删除优惠券:" + couponBean.couponName)
+                .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+                .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    // 删除商品
+                    mPresenter.deleteCouponById(position, couponBean.id);
+                }).create();
+        alertDialog.show();
+    }
+
+    @Override
+    public void onTemplateDataCome(boolean success, String message, List<CouponBean> couponBeanList) {
+        srl_template.setRefreshing(false);
+        if (!success) {
+            ToastTool.showShort(message);
+            if (mCouponBeanList.size() == 0) {
+                // 本地写死的数据
+                mCouponBeanList.addAll(DataTool.getTemplateList(mTemplateType));
+                mAdapter.notifyDataSetChanged();
+            }
+            return;
+        }
+
+        if (mCurrentPage == PAGE_START) {
+            mCouponBeanList.clear();
+            if (couponBeanList != null) {
+                mCouponBeanList.addAll(couponBeanList);
+            }
+
+            // 本地写死的数据
+            mCouponBeanList.addAll(DataTool.getTemplateList(mTemplateType));
+            mAdapter.notifyDataSetChanged();
+            // 如果是下拉刷新获取的第0页数据,item不要自动滚动一段距离
+            // 或者用这个mLayoutManager.scrollToPositionWithOffset(0, 0);
+            if (mIsRefresh) {
+                ToastTool.showShort("刷新成功");
+                rv_template.smoothScrollToPosition(0);
+            }
+        } else {
+            if (couponBeanList != null) {
+                mCouponBeanList.addAll(couponBeanList);
+            }
+
+            mAdapter.notifyDataSetChanged();
+        }
+
+        // 请求到数据了,页数自增
+        if (couponBeanList != null && couponBeanList.size() > 0) {
+            mCurrentPage++;
+        } else {
+            ToastTool.showShort("没有更多数据了");
+        }
+    }
+
+    /**
+     * 删除商品结果
+     * @param success
+     * @param message
+     * @param position
+     */
+    @Override
+    public void onDeleteCouponResult(boolean success, String message, int position) {
+        if (success) {
+            mAdapter.notifyItemRemoved(position);
+            ToastTool.showShort("删除成功");
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+
+    @Override
+    public void onTopAction() {
+        rv_template.smoothScrollToPosition(0);
+    }
+}

+ 71 - 0
app/src/main/java/com/itant/shibei/ui/home/coupon/TemplatePresenter.java

@@ -0,0 +1,71 @@
+package com.itant.shibei.ui.home.coupon;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import java.util.List;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:43
+ * Description: 模板的Presenter
+ */
+public class TemplatePresenter extends BasePresenter<ITemplateView<CouponBean>> {
+
+
+    public void getTemplateData(int pageNum, int pageSize) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getCouponList(pageNum, pageSize)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<List<CouponBean>>() {
+                    @Override
+                    public void onSuccess(List<CouponBean> result) {
+                        if (getView() != null) {
+                            getView().onTemplateDataCome(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onTemplateDataCome(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+
+
+    public void deleteCouponById(int position, long couponId) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .deleteCouponById(couponId)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<String>() {
+                    @Override
+                    public void onSuccess(String result) {
+                        if (getView() != null) {
+                            getView().onDeleteCouponResult(true, result, position);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onDeleteCouponResult(false, TextUtils.isEmpty(errMsg) ? "删除失败:"+e.getMessage() : errMsg, position);
+                        }
+                    }
+                });
+    }
+}

+ 179 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/GoodsAdapter.java

@@ -0,0 +1,179 @@
+package com.itant.shibei.ui.home.goods;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.bumptech.glide.request.transition.Transition;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.itant.shibei.R;
+import com.itant.shibei.base.ItemLongClickListener;
+import com.itant.shibei.constant.ConstantString;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.ui.home.goods.play.VideoPlayActivity;
+import com.makeramen.roundedimageview.RoundedImageView;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/6 20:08
+ * Description: 首页商品适配器
+ */
+public class GoodsAdapter extends BaseQuickAdapter<GoodsBean, BaseViewHolder> {
+    private Context mContext;
+    private int mRadius;
+
+    public GoodsAdapter(Context context, @Nullable List<GoodsBean> data) {
+        super(R.layout.item_goods, data);
+        mRadius = (int) context.getResources().getDimension(R.dimen.margin_ss);
+        mContext = context;
+    }
+
+    @Override
+    protected void convert(@NonNull BaseViewHolder holder, GoodsBean goodsBean) {
+        holder.setText(R.id.tv_title, goodsBean.title);
+        holder.setText(R.id.tv_shop_name, goodsBean.shopName);
+
+        // 好评
+        holder.setText(R.id.tv_good_comment,
+                String.format(ConstantString.GOOD_COMMENT, Math.round(goodsBean.goodCommentPercent*100)));
+
+        // 评论条数
+        holder.setText(R.id.tv_comment_num,
+                String.format(ConstantString.COMMENT_NUM, StringTool.getNumberString(goodsBean.commentNum)));
+
+        // 返现
+        TextView tv_rebate = holder.getView(R.id.tv_rebate);
+        tv_rebate.setText(String.format(ConstantString.MONEY_GAME,
+                StringTool.longCent2Yuan(goodsBean.oldPrice-goodsBean.nowPrice)));
+        // 走马灯
+        tv_rebate.setSelected(true);
+
+        // 自营
+        TextView tv_self = holder.getView(R.id.tv_self);
+        if (goodsBean.isSelfBusiness) {
+            tv_self.setVisibility(View.VISIBLE);
+        } else {
+            tv_self.setVisibility(View.GONE);
+        }
+
+        // 原价
+        TextView tv_old_price = holder.getView(R.id.tv_old_price);
+        tv_old_price.setText(String.format(ConstantString.MONEY_RMB, StringTool.longCent2Yuan(goodsBean.oldPrice)));
+        // 增加删除线
+        tv_old_price.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
+
+        // 现价
+        holder.setText(R.id.tv_now_price, String.format(ConstantString.MONEY_RMB, StringTool.longCent2Yuan(goodsBean.nowPrice)));
+
+        // 一句话推荐
+        TextView tv_reason = holder.getView(R.id.tv_reason);
+        if (TextUtils.isEmpty(goodsBean.reason)) {
+            tv_reason.setText("拾贝推荐");
+        } else {
+            tv_reason.setText(goodsBean.reason);
+        }
+
+        holder.setOnClickListener(R.id.cv_goods, v -> {
+            if (!goodsBean.enable) {
+                ToastTool.showShort("敬请期待");
+                return;
+            }
+            ActivityTool.openUrl((Activity) mContext, goodsBean.goodsUrl);
+        });
+
+        // 解决图片错乱
+        RoundedImageView aciv_goods = holder.getView(R.id.aciv_goods);
+        aciv_goods.setTag(R.id.aciv_goods, goodsBean.coverImageUrl);
+        Glide.with(mContext).load(goodsBean.coverImageUrl)
+                .apply(RequestOptions.bitmapTransform(new RoundedCorners(mRadius)))
+                //.apply(new RequestOptions()
+                //        .skipMemoryCache(true)
+                //        .diskCacheStrategy(DiskCacheStrategy.NONE))
+                //先加载原图大小的十分之一
+                .thumbnail(0.1f)
+                .into(new SimpleTarget<Drawable>() {
+                    @Override
+                    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
+                        if (resource == null) {
+                            aciv_goods.setImageResource(R.mipmap.logo_gray);
+                            return;
+                        }
+
+                        String coverUrl = null;
+                        try {
+                            coverUrl = (String) aciv_goods.getTag(R.id.aciv_goods);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                        if (TextUtils.isEmpty(coverUrl) || !TextUtils.equals(coverUrl, goodsBean.coverImageUrl)) {
+                            aciv_goods.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+                            aciv_goods.setImageResource(R.mipmap.logo_gray);
+                        } else {
+                            aciv_goods.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                            aciv_goods.setImageDrawable(resource);
+                        }
+                    }
+                });
+
+        View fl_video_play = holder.getView(R.id.fl_video_play);
+        View iv_video_play = holder.getView(R.id.iv_video_play);
+        if (!TextUtils.isEmpty(goodsBean.videoUrl)) {
+            iv_video_play.setVisibility(View.VISIBLE);
+            fl_video_play.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    // 全屏播放视频
+                    Intent playIntent = new Intent(mContext, VideoPlayActivity.class);
+                    playIntent.putExtra(VideoPlayActivity.KEY_URL, goodsBean.videoUrl);
+                    playIntent.putExtra(VideoPlayActivity.KEY_TITLE, goodsBean.title);
+                    mContext.startActivity(playIntent);
+                    //JzvdStd.startFullscreen(this, JZVideoPlayerStandard.class, "http://2449.vod.myqcloud.com/2449_22ca37a6ea9011e5acaaf51d105342e3.f20.mp4", "嫂子辛苦了");
+                }
+            });
+        } else {
+            iv_video_play.setVisibility(View.GONE);
+        }
+
+        if (UserInfoManager.getInstance().isLogin() &&
+                TextUtils.equals("[email protected]", UserInfoManager.getInstance().getBeiUser().email)) {
+            View view = holder.getView(R.id.cv_goods);
+            view.setOnLongClickListener(v -> {
+                if (goodsLongClickListener != null) {
+                    goodsLongClickListener.onItemLongClick(holder.getLayoutPosition());
+                }
+                return false;
+            });
+        }
+    }
+
+    private ItemLongClickListener goodsLongClickListener;
+    public ItemLongClickListener getGoodsLongClickListener() {
+        return goodsLongClickListener;
+    }
+
+    public void setGoodsLongClickListener(ItemLongClickListener goodsLongClickListener) {
+        this.goodsLongClickListener = goodsLongClickListener;
+    }
+
+}

+ 136 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/GoodsBean.java

@@ -0,0 +1,136 @@
+package com.itant.shibei.ui.home.goods;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/5 21:50
+ * Description: 商品实体
+ */
+public class GoodsBean implements Serializable {
+    public static final int TYPE_RECOMMEND = 0;
+    public static final int TYPE_TECHNOLOGY = 1;
+    public static final int TYPE_LIFE = 2;
+    public static final int TYPE_NETWORK = 3;
+
+
+    /**
+     * 商品id
+     */
+    public long id;
+    /**
+     * 封面图片地址
+     */
+    public String coverImageUrl;
+    /**
+     * 体验视频地址
+     */
+    public String videoUrl;
+    /**
+     * 标题
+     */
+    public String title;
+    /**
+     * 商品描述
+     */
+    public String description;
+    /**
+     * 商品推荐理由(一句话推荐、推荐者心声)
+     */
+    public String reason;
+    /**
+     * 原价
+     */
+    public long oldPrice;
+    /**
+     * 现价
+     */
+    public long nowPrice;
+    /**
+     * 返利
+     */
+    public long rebate;
+    /**
+     * 店名
+     */
+    public String shopName;
+    /**
+     * 商店所属省份
+     */
+    public String province;
+    /**
+     * 是否自营
+     */
+    public boolean isSelfBusiness;
+    /**
+     * 是否有券
+     */
+    public boolean hasCoupon;
+    /**
+     * 优惠券信息
+     */
+    public String couponInfo;
+    /**
+     * 所属类型
+     */
+    public int goodsType;
+    /**
+     * 商品链接
+     */
+    public String goodsUrl;
+    /**
+     * 月销量
+     */
+    public long salesPerMonth;
+
+    /**
+     * 评论条数
+     */
+    public long commentNum;
+
+    /**
+     * 好评率
+     */
+    public double goodCommentPercent;
+
+    /**
+     * 创建时间
+     */
+    public long createTimeMillis;
+
+    /**
+     * 更新时间
+     */
+    public long updateTimeMillis;
+
+    public boolean enable;
+
+    /**是否是本地写死的数据*/
+    public boolean isLocal;
+
+    public void updateData(GoodsBean goodsBean) {
+        this.coverImageUrl = goodsBean.coverImageUrl;
+        this.videoUrl = goodsBean.videoUrl;
+        this.title = goodsBean.title;
+        this.description = goodsBean.description;
+        this.reason = goodsBean.reason;
+        this.oldPrice = goodsBean.oldPrice;
+        this.nowPrice = goodsBean.nowPrice;
+        this.rebate = goodsBean.rebate;
+        this.shopName = goodsBean.shopName;
+        this.province = goodsBean.province;
+        this.isSelfBusiness = goodsBean.isSelfBusiness;
+        this.hasCoupon = goodsBean.hasCoupon;
+        this.couponInfo = goodsBean.couponInfo;
+        this.goodsType = goodsBean.goodsType;
+        this.goodsUrl = goodsBean.goodsUrl;
+        this.salesPerMonth = goodsBean.salesPerMonth;
+        this.goodCommentPercent = goodsBean.goodCommentPercent;
+        this.createTimeMillis = goodsBean.createTimeMillis;
+        this.updateTimeMillis = goodsBean.updateTimeMillis;
+        this.enable = goodsBean.enable;
+        this.isLocal = goodsBean.isLocal;
+    }
+}

+ 243 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/GoodsFragment.java

@@ -0,0 +1,243 @@
+package com.itant.shibei.ui.home.goods;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.util.TypedValue;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.itant.shibei.R;
+import com.itant.shibei.base.ITopActionListener;
+import com.itant.shibei.tool.DataTool;
+import com.itant.shibei.ui.mine.goods.AddGoodsActivity;
+import com.itant.shibei.widget.bottomlistener.OnRcvScrollListener;
+import com.itant.shibei.widget.decoration.SpacesItemDecoration;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+import com.miekir.mvp.view.BaseMVPFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import rx_activity_result2.RxActivityResult;
+
+import static android.app.Activity.RESULT_OK;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/5 22:07
+ * Description: 商品列表Fragment
+ */
+public class GoodsFragment extends BaseMVPFragment implements IGoodsView, ITopActionListener {
+    private static final int PAGE_START = 0;
+    private static final int PAGE_SIZE = 20;
+
+    @InjectPresenter
+    GoodsPresenter goodsPresenter;
+
+    private SwipeRefreshLayout srl_goods;
+    private GoodsAdapter mAdapter;
+    private RecyclerView rv_goods;
+    private List<GoodsBean> mGoodsList = new ArrayList<>();
+
+    /**当前页*/
+    private int mCurrentPage = PAGE_START;
+    /**是否下拉获取数据*/
+    private boolean mIsRefresh = true;
+
+
+    @Override
+    public int getLayoutResId() {
+        return R.layout.fragment_home_goods;
+    }
+
+    @Override
+    protected void onViewInit() {
+        rv_goods = rootView.findViewById(R.id.rv_goods);
+        // 必须要设置LayoutManager,否则RecyclerView不知道要使用什么布局,从而在界面上不显示
+        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
+        rv_goods.setLayoutManager(layoutManager);
+
+        //int dividerWidth = (int) getResources().getDimension(R.dimen.margin_s);
+        //DividerItemDecoration decoration = new DividerItemDecoration(dividerWidth);
+        int dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
+        SpacesItemDecoration decoration = new SpacesItemDecoration(dividerWidth);
+        rv_goods.addItemDecoration(decoration);
+        mAdapter = new GoodsAdapter(getActivity(), mGoodsList);
+        mAdapter.setGoodsLongClickListener(this::showAdminDialog);
+        rv_goods.setAdapter(mAdapter);
+        // 必须先绑定RecyclerView
+        mAdapter.setEmptyView(R.layout.view_empty, rv_goods);
+        // 不要滑动一段距离
+        rv_goods.smoothScrollToPosition(0);
+        srl_goods = rootView.findViewById(R.id.srl_goods);
+        srl_goods.setColorSchemeResources(
+                R.color.black_theme,
+                R.color.blue_droid,
+                R.color.yellow_droid,
+                R.color.red_droid);
+
+        // 下拉刷新
+        srl_goods.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                mIsRefresh = true;
+                mCurrentPage = PAGE_START;
+                goodsPresenter.getGoodsData(mCurrentPage, PAGE_SIZE);
+            }
+        });
+
+        // 加载更多
+        rv_goods.addOnScrollListener(new OnRcvScrollListener(){
+            @Override
+            public void onBottom() {
+                super.onBottom();
+                // 如果到底部了,而且不是正在加载状态,就变为正在加载状态,并及时去加载数据
+                if (!srl_goods.isRefreshing()) {
+                    mIsRefresh = false;
+                    srl_goods.setRefreshing(true);
+                    goodsPresenter.getGoodsData(mCurrentPage, PAGE_SIZE);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onLazyLoad() {
+        // 获取数据
+        srl_goods.setRefreshing(true);
+        goodsPresenter.getGoodsData(mCurrentPage, PAGE_SIZE);
+    }
+
+    /**
+     * 修改商品或者删除商品
+     */
+    private void showAdminDialog(int position) {
+        GoodsBean goodsBean = mGoodsList.get(position);
+        if (goodsBean.isLocal) {
+            // 本地写死的不能删除
+            return;
+        }
+        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                .setMessage("选择操作")
+                .setPositiveButton("修改", (dialog, which) -> {
+                    dialog.dismiss();
+                    Intent modifyIntent = new Intent(getActivity(), AddGoodsActivity.class);
+                    modifyIntent.putExtra(AddGoodsActivity.KEY_MODIFY_GOODS, goodsBean);
+                    RxActivityResult.on(getActivity()).startIntent(modifyIntent)
+                            .filter(result -> result.resultCode() == RESULT_OK)
+                            .doOnNext(result -> {
+                                // 修改商品成功之后刷新item
+                                GoodsBean modifiedBean = (GoodsBean) result.data().getSerializableExtra(AddGoodsActivity.KEY_MODIFY_GOODS);
+                                if (modifiedBean == null) {
+                                    return;
+                                }
+                                goodsBean.updateData(modifiedBean);
+                                mAdapter.notifyItemChanged(position);
+                            })
+                            .subscribe();
+                })
+                .setNeutralButton("取消", (dialog, which) -> {
+                    dialog.dismiss();
+                })
+                .setNegativeButton("删除", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    showDeleteDialog(position);
+                }).create();
+        alertDialog.setCanceledOnTouchOutside(false);
+        alertDialog.setCancelable(false);
+        alertDialog.show();
+    }
+
+    /**
+     * 是否删除商品
+     */
+    private void showDeleteDialog(int position) {
+        GoodsBean goodsBean = mGoodsList.get(position);
+        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                .setMessage("确定删除商品:" + goodsBean.title)
+                .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+                .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    // 删除商品
+                    goodsPresenter.deleteGoodsById(position, goodsBean.id);
+                }).create();
+        alertDialog.show();
+    }
+
+    /**
+     * 获取商品结果
+     * @param success
+     * @param message
+     * @param goodsList
+     */
+    @Override
+    public void onGoodsDataCome(boolean success, String message, List<GoodsBean> goodsList) {
+        srl_goods.setRefreshing(false);
+        if (!success) {
+            if (mGoodsList.size() == 0) {
+                // 本地写死的数据
+                mGoodsList.addAll(DataTool.getGoodsList());
+                mAdapter.notifyDataSetChanged();
+            }
+            ToastTool.showShort(message);
+            return;
+        }
+
+        if (mCurrentPage == PAGE_START) {
+            mGoodsList.clear();
+            if (goodsList != null) {
+                mGoodsList.addAll(goodsList);
+            }
+
+            // 本地写死的数据
+            mGoodsList.addAll(DataTool.getGoodsList());
+            mAdapter.notifyDataSetChanged();
+            // 如果是下拉刷新获取的第0页数据,item不要自动滚动一段距离
+            // 或者用这个mLayoutManager.scrollToPositionWithOffset(0, 0);
+            if (mIsRefresh) {
+                ToastTool.showShort("刷新成功");
+                rv_goods.smoothScrollToPosition(0);
+            }
+        } else {
+            if (goodsList != null) {
+                mGoodsList.addAll(goodsList);
+            }
+
+            mAdapter.notifyDataSetChanged();
+        }
+
+        // 请求到数据了,页数自增
+        if (goodsList != null && goodsList.size() > 0) {
+            mCurrentPage++;
+        } else {
+            ToastTool.showShort("没有更多数据了");
+        }
+    }
+
+    /**
+     * 删除商品结果
+     * @param success
+     * @param message
+     * @param position
+     */
+    @Override
+    public void onDeleteGoodsResult(boolean success, String message, int position) {
+        if (success) {
+            mAdapter.notifyItemRemoved(position);
+            ToastTool.showShort("删除成功");
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+
+    @Override
+    public void onTopAction() {
+        rv_goods.smoothScrollToPosition(0);
+    }
+}

+ 68 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/GoodsPresenter.java

@@ -0,0 +1,68 @@
+package com.itant.shibei.ui.home.goods;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import java.util.List;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class GoodsPresenter extends BasePresenter<IGoodsView> {
+    public void getGoodsData(int pageNum, int pageSize) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getGoodsList(pageNum, pageSize)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<List<GoodsBean>>() {
+                    @Override
+                    public void onSuccess(List<GoodsBean> result) {
+                        if (getView() != null) {
+                            getView().onGoodsDataCome(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onGoodsDataCome(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+
+    public void deleteGoodsById(int position, long goodsId) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .deleteGoodsById(goodsId)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<String>() {
+                    @Override
+                    public void onSuccess(String result) {
+                        if (getView() != null) {
+                            getView().onDeleteGoodsResult(true, result, position);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onDeleteGoodsResult(false, TextUtils.isEmpty(errMsg) ? "删除失败:"+e.getMessage() : errMsg, position);
+                        }
+                    }
+                });
+    }
+}

+ 17 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/IGoodsView.java

@@ -0,0 +1,17 @@
+package com.itant.shibei.ui.home.goods;
+
+import com.miekir.mvp.view.IView;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/9 20:52
+ * Description: 商品的View回调
+ */
+public interface IGoodsView extends IView {
+    void onGoodsDataCome(boolean success, String message, List<GoodsBean> goodsList);
+    void onDeleteGoodsResult(boolean success, String message, int position);
+}

+ 62 - 0
app/src/main/java/com/itant/shibei/ui/home/goods/play/VideoPlayActivity.java

@@ -0,0 +1,62 @@
+package com.itant.shibei.ui.home.goods.play;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.itant.shibei.R;
+import com.itant.shibei.widget.VideoPlayer;
+
+import cn.jzvd.Jzvd;
+import cn.jzvd.JzvdStd;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/25 11:28
+ * Description: 视频播放类,这是一个全屏的界面
+ */
+public class VideoPlayActivity extends Activity {
+    public static final String KEY_URL = "url";
+    public static final String KEY_TITLE = "title";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_video_play);
+
+        String url = getIntent().getStringExtra(KEY_URL);
+        String title = getIntent().getStringExtra(KEY_TITLE);
+
+        Jzvd.WIFI_TIP_DIALOG_SHOWED = true;
+        // 直接播放视频
+        VideoPlayer.setVideoPlayListener(new VideoPlayer.VideoPlayListener() {
+            @Override
+            public void onPlayComplete() {
+                finish();
+            }
+        });
+        JzvdStd.setCurrentJzvd(VideoPlayer.CURRENT_JZVD);
+        JzvdStd.startFullscreenDirectly(this, VideoPlayer.class, url, title);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Jzvd.releaseAllVideos();
+    }
+
+    @Override
+    public void onBackPressed() {
+        Jzvd.backPress();
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        VideoPlayer.setVideoPlayListener(null);
+    }
+}

+ 16 - 0
app/src/main/java/com/itant/shibei/ui/home/search/ISearchView.java

@@ -0,0 +1,16 @@
+package com.itant.shibei.ui.home.search;
+
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.miekir.mvp.view.IView;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:44
+ */
+public interface ISearchView extends IView {
+    void onSearchKeywordResult(boolean success, String message, List<GoodsBean> goodsList);
+}

+ 267 - 0
app/src/main/java/com/itant/shibei/ui/home/search/SearchActivity.java

@@ -0,0 +1,267 @@
+package com.itant.shibei.ui.home.search;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.ui.home.goods.GoodsAdapter;
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.itant.shibei.ui.home.goods.GoodsPresenter;
+import com.itant.shibei.ui.home.goods.IGoodsView;
+import com.itant.shibei.ui.mine.goods.AddGoodsActivity;
+import com.itant.shibei.widget.bottomlistener.OnRcvScrollListener;
+import com.itant.shibei.widget.decoration.CardViewDividerItemDecoration;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import rx_activity_result2.RxActivityResult;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/12 19:40
+ * Description: 输入文字搜索
+ */
+public class SearchActivity extends BaseBeiActivity implements View.OnClickListener, ISearchView, View.OnFocusChangeListener, IGoodsView {
+    private static final int PAGE_START = 0;
+    private static final int PAGE_SIZE = 20;
+    private int mCurrentPage = PAGE_START;
+
+    private EditText et_search;
+    private Button btn_search;
+
+    private RecyclerView rv_search_result;
+    private List<GoodsBean> mGoodsList = new ArrayList<>();
+    private GoodsAdapter mAdapter;
+    private boolean mIsRefresh = true;
+
+    private String mKeyword;
+
+    @InjectPresenter
+    SearchPresenter searchPresenter;
+
+    @InjectPresenter
+    GoodsPresenter goodsPresenter;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_search;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        setTitle("搜索");
+        et_search = findViewById(R.id.et_search);
+        et_search.setOnFocusChangeListener(this);
+        btn_search = findViewById(R.id.btn_search);
+
+        rv_search_result = findViewById(R.id.rv_search_result);
+        // 必须要设置LayoutManager,否则RecyclerView不知道要使用什么布局,从而在界面上不显示
+        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
+        rv_search_result.setLayoutManager(layoutManager);
+        int dividerWidth = (int) getResources().getDimension(R.dimen.margin_s);
+        CardViewDividerItemDecoration decoration = new CardViewDividerItemDecoration(dividerWidth);
+        rv_search_result.addItemDecoration(decoration);
+        mAdapter = new GoodsAdapter(this, mGoodsList);
+        mAdapter.setGoodsLongClickListener(this::showAdminDialog);
+        rv_search_result.setAdapter(mAdapter);
+        mAdapter.setEmptyView(R.layout.view_empty, rv_search_result);
+        // 加载更多
+        rv_search_result.addOnScrollListener(new OnRcvScrollListener(){
+            @Override
+            public void onBottom() {
+                super.onBottom();
+                if (mKeyword.startsWith("http")) {
+                    // 搜索链接不要加载更多功能
+                    return;
+                }
+                // 如果到底部了,而且不是正在加载状态,就变为正在加载状态,并及时去加载数据
+                showLoading();
+                mIsRefresh = false;
+                searchPresenter.searchByKeyword(mKeyword, mCurrentPage, PAGE_SIZE);
+            }
+        });
+
+        ViewTool.requestInputFocus(this, et_search);
+        ViewTool.setOnClickListener(this, this, new int[]{R.id.ll_search, R.id.btn_search, R.id.fl_clear});
+    }
+
+    @Override
+    public void onClick(View v) {
+
+        switch (v.getId()) {
+            case R.id.btn_search:
+                et_search.clearFocus();
+                mKeyword = et_search.getText().toString();
+                if (TextUtils.isEmpty(mKeyword)) {
+                    ToastTool.showShort("请输入关键字或链接");
+                    return;
+                }
+                showLoading();
+                mCurrentPage = PAGE_START;
+                mGoodsList.clear();
+                mAdapter.notifyDataSetChanged();
+                mIsRefresh = true;
+                searchPresenter.searchByKeyword(mKeyword, mCurrentPage, PAGE_SIZE);
+                break;
+            case R.id.ll_search:
+                et_search.clearFocus();
+                break;
+            case R.id.fl_clear:
+                et_search.setText("");
+                ViewTool.requestInputFocus(this, et_search);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * 修改商品或者删除商品
+     */
+    private void showAdminDialog(int position) {
+        GoodsBean goodsBean = mGoodsList.get(position);
+        if (goodsBean.isLocal) {
+            // 本地写死的不能删除
+            return;
+        }
+        AlertDialog alertDialog = new AlertDialog.Builder(this)
+                .setMessage("选择操作")
+                .setPositiveButton("修改", (dialog, which) -> {
+                    dialog.dismiss();
+                    Intent modifyIntent = new Intent(this, AddGoodsActivity.class);
+                    modifyIntent.putExtra(AddGoodsActivity.KEY_MODIFY_GOODS, goodsBean);
+                    RxActivityResult.on(this).startIntent(modifyIntent)
+                            .filter(result -> result.resultCode() == RESULT_OK)
+                            .doOnNext(result -> {
+                                // 修改商品成功之后刷新item
+                                GoodsBean modifiedBean = (GoodsBean) result.data().getSerializableExtra(AddGoodsActivity.KEY_MODIFY_GOODS);
+                                if (modifiedBean == null) {
+                                    return;
+                                }
+                                goodsBean.updateData(modifiedBean);
+                                mAdapter.notifyItemChanged(position);
+                            })
+                            .subscribe();
+                })
+                .setNeutralButton("取消", (dialog, which) -> {
+                    dialog.dismiss();
+                })
+                .setNegativeButton("删除", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    showDeleteDialog(position);
+                }).create();
+        alertDialog.setCanceledOnTouchOutside(false);
+        alertDialog.setCancelable(false);
+        alertDialog.show();
+    }
+
+    /**
+     * 是否删除商品
+     */
+    private void showDeleteDialog(int position) {
+        GoodsBean goodsBean = mGoodsList.get(position);
+        AlertDialog alertDialog = new AlertDialog.Builder(this)
+                .setMessage("确定删除商品:" + goodsBean.title)
+                .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+                .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+                    dialog.dismiss();
+                    // 删除商品
+                    goodsPresenter.deleteGoodsById(position, goodsBean.id);
+                }).create();
+        alertDialog.show();
+    }
+
+    @Override
+    public void onSearchKeywordResult(boolean success, String message, List<GoodsBean> goodsList) {
+        dismissLoading();
+
+        if (!success) {
+            ToastTool.showShort(message);
+            return;
+        }
+
+        if (mCurrentPage == PAGE_START) {
+            mGoodsList.clear();
+            if (goodsList != null) {
+                mGoodsList.addAll(goodsList);
+            }
+
+            // 本地写死的数据
+            mAdapter.notifyDataSetChanged();
+            // 如果是下拉刷新获取的第0页数据,item不要自动滚动一段距离
+            // 或者用这个mLayoutManager.scrollToPositionWithOffset(0, 0);
+            if (mIsRefresh) {
+                rv_search_result.smoothScrollToPosition(0);
+            }
+        } else {
+            if (goodsList != null) {
+                mGoodsList.addAll(goodsList);
+            }
+
+            mAdapter.notifyDataSetChanged();
+        }
+
+        // 请求到数据了,页数自增
+        if (goodsList != null && goodsList.size() > 0) {
+            mCurrentPage++;
+        } else {
+            ToastTool.showShort("没有更多数据了");
+        }
+    }
+
+    /**
+     * 获取商品结果
+     * @param success
+     * @param message
+     * @param goodsList
+     */
+    @Override
+    public void onGoodsDataCome(boolean success, String message, List<GoodsBean> goodsList) {
+
+    }
+
+    /**
+     * 删除商品结果
+     * @param success
+     * @param message
+     * @param position
+     */
+    @Override
+    public void onDeleteGoodsResult(boolean success, String message, int position) {
+        if (success) {
+            mAdapter.notifyItemRemoved(position);
+            ToastTool.showShort("删除成功");
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (v.getId() == R.id.et_search) {
+            if (hasFocus) {
+                rv_search_result.setVisibility(View.GONE);
+                btn_search.setVisibility(View.VISIBLE);
+            } else {
+                rv_search_result.setVisibility(View.VISIBLE);
+                btn_search.setVisibility(View.GONE);
+            }
+        }
+    }
+}

+ 47 - 0
app/src/main/java/com/itant/shibei/ui/home/search/SearchPresenter.java

@@ -0,0 +1,47 @@
+package com.itant.shibei.ui.home.search;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import java.util.List;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/16 9:34
+ * Description: 搜索
+ */
+public class SearchPresenter extends BasePresenter<ISearchView> {
+
+    public void searchByKeyword(String keyword, int pageNum, int pageSize) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getGoodsListByKeyword(keyword, pageNum, pageSize)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<List<GoodsBean>>() {
+                    @Override
+                    public void onSuccess(List<GoodsBean> result) {
+                        if (getView() != null) {
+                            getView().onSearchKeywordResult(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onSearchKeywordResult(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+}

+ 15 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/ISystemView.java

@@ -0,0 +1,15 @@
+package com.itant.shibei.ui.home.tool;
+
+import com.itant.shibei.bean.SystemBean;
+import com.miekir.mvp.view.IView;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:44
+ */
+public interface ISystemView extends IView {
+    void onGetConfigResult(boolean success, String message, SystemBean resultBean);
+    void onSetApiResult(boolean success, String message, String resultBean);
+}

+ 74 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/SystemPresenter.java

@@ -0,0 +1,74 @@
+package com.itant.shibei.ui.home.tool;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.bean.SystemBean;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/20 19:18
+ * Description:
+ */
+public class SystemPresenter extends BasePresenter<ISystemView> {
+
+    /**
+     * 获取配置信息
+     */
+    public void getSystemConfig() {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getSystemConfig()
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<SystemBean>() {
+                    @Override
+                    public void onSuccess(SystemBean result) {
+                        if (getView() != null) {
+                            getView().onGetConfigResult(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onGetConfigResult(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+
+    /**
+     * 设置API是否仅对会员开放
+     */
+    public void setApiConfig(boolean isApiVipOnly) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .setApiConfig(isApiVipOnly)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<String>() {
+                    @Override
+                    public void onSuccess(String result) {
+                        if (getView() != null) {
+                            getView().onSetApiResult(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onSetApiResult(false, TextUtils.isEmpty(errMsg) ? "设置失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+}

+ 122 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/ToolFragment.java

@@ -0,0 +1,122 @@
+package com.itant.shibei.ui.home.tool;
+
+import android.content.Intent;
+import android.view.View;
+import android.widget.TextView;
+
+import com.itant.shibei.R;
+import com.itant.shibei.bean.SystemBean;
+import com.itant.shibei.constant.ConstantUrl;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.ui.home.tool.json.JsonActivity;
+import com.itant.shibei.ui.home.tool.weather.WeatherActivity;
+import com.itant.shibei.ui.home.tool.yiji.YijiActivity;
+import com.itant.shibei.ui.mine.login.LoginActivity;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+import com.miekir.mvp.view.BaseMVPFragment;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 工具界面
+ */
+public class ToolFragment extends BaseMVPFragment implements View.OnClickListener, ISystemView {
+
+    @InjectPresenter
+    SystemPresenter mSystemPresenter;
+
+    private TextView tv_weather;
+    private TextView tv_yiji;
+
+    @Override
+    protected void onViewInit() {
+        tv_weather = rootView.findViewById(R.id.tv_weather);
+        tv_yiji = rootView.findViewById(R.id.tv_yiji);
+        rootView.findViewById(R.id.fl_get_json).setOnClickListener(this);
+        rootView.findViewById(R.id.fl_pc_template).setOnClickListener(this);
+        rootView.findViewById(R.id.fl_weather).setOnClickListener(this);
+        rootView.findViewById(R.id.fl_yiji).setOnClickListener(this);
+        rootView.findViewById(R.id.fl_biao_qing).setOnClickListener(this);
+    }
+
+    @Override
+    protected void onLazyLoad() {
+        mSystemPresenter.getSystemConfig();
+    }
+
+    @Override
+    public int getLayoutResId() {
+        return R.layout.fragment_tool;
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+
+            case R.id.fl_pc_template:
+                // 跳转京东模板
+                ActivityTool.openUrl(getActivity(), ConstantUrl.URL_JD_TEMPLATE_COMPUTER);
+                break;
+            case R.id.fl_get_json:
+                // JSON界面
+                if (UserInfoManager.getInstance().isLogin()) {
+                    startActivity(new Intent(getActivity(), JsonActivity.class));
+                } else {
+                    ToastTool.showShort("请先登录");
+                    startActivity(new Intent(getActivity(), LoginActivity.class));
+                }
+                break;
+
+            case R.id.fl_weather:
+                // 天气界面
+                if (UserInfoManager.getInstance().isLogin()) {
+                    startActivity(new Intent(getActivity(), WeatherActivity.class));
+                } else {
+                    ToastTool.showShort("请先登录");
+                    startActivity(new Intent(getActivity(), LoginActivity.class));
+                }
+                break;
+
+            case R.id.fl_yiji:
+                // 宜忌信息
+                if (UserInfoManager.getInstance().isLogin()) {
+                    startActivity(new Intent(getActivity(), YijiActivity.class));
+                } else {
+                    ToastTool.showShort("请先登录");
+                    startActivity(new Intent(getActivity(), LoginActivity.class));
+                }
+                break;
+
+            case R.id.fl_biao_qing:
+                // 跳转表情包
+                ActivityTool.openUrl(getActivity(), ConstantUrl.URL_BIAO_QING);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onGetConfigResult(boolean success, String message, SystemBean resultBean) {
+        if (!success || resultBean == null) {
+            return;
+        }
+
+        String tips;
+        if (resultBean.isVipLimit) {
+            tips = getResources().getString(R.string.api_tips_vip_only);
+        } else {
+            tips = getResources().getString(R.string.api_tips_free);
+        }
+
+        tv_weather.setText(tv_weather.getText().toString() + tips);
+        tv_yiji.setText(tv_yiji.getText().toString() + tips);
+    }
+
+    @Override
+    public void onSetApiResult(boolean success, String message, String resultBean) {
+
+    }
+}

+ 15 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/json/IJsonView.java

@@ -0,0 +1,15 @@
+package com.itant.shibei.ui.home.tool.json;
+
+import com.miekir.mvp.view.IView;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/8 19:44
+ * Description: Template的View
+ */
+public interface IJsonView extends IView {
+    void onSaveJsonResult(boolean success, String message, JsonBean jsonBean);
+    void onGetJsonResult(boolean success, String message, JsonBean jsonBean);
+}

+ 106 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonActivity.java

@@ -0,0 +1,106 @@
+package com.itant.shibei.ui.home.tool.json;
+
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.annotation.Nullable;
+
+import com.itant.shibei.BuildConfig;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.tool.SystemTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.view.AlignTextView;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+public class JsonActivity extends BaseBeiActivity implements View.OnClickListener, IJsonView {
+    private static final String FORMAT_URL = "%sshibei/api/getJson?s=%s";
+
+    @InjectPresenter
+    JsonPresenter jsonPresenter;
+
+    private BeiUser mUser;
+
+    private AlignTextView tv_json_url;
+    private EditText et_json;
+
+    private JsonBean mJsonBean;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_json;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        showLoading();
+        // 需要在super.onCreate后执行,否则会空指针
+        jsonPresenter.getJsonBean();
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        mUser = UserInfoManager.getInstance().getBeiUser();
+        setTitle("GetJson微服务");
+
+        tv_json_url = findViewById(R.id.tv_json_url);
+        tv_json_url.setText(String.format(FORMAT_URL, BuildConfig.BASE_URL, StringTool.getMixToken(mUser.token)));
+        et_json = findViewById(R.id.et_json);
+        findViewById(R.id.fl_json_url).setOnClickListener(this);
+        findViewById(R.id.btn_save_json).setOnClickListener(this);
+    }
+
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.fl_json_url:
+                // 复制GET链接URL
+                if (SystemTool.copyText(getApplicationContext(), tv_json_url.getText().toString())) {
+                    ToastTool.showShort("复制成功");
+                } else {
+                    ToastTool.showShort("复制失败");
+                }
+                break;
+
+            case R.id.btn_save_json:
+                // 保存JSON
+                showLoading();
+                if (mJsonBean == null) {
+                    JsonBean jsonBean = new JsonBean();
+                    jsonBean.json = et_json.getText().toString();
+                    jsonBean.email = mUser.email;
+                    jsonPresenter.saveJsonBean(jsonBean);
+                } else {
+                    mJsonBean.json = et_json.getText().toString();
+                    jsonPresenter.saveJsonBean(mJsonBean);
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onSaveJsonResult(boolean success, String message, JsonBean jsonBean) {
+        dismissLoading();
+        ToastTool.showShort(message);
+        mJsonBean = jsonBean;
+    }
+
+    @Override
+    public void onGetJsonResult(boolean success, String message, JsonBean jsonBean) {
+        dismissLoading();
+        if (jsonBean != null) {
+            mJsonBean = jsonBean;
+            et_json.setText(jsonBean.json);
+        }
+    }
+}

+ 14 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonBean.java

@@ -0,0 +1,14 @@
+package com.itant.shibei.ui.home.tool.json;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/13 20:20
+ * Description: json
+ */
+public class JsonBean {
+    public long id;
+    public String email;
+    public String json;
+}

+ 67 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/json/JsonPresenter.java

@@ -0,0 +1,67 @@
+package com.itant.shibei.ui.home.tool.json;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/15 12:41
+ * Description:
+ */
+public class JsonPresenter extends BasePresenter<IJsonView> {
+
+    public void getJsonBean() {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getJsonBean()
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<JsonBean>() {
+                    @Override
+                    public void onSuccess(JsonBean result) {
+                        if (getView() != null) {
+                            getView().onGetJsonResult(true, "", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onGetJsonResult(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+
+    public void saveJsonBean(JsonBean jsonBean) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .saveJson(jsonBean)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<JsonBean>() {
+                    @Override
+                    public void onSuccess(JsonBean result) {
+                        if (getView() != null) {
+                            getView().onSaveJsonResult(true, "保存成功", result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onSaveJsonResult(false, TextUtils.isEmpty(errMsg) ? "获取失败:"+e.getMessage() : errMsg, null);
+                        }
+                    }
+                });
+    }
+}

+ 69 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/weather/WeatherActivity.java

@@ -0,0 +1,69 @@
+package com.itant.shibei.ui.home.tool.weather;
+
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+import com.itant.shibei.BuildConfig;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.tool.SystemTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.view.AlignTextView;
+
+/**
+ * 天气界面
+ */
+public class WeatherActivity extends BaseBeiActivity implements View.OnClickListener {
+    private static final String FORMAT_URL = "%sshibei/api/getWeather?s=%s&city=深圳";
+
+    private BeiUser mUser;
+
+    private AlignTextView tv_weather_url;
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_weather;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        mUser = UserInfoManager.getInstance().getBeiUser();
+        setTitle("日期天气API");
+
+        tv_weather_url = findViewById(R.id.tv_weather_url);
+        tv_weather_url.setText(String.format(FORMAT_URL, BuildConfig.BASE_URL, StringTool.getMixToken(mUser.token)));
+
+        findViewById(R.id.fl_weather_url).setOnClickListener(this);
+
+        EditText et_weather = findViewById(R.id.et_weather);
+        et_weather.setText("{\n" +
+                "  \"code\": 0,\n" +
+                "  \"msg\": \"\",\n" +
+                "  \"temperatureRange\": \"28.5 ~ 31 ℃\",\n" +
+                "  \"temperatureNow\": \"29.2\",\n" +
+                "  \"wind\": \"无持续风,微风\",\n" +
+                "  \"air\": \"优\",\n" +
+                "  \"desc\": \"阵雨转多云\"\n" +
+                "}");
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.fl_weather_url:
+                // 复制GET链接URL
+                if (SystemTool.copyText(getApplicationContext(), tv_weather_url.getText().toString())) {
+                    ToastTool.showShort("复制成功");
+                } else {
+                    ToastTool.showShort("复制失败");
+                }
+                break;
+            default:
+                break;
+        }
+    }
+}

+ 18 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/weather/WeatherBean.java

@@ -0,0 +1,18 @@
+package com.itant.shibei.ui.home.tool.weather;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/14 20:44
+ * Description: 天气实体
+ */
+public class WeatherBean {
+    public int code;
+    public String msg;
+    public String temperatureRange;
+    public String temperatureNow;
+    public String wind;
+    public String air;
+    public String desc;
+}

+ 71 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/yiji/YijiActivity.java

@@ -0,0 +1,71 @@
+package com.itant.shibei.ui.home.tool.yiji;
+
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+import com.itant.shibei.BuildConfig;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.tool.SystemTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.view.AlignTextView;
+
+/**
+ * 黄历界面
+ */
+public class YijiActivity extends BaseBeiActivity implements View.OnClickListener {
+    private static final String FORMAT_URL = "%sshibei/api/getYiji?s=%s";
+
+    private BeiUser mUser;
+
+    private AlignTextView tv_yiji_url;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_yiji;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        mUser = UserInfoManager.getInstance().getBeiUser();
+        setTitle("宜忌API");
+
+        tv_yiji_url = findViewById(R.id.tv_yiji_url);
+        tv_yiji_url.setText(String.format(FORMAT_URL, BuildConfig.BASE_URL, StringTool.getMixToken(mUser.token)));
+
+        findViewById(R.id.fl_yiji_url).setOnClickListener(this);
+
+        EditText et_yiji = findViewById(R.id.et_yiji);
+        et_yiji.setText("{\n" +
+                "  \"code\": 0,\n" +
+                "  \"msg\": \"\",\n" +
+                "  \"newDate\": \"2020年8月14日 星期五\",\n" +
+                "  \"oldDate\": \"农历二零二零年 六月(小)廿五\",\n" +
+                "  \"hsDate\": \"庚子鼠年 甲申月 己丑日\",\n" +
+                "  \"yi\": \"结婚、领证、动土、安床\",\n" +
+                "  \"ji\": \"搬家、装修、开业\"\n" +
+                "}");
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.fl_yiji_url:
+                // 复制GET链接URL
+                if (SystemTool.copyText(getApplicationContext(), tv_yiji_url.getText().toString())) {
+                    ToastTool.showShort("复制成功");
+                } else {
+                    ToastTool.showShort("复制失败");
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+}

+ 18 - 0
app/src/main/java/com/itant/shibei/ui/home/tool/yiji/YijiBean.java

@@ -0,0 +1,18 @@
+package com.itant.shibei.ui.home.tool.yiji;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/14 22:16
+ * Description: 宜忌实体
+ */
+public class YijiBean {
+    public int code;
+    public String msg;
+    public String newDate;
+    public String oldDate;
+    public String hsDate;
+    public String yi;
+    public String ji;
+}

+ 0 - 37
app/src/main/java/com/itant/shibei/ui/mine/AboutActivity.java

@@ -1,37 +0,0 @@
-package com.itant.shibei.ui.mine;
-
-import android.os.Bundle;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.itant.shibei.R;
-import com.itant.shibei.base.BaseShiBeiActivity;
-import com.itant.shibei.tool.SystemTool;
-
-/**
- *
- *
- * @author 詹子聪
- * @date 2020/6/18 16:48
- * Description: 关于界面
- * todo 点击版本号检查更新
- */
-public class AboutActivity extends BaseShiBeiActivity {
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setTitle("关于");
-    }
-
-    @Override
-    public int getLayoutID() {
-        return R.layout.activity_about;
-    }
-
-    @Override
-    public void initViews(Bundle savedInstanceState) {
-        TextView tv_title = findViewById(R.id.tv_title);
-        tv_title.setText(" 拾贝 v" + SystemTool.getVersionName(this));
-    }
-}

+ 166 - 0
app/src/main/java/com/itant/shibei/ui/mine/MineActivity.java

@@ -0,0 +1,166 @@
+package com.itant.shibei.ui.mine;
+
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.bean.SystemBean;
+import com.itant.shibei.constant.ConstantString;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.tool.TimeTool;
+import com.itant.shibei.ui.home.tool.ISystemView;
+import com.itant.shibei.ui.home.tool.SystemPresenter;
+import com.itant.shibei.ui.mine.coupon.AddCouponActivity;
+import com.itant.shibei.ui.mine.goods.AddGoodsActivity;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+public class MineActivity extends BaseBeiActivity implements View.OnClickListener, ISystemView, CompoundButton.OnCheckedChangeListener {
+
+    @InjectPresenter
+    SystemPresenter mSystemPresenter;
+
+    // 当前金额
+    private TextView tv_amount_rest;
+    // 提现支付宝
+    private TextView tv_cash_account;
+    private Switch switch_api;
+
+    private BeiUser mUser;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_mine;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        mUser = UserInfoManager.getInstance().getBeiUser();
+        setTitle(String.format(ConstantString.WELCOME_HELLO, TimeTool.getCurrentTimePeriod(), mUser.nickName));
+        ViewTool.setOnClickListener(this, this,
+                new int[]{R.id.ll_user, R.id.tv_add_goods, R.id.btn_exit_login, R.id.tv_add_coupon,
+                R.id.tv_add_goods_from_url, R.id.tv_deal_cash});
+
+        // 只有我才能管理后台
+        View ll_admin = findViewById(R.id.ll_admin);
+        if (TextUtils.equals("[email protected]", mUser.email)) {
+            ll_admin.setVisibility(View.VISIBLE);
+        } else {
+            ll_admin.setVisibility(View.GONE);
+        }
+
+        switch_api = findViewById(R.id.switch_api);
+        switch_api.setOnCheckedChangeListener(this);
+
+        tv_amount_rest = findViewById(R.id.tv_amount_rest);
+        tv_cash_account = findViewById(R.id.tv_cash_account);
+        tv_amount_rest.setText(String.format(ConstantString.MONEY_RMB, StringTool.longCent2Yuan(mUser.currentMoney)));
+        tv_cash_account.setText("提现支付宝:" + mUser.cashAccount);
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // 这个界面只有我才需要获取API设置
+        if (TextUtils.equals("[email protected]", mUser.email)) {
+            mSystemPresenter.getSystemConfig();
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+
+            case R.id.ll_user:
+                // todo 查看账户明细
+                break;
+
+            case R.id.tv_deal_cash:
+                // todo 处理提现工单
+                break;
+
+            case R.id.tv_add_goods_from_url:
+                // todo 解析链接添加商品
+                break;
+
+            case R.id.tv_add_goods:
+                // 上架新商品
+                startActivity(new Intent(this, AddGoodsActivity.class));
+                break;
+
+            case R.id.tv_add_coupon:
+                // 上架优惠券
+                startActivity(new Intent(this, AddCouponActivity.class));
+                break;
+
+            case R.id.btn_exit_login:
+                sureToExit();
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * 确认退出登录
+     */
+    private void sureToExit() {
+        AlertDialog alertDialog = new AlertDialog.Builder(this)
+               .setMessage("确定退出当前账号?")
+               .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
+               .setPositiveButton("确定", (DialogInterface dialog, int which) -> {
+            dialog.dismiss();
+            UserInfoManager.getInstance().setBeiUser(null);
+            finish();
+        }).create();
+        alertDialog.show();
+    }
+
+
+    @Override
+    public void onGetConfigResult(boolean success, String message, SystemBean resultBean) {
+        if (!success || resultBean == null) {
+            return;
+        }
+
+        switch_api.setOnCheckedChangeListener(null);
+        switch_api.setChecked(resultBean.isVipLimit);
+        switch_api.setOnCheckedChangeListener(this);
+    }
+
+    @Override
+    public void onSetApiResult(boolean success, String message, String resultBean) {
+        dismissLoading();
+        if (success) {
+            ToastTool.showShort("设置成功");
+        } else {
+            ToastTool.showShort("设置失败");
+            switch_api.setOnCheckedChangeListener(null);
+            switch_api.setChecked(!switch_api.isChecked());
+            switch_api.setOnCheckedChangeListener(this);
+        }
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (buttonView.getId() == switch_api.getId()) {
+            showLoading();
+            mSystemPresenter.setApiConfig(isChecked);
+        }
+    }
+}

+ 3 - 13
app/src/main/java/com/itant/shibei/ui/mine/MineFragment.java

@@ -10,8 +10,7 @@ import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
 
 import com.itant.shibei.R;
-import com.itant.shibei.tool.SystemTool;
-import com.itant.shibei.ui.mine.login.UserInfoManager;
+import com.itant.shibei.manager.UserInfoManager;
 import com.itant.shibei.ui.mine.login.LoginActivity;
 
 public class MineFragment extends Fragment implements View.OnClickListener {
@@ -20,10 +19,9 @@ public class MineFragment extends Fragment implements View.OnClickListener {
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater,
                              ViewGroup container, Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.fragment_mine, container, false);
-        root.findViewById(R.id.ll_about).setOnClickListener(this);
+        View root = inflater.inflate(R.layout.activity_mine, container, false);
         root.findViewById(R.id.ll_user).setOnClickListener(this);
-        root.findViewById(R.id.ll_feedback).setOnClickListener(this);
+        //root.findViewById(R.id.ll_feedback).setOnClickListener(this);
         return root;
 
     }
@@ -31,10 +29,6 @@ public class MineFragment extends Fragment implements View.OnClickListener {
     @Override
     public void onClick(View v) {
         switch (v.getId()) {
-            case R.id.ll_about:
-                startActivity(new Intent(getActivity(), AboutActivity.class));
-                break;
-
             case R.id.ll_user:
                 if (!UserInfoManager.getInstance().isLogin()) {
                     // 未登录,跳转登录
@@ -44,10 +38,6 @@ public class MineFragment extends Fragment implements View.OnClickListener {
                 }
                 break;
 
-            case R.id.ll_feedback:
-                SystemTool.sendEmail(getActivity());
-                break;
-
             default:
                 break;
         }

+ 104 - 0
app/src/main/java/com/itant/shibei/ui/mine/coupon/AddCouponActivity.java

@@ -0,0 +1,104 @@
+package com.itant.shibei.ui.mine.coupon;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.ui.home.coupon.CouponBean;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 添加优惠券界面
+ */
+public class AddCouponActivity extends BaseBeiActivity implements View.OnClickListener, IAddCouponView {
+    public static final String KEY_MODIFY_COUPON = "key_modify_coupon";
+
+    private TextInputEditText et_coupon_title;
+    private TextInputEditText et_cover_url;
+    private TextInputEditText et_jump_url;
+
+    private CheckBox cb_enable;
+
+    private CouponBean couponBean;
+
+    @InjectPresenter
+    AddCouponPresenter presenter;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_coupon_add;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+
+        et_coupon_title = findViewById(R.id.et_coupon_title);
+        et_cover_url = findViewById(R.id.et_cover_url);
+        et_jump_url = findViewById(R.id.et_jump_url);
+        cb_enable = findViewById(R.id.cb_enable);
+        ViewTool.setOnClickListener(this, this, new int[]{R.id.btn_add_goods});
+        Button btn_add_goods = findViewById(R.id.btn_add_goods);
+
+        couponBean = (CouponBean) getIntent().getSerializableExtra(KEY_MODIFY_COUPON);
+        if (couponBean != null) {
+            initCoupon();
+            setTitle("更新优惠券");
+            btn_add_goods.setText("立即更新");
+        } else {
+            setTitle("上架新的优惠券");
+        }
+    }
+
+    private void initCoupon() {
+        et_coupon_title.setText(couponBean.couponName);
+        et_cover_url.setText(couponBean.coverUrl);
+        et_jump_url.setText(couponBean.jumpUrl);
+        cb_enable.setChecked(couponBean.actionEnable);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.btn_add_goods:
+                String title = et_coupon_title.getEditableText().toString();
+                String coverUrl = et_cover_url.getEditableText().toString();
+                String jumpUrl = et_jump_url.getEditableText().toString();
+                boolean isEnable = cb_enable.isChecked();
+
+                if (couponBean == null) {
+                    couponBean = new CouponBean();
+                }
+                couponBean.couponName = title;
+                couponBean.coverUrl = coverUrl;
+                couponBean.jumpUrl = jumpUrl;
+                couponBean.actionEnable = isEnable;
+
+                showLoading();
+                presenter.addCoupon(couponBean);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onAddCouponResult(boolean success, String message) {
+        dismissLoading();
+        ToastTool.showShort(message);
+        if (success) {
+            Intent intent = getIntent();
+            intent.putExtra(KEY_MODIFY_COUPON, couponBean);
+            setResult(RESULT_OK, intent);
+            finish();
+        }
+    }
+}

+ 43 - 0
app/src/main/java/com/itant/shibei/ui/mine/coupon/AddCouponPresenter.java

@@ -0,0 +1,43 @@
+package com.itant.shibei.ui.mine.coupon;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.itant.shibei.ui.home.coupon.CouponBean;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class AddCouponPresenter extends BasePresenter<IAddCouponView> {
+
+    public void addCoupon(CouponBean couponBean) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .addCoupon(couponBean)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<String>() {
+                    @Override
+                    public void onSuccess(String result) {
+                        if (getView() != null) {
+                            getView().onAddCouponResult(true, result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onAddCouponResult(false, TextUtils.isEmpty(errMsg) ? "操作失败"+e.getMessage() : errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 18 - 0
app/src/main/java/com/itant/shibei/ui/mine/coupon/IAddCouponView.java

@@ -0,0 +1,18 @@
+package com.itant.shibei.ui.mine.coupon;
+
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ */
+public interface IAddCouponView extends IView {
+    /**
+     * 增加优惠券的结果
+     * @param success
+     * @param message
+     */
+    void onAddCouponResult(boolean success, String message);
+}

+ 130 - 0
app/src/main/java/com/itant/shibei/ui/mine/forget/ForgetActivity.java

@@ -0,0 +1,130 @@
+package com.itant.shibei.ui.mine.forget;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.ui.mine.register.CodePresenter;
+import com.itant.shibei.ui.mine.register.ICodeView;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 登录界面
+ */
+public class ForgetActivity extends BaseBeiActivity implements View.OnClickListener, ICodeView, IForgetView {
+    private TextInputEditText et_email;
+    private TextInputEditText et_code;
+    private TextInputEditText et_new_password;
+
+    private String email;
+    private String code;
+    private String password;
+
+    @InjectPresenter
+    private CodePresenter mCodePresenter;
+
+    @InjectPresenter
+    private ForgetPresenter mForgetPresenter;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_forget;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        setTitle("重置密码");
+
+        et_email = findViewById(R.id.et_email);
+        et_code = findViewById(R.id.et_code);
+        et_new_password = findViewById(R.id.et_new_password);
+        ViewTool.setOnClickListener(this, this,
+                new int[]{R.id.btn_get_code, R.id.btn_reset_password});
+    }
+
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.btn_get_code:
+                // 获取验证码
+                email = et_email.getEditableText().toString();
+                if (TextUtils.isEmpty(email)) {
+                    ToastTool.showShort("邮箱不能为空");
+                    return;
+                }
+
+                showLoading();
+                mCodePresenter.getCode(email);
+                break;
+            case R.id.btn_reset_password:
+                email = et_email.getEditableText().toString();
+                if (TextUtils.isEmpty(email)) {
+                    ToastTool.showShort("邮箱不能为空");
+                    return;
+                }
+
+                if (!email.contains("@")) {
+                    ToastTool.showShort("请输入合法的邮箱");
+                    return;
+                }
+
+                // 重设密码
+                code = et_code.getEditableText().toString();
+                if (TextUtils.isEmpty(code)) {
+                    ToastTool.showShort("验证码不能为空");
+                    return;
+                }
+
+                password = et_new_password.getEditableText().toString();
+                if (TextUtils.isEmpty(password)) {
+                    ToastTool.showShort("新密码不能为空");
+                    return;
+                }
+
+                String encodePassword = StringTool.getEncodeString(password);
+                if (TextUtils.isEmpty(encodePassword)) {
+                    ToastTool.showShort("该手机不支持加密算法");
+                    return;
+                }
+                showLoading();
+                mForgetPresenter.submitReset(email, code, encodePassword);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onGetCode(boolean isSuccess, String message) {
+        dismissLoading();
+        if (isSuccess) {
+            ToastTool.showShort("验证码发送成功");
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+
+    @Override
+    public void onForgetResult(BeiUser user, String message) {
+        dismissLoading();
+        if (user != null) {
+            UserInfoManager.getInstance().setBeiUser(user);
+            ToastTool.showShort("重设密码成功");
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+}

+ 50 - 0
app/src/main/java/com/itant/shibei/ui/mine/forget/ForgetPresenter.java

@@ -0,0 +1,50 @@
+package com.itant.shibei.ui.mine.forget;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class ForgetPresenter extends BasePresenter<IForgetView> {
+
+    public void submitReset(String email, String code, String password) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("email", email);
+        map.put("code", code);
+        map.put("password", password);
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .resetPassword(map)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<BeiUser>() {
+                    @Override
+                    public void onSuccess(BeiUser result) {
+                        if (getView() != null) {
+                            getView().onForgetResult(result, null);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onForgetResult(null, TextUtils.isEmpty(errMsg) ? "密码重设失败" : errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 16 - 0
app/src/main/java/com/itant/shibei/ui/mine/forget/IForgetView.java

@@ -0,0 +1,16 @@
+package com.itant.shibei.ui.mine.forget;
+
+import com.itant.shibei.bean.BeiUser;
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ * Description: 忘记密码的回调
+ */
+public interface IForgetView extends IView {
+
+    void onForgetResult(BeiUser user, String message);
+}

+ 193 - 0
app/src/main/java/com/itant/shibei/ui/mine/goods/AddGoodsActivity.java

@@ -0,0 +1,193 @@
+package com.itant.shibei.ui.mine.goods;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.constant.ConstantUrl;
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.miekir.common.utils.ActivityTool;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 添加京东商品界面
+ */
+public class AddGoodsActivity extends BaseBeiActivity implements View.OnClickListener, IAddGoodsView {
+    public static final String KEY_MODIFY_GOODS = "key_modify_goods";
+
+    private TextInputEditText et_goods_title;
+    private TextInputEditText et_cover_url;
+    private TextInputEditText et_video_url;
+    private TextInputEditText et_desc;
+    private TextInputEditText et_reason;
+    private TextInputEditText et_old_price;
+    private TextInputEditText et_rebate_all;
+    private TextInputEditText et_province;
+    private TextInputEditText et_coupon;
+    private TextInputEditText et_goods_url;
+    private TextInputEditText et_sales_per_month;
+    private TextInputEditText et_good_comment_percent;
+    private TextInputEditText et_shop_name;
+    private TextInputEditText et_comment_num;
+    private CheckBox cb_self;
+    private CheckBox cb_enable;
+
+    private GoodsBean mGoodsBean;
+
+    @InjectPresenter
+    AddGoodsPresenter presenter;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_goods_add;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.menu_done, menu);
+        MenuItem item = menu.findItem(R.id.action_done);
+        item.setTitle("  返利链接  ");
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_done:
+                // 注册
+                ActivityTool.openUrl(this, ConstantUrl.URL_JD_UNION);
+                break;
+            default:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        et_goods_title = findViewById(R.id.et_goods_title);
+        et_cover_url = findViewById(R.id.et_cover_url);
+        et_video_url = findViewById(R.id.et_video_url);
+        et_desc = findViewById(R.id.et_desc);
+        et_reason = findViewById(R.id.et_reason);
+        et_old_price = findViewById(R.id.et_old_price);
+        et_rebate_all = findViewById(R.id.et_rebate_all);
+        et_province = findViewById(R.id.et_province);
+        et_coupon = findViewById(R.id.et_coupon);
+        et_goods_url = findViewById(R.id.et_goods_url);
+        et_sales_per_month = findViewById(R.id.et_sales_per_month);
+        et_good_comment_percent = findViewById(R.id.et_good_comment_percent);
+        et_shop_name = findViewById(R.id.et_shop_name);
+        et_comment_num = findViewById(R.id.et_comment_num);
+        cb_self = findViewById(R.id.cb_self);
+        cb_enable = findViewById(R.id.cb_enable);
+        ViewTool.setOnClickListener(this, this, new int[]{R.id.btn_add_goods});
+        Button btn_add_goods = findViewById(R.id.btn_add_goods);
+
+        mGoodsBean = (GoodsBean) getIntent().getSerializableExtra(KEY_MODIFY_GOODS);
+        if (mGoodsBean != null) {
+            initGoods();
+            setTitle("更新商品");
+            btn_add_goods.setText("立即更新");
+        } else {
+            setTitle("上架新的商品");
+        }
+    }
+
+    private void initGoods() {
+        et_goods_title.setText(mGoodsBean.title);
+        et_cover_url.setText(mGoodsBean.coverImageUrl);
+        et_video_url.setText(mGoodsBean.videoUrl);
+        et_desc.setText(mGoodsBean.description);
+        et_reason.setText(mGoodsBean.reason);
+        et_old_price.setText(String.valueOf(mGoodsBean.oldPrice));
+        et_rebate_all.setText(String.valueOf(mGoodsBean.rebate));
+        et_province.setText(mGoodsBean.province);
+        et_coupon.setText(mGoodsBean.couponInfo);
+        et_goods_url.setText(mGoodsBean.goodsUrl);
+        et_sales_per_month.setText(String.valueOf(mGoodsBean.salesPerMonth));
+        et_good_comment_percent.setText(String.valueOf(mGoodsBean.goodCommentPercent));
+        et_shop_name.setText(mGoodsBean.shopName);
+        et_comment_num.setText(String.valueOf(mGoodsBean.commentNum));
+        cb_self.setChecked(mGoodsBean.isSelfBusiness);
+        cb_enable.setChecked(mGoodsBean.enable);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.btn_add_goods:
+                String title = et_goods_title.getEditableText().toString();
+                String coverUrl = et_cover_url.getEditableText().toString();
+                String videoUrl = et_video_url.getEditableText().toString();
+                String description = et_desc.getEditableText().toString();
+                String reason = et_reason.getEditableText().toString();
+                long oldPrice = Long.parseLong(et_old_price.getEditableText().toString());
+                long rebate = Long.parseLong(et_rebate_all.getEditableText().toString());
+                long nowPrice = oldPrice - rebate/2;
+                String province = et_province.getEditableText().toString();
+                String shopName = et_shop_name.getEditableText().toString();
+                String coupon = et_coupon.getEditableText().toString();
+                String goodsUrl = et_goods_url.getEditableText().toString();
+                String salesPerMonth = et_sales_per_month.getEditableText().toString();
+                String goodCommentPercent = et_good_comment_percent.getEditableText().toString();
+                String commentNum = et_comment_num.getEditableText().toString();
+                boolean isSelf = cb_self.isChecked();
+                boolean isEnable = cb_enable.isChecked();
+
+                if (mGoodsBean == null) {
+                    mGoodsBean = new GoodsBean();
+                }
+                mGoodsBean.title = title;
+                mGoodsBean.coverImageUrl = coverUrl;
+                mGoodsBean.videoUrl = videoUrl;
+                mGoodsBean.description = description;
+                mGoodsBean.reason = reason;
+                mGoodsBean.oldPrice = oldPrice;
+                mGoodsBean.nowPrice = nowPrice;
+                mGoodsBean.rebate = rebate;
+                mGoodsBean.province = province;
+                mGoodsBean.shopName = shopName;
+                mGoodsBean.couponInfo = coupon;
+                mGoodsBean.goodsUrl = goodsUrl;
+                mGoodsBean.salesPerMonth = Long.parseLong(salesPerMonth);
+                mGoodsBean.goodCommentPercent = Double.parseDouble(goodCommentPercent);
+                mGoodsBean.commentNum = Long.parseLong(commentNum);
+                mGoodsBean.isSelfBusiness = isSelf;
+                mGoodsBean.enable = isEnable;
+                mGoodsBean.hasCoupon = !TextUtils.isEmpty(coupon);
+
+                showLoading();
+                presenter.addGoods(mGoodsBean);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onAddGoodsResult(boolean success, String message) {
+        dismissLoading();
+        ToastTool.showShort(message);
+        if (success) {
+            Intent intent = getIntent();
+            intent.putExtra(KEY_MODIFY_GOODS, mGoodsBean);
+            setResult(RESULT_OK, intent);
+            finish();
+        }
+    }
+}

+ 43 - 0
app/src/main/java/com/itant/shibei/ui/mine/goods/AddGoodsPresenter.java

@@ -0,0 +1,43 @@
+package com.itant.shibei.ui.mine.goods;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.itant.shibei.ui.home.goods.GoodsBean;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class AddGoodsPresenter extends BasePresenter<IAddGoodsView> {
+
+    public void addGoods(GoodsBean goodsBean) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .addGoods(goodsBean)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<String>() {
+                    @Override
+                    public void onSuccess(String result) {
+                        if (getView() != null) {
+                            getView().onAddGoodsResult(true, result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onAddGoodsResult(false, TextUtils.isEmpty(errMsg) ? "操作失败"+e.getMessage() : errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 15 - 0
app/src/main/java/com/itant/shibei/ui/mine/goods/IAddGoodsView.java

@@ -0,0 +1,15 @@
+package com.itant.shibei.ui.mine.goods;
+
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ * Description: 获取验证码的回调
+ */
+public interface IAddGoodsView extends IView {
+
+    void onAddGoodsResult(boolean success, String message);
+}

+ 16 - 0
app/src/main/java/com/itant/shibei/ui/mine/login/ILoginView.java

@@ -0,0 +1,16 @@
+package com.itant.shibei.ui.mine.login;
+
+import com.itant.shibei.bean.BeiUser;
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ * Description: 获取验证码的回调
+ */
+public interface ILoginView extends IView {
+
+    void onLoginResult(BeiUser user, String message);
+}

+ 79 - 19
app/src/main/java/com/itant/shibei/ui/mine/login/LoginActivity.java

@@ -2,31 +2,38 @@ package com.itant.shibei.ui.mine.login;
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
+import com.google.android.material.textfield.TextInputEditText;
 import com.itant.shibei.R;
-import com.itant.shibei.base.BaseShiBeiActivity;
-import com.itant.shibei.ui.mine.login.forget.ForgetActivity;
-import com.itant.shibei.ui.mine.login.register.RegisterActivity;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.ui.mine.forget.ForgetActivity;
+import com.itant.shibei.ui.mine.register.RegisterActivity;
+import com.miekir.common.utils.ToastTool;
 import com.miekir.common.utils.ViewTool;
-import com.miekir.mvp.view.BaseMVPActivity;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+import rx_activity_result2.RxActivityResult;
 
 /**
- * @author 詹子聪
+ * @author Miekir
  * @date 2020/6/18 16:48
  * Description: 登录界面
  */
-public class LoginActivity extends BaseShiBeiActivity implements View.OnClickListener {
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+public class LoginActivity extends BaseBeiActivity implements View.OnClickListener, ILoginView {
+    private TextInputEditText et_email;
+    private TextInputEditText et_password;
 
-    }
+    @InjectPresenter
+    LoginPresenter presenter;
 
     @Override
     public int getLayoutID() {
@@ -35,14 +42,17 @@ public class LoginActivity extends BaseShiBeiActivity implements View.OnClickLis
 
     @Override
     public void initViews(Bundle savedInstanceState) {
-        setTitle("登录");
-        //setOnDoneListener(this);
-        ViewTool.setOnClickListener(this, new int[]{R.id.tv_register, R.id.tv_forget}, this);
+        setTitle("使用电子邮箱登录");
+        et_email = findViewById(R.id.et_email);
+        et_password = findViewById(R.id.et_password);
+        ViewTool.setOnClickListener(this, this, new int[]{R.id.btn_login, R.id.tv_forget});
     }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.menu_done, menu);
+        MenuItem item = menu.findItem(R.id.action_done);
+        item.setTitle("  注册账号  ");
         return super.onCreateOptionsMenu(menu);
     }
 
@@ -50,7 +60,17 @@ public class LoginActivity extends BaseShiBeiActivity implements View.OnClickLis
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
             case R.id.action_done:
-                finish();
+                // 注册
+                Intent registerIntent = new Intent(this, RegisterActivity.class);
+                RxActivityResult.on(this).startIntent(registerIntent)
+                        .filter(result -> result.resultCode() == RESULT_OK)
+                        .doOnNext(result -> {
+                            // 注册成功之后关闭登录界面
+                            ToastTool.showShort("注册成功");
+                            //startActivity(new Intent(LoginActivity.this, MineActivity.class));
+                            finish();
+                        })
+                        .subscribe();
                 break;
             default:
                 break;
@@ -61,14 +81,54 @@ public class LoginActivity extends BaseShiBeiActivity implements View.OnClickLis
     @Override
     public void onClick(View v) {
         switch (v.getId()) {
-            case R.id.tv_register:
-                startActivity(new Intent(this, RegisterActivity.class));
-                break;
             case R.id.tv_forget:
-                startActivity(new Intent(this, ForgetActivity.class));
+                // 忘记密码
+                Intent forgetIntent = new Intent(this, ForgetActivity.class);
+                RxActivityResult.on(this).startIntent(forgetIntent)
+                        .filter(result -> result.resultCode() == RESULT_OK)
+                        .doOnNext(result -> {
+                            // 找回密码成功之后关闭登录界面
+                            //startActivity(new Intent(LoginActivity.this, MineActivity.class));
+                            finish();
+                        })
+                        .subscribe();
+                break;
+
+            case R.id.btn_login:
+                String email = et_email.getEditableText().toString();
+                String password = et_password.getEditableText().toString();
+                if (TextUtils.isEmpty(email)) {
+                    ToastTool.showShort("邮箱不能为空");
+                    return;
+                }
+                if (TextUtils.isEmpty(password)) {
+                    ToastTool.showShort("密码不能为空");
+                    return;
+                }
+
+                password = StringTool.getEncodeString(password);
+                if (TextUtils.isEmpty(password)) {
+                    ToastTool.showShort("该手机不支持加密算法");
+                    return;
+                }
+                showLoading();
+                presenter.submitLogin(email, password);
                 break;
             default:
                 break;
         }
     }
+
+    @Override
+    public void onLoginResult(BeiUser user, String message) {
+        dismissLoading();
+        if (user != null) {
+            UserInfoManager.getInstance().setBeiUser(user);
+            ToastTool.showShort("登录成功");
+            //startActivity(new Intent(this, MineActivity.class));
+            finish();
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
 }

+ 43 - 0
app/src/main/java/com/itant/shibei/ui/mine/login/LoginPresenter.java

@@ -0,0 +1,43 @@
+package com.itant.shibei.ui.mine.login;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class LoginPresenter extends BasePresenter<ILoginView> {
+
+    public void submitLogin(String email, String password) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .submitLogin(email, password)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<BeiUser>() {
+                    @Override
+                    public void onSuccess(BeiUser result) {
+                        if (getView() != null) {
+                            getView().onLoginResult(result, null);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onLoginResult(null, TextUtils.isEmpty(errMsg) ? "登录失败" : errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 0 - 63
app/src/main/java/com/itant/shibei/ui/mine/login/forget/ForgetActivity.java

@@ -1,63 +0,0 @@
-package com.itant.shibei.ui.mine.login.forget;
-
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.itant.shibei.R;
-import com.itant.shibei.base.BaseShiBeiActivity;
-
-/**
- * @author 詹子聪
- * @date 2020/6/18 16:48
- * Description: 登录界面
- */
-public class ForgetActivity extends BaseShiBeiActivity implements View.OnClickListener {
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-    }
-
-    @Override
-    public int getLayoutID() {
-        return R.layout.activity_forget;
-    }
-
-    @Override
-    public void initViews(Bundle savedInstanceState) {
-        setTitle("重置密码");
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.menu_done, menu);
-        //MenuItem item = menu.findItem(R.id.action_done);
-        //item.setTitle("重置完成");
-        return super.onCreateOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_done:
-                finish();
-                break;
-            default:
-                break;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-            default:
-                break;
-        }
-    }
-}

+ 0 - 63
app/src/main/java/com/itant/shibei/ui/mine/login/register/RegisterActivity.java

@@ -1,63 +0,0 @@
-package com.itant.shibei.ui.mine.login.register;
-
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.itant.shibei.R;
-import com.itant.shibei.base.BaseShiBeiActivity;
-
-/**
- * @author 詹子聪
- * @date 2020/6/18 16:48
- * Description: 登录界面
- */
-public class RegisterActivity extends BaseShiBeiActivity implements View.OnClickListener {
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-    }
-
-    @Override
-    public int getLayoutID() {
-        return R.layout.activity_register;
-    }
-
-    @Override
-    public void initViews(Bundle savedInstanceState) {
-        setTitle("注册");
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.menu_done, menu);
-        //MenuItem item = menu.findItem(R.id.action_done);
-        //item.setTitle("完成注册");
-        return super.onCreateOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_done:
-                finish();
-                break;
-            default:
-                break;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-            default:
-                break;
-        }
-    }
-}

+ 40 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/CodePresenter.java

@@ -0,0 +1,40 @@
+package com.itant.shibei.ui.mine.register;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class CodePresenter extends BasePresenter<ICodeView> {
+
+    public void getCode(String email) {
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .getCode(email)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean result) {
+                        if (getView() != null) {
+                            getView().onGetCode(result, null);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onGetCode(false, errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 18 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/ICodeView.java

@@ -0,0 +1,18 @@
+package com.itant.shibei.ui.mine.register;
+
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ * Description: 获取验证码的回调
+ */
+public interface ICodeView extends IView {
+    /**
+     *
+     * @param isSuccess 是否成功
+     */
+    void onGetCode(boolean isSuccess, String message);
+}

+ 159 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/RegisterActivity.java

@@ -0,0 +1,159 @@
+package com.itant.shibei.ui.mine.register;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.ui.mine.register.fill.FillDataActivity;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.common.utils.ViewTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+import java.lang.ref.WeakReference;
+
+import rx_activity_result2.RxActivityResult;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 登录界面
+ */
+public class RegisterActivity extends BaseBeiActivity implements View.OnClickListener, ICodeView {
+    public static final String KEY_EMAIL = "email";
+    public static final String KEY_CODE = "code";
+
+    private TextInputEditText et_email;
+    private TextInputEditText et_code;
+    private Button btn_get_code;
+
+    private String email;
+    private String code;
+
+    @InjectPresenter
+    CodePresenter presenter;
+
+    /**验证码倒计时*/
+    private static final int MSG_TIME = 0;
+    private static final int TIME_GET_PERIOD = 61;
+    private int mRestTime = TIME_GET_PERIOD;
+    private static class TimeHandler extends Handler {
+        private WeakReference<RegisterActivity> weakReference;
+        public TimeHandler(RegisterActivity activity) {
+            weakReference = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            super.handleMessage(msg);
+            RegisterActivity activity = weakReference.get();
+            if (activity == null) {
+                return;
+            }
+            activity.mRestTime -= 1;
+            if (activity.mRestTime == 0) {
+                activity.btn_get_code.setText("获取验证码");
+                activity.btn_get_code.setEnabled(true);
+                activity.mRestTime = TIME_GET_PERIOD;
+            } else {
+                activity.btn_get_code.setText("重新获取(" + activity.mRestTime + "S)");
+                activity.mTimeHandler.sendEmptyMessageDelayed(MSG_TIME, 1000);
+            }
+        }
+    }
+    private Handler mTimeHandler = new TimeHandler(this);
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_register;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        setTitle("使用电子邮箱注册");
+        ViewTool.setOnClickListener(this, this, new int[]{R.id.btn_get_code, R.id.btn_next});
+        et_email = findViewById(R.id.et_email);
+        et_code = findViewById(R.id.et_code);
+        btn_get_code = findViewById(R.id.btn_get_code);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.btn_next:
+                // 完善资料
+                email = et_email.getEditableText().toString();
+                if (TextUtils.isEmpty(email)) {
+                    ToastTool.showShort("邮箱不能为空");
+                    return;
+                }
+
+                if (!email.contains("@")) {
+                    ToastTool.showShort("请输入合法的邮箱");
+                    return;
+                }
+
+                code = et_code.getEditableText().toString();
+                if (TextUtils.isEmpty(code)) {
+                    ToastTool.showShort("验证码不能为空");
+                    return;
+                }
+
+                Intent fillDataIntent = new Intent(this, FillDataActivity.class);
+                fillDataIntent.putExtra(KEY_EMAIL, email);
+                fillDataIntent.putExtra(KEY_CODE, code);
+                RxActivityResult.on(this).startIntent(fillDataIntent)
+                        .filter(result -> result.resultCode() == RESULT_OK)
+                        .doOnNext(result -> {
+                            // 注册成功之后关闭登录界面
+                            setResult(RESULT_OK);
+                            finish();
+                        })
+                        .subscribe();
+                break;
+
+            case R.id.btn_get_code:
+                // 获取验证码
+                email = et_email.getEditableText().toString();
+                if (TextUtils.isEmpty(email)) {
+                    ToastTool.showShort("邮箱不能为空");
+                    return;
+                }
+                showLoading();
+                presenter.getCode(email);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onGetCode(boolean isSuccess, String message) {
+        dismissLoading();
+        if (isSuccess) {
+            ToastTool.showShort("验证码发送成功");
+            btn_get_code.setEnabled(false);
+            mTimeHandler.sendEmptyMessage(MSG_TIME);
+        } else {
+            if (TextUtils.isEmpty(message)) {
+                ToastTool.showShort("验证码发送失败");
+            } else {
+                ToastTool.showShort(message);
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mTimeHandler.removeMessages(MSG_TIME);
+    }
+}

+ 94 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/fill/FillDataActivity.java

@@ -0,0 +1,94 @@
+package com.itant.shibei.ui.mine.register.fill;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.itant.shibei.R;
+import com.itant.shibei.base.BaseBeiActivity;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.manager.UserInfoManager;
+import com.itant.shibei.tool.StringTool;
+import com.itant.shibei.ui.mine.register.RegisterActivity;
+import com.miekir.common.utils.ToastTool;
+import com.miekir.mvp.presenter.InjectPresenter;
+
+/**
+ * @author Miekir
+ * @date 2020/6/18 16:48
+ * Description: 完善资料界面
+ */
+public class FillDataActivity extends BaseBeiActivity implements IRegisterView {
+    private TextInputEditText et_password;
+    private TextInputEditText et_nickname;
+    private TextInputEditText et_cash_account;
+
+    private BeiUser mBeiUser;
+    private String email;
+    private String code;
+
+    @InjectPresenter
+    RegisterPresenter presenter;
+
+    @Override
+    public int getLayoutID() {
+        return R.layout.activity_fill_data;
+    }
+
+    @Override
+    public void initViews(Bundle savedInstanceState) {
+        setTitle("完善提现资料");
+        et_password = findViewById(R.id.et_password);
+        et_nickname = findViewById(R.id.et_nickname);
+        et_cash_account = findViewById(R.id.et_cash_account);
+        email = getIntent().getStringExtra(RegisterActivity.KEY_EMAIL);
+        code = getIntent().getStringExtra(RegisterActivity.KEY_CODE);
+        findViewById(R.id.btn_finish).setOnClickListener(v -> {
+            submitRegister();
+        });
+    }
+
+    private void submitRegister() {
+        String password = et_password.getEditableText().toString();
+        String nickname = et_nickname.getEditableText().toString();
+        String cashAccount = et_cash_account.getEditableText().toString();
+        if (TextUtils.isEmpty(password)) {
+            ToastTool.showShort("请输入密码");
+            return;
+        }
+        if (TextUtils.isEmpty(nickname)) {
+            ToastTool.showShort("请输入昵称");
+            return;
+        }
+        if (TextUtils.isEmpty(cashAccount)) {
+            ToastTool.showShort("请输入提现账号");
+            return;
+        }
+
+        String encodeString = StringTool.getEncodeString(password);
+        if (TextUtils.isEmpty(encodeString)) {
+            ToastTool.showShort("该手机不支持加密算法");
+            return;
+        }
+        mBeiUser = new BeiUser();
+        mBeiUser.email = email;
+        mBeiUser.password = encodeString;
+        mBeiUser.nickName = nickname;
+        mBeiUser.cashAccount = cashAccount;
+        showLoading();
+        presenter.submitRegister(mBeiUser, code);
+    }
+
+    @Override
+    public void onRegisterResult(BeiUser user, String message) {
+        dismissLoading();
+        if (user != null) {
+            // 保存到本地
+            UserInfoManager.getInstance().setBeiUser(user);
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            ToastTool.showShort(message);
+        }
+    }
+}

+ 16 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/fill/IRegisterView.java

@@ -0,0 +1,16 @@
+package com.itant.shibei.ui.mine.register.fill;
+
+import com.itant.shibei.bean.BeiUser;
+import com.miekir.mvp.view.IView;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/2 10:29
+ * Description: 注册的回调
+ */
+public interface IRegisterView extends IView {
+
+    void onRegisterResult(BeiUser user, String message);
+}

+ 52 - 0
app/src/main/java/com/itant/shibei/ui/mine/register/fill/RegisterPresenter.java

@@ -0,0 +1,52 @@
+package com.itant.shibei.ui.mine.register.fill;
+
+import android.text.TextUtils;
+
+import com.itant.shibei.base.ApiService;
+import com.itant.shibei.bean.BeiUser;
+import com.itant.shibei.net.RetrofitHelper;
+import com.miekir.mvp.presenter.BasePresenter;
+import com.miekir.network.core.base.BaseObserver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * @author Miekir
+ * @date 2020/7/9 20:54
+ * Description: 商品的相关数据接口处理
+ */
+public class RegisterPresenter extends BasePresenter<IRegisterView> {
+
+    public void submitRegister(BeiUser user, String code) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("email", user.email);
+        map.put("code", code);
+        map.put("password", user.password);
+        map.put("nickname", user.nickName);
+        map.put("cashAccount", user.cashAccount);
+        RetrofitHelper.getInstance()
+                .getRequestApi(ApiService.class)
+                .submitRegister(map)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new BaseObserver<BeiUser>() {
+                    @Override
+                    public void onSuccess(BeiUser result) {
+                        if (getView() != null) {
+                            getView().onRegisterResult(result, null);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable e, String errMsg) {
+                        if (getView() != null) {
+                            getView().onRegisterResult(null, TextUtils.isEmpty(errMsg) ? "注册失败" : errMsg);
+                        }
+                    }
+                });
+    }
+}

+ 129 - 0
app/src/main/java/com/itant/shibei/widget/AppbarTranslateListener.java

@@ -0,0 +1,129 @@
+package com.itant.shibei.widget;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.google.android.material.appbar.AppBarLayout;
+
+/**
+ *
+ *
+ * @author Miekir
+ * @date 2020/7/21 8:35
+ * Description: AppBar移动时的图标动画监听
+ */
+public class AppbarTranslateListener implements AppBarLayout.OnOffsetChangedListener {
+    private static final int STATUS_CLOSE = 1;
+    private static final int STATUS_OPEN = 2;
+    private static final int STATUS_MOVE = 3;
+    /**
+     * 1. 完全关闭 2. 完全打开 3.运动中
+     */
+    private int mLastStatus = STATUS_CLOSE;
+
+    private double mDefaultHeight;
+    private double mDefaultWidth;
+
+    private ViewGroup mViewGroup;
+    private View rl_search_top;
+    private View rl_search_top_shadow;
+    private View rl_search;
+
+    private long mLastAnimateMillis;
+
+    public AppbarTranslateListener(Activity activity,
+                                   double defaultWidth, double defaultHeight,
+                                   View rl_search_top, View rl_search_top_shadow,
+                                   View rl_search) {
+        mViewGroup = (ViewGroup) activity.getWindow().getDecorView();
+
+        mDefaultWidth = defaultWidth;
+        mDefaultHeight = defaultHeight;
+        this.rl_search = rl_search;
+        this.rl_search_top = rl_search_top;
+        this.rl_search_top_shadow = rl_search_top_shadow;
+    }
+
+    @Override
+    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
+        // 小于10ms的间隔直接忽略
+        if (System.currentTimeMillis() - mLastAnimateMillis < 10) {
+            return;
+        }
+        mLastAnimateMillis = System.currentTimeMillis();
+
+        // AppBarLayout完全折叠时,fraction为1
+        float fraction = ((float) Math.abs(verticalOffset)) / appBarLayout.getTotalScrollRange();
+
+        float reFraction = 1 - fraction;
+        if (reFraction < 0.007) {
+            // 完全折叠
+            if (mLastStatus != STATUS_CLOSE) {
+                mLastStatus = STATUS_CLOSE;
+                mViewGroup.getOverlay().remove(rl_search_top);
+                rl_search.setVisibility(View.VISIBLE);
+            } else {
+                return;
+            }
+        } else {
+            rl_search.setVisibility(View.INVISIBLE);
+            if (reFraction > 0.988) {
+                // 完全展开
+                if (mLastStatus != STATUS_OPEN) {
+                    mLastStatus = STATUS_OPEN;
+                    rl_search_top_shadow.setVisibility(View.VISIBLE);
+                    mViewGroup.getOverlay().add(rl_search_top);
+                } else {
+                    return;
+                }
+            } else {
+                // 移动中
+                if (mLastStatus != STATUS_MOVE) {
+                    mLastStatus = STATUS_MOVE;
+                    rl_search_top_shadow.setVisibility(View.INVISIBLE);
+                }
+            }
+        }
+
+        followFinger(fraction);
+
+        /*double searchSize = mDefaultImageSize * fraction;
+        ViewGroup.LayoutParams searchParams = iv_search.getLayoutParams();
+        searchParams.width = (int) searchSize;
+        searchParams.height = (int) searchSize;
+        iv_search.requestLayout();
+
+        double topSearchSize = mDefaultImageSize - searchSize;
+        ViewGroup.LayoutParams topSearchParams = iv_search_top.getLayoutParams();
+        topSearchParams.width = (int) topSearchSize;
+        topSearchParams.height = (int) topSearchSize;
+        iv_search_top.requestLayout();*/
+    }
+
+
+
+
+    private void followFinger(float fraction) {
+        // 完全展开时fraction为0,所以实时translationX为mDefaultWidth*fraction,
+        // translationY为mDefaultHeight*fraction
+
+        // add之后,要紧接动画
+        mViewGroup.getOverlay().add(rl_search_top);
+        // 横向移动
+        ObjectAnimator tranXAnim = ObjectAnimator.ofFloat(rl_search_top, "translationX", (float) (mDefaultWidth*fraction));
+        tranXAnim.setDuration(0);
+        // 纵向移动
+        ObjectAnimator tranYAnim = ObjectAnimator.ofFloat(rl_search_top, "translationY", (float) (-mDefaultHeight*fraction));
+        tranYAnim.setDuration(0);
+
+        //tranXAnim.start();
+
+        // 一起做运动
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(tranXAnim, tranYAnim);
+        set.start();
+    }
+}

+ 0 - 36
app/src/main/java/com/itant/shibei/widget/DividerItemDecoration.java

@@ -1,36 +0,0 @@
-package com.itant.shibei.widget;
-
-import android.graphics.Rect;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-/**
- * 均等分割线
- */
-public class DividerItemDecoration extends RecyclerView.ItemDecoration {
-    private int halfSpace;
-
-    public DividerItemDecoration(int space) {
-        this.halfSpace = space / 2;
-    }
-
-    @Override
-    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
-
-        if (parent.getPaddingLeft() != halfSpace) {
-            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
-            parent.setClipToPadding(false);
-        }
-
-        outRect.top = halfSpace;
-        outRect.left = halfSpace;
-        outRect.right = halfSpace;
-
-        if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
-            outRect.bottom = halfSpace;
-        } else {
-            outRect.bottom = 0;
-        }
-    }
-}

+ 50 - 0
app/src/main/java/com/itant/shibei/widget/VideoPlayer.java

@@ -0,0 +1,50 @@
+package com.itant.shibei.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import cn.jzvd.JzvdStd;
+
+/**
+ * Copyright (C), 2019-2020, Miekir
+ *
+ * @author Miekir
+ * @date 2020/8/25 14:29
+ * Description: 视频播放
+ */
+public class VideoPlayer extends JzvdStd {
+
+    public VideoPlayer(Context context) {
+        super(context);
+    }
+
+    public VideoPlayer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onStateAutoComplete() {
+        super.onStateAutoComplete();
+        if (mVideoPlayListener != null) {
+            mVideoPlayListener.onPlayComplete();
+        }
+    }
+
+    @Override
+    protected void clickBack() {
+        super.clickBack();
+        if (mVideoPlayListener != null) {
+            mVideoPlayListener.onPlayComplete();
+        }
+    }
+
+    private static VideoPlayListener mVideoPlayListener;
+
+    public static void setVideoPlayListener(VideoPlayListener videoPlayListener) {
+        mVideoPlayListener = videoPlayListener;
+    }
+
+    public interface VideoPlayListener {
+        void onPlayComplete();
+    }
+}

+ 10 - 0
app/src/main/java/com/itant/shibei/widget/bottomlistener/OnBottomListener.java

@@ -0,0 +1,10 @@
+package com.itant.shibei.widget.bottomlistener;
+
+/**
+ * @author Jack Tony
+ * @brief
+ * @date 2015/4/6
+ */
+public interface OnBottomListener {
+    public void onBottom();
+}

+ 117 - 0
app/src/main/java/com/itant/shibei/widget/bottomlistener/OnRcvScrollListener.java

@@ -0,0 +1,117 @@
+package com.itant.shibei.widget.bottomlistener;
+
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
+
+/**
+ * @author Jack Tony
+ * @brief recyle view 滚动监听器
+ * @date 2015/4/6
+ */
+public class OnRcvScrollListener extends RecyclerView.OnScrollListener implements OnBottomListener {
+    private String TAG = getClass().getSimpleName();
+
+    public static enum LAYOUT_MANAGER_TYPE {
+        LINEAR,
+        GRID,
+        STAGGERED_GRID
+    }
+
+    /**
+     * layoutManager的类型(枚举)
+     */
+    protected LAYOUT_MANAGER_TYPE layoutManagerType;
+
+    /**
+     * 最后一个的位置
+     */
+    private int[] lastPositions;
+
+    /**
+     * 最后一个可见的item的位置
+     */
+    private int lastVisibleItemPosition;
+/*    *//**
+     * 是否正在加载
+     *//*
+    private boolean isLoadingMore = false;*/
+
+    /**
+     * 当前滑动的状态
+     */
+    private int currentScrollState = 0;
+
+    @Override
+    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+        super.onScrolled(recyclerView, dx, dy);
+
+        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+        //  int lastVisibleItemPosition = -1;
+        if (layoutManagerType == null) {
+            if (layoutManager instanceof LinearLayoutManager) {
+                layoutManagerType = LAYOUT_MANAGER_TYPE.LINEAR;
+            } else if (layoutManager instanceof GridLayoutManager) {
+                layoutManagerType = LAYOUT_MANAGER_TYPE.GRID;
+            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
+                layoutManagerType = LAYOUT_MANAGER_TYPE.STAGGERED_GRID;
+            } else {
+                throw new RuntimeException(
+                        "Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
+            }
+        }
+
+        switch (layoutManagerType) {
+            case LINEAR:
+                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager)
+                        .findLastCompletelyVisibleItemPosition();
+                break;
+            case GRID:
+                lastVisibleItemPosition = ((GridLayoutManager) layoutManager)
+                        .findLastVisibleItemPosition();
+                break;
+            case STAGGERED_GRID:
+                StaggeredGridLayoutManager staggeredGridLayoutManager
+                        = (StaggeredGridLayoutManager) layoutManager;
+                if (lastPositions == null) {
+                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
+                }
+                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
+                lastVisibleItemPosition = findMax(lastPositions);
+                break;
+        }
+
+    }
+
+    @Override
+    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+        super.onScrollStateChanged(recyclerView, newState);
+        currentScrollState = newState;
+        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+        int visibleItemCount = layoutManager.getChildCount();
+        int totalItemCount = layoutManager.getItemCount();
+        if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE &&
+                (lastVisibleItemPosition) >= totalItemCount - 1)) {
+            //Log.d(TAG, "is loading more");
+            onBottom();
+        }
+    }
+
+
+    @Override
+    public void onBottom() {
+        //Log.d(TAG, "is onBottom");
+    }
+
+    private int findMax(int[] lastPositions) {
+        int max = lastPositions[0];
+        for (int value : lastPositions) {
+            if (value > max) {
+                max = value;
+            }
+        }
+        return max;
+    }
+}

+ 44 - 0
app/src/main/java/com/itant/shibei/widget/decoration/CardViewDividerItemDecoration.java

@@ -0,0 +1,44 @@
+package com.itant.shibei.widget.decoration;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 均等分割线(针对CardView)
+ */
+public class CardViewDividerItemDecoration extends RecyclerView.ItemDecoration {
+    private int halfSpace;
+
+    public CardViewDividerItemDecoration(int space) {
+        this.halfSpace = space / 2;
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+        super.getItemOffsets(outRect, view, parent, state);
+        /*if (parent.getPaddingLeft() != halfSpace) {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
+            parent.setClipToPadding(false);
+        }*/
+
+        /*outRect.left = halfSpace;
+        outRect.right = halfSpace;
+        outRect.top = halfSpace;
+        if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
+            outRect.bottom = halfSpace;
+        } else {
+            outRect.bottom = 0;
+        }*/
+
+        // 让分割线一致
+        parent.setClipToPadding(false);
+        if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, 0);
+        } else {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
+        }
+
+    }
+}

+ 54 - 0
app/src/main/java/com/itant/shibei/widget/decoration/NormalDividerItemDecoration.java

@@ -0,0 +1,54 @@
+package com.itant.shibei.widget.decoration;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 均等分割线,对于CardView,默认有8dp的margin_bottom,可根据需要进行加减
+ */
+public class NormalDividerItemDecoration extends RecyclerView.ItemDecoration {
+    private int mSpace;
+
+    public NormalDividerItemDecoration(int space) {
+        this.mSpace = space;
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+        super.getItemOffsets(outRect, view, parent, state);
+        /*if (parent.getPaddingLeft() != halfSpace) {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
+            parent.setClipToPadding(false);
+        }*/
+
+        /*outRect.left = halfSpace;
+        outRect.right = halfSpace;
+        outRect.top = halfSpace;
+        if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
+            outRect.bottom = halfSpace;
+        } else {
+            outRect.bottom = 0;
+        }*/
+
+        /*parent.setClipToPadding(false);
+        if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, 0);
+        } else {
+            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
+        }*/
+
+        // Rect outRect表示 item 的上下左右所留下的边距。其中 outRect 的 left,top,right,bottom 即为 item 四周留下的边距的距离,默认都为 0 ;
+        // 让分割线一致
+        parent.setClipToPadding(false);
+        outRect.left = mSpace;
+        outRect.top = mSpace;
+        outRect.right = mSpace;
+        if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
+            outRect.bottom = mSpace;
+        } else {
+            outRect.bottom = 0;
+        }
+    }
+}

+ 37 - 0
app/src/main/java/com/itant/shibei/widget/decoration/SpacesItemDecoration.java

@@ -0,0 +1,37 @@
+package com.itant.shibei.widget.decoration;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 第一个item顶部有分割线,其他item的顶部没有,因为CardView自带margin_bottom
+ */
+public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
+    private int space;
+
+    public SpacesItemDecoration(int space) {
+        this.space = space;
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view,
+                               RecyclerView parent, RecyclerView.State state) {
+        outRect.left = space;
+        outRect.right = space;
+
+        // Add top margin only for the first item to avoid double space between items
+        if (parent.getChildLayoutPosition(view) == 0) {
+            outRect.top = space;
+        } else {
+            outRect.top = 0;
+        }
+
+        if (parent.getChildLayoutPosition(view) == parent.getAdapter().getItemCount() - 1) {
+            outRect.bottom = space;
+        } else {
+            outRect.bottom = 0;
+        }
+    }
+}

+ 9 - 0
app/src/main/res/drawable/ic_more_vert.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>

+ 5 - 5
app/src/main/res/drawable/selector_btn.xml

@@ -3,20 +3,20 @@
     <item android:state_pressed="true">
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/margin_ss"/>
-            <solid android:color="@color/black_title"/>
+            <solid android:color="@color/green_pressed"/>
         </shape>
     </item>
 
-    <item android:state_enabled="false">
+    <!--<item android:state_enabled="false">
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/margin_ss"/>
-            <solid android:color="@color/black_title"/>
+            <solid android:color="@color/green_logo"/>
         </shape>
-    </item>
+    </item>-->
     <item>
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/margin_ss"/>
-            <solid android:color="@color/black_standard"/>
+            <solid android:color="@color/green_logo"/>
         </shape>
     </item>
 </selector>

+ 22 - 0
app/src/main/res/drawable/selector_btn_exit.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/margin_ss"/>
+            <solid android:color="@color/red"/>
+        </shape>
+    </item>
+
+    <!--<item android:state_enabled="false">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/margin_ss"/>
+            <solid android:color="@color/green_logo"/>
+        </shape>
+    </item>-->
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/margin_ss"/>
+            <solid android:color="@color/red_text"/>
+        </shape>
+    </item>
+</selector>

+ 0 - 0
app/src/main/res/drawable/shape_gray_stroke_solid.xml


Някои файлове не бяха показани, защото твърде много файлове са промени