From 623156bdb525786e9441b290dce7f5b1cdf62394 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Thu, 4 Nov 2021 10:19:34 +0800 Subject: [PATCH 01/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E5=88=9B=E5=BB=BA/=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95(=E5=88=9B=E5=BB=BA=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=8D=95UI)=20https://git.ghzs.com/pm/halo-app-issues?= =?UTF-8?q?/-/issues/1604?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 4 + .../com/gh/gamecenter/CropImageActivity.java | 17 +- .../publish/GameCollectionEditActivity.kt | 118 ++++++++ .../publish/GameCollectionViewModel.kt | 8 + .../drawable-xxhdpi/icon_game_collection.webp | Bin 0 -> 884 bytes .../res/drawable-xxxhdpi/ic_choose_game.webp | Bin 0 -> 1106 bytes .../ic_game_collection_upload.webp | Bin 0 -> 1290 bytes .../bg_game_collectionchange_poster.xml | 5 + .../main/res/layout/activity_cropimage.xml | 31 +- .../layout/activity_game_collection_edit.xml | 275 ++++++++++++++++++ .../layout_menu_game_collection_post.xml | 22 ++ .../res/menu/menu_game_collection_edit.xml | 11 + 12 files changed, 480 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt create mode 100644 app/src/main/res/drawable-xxhdpi/icon_game_collection.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_choose_game.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_upload.webp create mode 100644 app/src/main/res/drawable/bg_game_collectionchange_poster.xml create mode 100644 app/src/main/res/layout/activity_game_collection_edit.xml create mode 100644 app/src/main/res/layout/layout_menu_game_collection_post.xml create mode 100644 app/src/main/res/menu/menu_game_collection_edit.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e4ec48515..df0dbb1963 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -726,6 +726,10 @@ android:name="com.gh.gamecenter.teenagermode.TeenagerModeActivity" android:screenOrientation="portrait" /> + + + mBinding.introduceSizeTv.text = "${text?.length ?: 0}/200" + } + } + + private fun initData() { + + } + + private fun observeData() { + + } + + override fun onMenuItemClick(item: MenuItem?): Boolean { + return super.onMenuItemClick(item) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == REQUEST_CODE_IMAGE) { + val selectedPaths = Matisse.obtainResult(data) + if (!selectedPaths.isNullOrEmpty()) { + val path = PathUtils.getPath(this, selectedPaths[0]) + val intent = CropImageActivity.getIntent(this, path, 142 / 328F, false, mEntrance) + startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP) + } + } else if (requestCode == REQUEST_CODE_IMAGE_CROP) { + val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: "" + initPosterUI(imagePath) + } + } + + private fun initPosterUI(imagePath: String) { + mViewModel.imagePath = imagePath + mBinding.placeholderView.goneIf(imagePath.isNotEmpty()) + mBinding.uploadPictureBtn.goneIf(imagePath.isNotEmpty()) + mBinding.posterView.goneIf(imagePath.isEmpty()) + mBinding.changePosterBtn.goneIf(imagePath.isEmpty()) + mBinding.deleteBtn.goneIf(imagePath.isEmpty()) + if (imagePath.isNotEmpty()) { + ImageUtils.display(mBinding.posterView, "file:///$imagePath") + } + } + + companion object { + + const val REQUEST_CODE_IMAGE = 100 + const val REQUEST_CODE_IMAGE_CROP = 101 + + @JvmStatic + fun getIntent(context: Context): Intent { + return Intent(context, GameCollectionEditActivity::class.java) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt new file mode 100644 index 0000000000..251c02bcf6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt @@ -0,0 +1,8 @@ +package com.gh.gamecenter.gamecollection.publish + +import android.app.Application +import androidx.lifecycle.AndroidViewModel + +class GameCollectionViewModel(application: Application) : AndroidViewModel(application) { + var imagePath = "" +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/icon_game_collection.webp b/app/src/main/res/drawable-xxhdpi/icon_game_collection.webp new file mode 100644 index 0000000000000000000000000000000000000000..c5daff48b213e9bf4d44658bd5a47011d981ca7f GIT binary patch literal 884 zcmV-)1B?7pNk&F&0{{S5MM6+kP&il$0000G0000}002_}06|PpNW1_500A$-ux%v8 z!OPAu-(y6C0~iYCA3BeIF}B3{{m`+jVgMYuk)%j9%*+hi_5W{HclK@P5Yhh$pwGat zSet_QeV!9fwmQ5)D5ZoARc5_=pStw#4^~h(Ac_J20B{fhodGIT08{`zkv^PBC8HxD zC=;9@uo4Mm0M&WyKX33CquSctKBu<8d5`mB|3&IEc>~Y`_K&S_uTI=zWq+n;h~x!k zF@YZNRyzD|o*$g2)9FW}?o z)HRw`7px}bH{|9}NVAXOxB&k9oX+2%ZGWdZk{^DEOlME^-|(PwX?X~&>_!SrhFOP{ zb%y9y6}$j415JYa=KdAID15Dmu($?3kGz1D^Fg8B4&w5+OvDx;f)a3EHh2PjKnMd+ zeR-LieD0QnP2Di1+~m)X z@nDA3PiI4Rcu4=3{EgnmlClqfjUXIU=;H}NM@E&VgEZtA~@forJil8>kV9%U< zo8M_E)WAtwhN1eeQKIt0>>GDeq@x+1*o81q`s_^A`>^RAqCM6_l84oceJSj*u|o;t z9+Ml``w^~IwDr|p5dFk?C$3R&%$jx7hWC*tJ-(Sp{GquvE-K#Zs6W!05?yZrmUN0i z3l2-AOcwc_i{MMkbPW|@l42tKQV0K=+?1lwliS;exz#tIHNwkggaN?mkzlraVc(p- zcp3>)sw?|G1>PM0;b2u$hxfL|qr&w%*stLTbQC)`edkLyKS7_rlDO)N&DYcLe1`?1 zb^R62x-8VTeCBV-cyz#Dc<%g`aiV$kO#G7;UVx=(U9YEHGnAcIB^Szx>C^}`GF;hM zx{yg$@BnSnvoJ90e8jnyq$v7pLKF7=0ccfwT(jrPi$c0T$c5khO>t(}{r`u5iuk`` KYXQRm0002we4;velZj= zy8fo?-@5Un8^5~wBBMwaQzX{SzpDAZ2Wm>xy7f%9ernfiT|Us|5n=w(oBOJZ`%LVzs5f|2&w@rO#V}r27t#lrts@V2VsKcGuxMs^}>Z{F?P7^+uCB28cyd>>g0~Z}gsdH~y z>I5dvK*m{mfRTX*qF^0SQwV4ShGWBx+4t~20goY`k7LyC(FBs5-brVAM!GfTW+LGP zL6wAvUAH$*+C+sx0T5v zMuMjEB>0R3=A=yeEH;C&UlCcmLLZ}P(do9{ZvfH{7X{xY3*n1H3E* zsbiJq!Zfeb;AVfMao_W%T_0Lg_)o6|N`7Vi{3bT%_A~9-SyT>Sf@l6hDycPFnEB72 zIpa|-R|>b6WGisuIU{|fhyH0KH`rkcf;Wdzj<#{;IjBhRDGtd-rd^7$&J`&bC1vzw zL$f~4KAb^;kDV<3Uv2=p99g_nan|41(rKk?1o(?4B8-p~Kn(7RhhKAX?y(WstL7q%%p<;&O;3E7LfJ~$lUN`zlO`+e3D`+5gB**>u%N@Bs7D{G05cxV z{u?WP;rAtP7}H97p;qNzu_{yg^=AGY|Jq59=xf;@Dc~y8KitPQF%KWhHOapc(A2;K Yd8W)+A+!9O?iR?Dv96A~fu$+n08~^OE&u=k literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_upload.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_upload.webp new file mode 100644 index 0000000000000000000000000000000000000000..65d14fb5face6d323b44b7f373d8a4717ebf79d6 GIT binary patch literal 1290 zcmV+l1@-z;Nk&Ej1pok7MM6+kP&il$0000G0001g004gg06|PpNKFC&00DQQD3YXx zy-emsp#lF6K}1AkB8DGpPisAhCe|=jl z{W(Y97+yv6AA%c6illVrG9KR-!fyzT-hA=A-5)J0?6=R0d9M*JR|d<2WsK$E!V9YD z+OqdCt%6_w#U#)VPOX*6pyjv(x@!|c*Rj!|NMV%R>||n2BsMB0=M+ELJ(wV3cWA9m z5|QUwnJ8-Oo2)(n)r-lZ`kHAGMu5iJgncvuLz6~eY0{Q74knF8&&2iSCT_kkaf@dY z_q;Q4+r5e7Sj16&?RXttF?=n$o^AqAo=nyGgj=6%BU5c;s*Oywk*U@*wCWp5^$wl- zhe|IQ8jUXU3a;xfK%wz{uD^m$LQTx-FQMo(wOC}DTr?t0FP`!wP{$GxsAs8U-!DtQ zt7I~-Z|P)R=N!qn-g%O3-x7}&;Yz%BTg%3Ty2N~mcVgLSQ3I|T zBJEf<#sR=FcOuQ`%=#_>w~lWlcWS-gT0Y(;?Z@%j@pAmBzs~$cBJ1B@xjkRUWKGUf zOxDLk0PZ(M*Za8DnQZLWj$;wWzKCOM;<6zQggr!Ma!qB7* z0*wz7MkBz~gb{%1rO2WR0DY6y=iJIfQ5&tbNupH`UCAGQbR&b&$>f}f4rX#akr<4` zt|WCDpeyxYbRC11(38>%PJNM|!1RM(H5IrM(<)q~F326Wo=)c=K zoBFrWi_i?6zg*!5X+6-KGq|=S9pJ|^{dfMa_rEb8l7Dgb0R2Jq0QCX-ee41HDjUEZ zN=EDjaMXB#>VBE{%q^JLIh-pSeeBRTrF1^i1bnZ>ikP@4fNfw0B;?o}2mEID7r`8{ zFrq&PmuOt8A{5{8^oE66LbVfAg>ia#QOPxipJJduS+a*+x>wjXdxYpRtrO2qiDL&feDrcIMeMIVgzD0Nc zjqDi^d+LVLmk!T=G<^BRd%YCHF>N#c+1}S*2~FO=n~jPTlfu!n#WEqqGgdgm3G0Yf zbJ7YyTSGDjW^h>vcws&R;rG0WDeHv*Cg)lFgH zHOBS~h*RQhLXWaRIGzRT3KX7>@&UOU3nDJbM0*pstg5kb&3brJ-W{S&`iUcYOyrGD z3hfyZ6YMf_UoLo0uKkY?!WhbUL3z^=_}8djcw&@nUed#a)^PHi0`H z4mwAb_`zK}%GQmchD?_IiV4?_C5s|C53t;k3)1O3Dw)GM40>gRnk8aB4-ldo$xERQ z + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_cropimage.xml b/app/src/main/res/layout/activity_cropimage.xml index b856815e21..a32853d74d 100644 --- a/app/src/main/res/layout/activity_cropimage.xml +++ b/app/src/main/res/layout/activity_cropimage.xml @@ -1,9 +1,17 @@ - + android:fitsSystemWindows="true" + android:orientation="vertical" + app:consumeWindowInsets="true"> + + - - - + android:layout_below="@+id/status_bar" + android:orientation="vertical"> - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_game_collection_edit.xml b/app/src/main/res/layout/activity_game_collection_edit.xml new file mode 100644 index 0000000000..2f2d561760 --- /dev/null +++ b/app/src/main/res/layout/activity_game_collection_edit.xml @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_menu_game_collection_post.xml b/app/src/main/res/layout/layout_menu_game_collection_post.xml new file mode 100644 index 0000000000..4f9ecb7707 --- /dev/null +++ b/app/src/main/res/layout/layout_menu_game_collection_post.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/main/res/menu/menu_game_collection_edit.xml b/app/src/main/res/menu/menu_game_collection_edit.xml new file mode 100644 index 0000000000..60dbd3b86e --- /dev/null +++ b/app/src/main/res/menu/menu_game_collection_edit.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file From be5cbf0ca07f1a6b928e1c0ab7bc26137b594da5 Mon Sep 17 00:00:00 2001 From: lyr <15622190878@163.com> Date: Fri, 5 Nov 2021 14:27:15 +0800 Subject: [PATCH 02/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E6=88=91?= =?UTF-8?q?=E7=9A=84=E6=B8=B8=E6=88=8F=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1594?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gh/common/constant/Constants.java | 2 + .../gh/gamecenter/mygame/MyGameActivity.kt | 40 +++++++++++++++++- .../ic_create_game_collection.webp | Bin 0 -> 1930 bytes .../drawable-xxxhdpi/pic_my_game_guide.webp | Bin 0 -> 16588 bytes .../main/res/layout/popup_my_game_guide.xml | 13 ++++++ app/src/main/res/menu/menu_my_game.xml | 11 +++++ 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_create_game_collection.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/pic_my_game_guide.webp create mode 100644 app/src/main/res/layout/popup_my_game_guide.xml create mode 100644 app/src/main/res/menu/menu_my_game.xml diff --git a/app/src/main/java/com/gh/common/constant/Constants.java b/app/src/main/java/com/gh/common/constant/Constants.java index 1439b344fe..d929e4735e 100644 --- a/app/src/main/java/com/gh/common/constant/Constants.java +++ b/app/src/main/java/com/gh/common/constant/Constants.java @@ -228,6 +228,8 @@ public class Constants { public static final String SP_SHOULD_SHOW_GAME_DETAIL_INSTALL_GUIDE = "should_show_game_detail_install_guide"; // 儿童/青少年模式 public static final String SP_TEENAGER_MODE = "teenager_mode"; + // 我的游戏引导 + public static final String SP_MY_GAME_GUIDE = "my_game_guide"; //手机号码匹配规则 public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"; diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt index 2df1c1b7d8..a5ed9516a9 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt @@ -3,15 +3,26 @@ package com.gh.gamecenter.mygame import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.Gravity +import android.view.MenuItem +import android.widget.LinearLayout import androidx.fragment.app.Fragment import com.gh.base.BaseActivity_TabLayout -import com.gh.common.util.MtaHelper +import com.gh.common.AppExecutor +import com.gh.common.constant.Constants +import com.gh.common.util.* +import com.gh.common.view.BugFixedPopupWindow +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.PopupMyGameGuideBinding +import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity class MyGameActivity : BaseActivity_TabLayout() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setNavigationTitle("我的游戏") + setToolbarMenu(R.menu.menu_my_game) + showGuide() } override fun initFragmentList(fragments: MutableList?) { @@ -32,6 +43,33 @@ class MyGameActivity : BaseActivity_TabLayout() { MtaHelper.onEvent("我的光环_新", "我的游戏", "${mTabTitleList[position]}Tab") } + override fun onMenuItemClick(item: MenuItem?): Boolean { + if (item?.itemId == R.id.menu_create_game_collection) { + showRegulationTestDialogIfNeeded { + startActivity(GameCollectionEditActivity.getIntent(this)) + } + } + return super.onMenuItemClick(item) + } + + private fun showGuide() { + AppExecutor.uiExecutor.executeWithDelay({ + if (!SPUtils.getBoolean(Constants.SP_MY_GAME_GUIDE)) { + val binding = PopupMyGameGuideBinding.inflate(layoutInflater, null, false) + val popupWindow = BugFixedPopupWindow(binding.root, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT) + binding.root.setOnClickListener { + SPUtils.setBoolean(Constants.SP_MY_GAME_GUIDE, true) + popupWindow.dismiss() + } + popupWindow.run { + isTouchable = true + isFocusable = true + showAtLocation(window.decorView, Gravity.TOP, 0, 0) + } + } + }, 500) + } + companion object { @JvmStatic fun getIntentWithConfig(context: Context, defaultFragmentPosition: Int): Intent { diff --git a/app/src/main/res/drawable-xxxhdpi/ic_create_game_collection.webp b/app/src/main/res/drawable-xxxhdpi/ic_create_game_collection.webp new file mode 100644 index 0000000000000000000000000000000000000000..3b929d90fe09c230a18c974cb2afb7d7066af3cb GIT binary patch literal 1930 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

$T(+q%K0|_u=MT-^_ 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 + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_my_game.xml b/app/src/main/res/menu/menu_my_game.xml new file mode 100644 index 0000000000..068cc30ff4 --- /dev/null +++ b/app/src/main/res/menu/menu_my_game.xml @@ -0,0 +1,11 @@ + +

+ + + + \ No newline at end of file From 44dab8246f90ae72d51009c6aa70f404eef2ba7d Mon Sep 17 00:00:00 2001 From: leafwai Date: Fri, 5 Nov 2021 18:08:15 +0800 Subject: [PATCH 03/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(=E9=80=89=E6=8B=A9=E6=A0=87=E7=AD=BEUI=EF=BC=89https:?= =?UTF-8?q?//git.ghzs.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 ++ .../square/GameCollectionTagSelectActivity.kt | 29 ++++++ .../square/GameCollectionTagSelectFragment.kt | 93 ++++++++++++++++++ ...ame_collection_tag_text_color_selector.xml | 5 + .../ic_game_collection_tag_close.png | Bin 0 -> 389 bytes .../bg_game_collection_tag_select.xml | 5 + .../bg_game_collection_tag_unselect.xml | 7 ++ .../drawable/game_collection_tag_selector.xml | 5 + .../fragment_game_collection_tag_select.xml | 38 +++++++ .../game_collection_selected_tag_item.xml | 31 ++++++ .../layout/game_collection_tag_body_item.xml | 20 ++++ .../game_collection_tag_header_item.xml | 19 ++++ .../item_game_collection_selected_tag.xml | 40 ++++++++ .../res/layout/item_game_collection_tag.xml | 25 +++++ app/src/main/res/layout/layout_menu_save.xml | 18 ++++ app/src/main/res/menu/menu_save.xml | 11 +++ 16 files changed, 354 insertions(+) create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectFragment.kt create mode 100644 app/src/main/res/color/game_collection_tag_text_color_selector.xml create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_tag_close.png create mode 100644 app/src/main/res/drawable/bg_game_collection_tag_select.xml create mode 100644 app/src/main/res/drawable/bg_game_collection_tag_unselect.xml create mode 100644 app/src/main/res/drawable/game_collection_tag_selector.xml create mode 100644 app/src/main/res/layout/fragment_game_collection_tag_select.xml create mode 100644 app/src/main/res/layout/game_collection_selected_tag_item.xml create mode 100644 app/src/main/res/layout/game_collection_tag_body_item.xml create mode 100644 app/src/main/res/layout/game_collection_tag_header_item.xml create mode 100644 app/src/main/res/layout/item_game_collection_selected_tag.xml create mode 100644 app/src/main/res/layout/item_game_collection_tag.xml create mode 100644 app/src/main/res/layout/layout_menu_save.xml create mode 100644 app/src/main/res/menu/menu_save.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df0dbb1963..c20004c6ba 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -730,6 +730,14 @@ android:name="com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity" android:screenOrientation="portrait" /> + + + + Unit) = { + mAdapter?.multipleTagList?.let { list -> + mBinding?.selectedTagContainer?.removeAllViews() + for (name in list) { + val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(layoutInflater) + selectedTagView.run { + tagTv.text = name + root.setOnClickListener { + list.remove(name) + mBinding?.selectedTagContainer?.removeView(selectedTagView.root) + mAdapter?.notifyDataSetChanged() + mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE + } + } + mBinding?.selectedTagContainer?.addView(selectedTagView.root) + } + mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE + } + } + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout() = + FragmentGameCollectionTagSelectBinding.inflate(layoutInflater).apply { + mBinding = this + }.root + + override fun onMenuItemClick(menuItem: MenuItem?) { + if (menuItem?.itemId == R.id.layout_menu_save) { + requireActivity().setResult( + GameCollectionSquareFragment.TAG_SELECT_REQUEST, + Intent().putExtra("tagName", mAdapter?.singleTagName) + ) + requireActivity().finish() + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val data = arrayListOf( + "游戏玩法", + "body", + "游戏题材", + "body", + "游戏题材", + "body", + "游戏玩法", + "body", + "游戏玩法", + "body", + "游戏玩法", + "body", + "游戏玩法", + "body" + ) + mAdapter = GameCollectionTagAdapter(requireContext(), true, data, updateSelectedTagView) + mBinding?.tagRv?.run { + layoutManager = LinearLayoutManager(requireContext()) + adapter = mAdapter + addOnScrollListener(object :RecyclerView.OnScrollListener(){ + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + when (newState) { + RecyclerView.SCROLL_STATE_IDLE -> firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + RecyclerView.SCROLL_STATE_DRAGGING -> firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + } + mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || mAdapter?.multipleTagList?.isEmpty() == true) View.GONE else View.VISIBLE + } + }) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/res/color/game_collection_tag_text_color_selector.xml b/app/src/main/res/color/game_collection_tag_text_color_selector.xml new file mode 100644 index 0000000000..ec1c4419b0 --- /dev/null +++ b/app/src/main/res/color/game_collection_tag_text_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_tag_close.png b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_tag_close.png new file mode 100644 index 0000000000000000000000000000000000000000..98a1e5dfc15f0de3e2ed65aac8fd3a5baf117448 GIT binary patch literal 389 zcmV;00eb$4P)k5M~427E~pFVj|F)9%zS<8hn0cZqiVn#4(0E-Yy>>Wl0fDnM#Lv@g$5_j%S;?r8> z08;$m+985q19*r>K(GKj#akh`0DtpoQPiK@!>C?Yxy{POazG>Gsh9wGs?xnY;XpWG jtU$Dov4H0A + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_tag_unselect.xml b/app/src/main/res/drawable/bg_game_collection_tag_unselect.xml new file mode 100644 index 0000000000..0683c258fc --- /dev/null +++ b/app/src/main/res/drawable/bg_game_collection_tag_unselect.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/game_collection_tag_selector.xml b/app/src/main/res/drawable/game_collection_tag_selector.xml new file mode 100644 index 0000000000..dd68f2d7fe --- /dev/null +++ b/app/src/main/res/drawable/game_collection_tag_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_tag_select.xml b/app/src/main/res/layout/fragment_game_collection_tag_select.xml new file mode 100644 index 0000000000..bdc9bc5918 --- /dev/null +++ b/app/src/main/res/layout/fragment_game_collection_tag_select.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_selected_tag_item.xml b/app/src/main/res/layout/game_collection_selected_tag_item.xml new file mode 100644 index 0000000000..9957806ec3 --- /dev/null +++ b/app/src/main/res/layout/game_collection_selected_tag_item.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_tag_body_item.xml b/app/src/main/res/layout/game_collection_tag_body_item.xml new file mode 100644 index 0000000000..402bc80584 --- /dev/null +++ b/app/src/main/res/layout/game_collection_tag_body_item.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_tag_header_item.xml b/app/src/main/res/layout/game_collection_tag_header_item.xml new file mode 100644 index 0000000000..65e105061b --- /dev/null +++ b/app/src/main/res/layout/game_collection_tag_header_item.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_game_collection_selected_tag.xml b/app/src/main/res/layout/item_game_collection_selected_tag.xml new file mode 100644 index 0000000000..21f15f9800 --- /dev/null +++ b/app/src/main/res/layout/item_game_collection_selected_tag.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_game_collection_tag.xml b/app/src/main/res/layout/item_game_collection_tag.xml new file mode 100644 index 0000000000..afae12e218 --- /dev/null +++ b/app/src/main/res/layout/item_game_collection_tag.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_menu_save.xml b/app/src/main/res/layout/layout_menu_save.xml new file mode 100644 index 0000000000..860f643b6b --- /dev/null +++ b/app/src/main/res/layout/layout_menu_save.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_save.xml b/app/src/main/res/menu/menu_save.xml new file mode 100644 index 0000000000..b14097d34f --- /dev/null +++ b/app/src/main/res/menu/menu_save.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file From 001cbfcdb68b1df7df4986d692d89bf5391df4b4 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Sat, 6 Nov 2021 15:55:06 +0800 Subject: [PATCH 04/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E5=88=9B=E5=BB=BA/=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95(=E9=80=89=E6=8B=A9=E6=B8=B8?= =?UTF-8?q?=E6=88=8F/=E6=B7=BB=E5=8A=A0=E6=B8=B8=E6=88=8FUI)=20https://git?= =?UTF-8?q?.ghzs.com/pm/halo-app-issues/-/issues/1604?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 10 +- .../com/gh/common/util/SingletonHolder.kt | 26 +++ .../gamecollection/choose/AddGamesActivity.kt | 19 ++ .../gamecollection/choose/AddGamesFragment.kt | 19 ++ .../choose/AddSearchAndPlayedGameAdapter.kt | 44 ++++ .../choose/AddSearchGameFragment.kt | 59 ++++++ .../choose/AddUserPlayedGameFragment.kt | 51 +++++ .../choose/ChooseGamesActivity.kt | 23 ++ .../choose/ChooseGamesAdapter.kt | 69 ++++++ .../choose/ChooseGamesFragment.kt | 108 ++++++++++ .../choose/ChooseGamesRepository.kt | 12 ++ .../choose/ChooseGamesViewModel.kt | 21 ++ .../publish/GameCollectionEditActivity.kt | 39 +++- ...odel.kt => GameCollectionEditViewModel.kt} | 2 +- .../gamecenter/mygame/PlayedGameViewModel.kt | 50 ++--- .../home/game/UserPlayedGameFragment.kt | 2 +- .../gh/gamecenter/qa/editor/GameActivity.kt | 197 +----------------- .../gh/gamecenter/qa/editor/GameAdapter.kt | 2 +- .../gh/gamecenter/qa/editor/GameFragment.kt | 190 +++++++++++++++++ .../drawable-xxxhdpi/ic_choose_games_drag.png | Bin 0 -> 473 bytes .../drawable-xxxhdpi/ic_choose_games_top.png | Bin 0 -> 1491 bytes .../res/drawable-xxxhdpi/icon_add_games.webp | Bin 0 -> 3594 bytes .../res/drawable/bg_shape_f5_radius_6.xml | 6 + .../layout/activity_editor_insert_game.xml | 3 - .../layout/activity_game_collection_edit.xml | 10 +- .../layout/fragment_add_user_played_game.xml | 57 +++++ .../main/res/layout/fragment_choose_games.xml | 56 +++++ .../main/res/layout/fragment_search_game.xml | 162 ++++++++++++++ app/src/main/res/layout/item_choose_games.xml | 116 +++++++++++ app/src/main/res/layout/layout_menu_save.xml | 18 ++ app/src/main/res/menu/menu_save.xml | 11 + 31 files changed, 1146 insertions(+), 236 deletions(-) create mode 100644 app/src/main/java/com/gh/common/util/SingletonHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchAndPlayedGameAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddUserPlayedGameFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesRepository.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesViewModel.kt rename app/src/main/java/com/gh/gamecenter/gamecollection/publish/{GameCollectionViewModel.kt => GameCollectionEditViewModel.kt} (61%) create mode 100644 app/src/main/java/com/gh/gamecenter/qa/editor/GameFragment.kt create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_choose_games_drag.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_choose_games_top.png create mode 100644 app/src/main/res/drawable-xxxhdpi/icon_add_games.webp create mode 100644 app/src/main/res/drawable/bg_shape_f5_radius_6.xml create mode 100644 app/src/main/res/layout/fragment_add_user_played_game.xml create mode 100644 app/src/main/res/layout/fragment_choose_games.xml create mode 100644 app/src/main/res/layout/fragment_search_game.xml create mode 100644 app/src/main/res/layout/item_choose_games.xml create mode 100644 app/src/main/res/layout/layout_menu_save.xml create mode 100644 app/src/main/res/menu/menu_save.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df0dbb1963..74fc368709 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -727,7 +727,15 @@ android:screenOrientation="portrait" /> + + + + (creator: () -> T) { + private var creator: (() -> T)? = creator + + @Volatile + private var instance: T? = null + + fun getInstance(): T { + val obj = instance + if (obj != null) { + return obj + } + return synchronized(this) { + val obj1 = instance + if (obj1 != null) { + obj1 + } else { + val created = creator!!() + instance = created + creator = null + created + } + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesActivity.kt new file mode 100644 index 0000000000..09a979b0d0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesActivity.kt @@ -0,0 +1,19 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.gamecenter.NormalActivity + +class AddGamesActivity : NormalActivity() { + + override fun provideNormalIntent(): Intent { + return getTargetIntent(this, AddGamesActivity::class.java, AddGamesFragment::class.java) + } + + companion object { + fun getIntent(context: Context): Intent { + return getTargetIntent(context, AddGamesActivity::class.java, AddGamesFragment::class.java) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesFragment.kt new file mode 100644 index 0000000000..b994ba2dfa --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddGamesFragment.kt @@ -0,0 +1,19 @@ +package com.gh.gamecenter.gamecollection.choose + +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import com.gh.base.fragment.BaseLazyTabFragment +import com.gh.common.util.EntranceUtils + +class AddGamesFragment : BaseLazyTabFragment() { + + override fun initFragmentList(fragments: MutableList) { + fragments.add(AddSearchGameFragment().apply { bundleOf(EntranceUtils.KEY_NAVIGATION_TITLE to "添加游戏") }) + fragments.add(AddUserPlayedGameFragment()) + } + + override fun initTabTitleList(tabTitleList: MutableList) { + tabTitleList.add("搜索游戏") + tabTitleList.add("玩过游戏") + } +} diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchAndPlayedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchAndPlayedGameAdapter.kt new file mode 100644 index 0000000000..3864f12d74 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchAndPlayedGameAdapter.kt @@ -0,0 +1,44 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.ToastUtils +import com.gh.common.util.toColor +import com.gh.common.util.toDrawable +import com.gh.gamecenter.R +import com.gh.gamecenter.game.GameItemViewHolder +import com.gh.gamecenter.qa.editor.GameAdapter + +class AddSearchAndPlayedGameAdapter(context: Context, val mChooseGamesViewModel: ChooseGamesViewModel) : GameAdapter(context) { + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is GameItemViewHolder) { + val entity = mEntityList[position] + val isSelected = mChooseGamesViewModel.chooseGamesLiveData.value?.find { it.id == entity.id } != null + holder.binding.game = entity + holder.binding.downloadBtn.text = if (isSelected) "删除" else "添加" + holder.binding.downloadBtn.background = + if (isSelected) R.drawable.bg_shape_f5_radius_999.toDrawable() else R.drawable.download_button_normal_style.toDrawable() + holder.binding.downloadBtn.setTextColor(if (isSelected) R.color.text_999999.toColor() else R.color.white.toColor()) + + holder.binding.downloadBtn.setOnClickListener { + val chooseGameList = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf() + if (isSelected) { + chooseGameList.remove(chooseGameList.find { it.id == entity.id }) + ToastUtils.showToast("游戏已移除") + } else { + if (chooseGameList.size >= 100) { + ToastUtils.showToast("已添加游戏到达上限") + return@setOnClickListener + } + chooseGameList.add(entity) + ToastUtils.showToast("游戏已添加") + } + notifyItemChanged(position) + mChooseGamesViewModel.chooseGamesLiveData.postValue(chooseGameList) + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt new file mode 100644 index 0000000000..ab0a01cb9c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt @@ -0,0 +1,59 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.os.Bundle +import android.view.View +import androidx.core.widget.doOnTextChanged +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.goneIf +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.R +import com.gh.gamecenter.SuggestionActivity +import com.gh.gamecenter.databinding.FragmentSearchGameBinding +import com.gh.gamecenter.qa.editor.GameAdapter +import com.gh.gamecenter.qa.editor.GameFragment +import com.gh.gamecenter.suggest.SuggestType + +class AddSearchGameFragment : GameFragment() { + + private lateinit var mBinding: FragmentSearchGameBinding + private lateinit var mChooseGamesViewModel: ChooseGamesViewModel + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + return FragmentSearchGameBinding.bind(layoutInflater.inflate(R.layout.fragment_search_game, null, false)).apply { + mBinding = this + }.root + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setNavigationTitle("添加游戏") + noneText.text = "没有找到相关游戏,换个搜索词试试?" + searchEt.doOnTextChanged { text, _, _, _ -> + mBinding.promptTv.goneIf(!text.isNullOrEmpty()) + } + + mBinding.applyGameTv.setOnClickListener { + SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.gameCollect, "求游戏:") + } + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration? { + return null + } + + override fun provideListAdapter(): GameAdapter? { + if (mAdapter == null) { + mAdapter = AddSearchAndPlayedGameAdapter(requireContext(), mChooseGamesViewModel) + } + return mAdapter + } + + override fun isAutoShowKeyboard(): Boolean = false +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddUserPlayedGameFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddUserPlayedGameFragment.kt new file mode 100644 index 0000000000..c2801c2e23 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddUserPlayedGameFragment.kt @@ -0,0 +1,51 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.EntranceUtils +import com.gh.common.util.toColor +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mygame.PlayedGameViewModel +import com.gh.gamecenter.qa.editor.GameAdapter + +class AddUserPlayedGameFragment : ListFragment() { + + private var mAdapter: GameAdapter? = null + private lateinit var mViewModel: PlayedGameViewModel + private lateinit var mChooseGamesViewModel: ChooseGamesViewModel + + override fun getLayoutId(): Int = R.layout.fragment_add_user_played_game + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mCachedView.setBackgroundColor(R.color.white.toColor()) + } + + override fun provideListAdapter(): ListAdapter { + return mAdapter ?: AddSearchAndPlayedGameAdapter(requireContext(), mChooseGamesViewModel).apply { + mAdapter = this + } + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration? { + return null + } + + override fun provideListViewModel(): PlayedGameViewModel { + val userId = arguments?.getString(EntranceUtils.KEY_USER_ID) + ?: UserManager.getInstance().userId + mViewModel = viewModelProvider(PlayedGameViewModel.Factory(userId, true)) + return mViewModel + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesActivity.kt new file mode 100644 index 0000000000..0769fa62a5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesActivity.kt @@ -0,0 +1,23 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.gamecenter.NormalActivity + +class ChooseGamesActivity : NormalActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle("选择游戏") + } + + override fun provideNormalIntent(): Intent { + return getTargetIntent(this, ChooseGamesActivity::class.java, ChooseGamesFragment::class.java) + } + + companion object { + fun getIntent(context: Context): Intent { + return getTargetIntent(context, ChooseGamesActivity::class.java, ChooseGamesFragment::class.java) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt new file mode 100644 index 0000000000..3fcf57c08e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt @@ -0,0 +1,69 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.annotation.SuppressLint +import android.content.Context +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.TextHelper +import com.gh.common.util.consume +import com.gh.common.util.toBinding +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ItemChooseGamesBinding +import com.gh.gamecenter.entity.GameEntity +import com.lightgame.adapter.BaseRecyclerAdapter + +class ChooseGamesAdapter(context: Context, val dragListener: ItemDragListener) : + ListAdapter(context) { + + + public override fun setListData(updateData: MutableList?) { + super.setListData(updateData) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return ChooseGamesViewHolder(parent.toBinding()) + } + + override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { + return oldItem?.id == newItem?.id + } + + @SuppressLint("ClickableViewAccessibility") + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is ChooseGamesViewHolder) { + val gameEntity = mEntityList[position] + holder.binding.gameNameTv.text = gameEntity.name + holder.binding.gameIcon.displayGameIcon(gameEntity) + holder.binding.recommendReasonEt.filters = arrayOf(TextHelper.getFilter(45, "最多输入45个字")) + holder.binding.deleteIv.setOnClickListener { + dragListener.deleteItem(gameEntity) + } + holder.binding.dragView.setOnTouchListener { _, event -> + consume { + if (event.action == MotionEvent.ACTION_DOWN) { + dragListener.startDragItem(holder) + } + } + } + holder.binding.topView.setOnClickListener { + dragListener.setToTop(holder) + } + } + } + + override fun getItemCount(): Int = mEntityList.size + + class ChooseGamesViewHolder(val binding: ItemChooseGamesBinding) : BaseRecyclerViewHolder(binding.root) + + interface ItemDragListener { + fun startDragItem(holder: RecyclerView.ViewHolder) + fun setToTop(holder: RecyclerView.ViewHolder) + fun deleteItem(entity: GameEntity) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt new file mode 100644 index 0000000000..9aed0f7833 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt @@ -0,0 +1,108 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.goneIf +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentChooseGamesBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.normal.NormalFragment +import java.util.* + +class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListener { + + private lateinit var mBinding: FragmentChooseGamesBinding + private lateinit var mViewModel: ChooseGamesViewModel + private lateinit var mAdapter: ChooseGamesAdapter + + private val mItemTouchCallback = object : ItemTouchHelper.Callback() { + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + } + + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + mAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + Collections.swap(mAdapter.entityList, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf() + Collections.swap(games, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + + } + + override fun canDropOver(recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + return true + } + + override fun isLongPressDragEnabled(): Boolean { + return false + } + + } + private val mItemTouchHelper = ItemTouchHelper(mItemTouchCallback) + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + return FragmentChooseGamesBinding.inflate(layoutInflater, null, false).apply { + mBinding = this + }.root + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initMenu(R.menu.menu_save) + mViewModel = viewModelProvider(ChooseGamesViewModel.Factory()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mViewModel.chooseGamesLiveData.observe(viewLifecycleOwner) { + mBinding.addGamesTv.goneIf(it.isNotEmpty()) + mBinding.gamesRv.goneIf(it.isEmpty()) + mAdapter.setListData(it) + mBinding.gameCountTv.text = "已收录${it.size}款游戏" + } + mBinding.gamesRv.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = ChooseGamesAdapter(requireContext(), this@ChooseGamesFragment).apply { + mAdapter = this + } + mItemTouchHelper.attachToRecyclerView(this) + } + + mBinding.addGamesButton.setOnClickListener { + requireContext().startActivity(AddGamesActivity.getIntent(requireContext())) + } + + mBinding.addGamesTv.setOnClickListener { mBinding.addGamesButton.performClick() } + + } + + override fun startDragItem(holder: RecyclerView.ViewHolder) { + mItemTouchHelper.startDrag(holder) + } + + override fun setToTop(holder: RecyclerView.ViewHolder) { + val holderPosition = holder.bindingAdapterPosition + for (i in holderPosition downTo 1) { + mAdapter.notifyItemMoved(i, i - 1) + Collections.swap(mAdapter.entityList, i, i - 1) + val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf() + Collections.swap(games, i, i - 1) + } + } + + override fun deleteItem(entity: GameEntity) { + val chooseGames = mViewModel.chooseGamesLiveData.value + chooseGames?.remove(entity) + mViewModel.chooseGamesLiveData.postValue(chooseGames) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesRepository.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesRepository.kt new file mode 100644 index 0000000000..4ccdd3d248 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesRepository.kt @@ -0,0 +1,12 @@ +package com.gh.gamecenter.gamecollection.choose + +import androidx.lifecycle.MutableLiveData +import com.gh.common.util.SingletonHolder +import com.gh.gamecenter.entity.GameEntity + +class ChooseGamesRepository private constructor() { + + val chooseGamesLiveData: MutableLiveData> = MutableLiveData() + + companion object : SingletonHolder(::ChooseGamesRepository) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesViewModel.kt new file mode 100644 index 0000000000..3822a7e5d1 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesViewModel.kt @@ -0,0 +1,21 @@ +package com.gh.gamecenter.gamecollection.choose + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.halo.assistant.HaloApp + +class ChooseGamesViewModel(application: Application, repository: ChooseGamesRepository) : AndroidViewModel(application) { + + val chooseGamesLiveData = repository.chooseGamesLiveData + + class Factory : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return ChooseGamesViewModel( + HaloApp.getInstance().application, + ChooseGamesRepository.getInstance() + ) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index f80adc305c..c85d47a426 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -11,6 +11,8 @@ import com.gh.common.util.* import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding +import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity +import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel import com.gh.gamecenter.qa.editor.LocalMediaActivity import com.zhihu.matisse.Matisse import com.zhihu.matisse.internal.utils.PathUtils @@ -19,7 +21,8 @@ class GameCollectionEditActivity : ToolBarActivity() { private lateinit var mMenuPost: MenuItem private lateinit var mBinding: ActivityGameCollectionEditBinding - private lateinit var mViewModel: GameCollectionViewModel + private lateinit var mViewModel: GameCollectionEditViewModel + private lateinit var mChooseGamesViewModel: ChooseGamesViewModel override fun getLayoutId(): Int = R.layout.activity_game_collection_edit @@ -27,6 +30,7 @@ class GameCollectionEditActivity : ToolBarActivity() { super.onCreate(savedInstanceState) mBinding = ActivityGameCollectionEditBinding.bind(mContentView) mViewModel = viewModelProvider() + mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory()) setToolbarMenu(R.menu.menu_game_collection_edit) mMenuPost = getMenuItem(R.id.menu_game_collection_post) setNavigationTitle("创建游戏单") @@ -63,6 +67,10 @@ class GameCollectionEditActivity : ToolBarActivity() { mBinding.gameCollectionIntroduceEt.doOnTextChanged { text, start, before, count -> mBinding.introduceSizeTv.text = "${text?.length ?: 0}/200" } + + mBinding.chooseGameContainer.setOnClickListener { + startActivityForResult(ChooseGamesActivity.getIntent(this), REQUEST_CHOOSE_GAMES) + } } private fun initData() { @@ -70,7 +78,10 @@ class GameCollectionEditActivity : ToolBarActivity() { } private fun observeData() { - + mChooseGamesViewModel.chooseGamesLiveData.observe(this) { + mBinding.gamesTv.text = if (it.isEmpty()) "选择游戏" else "已选 ${it.size} 款游戏" + mBinding.gamesTipTv.goneIf(it.isNotEmpty()) + } } override fun onMenuItemClick(item: MenuItem?): Boolean { @@ -80,16 +91,21 @@ class GameCollectionEditActivity : ToolBarActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (data == null || resultCode != Activity.RESULT_OK) return - if (requestCode == REQUEST_CODE_IMAGE) { - val selectedPaths = Matisse.obtainResult(data) - if (!selectedPaths.isNullOrEmpty()) { - val path = PathUtils.getPath(this, selectedPaths[0]) - val intent = CropImageActivity.getIntent(this, path, 142 / 328F, false, mEntrance) - startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP) + when (requestCode) { + REQUEST_CODE_IMAGE -> { + val selectedPaths = Matisse.obtainResult(data) + if (!selectedPaths.isNullOrEmpty()) { + val path = PathUtils.getPath(this, selectedPaths[0]) + val intent = CropImageActivity.getIntent(this, path, 142 / 328F, false, mEntrance) + startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP) + } + } + REQUEST_CODE_IMAGE_CROP -> { + val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: "" + initPosterUI(imagePath) + } + REQUEST_CHOOSE_GAMES -> { } - } else if (requestCode == REQUEST_CODE_IMAGE_CROP) { - val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: "" - initPosterUI(imagePath) } } @@ -109,6 +125,7 @@ class GameCollectionEditActivity : ToolBarActivity() { const val REQUEST_CODE_IMAGE = 100 const val REQUEST_CODE_IMAGE_CROP = 101 + const val REQUEST_CHOOSE_GAMES = 102 @JvmStatic fun getIntent(context: Context): Intent { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt similarity index 61% rename from app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index 251c02bcf6..173e63e661 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -3,6 +3,6 @@ package com.gh.gamecenter.gamecollection.publish import android.app.Application import androidx.lifecycle.AndroidViewModel -class GameCollectionViewModel(application: Application) : AndroidViewModel(application) { +class GameCollectionEditViewModel(application: Application) : AndroidViewModel(application) { var imagePath = "" } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt index 203f6982f9..17bb6bd809 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt @@ -16,8 +16,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody -class PlayedGameViewModel(application: Application, var userId: String) - : ListViewModel(application) { +class PlayedGameViewModel(application: Application, var userId: String, val isKeepTagStyle: Boolean = false) : + ListViewModel(application) { override fun provideDataObservable(page: Int): Observable>? { return null @@ -29,9 +29,11 @@ class PlayedGameViewModel(application: Application, var userId: String) override fun mergeResultLiveData() { mResultLiveData.addSource(mListLiveData) { - it.forEach { game -> - game.hideSizeInsideDes = true - game.tagStyle.clear() + if (!isKeepTagStyle) { + it.forEach { game -> + game.hideSizeInsideDes = true + game.tagStyle.clear() + } } mResultLiveData.postValue(it) } @@ -40,32 +42,32 @@ class PlayedGameViewModel(application: Application, var userId: String) @SuppressLint("CheckResult") fun deletePlayedGame(gameEntity: GameEntity) { RetrofitManager.getInstance(getApplication()).api - .deletePlayedGame(userId, gameEntity.playedGameId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - mListLiveData.value?.let { - for (game in it) { - if (gameEntity.id == game.id) { - it.remove(game) - mListLiveData.postValue(it) - break - } + .deletePlayedGame(userId, gameEntity.playedGameId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + mListLiveData.value?.let { + for (game in it) { + if (gameEntity.id == game.id) { + it.remove(game) + mListLiveData.postValue(it) + break } } } + } - override fun onFailure(exception: Exception) { - super.onFailure(exception) - Utils.toast(getApplication(), exception.localizedMessage) - } - }) + override fun onFailure(exception: Exception) { + super.onFailure(exception) + Utils.toast(getApplication(), exception.localizedMessage) + } + }) } - class Factory(private val mUserId: String) : ViewModelProvider.NewInstanceFactory() { + class Factory(private val mUserId: String, val isKeepTagStyle: Boolean = false) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return PlayedGameViewModel(HaloApp.getInstance().application, mUserId) as T + return PlayedGameViewModel(HaloApp.getInstance().application, mUserId, isKeepTagStyle) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt index 5eab999e09..e810967e03 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt @@ -26,7 +26,7 @@ import com.lightgame.download.DownloadEntity import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -class UserPlayedGameFragment: ListFragment() { +open class UserPlayedGameFragment: ListFragment() { private var mUserId = "" private var mAdapter: UserPlayedGameAdapter? = null diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/GameActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/GameActivity.kt index 0028f12c36..bddaa7d784 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/GameActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/GameActivity.kt @@ -2,201 +2,22 @@ package com.gh.gamecenter.qa.editor import android.content.Context import android.content.Intent -import android.os.Bundle -import android.os.Message -import android.text.Editable -import android.text.TextWatcher -import android.view.View -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import androidx.lifecycle.ViewModelProviders -import androidx.recyclerview.widget.RecyclerView -import com.gh.common.constant.Config +import androidx.core.os.bundleOf import com.gh.common.util.EntranceUtils -import com.gh.common.view.FixLinearLayoutManager -import com.gh.common.view.VerticalItemDecoration -import com.gh.gamecenter.BuildConfig -import com.gh.gamecenter.R -import com.gh.gamecenter.baselist.ListActivity -import com.gh.gamecenter.baselist.NormalListViewModel -import com.gh.gamecenter.entity.GameEntity -import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.qa.entity.EditorInsertDefaultEntity -import com.gh.gamecenter.retrofit.Response -import com.gh.gamecenter.retrofit.RetrofitManager -import com.google.android.material.appbar.AppBarLayout -import com.halo.assistant.HaloApp -import com.lightgame.utils.Util_System_Keyboard -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import kotterknife.bindView +import com.gh.gamecenter.NormalActivity -class GameActivity : ListActivity>() { +class GameActivity : NormalActivity() { - val searchEt by bindView(R.id.search_input) - val searchTv by bindView(R.id.search_button) - val searchBack by bindView(R.id.search_back) - val appBar by bindView(R.id.list_appbar) - val noneText by bindView(R.id.reuse_tv_none_data) - val defaultList by bindView(R.id.default_list) - val defaultListContainer by bindView(R.id.default_list_container) - - private var mAdapter: GameAdapter? = null - - private var mSearchKey: String = "" - - override fun handleMessage(msg: Message) { - if (msg.what == 1) { - if (mSearchKey.isEmpty()) { - clearPage() - } else { - search() - } - } - } - - override fun getLayoutId(): Int { - return R.layout.activity_editor_insert_game - } - - override fun isAutomaticLoad(): Boolean { - return false - } - - override fun getItemDecoration(): RecyclerView.ItemDecoration { - return VerticalItemDecoration(this, 8f, false) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val title = intent?.getStringExtra(EntranceUtils.KEY_NAVIGATION_TITLE) ?: INSERT_GAME_TITLE - setNavigationTitle(title) - noneText.text = "搜索结果为空" - mListLoading.visibility = View.GONE - mListRefresh.isEnabled = false -// appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> -// val totalScrollRange = appBarLayout.totalScrollRange -// if (totalScrollRange == -verticalOffset) { -// Util_System_Keyboard.hideSoftKeyboard(this) -// } -// }) - - searchEt.setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_SEARCH) { - search() - } - false - } - - searchTv.setOnClickListener { - search() - Util_System_Keyboard.hideSoftKeyboard(this) - } - searchBack.setOnClickListener { - searchEt.setText("") - } - - searchEt.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - } - - override fun afterTextChanged(s: Editable) { - val newSearchKey = s.toString().trim() - if (newSearchKey != mSearchKey) { - mBaseHandler.removeMessages(1) - mSearchKey = newSearchKey - mBaseHandler.sendEmptyMessageDelayed(1, 500) - } - searchBack.visibility = if (newSearchKey.isNotEmpty()) View.VISIBLE - else View.GONE - } - }) - - mListRv.clearOnScrollListeners() - - // default open soft keyboard - searchEt.requestFocus() - Util_System_Keyboard.showSoftKeyboard(this, searchEt) - - if (title == SELECT_GAME_TITLE) initDefaultData() - } - - private fun clearPage() { - mAdapter?.setListData(ArrayList()) - mListLoading.visibility = View.GONE - mReuseNoData.visibility = View.GONE - mReuseNoConn.visibility = View.GONE - mListRv.visibility = View.GONE - } - - fun search() { - if (mSearchKey.isEmpty()) { - toast("请输入搜索关键字") - } else { - clearPage() - onLoadRefresh() - } - } - - override fun provideListAdapter(): GameAdapter? { - if (mAdapter == null) { - mAdapter = GameAdapter(this) - } - return mAdapter - } - - override fun provideDataObservable(page: Int): Observable>? { - return RetrofitManager - .getInstance(this).api - .getSearchGame(Config.SENSITIVE_API_HOST + "games:search?keyword=" + searchEt.text + "&view=digest" + "&channel=" + HaloApp.getInstance().channel + "&version" + BuildConfig.VERSION_NAME) - } - - override fun provideListViewModel(): NormalListViewModel { - val factory = NormalListViewModel.Factory(HaloApp.getInstance().application, this) - return ViewModelProviders.of(this, factory).get(NormalListViewModel::class.java) as NormalListViewModel - } - - private fun initDefaultData() { - RetrofitManager.getInstance(this).api - .getEditorInsertDefaultData(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response>() { - override fun onResponse(response: List?) { - if (response == null || response.isEmpty()) return - // init default page - defaultList.layoutManager = FixLinearLayoutManager(baseContext) - defaultList.adapter = GameDefaultAdapter(this@GameActivity, response) - defaultListContainer.visibility = View.VISIBLE - } - }) - } - - override fun onLoadRefresh() { - super.onLoadRefresh() - mListRv.visibility = View.VISIBLE - } - - override fun onLoadEmpty() { - super.onLoadEmpty() - mListRv.visibility = View.VISIBLE - } - - override fun onLoadDone() { - super.onLoadDone() - mListRv.visibility = View.VISIBLE + override fun provideNormalIntent(): Intent { + return getTargetIntent(this, GameActivity::class.java, GameFragment::class.java) } companion object { fun getIntent(context: Context, title: String): Intent { - val intent = Intent(context, GameActivity::class.java) - intent.putExtra(EntranceUtils.KEY_NAVIGATION_TITLE, title) - return intent + val bundle = bundleOf( + EntranceUtils.KEY_NAVIGATION_TITLE to title + ) + return getTargetIntent(context, GameActivity::class.java, GameFragment::class.java, bundle) } const val INSERT_GAME_TITLE = "插入游戏" diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/GameAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/GameAdapter.kt index 332bd4e242..735ed0b950 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/GameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/GameAdapter.kt @@ -11,7 +11,7 @@ import com.gh.gamecenter.databinding.GameItemBinding import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.game.GameItemViewHolder -class GameAdapter(context: Context) : ListAdapter(context) { +open class GameAdapter(context: Context) : ListAdapter(context) { public override fun setListData(updateData: MutableList?) { super.setListData(updateData) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/GameFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/GameFragment.kt new file mode 100644 index 0000000000..8fa0824cef --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/GameFragment.kt @@ -0,0 +1,190 @@ +package com.gh.gamecenter.qa.editor + +import android.os.Bundle +import android.os.Message +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.TextView +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.Config +import com.gh.common.util.EntranceUtils +import com.gh.common.view.FixLinearLayoutManager +import com.gh.common.view.VerticalItemDecoration +import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.NormalListViewModel +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.entity.EditorInsertDefaultEntity +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.google.android.material.appbar.AppBarLayout +import com.halo.assistant.HaloApp +import com.lightgame.utils.Util_System_Keyboard +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import kotterknife.bindView + +open class GameFragment : ListFragment>() { + val searchEt by bindView(R.id.search_input) + val searchTv by bindView(R.id.search_button) + val searchBack by bindView(R.id.search_back) + val appBar by bindView(R.id.list_appbar) + val noneText by bindView(R.id.reuse_tv_none_data) + val defaultList by bindView(R.id.default_list) + val defaultListContainer by bindView(R.id.default_list_container) + + protected var mAdapter: GameAdapter? = null + + private var mSearchKey: String = "" + + override fun handleMessage(msg: Message) { + if (msg.what == 1) { + if (mSearchKey.isEmpty()) { + clearPage() + } else { + search() + } + } + } + + override fun getLayoutId(): Int { + return R.layout.activity_editor_insert_game + } + + override fun isAutomaticLoad(): Boolean { + return false + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration? { + return VerticalItemDecoration(requireContext(), 8f, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val title = arguments?.getString(EntranceUtils.KEY_NAVIGATION_TITLE) ?: "" + if (title.isNotEmpty()) { + setNavigationTitle(title) + } + noneText.text = "搜索结果为空" + mListLoading?.visibility = View.GONE + mListRefresh?.isEnabled = false + + searchEt.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + search() + } + false + } + + searchTv.setOnClickListener { + search() + Util_System_Keyboard.hideSoftKeyboard(requireActivity()) + } + searchBack.setOnClickListener { + searchEt.setText("") + } + + searchEt.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable) { + val newSearchKey = s.toString().trim() + if (newSearchKey != mSearchKey) { + mBaseHandler.removeMessages(1) + mSearchKey = newSearchKey + mBaseHandler.sendEmptyMessageDelayed(1, 500) + } + searchBack.visibility = if (newSearchKey.isNotEmpty()) View.VISIBLE else View.GONE + } + }) + + mListRv.clearOnScrollListeners() + + // default open soft keyboard + if (isAutoShowKeyboard()) { + searchEt.requestFocus() + } + Util_System_Keyboard.showSoftKeyboard(requireActivity(), searchEt) + + if (title == GameActivity.SELECT_GAME_TITLE) initDefaultData() + } + + private fun clearPage() { + mAdapter?.setListData(ArrayList()) + mListLoading?.visibility = View.GONE + mReuseNoData?.visibility = View.GONE + mReuseNoConn?.visibility = View.GONE + mListRv.visibility = View.GONE + } + + fun search() { + if (mSearchKey.isEmpty()) { + toast("请输入搜索关键字") + } else { + clearPage() + onLoadRefresh() + } + } + + override fun provideListAdapter(): GameAdapter? { + if (mAdapter == null) { + mAdapter = GameAdapter(requireContext()) + } + return mAdapter + } + + override fun provideDataObservable(page: Int): Observable>? { + return RetrofitManager + .getInstance(requireContext()).api + .getSearchGame(Config.SENSITIVE_API_HOST + "games:search?keyword=" + searchEt.text + "&view=digest" + "&channel=" + HaloApp.getInstance().channel + "&version" + BuildConfig.VERSION_NAME) + } + + override fun provideListViewModel(): NormalListViewModel { + val factory = NormalListViewModel.Factory(HaloApp.getInstance().application, this) + return ViewModelProviders.of(this, factory).get(NormalListViewModel::class.java) as NormalListViewModel + } + + private fun initDefaultData() { + RetrofitManager.getInstance(requireContext()).api + .getEditorInsertDefaultData(UserManager.getInstance().userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + if (response == null || response.isEmpty()) return + // init default page + defaultList.layoutManager = FixLinearLayoutManager(requireContext()) + defaultList.adapter = GameDefaultAdapter(requireContext(), response) + defaultListContainer.visibility = View.VISIBLE + } + }) + } + + override fun onLoadRefresh() { + super.onLoadRefresh() + mListRv.visibility = View.VISIBLE + } + + override fun onLoadEmpty() { + super.onLoadEmpty() + mListRv.visibility = View.VISIBLE + } + + override fun onLoadDone() { + super.onLoadDone() + mListRv.visibility = View.VISIBLE + } + + open fun isAutoShowKeyboard(): Boolean = true +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/ic_choose_games_drag.png b/app/src/main/res/drawable-xxxhdpi/ic_choose_games_drag.png new file mode 100644 index 0000000000000000000000000000000000000000..c0caeb6fb2c1748dc54b618f2a2f233c5f852e12 GIT binary patch literal 473 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;NuTe=IrU>7*fIb z_KqQ6lY;le3Gj9A78m@mhaR zu*&YHGl>-ee$MAtPAy(JKUZ_YwRrzHm0h!ZwmcVjkQE}9p~5obTMMh*@74E>ZWU+n z`$%Wq68`sA;O8srH>U+5w!y7MX8e$5+-!I`I@c^M%V>3yS(;e5T8^0-^XHeFZqDdg zadQTb&jK4crN%WJNX|yq4>7u%dq(d}p?>@9U-xHd`YlN^Q(F{rYDTXnXKlva93Qqz pK&%!Z$3M{_tOe+8sQ$)smU-FBo15M9Z})?O)zj6_Wt~$(69A4IlMVm? literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_choose_games_top.png b/app/src/main/res/drawable-xxxhdpi/ic_choose_games_top.png new file mode 100644 index 0000000000000000000000000000000000000000..5b68501e04cfc980c210bc3f719cc3ae7712222d GIT binary patch literal 1491 zcmbtU`#aMM82`?eHqzN#%1BL|kwiGunuOdI8wp1`PD~7QNkgk>bDz6XJ*2TB%-zf_ zj@uch;*5@-+~cgYbT*egagglH51sP|ocDP?@8`XIp67kvU*2>#f}@gxh5`TpN;oGw zcNx3w04gu@IS5Iuj6f83M_Ztde_%nD*adpyg7A32Ko&y*P+AB8*^$VgAp-z_Ss(x` zW6(~GCHHUFjRpQM-VvJ2xwrzruG2U>8xJZ-{0JMYW}}))$uH7}ozp-x!i!XU3ECQ( zYIZzFWZX}X7m9I-c<%A_VE2nqg&T-F`snaDR^q~Ix>tsuMl)Qk$j|9G8ok|J<9{(I zmb4@i&G4EUre_Mxgj>Q63TZ8j$1%ku)8TMATWcs3q>X~e5(XYcG9^-}ZJ|V>;ALN5 zpUHT=8L}xkyzBk7_NnIPW+onw?|veA?ucxdO&aqjcI*jz+su+jSV54Yoqrlp8L3#j~1`F0HVc3 zs}!Ci5bUW63a^rWPu z>WYerG*DzN{xE&L?b)+uY&ZR_hLsDV@w)(z(19RN#53xQy{LZE9TJJ;>-gg{kV)oj zd6zs99L%fHJR5}T)hWBVSr#ee$${K=F;b*Z<@i(z-a1XX#+0J3){0AAH-oZz)GJ!q zfE^K|8w)F(?rlG;TdMa_vB+Ljn!U(c!e;dsoGR6wV(vz&t9xUxSS&qxZwl=1rAnZI z#R-p$94=g(UcO$>;Yd#Alo(vbHDj0whJ!%l4|@(O&+9y&XlZH5N9XErA0PXT8gSoG zJ-?A{6O!jvYDVwkgjExc0xkkeMq!LEsgx{K&w2VCQwbH(zrfw1$c>a4o)whjO~+c^ zw0sR>gbKYkS;ov`o6HBabTFiI)CpZ>sCo7er2_lN==7qN(cSUA#kmeI!SIWRbEuQ| zEcz|b@_Ct9$XmMigCI5S#MG*)s#x6(pNy`LDkf>sQS9`|$~!(Wu~Abhpy!-D=3+23 zt16_P2tFwylFD2+<>}S_aG8RQ8RBz?HBf^bHk-X4zRzovKS%QF8Lm|+dFiNnq1Qk5 zv$$WsC&Gc`T>IKZd`fdP_9%TFWA`A?=@XXkTc8!kAk6z24qOrC(bMnI>}9)`uA_AZ zO?6+Ocp3s$cAqL9jLpR3C z*S9Wh-@x?O4G}>Hw6uN?m}yg_G8MEl@$jMj%rECwb0W?|jVcp-DqqYV>_r{8b(#f zjYt$#>2f1?>GM|$6aE)3*YJCcGdexO6`G=pOkr>uu~Uo33RRrEC$M_cosDz$o0GDS o=vcNqRl6+s_O!lzu(^VsLjlefKe`$HIt0iJXHT%Jv-L~-2cl}K$N&HU literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/icon_add_games.webp b/app/src/main/res/drawable-xxxhdpi/icon_add_games.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc412c8bbabb2c3440332bbbbe3d928bf6ed62de GIT binary patch literal 3594 zcmV+l4)yU;Nk&Ej4gdgGMM6+kP&il$0000G0002*008d*06|PpNaqFs00ED~Fp?w@ zdiU;;f5-$85qXJU7}Cy(eIb>%lzD|5MJr-Cot#@&lk9Xcl>~vU5MKxdt=qQNwv`mu zuHqXm#tt*P+{R&MhSs;?r0lx+{{LeVgLJ-gUg3-A{{(o}^czzBne6P+=0Wc4^5*wH z6~1)2gUzMcY^Fce;CqBjHV@6LAOA5GR#~6n<|O*6+utweZcT_aw=C_9wfes4-GYgv z@N{22wyIL&JGUm%y2Gg|GzC42*9Jt^o&Z6K?u8!)&>x}++@$Bf8*r7=XsOE_8K5~+ zhmjWxc>{P}7#V)&mH}Vb$HU{1&(4Z6qBwXodf}`o%Jz#5oEWR>K+JNzUyOB?ZT4it zSb2kO6^!SNrSlwEspG4$xOCX3X~kHco9q&Q_sdydIBt>VV~0XfTlla3IwZ3AR;XPy zh?H6jq|Xd0&(hHOZwm&Q^415fZp|Q68L*afhf;0v3Y}9j)4=b z8stg@zFLGqOV@$#oZGYbfMWe|O(05qN&RG&6R zuIA7n`>-Zr!L~uS29b)b8iY$MO7jNg9HB{r^vTGK8MKc@q@{p2;Yie-JH&Hc1pGCJ zdX@0KHOPM(x^w}1k`7tznL+C{Wk(2tue6Y$a#=&n@rpgkQzOrn+A!qYs8F9FC|4x*g(4cO#GoPR zUKhs70@^m@EN_n;@BV~EvNXz9|ppBHFE>D|0 zmzFWa<G65lzM+> znNrJl{-;zEBRqLwyLOZ`8zQr|nK)v+h*v|1OLq1B8h zL$qR%W?HF8@|IGX^h9!$%87_9Q)>Cn2THxa(@v@OJJkg`75I+q(uw8XX*x~c*GDJ5 zul|-wT7RGHQfWsX9Jflgl&n?wp_EnTN^w@vhpMh^vR}%+O;{h2MVq*%#XWO2U6dkw zt+G-aTcxL%;D=4z>tfW|^hZ#PELg=&ChpTIW7P$(=x)2XlQ++k;`GGWwY#{)ce~hG z6UE6qeOO9N(17{P`8}pgj)pe zx;2a_3*`RW0ygBW?*X4a!^K%z;ICc9gw%RA^k4tQ0;_nIY(B<;sAtdkyI=Mbj(b*Z zTCp8BiFG@^+Dtk;8wc6Et@09hmVUBfrwta1_kFRCve-Q~cw!eFjEVK*qZjrNje973 zJo4ENGNK+D{jM$eq|5hk`C=gtSzg#fXkF$AD$djiR@3v};ZaTtY7^ayKOp&`-3h^O z(7SjI18Z*}SguNq@7_jBc|27m$gc03-YY~yc)G9NLwa@lhvolo!=$|Yp}ktLpKKnU zSwH?$DV6n^;pU`ANWUS~pUKWHZ64&#FK>STQ{l^Vcd)rMo6Yp68hp>109H^qAoKz+REqjyh-6XPlQAwe`IOBhv6^Se?Y z!AwA?HJE4DIfu;fT0j$>1^;E~E=QwcVqv;3sIb(VT18-{Ap7+$^bpRGOvZg{*ZNDg zS)!}rW{Is5Kk8XgA!Zo2i_X+m0CHVRM3L?0Ft=-{t09$P?ZI~Gpwm0&i!zcc(~0x7 zCq%|H!pRO}4{r#uLm4rJK9~I4%A&AM)oed#^x1kQX}<7xMyoK@OhNg2ON&ut@w3x$ z)e%Uj6vQ8whyiQ*73AM6ZvG)^I9Z~wPKunv#JolTagxJGN7Mb^dP}p_?6m>cG7rCS zIpC`_s1(M~Qp-3~bFO8b`S9t}0$>3C_|JAyWCHeT>cUfBH|ul$Cdei706{hUY zf-+x^;R>QxW9D2=ZvaB->i3Ws+CDt+8#^ivg~ za1}-vbPxtA zf>(bxt^m4qhY*IF-9x~U^Mvfi{xWi;@a*wkCT;!+^eQ`Yrmzz;0QxZUu9Mes1TN3MyD zK5Dq*Jk_@czaJa!&B7j?Lv3T%Lh4yVxY_R8)-UwvheynYj+qeL){4!* z=_>4bcI_vT2#r>jpfWU&mrv6f2xxf?oUFFa&nZY$VM%IH&g;;d?u|6=w_lQ(?-Xfw zTNp@~@SZ4S2CX}gkkuy0Jpu%l>$(?64Guw_FQ#lrx^(p*?SATH+e_aEq?m5LwKXCB zEeZEIZNqYioovRko-8G7wc*Xb> z$08w9CVLOk;=!_kZDbZJW9+(vl-wGRYvmZ#0oo`UE&^TN9!FlAJK0M;n|;a+sPOQS z@S0Xqp4@o%*8E$Kwp>$JmxqU(@-72>9fN{i+2{pRGuq`+OR{_cXniG!xgLZaI!Eh; zb}$!H!LK!nq4IdNt#hgh7Cyne84QbfacsK|-$0a6p}-6)3@ep6P#hpX=NcO!Oi?PH zT>Fb`&asziq}53el7x7=Dl}qjZH=owQB>4~!^|b*R{Nm*L*X!nT9g#ZO~$eJ*ID43 z1dkUHmrzt#(rGvj1d@)yLz*?vUm86#Ic~o&b>oo(7m#d^t+8B@8;L>xg1G z0y(g9V)Vu1W{UfN=l`b;Q4gPaPUJhybDOZljdl-+S_ev*a>*{*Oz_ZFxlZgPrIvf0SxySJ1iy)iC zk8B^MjL3oa+V%DLxc&EpZ$5+&s4uf9qI+fk&Fy9iKy+#qTl(awD2djTY(4o(@Qi(i z9)zvknVE(*gle-KuKlUo^PPQh1A-1 + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_editor_insert_game.xml b/app/src/main/res/layout/activity_editor_insert_game.xml index 1ed9c790e8..2278b66dfa 100644 --- a/app/src/main/res/layout/activity_editor_insert_game.xml +++ b/app/src/main/res/layout/activity_editor_insert_game.xml @@ -5,8 +5,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - - @@ -32,7 +30,6 @@ @@ -104,6 +103,7 @@ android:textColor="@color/text_333333" /> @@ -154,6 +152,7 @@ android:textColor="@color/text_333333" /> diff --git a/app/src/main/res/layout/fragment_add_user_played_game.xml b/app/src/main/res/layout/fragment_add_user_played_game.xml new file mode 100644 index 0000000000..f53ddaef94 --- /dev/null +++ b/app/src/main/res/layout/fragment_add_user_played_game.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_choose_games.xml b/app/src/main/res/layout/fragment_choose_games.xml new file mode 100644 index 0000000000..a51dacfd0e --- /dev/null +++ b/app/src/main/res/layout/fragment_choose_games.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_game.xml b/app/src/main/res/layout/fragment_search_game.xml new file mode 100644 index 0000000000..462a6ea270 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_game.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_choose_games.xml b/app/src/main/res/layout/item_choose_games.xml new file mode 100644 index 0000000000..3cd59056e4 --- /dev/null +++ b/app/src/main/res/layout/item_choose_games.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_menu_save.xml b/app/src/main/res/layout/layout_menu_save.xml new file mode 100644 index 0000000000..e49260fb44 --- /dev/null +++ b/app/src/main/res/layout/layout_menu_save.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_save.xml b/app/src/main/res/menu/menu_save.xml new file mode 100644 index 0000000000..5f934c0ac6 --- /dev/null +++ b/app/src/main/res/menu/menu_save.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file From 9a1266d9d9b9a4cc7b562d239ed48cd573fc8e59 Mon Sep 17 00:00:00 2001 From: leafwai Date: Mon, 8 Nov 2021 09:24:26 +0800 Subject: [PATCH 05/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(=E9=80=89=E6=8B=A9=E6=A0=87=E7=AD=BEUI=EF=BC=89https:?= =?UTF-8?q?//git.ghzs.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../square/GameCollectionTagAdapter.kt | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt new file mode 100644 index 0000000000..65b9c8a3e0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt @@ -0,0 +1,147 @@ +package com.gh.gamecenter.gamecollection.square + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DisplayUtils +import com.gh.common.util.dip2px +import com.gh.gamecenter.databinding.* +import com.lightgame.adapter.BaseRecyclerAdapter + +class GameCollectionTagAdapter( + context: Context, + val singleChoice: Boolean = true, + val data: ArrayList, + private val updateCallback: (() -> Unit) +) : + BaseRecyclerAdapter(context) { + + private var mTagViewList = arrayListOf() + var singleTagName = "" + var multipleTagList = arrayListOf() + + + override fun getItemViewType(position: Int): Int { + return if (singleChoice) { + if (position % 2 == 0) TAG_HEADER else TAG_BODY + } else { + if (position == 0) SELECTED_TAGS else if (position % 2 != 0) TAG_HEADER else TAG_BODY + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType){ + SELECTED_TAGS -> GameCollectionSelectedTagViewHolder( + GameCollectionSelectedTagItemBinding.inflate(mLayoutInflater, parent, false) + ) + TAG_HEADER -> GameCollectionTagHeaderViewHolder( + GameCollectionTagHeaderItemBinding.inflate(mLayoutInflater, parent, false) + ) + else -> GameCollectionTagBodyViewHolder( + GameCollectionTagBodyItemBinding.inflate(mLayoutInflater, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GameCollectionSelectedTagViewHolder -> { + holder.binding.selectedTagFlexbox.removeAllViews() + holder.binding.hintTv.visibility = if (multipleTagList.size == 0) View.VISIBLE else View.GONE + for (name in multipleTagList) { + val selectedTagView = + ItemGameCollectionSelectedTagBinding.inflate(mLayoutInflater) + .apply { + tagTv.text = name + root.setOnClickListener { + multipleTagList.remove(name) + updateCallback.invoke() + notifyDataSetChanged() + } + }.root + holder.binding.selectedTagFlexbox.addView(selectedTagView) + } + } + is GameCollectionTagHeaderViewHolder -> { + holder.binding.headerTv.text = data[position] + if (position == 0) { + holder.binding.root.setPadding( + 16F.dip2px(), + 40F.dip2px(), + 16F.dip2px(), + 10F.dip2px() + ) + } + } + is GameCollectionTagBodyViewHolder -> { + val dataList = arrayListOf( + "ghgg", + "das5555554a44", + "dasda54544", + "dsdasdasdas", + "dasdsa8787", + "58748sss" + ) + holder.binding.tagFlexbox.removeAllViews() + for (name in dataList) { + val tag = if(singleChoice) getSingleTag(name) else getMultipleTag(name) + mTagViewList.add(tag) + holder.binding.tagFlexbox.addView(tag.root) + } + if (position == data.size - 1) { + holder.binding.root.setPadding( + 12F.dip2px(), + 5F.dip2px(), + 12F.dip2px(), + 97F.dip2px() + ) + } + } + } + } + + private fun getSingleTag(name: String) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { + tagTv.text = name + tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } + root.setOnClickListener { + for (tag in mTagViewList) { + if (tag.tagTv != tagTv) tag.tagTv.isChecked = false + } + tagTv.isChecked = !tagTv.isChecked + if (tagTv.isChecked) singleTagName = tagTv.text.toString() + } + } + + private fun getMultipleTag(name: String) = + ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { + tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } + tagTv.text = name + if (multipleTagList.contains(name)) tagTv.isChecked = true + root.setOnClickListener { + tagTv.isChecked = !tagTv.isChecked + if (tagTv.isChecked) { + multipleTagList.add(name) + } else { + if (multipleTagList.contains(name)) multipleTagList.remove(name) + } + updateCallback.invoke() + notifyItemChanged(0) + } + } + + override fun getItemCount() = data.size + + class GameCollectionSelectedTagViewHolder(var binding: GameCollectionSelectedTagItemBinding) : + RecyclerView.ViewHolder(binding.root) + + class GameCollectionTagBodyViewHolder(var binding: GameCollectionTagBodyItemBinding) : + RecyclerView.ViewHolder(binding.root) + + class GameCollectionTagHeaderViewHolder(var binding: GameCollectionTagHeaderItemBinding) : + RecyclerView.ViewHolder(binding.root) + + companion object { + const val SELECTED_TAGS = 100 + const val TAG_HEADER = 101 + const val TAG_BODY = 102 + } +} \ No newline at end of file From 42a4210290a92cb76da4acd02582619f365da6a2 Mon Sep 17 00:00:00 2001 From: leafwai Date: Mon, 8 Nov 2021 09:40:57 +0800 Subject: [PATCH 06/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(=E9=80=89=E6=8B=A9=E6=A0=87=E7=AD=BEUI=EF=BC=89https:?= =?UTF-8?q?//git.ghzs.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 +++--- .../square/GameCollectionTagSelectFragment.kt | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index efa5a42661..5b57a4965c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -738,9 +738,9 @@ android:name=".gamecollection.choose.AddGamesActivity" android:screenOrientation="portrait" /> - + + + Date: Mon, 8 Nov 2021 18:12:53 +0800 Subject: [PATCH 07/49] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83api?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A7=E4=B8=BAv5d5d0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 620ce711ce..c80a20c77a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -65,7 +65,7 @@ QUICK_LOGIN_APPID=300012035775 QUICK_LOGIN_APPKEY=002BAABA2C078342DA33BEAB0A4C6A25 # hosts -DEV_API_HOST=https\://dev-and-api.ghzs.com/v5d4d0/ +DEV_API_HOST=https\://dev-and-api.ghzs.com/v5d5d0/ API_HOST=https\://and-api.ghzs.com/v5d4d0/ SENSITIVE_API_HOST=https\://and-api.ghzs.com/v5d4d0/ From 831ca71a5843bcc765fa43a96f7b367ffdae5a79 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Wed, 10 Nov 2021 16:52:01 +0800 Subject: [PATCH 08/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E5=88=9B=E5=BB=BA/=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1604?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 4 + .../com/gh/gamecenter/entity/GameEntity.kt | 35 ++- .../entity/GamesCollectionEntity.kt | 43 ++++ .../MyGameCollectionActivity.kt | 18 ++ .../gamecollection/MyGameCollectionAdapter.kt | 57 +++++ .../MyGameCollectionFragment.kt | 57 +++++ .../MyGameCollectionViewModel.kt | 21 ++ .../choose/AddSearchGameFragment.kt | 2 +- .../choose/ChooseGamesAdapter.kt | 9 + .../choose/ChooseGamesFragment.kt | 26 +++ .../publish/GameCollectionEditActivity.kt | 212 ++++++++++++++++-- .../publish/GameCollectionEditViewModel.kt | 119 ++++++++++ .../personal/PersonalFunctionAdapter.kt | 12 +- .../gamecenter/personal/PersonalViewModel.kt | 3 +- .../gh/gamecenter/qa/entity/ArticleEntity.kt | 108 ++++----- .../retrofit/service/ApiService.java | 24 ++ .../com/gh/gamecenter/room/AppDatabase.java | 21 +- .../room/converter/SimpleGameListConverter.kt | 20 ++ .../room/dao/GameCollectionDraftDao.kt | 21 ++ .../drawable-xxxhdpi/ic_choose_game_del.webp | Bin 0 -> 1202 bytes .../ic_game_collection_del.webp | Bin 0 -> 782 bytes .../ic_game_collection_draft.webp | Bin 0 -> 1726 bytes .../ic_game_collection_edit.webp | Bin 0 -> 972 bytes .../ic_game_collection_fail.webp | Bin 0 -> 3394 bytes .../ic_game_collection_pending.webp | Bin 0 -> 3208 bytes .../ic_game_collection_private.webp | Bin 0 -> 2444 bytes .../ic_game_collection_publish.webp | Bin 0 -> 744 bytes .../ic_menu_game_collection_square.webp | Bin 0 -> 2176 bytes .../drawable/bg_my_game_collection_btn.xml | 8 + .../layout/activity_game_collection_edit.xml | 6 +- .../fragment_my_game_collection_list.xml | 97 ++++++++ app/src/main/res/layout/item_choose_games.xml | 2 +- .../res/layout/item_my_game_collection.xml | 143 ++++++++++++ .../main/res/menu/menu_my_game_collection.xml | 11 + 34 files changed, 998 insertions(+), 81 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameListConverter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_choose_game_del.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_del.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_draft.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_edit.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_fail.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_pending.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_private.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_publish.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_menu_game_collection_square.webp create mode 100644 app/src/main/res/drawable/bg_my_game_collection_btn.xml create mode 100644 app/src/main/res/layout/fragment_my_game_collection_list.xml create mode 100644 app/src/main/res/layout/item_my_game_collection.xml create mode 100644 app/src/main/res/menu/menu_my_game_collection.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5b57a4965c..d7c76517d5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -738,6 +738,10 @@ android:name=".gamecollection.choose.AddGamesActivity" android:screenOrientation="portrait" /> + + diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt index d9ef421c6f..eaed9e11ed 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt @@ -252,6 +252,13 @@ data class GameEntity( @SerializedName("bbs_id") var bbsId: String = "", + //游戏单推荐分数 + @SerializedName("recommend_star") + var recommendStar: Int = 5, + //游戏单推荐理由 + @SerializedName("recommend_text") + var recommendText: String = "", + // 本地字段,使用镜像信息 var useMirrorInfo: Boolean = false, // 本地字段,曝光用 @@ -549,6 +556,20 @@ data class GameEntity( && (useMirrorInfo || RegionSettingHelper.shouldThisGameDisplayMirrorInfo(id))) } + fun toSimpleGame(): SimpleGame { + val simpleGame = SimpleGame() + simpleGame.id = id + simpleGame.mName = mName + simpleGame.nameSuffix = nameSuffix + simpleGame.mIcon = mIcon + simpleGame.mRawIcon = mRawIcon + simpleGame.active=active + simpleGame.iconSubscript=iconSubscript + simpleGame.recommendStar=recommendStar + simpleGame.recommendText=recommendText + return simpleGame + } + fun clone(): GameEntity { val gameEntity = GameEntity() gameEntity.id = id @@ -762,12 +783,18 @@ data class SimpleGame( @SerializedName("name_suffix") var nameSuffix: String? = null, @SerializedName("icon") - private var mIcon: String? = null, + var mIcon: String? = null, @SerializedName("ori_icon") - private var mRawIcon: String? = null, + var mRawIcon: String? = null, @SerializedName("icon_subscript") var iconSubscript: String? = null, - var active: Boolean = false + var active: Boolean = false, + //游戏单推荐分数 + @SerializedName("recommend_star") + var recommendStar: Int = 5, + //游戏单推荐理由 + @SerializedName("recommend_text") + var recommendText: String = "", ) : Parcelable { @IgnoredOnParcel @@ -788,6 +815,8 @@ data class SimpleGame( gameEntity.icon = mIcon gameEntity.rawIcon = mRawIcon gameEntity.iconSubscript = iconSubscript + gameEntity.recommendStar = recommendStar + gameEntity.recommendText = recommendText return gameEntity } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt new file mode 100644 index 0000000000..2d2e7d4749 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt @@ -0,0 +1,43 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import androidx.room.TypeConverters +import com.gh.gamecenter.qa.entity.Count +import com.gh.gamecenter.qa.entity.TimeEntity +import com.gh.gamecenter.room.converter.ListStringConverter +import com.gh.gamecenter.room.converter.SimpleGameListConverter +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Entity(tableName = "GameCollectionDraft") +@Parcelize +class GamesCollectionEntity( + @Ignore + @SerializedName("_id") + var id: String = "", + @PrimaryKey + var primaryKey: String = "", // db key + @TypeConverters(ListStringConverter::class) + var tags: ArrayList? = null, + @TypeConverters(SimpleGameListConverter::class) + var games: ArrayList? = null, + var title: String = "", + var intro: String = "", + var cover: String = "", + var display: String = "",//self_only: 仅自己可见 + @Ignore + var time: TimeEntity? = null, + @Ignore + var stamp: String = "",//special_choice: 精选 offical: 官方 + @Ignore + var count: Count? = null, + @Ignore + var status: String = "",// draft/pending/pass/failed + @Ignore + var user: User? = null, + @Ignore + var me: MeEntity? = null, +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt new file mode 100644 index 0000000000..f894d8df15 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt @@ -0,0 +1,18 @@ +package com.gh.gamecenter.gamecollection + +import android.content.Context +import android.content.Intent +import com.gh.gamecenter.NormalActivity + +class MyGameCollectionActivity : NormalActivity() { + + override fun provideNormalIntent(): Intent { + return getTargetIntent(this, MyGameCollectionActivity::class.java, MyGameCollectionFragment::class.java) + } + + companion object { + fun getIntent(context: Context): Intent { + return getTargetIntent(context, MyGameCollectionActivity::class.java, MyGameCollectionFragment::class.java) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt new file mode 100644 index 0000000000..2fee46c81a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt @@ -0,0 +1,57 @@ +package com.gh.gamecenter.gamecollection + +import android.content.Context +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.constant.ItemViewType +import com.gh.common.util.DisplayUtils +import com.gh.common.util.toBinding +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ItemMyGameCollectionBinding +import com.gh.gamecenter.entity.GamesCollectionEntity + +class MyGameCollectionAdapter(context: Context) : ListAdapter(context) { + + override fun getItemViewType(position: Int): Int { + return if (position == itemCount - 1) { + ItemViewType.ITEM_FOOTER + } else { + ItemViewType.ITEM_BODY + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_BODY -> MyGameCollectionViewHolder(parent.toBinding()) + ItemViewType.ITEM_FOOTER -> FooterViewHolder(parent.toBinding()) + else -> MyGameCollectionViewHolder(parent.toBinding()) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is MyGameCollectionViewHolder -> { + + } + is FooterViewHolder -> { + holder.initItemPadding() + holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver) + holder.hint.setTextColor(ContextCompat.getColor(mContext, R.color.aaaaaa)) + val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams + lp.height = DisplayUtils.dip2px(48F) + lp.width = ViewGroup.LayoutParams.MATCH_PARENT + holder.itemView.layoutParams = lp + } + } + } + + override fun getItemCount(): Int { + return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1 + } + + class MyGameCollectionViewHolder(val binding: ItemMyGameCollectionBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt new file mode 100644 index 0000000000..828d06eb37 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt @@ -0,0 +1,57 @@ +package com.gh.gamecenter.gamecollection + +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import com.gh.common.util.showRegulationTestDialogIfNeeded +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.databinding.FragmentMyGameCollectionListBinding +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity + +class MyGameCollectionFragment : ListFragment() { + + private var mAdapter: MyGameCollectionAdapter? = null + private lateinit var mBinding: FragmentMyGameCollectionListBinding + + override fun getLayoutId(): Int = R.layout.fragment_my_game_collection_list + + override fun getInflatedLayout(): View { + return FragmentMyGameCollectionListBinding.inflate(layoutInflater, null, false).apply { + mBinding = this + }.root + } + + override fun onMenuItemClick(menuItem: MenuItem?) { + super.onMenuItemClick(menuItem) + menuItem?.run { + if (itemId == R.id.menu_game_collection_square) { + + } + } + } + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapter ?: MyGameCollectionAdapter(requireContext()).apply { + mAdapter = this + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setNavigationTitle("我的游戏单") + initMenu(R.menu.menu_my_game_collection) + + mBinding.createBtn.setOnClickListener { + showRegulationTestDialogIfNeeded { + startActivity(GameCollectionEditActivity.getIntent(requireContext())) + } + } + + mBinding.reuseNoneDataIv.setOnClickListener { + mBinding.createBtn.performClick() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt new file mode 100644 index 0000000000..46bc427ca0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt @@ -0,0 +1,21 @@ +package com.gh.gamecenter.gamecollection + +import android.app.Application +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.Observable + +class MyGameCollectionViewModel(application: Application) : ListViewModel(application) { + + override fun provideDataObservable(page: Int): Observable>? { + return RetrofitManager.getInstance(HaloApp.getInstance()).api + .getGameCollectionList(UserManager.getInstance().userId) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt index ab0a01cb9c..7f166d03bb 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt @@ -21,7 +21,7 @@ class AddSearchGameFragment : GameFragment() { override fun getLayoutId(): Int = 0 override fun getInflatedLayout(): View { - return FragmentSearchGameBinding.bind(layoutInflater.inflate(R.layout.fragment_search_game, null, false)).apply { + return FragmentSearchGameBinding.inflate(layoutInflater, null, false).apply { mBinding = this }.root } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt index 3fcf57c08e..5786168d44 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.widget.doOnTextChanged import androidx.recyclerview.widget.RecyclerView import com.gh.base.BaseRecyclerViewHolder import com.gh.common.util.TextHelper @@ -40,7 +41,15 @@ class ChooseGamesAdapter(context: Context, val dragListener: ItemDragListener) : val gameEntity = mEntityList[position] holder.binding.gameNameTv.text = gameEntity.name holder.binding.gameIcon.displayGameIcon(gameEntity) + holder.binding.recommendReasonEt.setText(gameEntity.recommendText) + holder.binding.ratingScore.rating = gameEntity.recommendStar.toFloat() holder.binding.recommendReasonEt.filters = arrayOf(TextHelper.getFilter(45, "最多输入45个字")) + holder.binding.recommendReasonEt.doOnTextChanged { text, _, _, _ -> + gameEntity.recommendText = text.toString() + } + holder.binding.ratingScore.setOnRatingChangeListener { _, rating -> + gameEntity.recommendStar = rating.toInt() + } holder.binding.deleteIv.setOnClickListener { dragListener.deleteItem(gameEntity) } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt index 9aed0f7833..d987182fe3 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt @@ -1,16 +1,19 @@ package com.gh.gamecenter.gamecollection.choose import android.os.Bundle +import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DialogHelper import com.gh.common.util.goneIf import com.gh.common.util.viewModelProvider import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentChooseGamesBinding import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.normal.NormalFragment +import java.lang.ref.WeakReference import java.util.* class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListener { @@ -61,6 +64,15 @@ class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListene mViewModel = viewModelProvider(ChooseGamesViewModel.Factory()) } + override fun onMenuItemClick(menuItem: MenuItem?) { + super.onMenuItemClick(menuItem) + menuItem?.run { + if (itemId == R.id.layout_menu_save) { + requireActivity().finish() + } + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mViewModel.chooseGamesLiveData.observe(viewLifecycleOwner) { @@ -105,4 +117,18 @@ class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListene mViewModel.chooseGamesLiveData.postValue(chooseGames) } + override fun onBackPressed(): Boolean { + val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf() + if (games.isNotEmpty()) { + DialogHelper.showDialog(requireContext(), "提示", "是否保存本次选择的游戏", "保存", "取消", { + requireActivity().finish() + }, { + mViewModel.chooseGamesLiveData.postValue(arrayListOf()) + requireActivity().finish() + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + return true + } + return super.onBackPressed() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index c85d47a426..bbcf2d5f20 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -7,12 +7,17 @@ import android.os.Bundle import android.view.MenuItem import androidx.core.widget.doOnTextChanged import com.gh.base.ToolBarActivity +import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.util.* import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.entity.SimpleGame import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel +import com.gh.gamecenter.gamecollection.square.GameCollectionTagSelectActivity +import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.qa.editor.LocalMediaActivity import com.zhihu.matisse.Matisse import com.zhihu.matisse.internal.utils.PathUtils @@ -23,6 +28,7 @@ class GameCollectionEditActivity : ToolBarActivity() { private lateinit var mBinding: ActivityGameCollectionEditBinding private lateinit var mViewModel: GameCollectionEditViewModel private lateinit var mChooseGamesViewModel: ChooseGamesViewModel + private var mProcessingDialog: WaitingDialogFragment? = null override fun getLayoutId(): Int = R.layout.activity_game_collection_edit @@ -34,6 +40,7 @@ class GameCollectionEditActivity : ToolBarActivity() { setToolbarMenu(R.menu.menu_game_collection_edit) mMenuPost = getMenuItem(R.id.menu_game_collection_post) setNavigationTitle("创建游戏单") + mViewModel.gameCollectionPatch = intent.getParcelableExtra(GamesCollectionEntity::class.java.name) initData() observeData() @@ -61,20 +68,42 @@ class GameCollectionEditActivity : ToolBarActivity() { mBinding.changePosterBtn.setOnClickListener { mBinding.uploadPictureBtn.performClick() } mBinding.deleteBtn.setOnClickListener { - initPosterUI("") + mViewModel.imageUrl = "" + mViewModel.imagePath = "" + mBinding.uploadPictureBtn.text = "点击上传图片" + initPosterUI() } - mBinding.gameCollectionIntroduceEt.doOnTextChanged { text, start, before, count -> + mBinding.gameCollectionIntroduceEt.doOnTextChanged { text, _, _, _ -> mBinding.introduceSizeTv.text = "${text?.length ?: 0}/200" } + mBinding.chooseLabelContainer.setOnClickListener { + startActivityForResult(GameCollectionTagSelectActivity.getIntent(this), REQUEST_CHOOSE_TAG) + } + mBinding.chooseGameContainer.setOnClickListener { - startActivityForResult(ChooseGamesActivity.getIntent(this), REQUEST_CHOOSE_GAMES) + startActivity(ChooseGamesActivity.getIntent(this)) } } private fun initData() { + mBinding.gameCollectionTitleEt.filters = arrayOf(TextHelper.getFilter(20, "最多输入20个字")) + mBinding.gameCollectionIntroduceEt.filters = arrayOf(TextHelper.getFilter(200, "最多输入200个字")) + mViewModel.gameCollectionPatch?.run { + if (status.isNotEmpty() && status != "draft") { + setNavigationTitle("编辑游戏单") + } + mViewModel.imageUrl = cover + initPosterUI() + mBinding.gameCollectionTitleEt.setText(title) + mBinding.gameCollectionTitleEt.setSelection(title.length) + mBinding.gameCollectionIntroduceEt.setText(intro) + mBinding.gameCollectionIntroduceEt.setSelection(intro.length) + mBinding.selfOnlyCb.isChecked = display == "self_only" + mViewModel.getGameCollectionDetail(id) + } ?: mViewModel.getDraft() } private fun observeData() { @@ -82,9 +111,59 @@ class GameCollectionEditActivity : ToolBarActivity() { mBinding.gamesTv.text = if (it.isEmpty()) "选择游戏" else "已选 ${it.size} 款游戏" mBinding.gamesTipTv.goneIf(it.isNotEmpty()) } + mViewModel.processDialog.observe(this) { + if (it.isShow) { + if (mProcessingDialog?.dialog?.isShowing == true) { + mProcessingDialog?.uploadWaitingHint(it.msg) + } else { + mProcessingDialog = WaitingDialogFragment.newInstance(it.msg, false) + mProcessingDialog?.show(supportFragmentManager, null) + } + } else { + mProcessingDialog?.dismiss() + } + } + mViewModel.uploadImageSuccessLiveData.observe(this) { + if (it.isNotEmpty()) { + initPosterUI() + } + mBinding.uploadPictureBtn.text = "点击上传图片" + } + mViewModel.postLiveData.observe(this) { + if (it.status == Status.SUCCESS) { + toast("提交成功") + finish() + } else { + ErrorHelper.handleError(this, it.exception?.response()?.errorBody()?.string()) + } + } + mViewModel.detailLiveData.observe(this) { + mViewModel.gameCollectionPatch = it + mViewModel.gameCollectionPatch?.run { + //TODO:initTags + mViewModel.tags = tags ?: arrayListOf() + val simpleGames = games?.map { game -> game.toGameEntity() }?.toList() + mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames)) + } + } + mViewModel.draftLiveData.observe(this) { + mViewModel.gameCollectionPatch = it + mViewModel.gameCollectionPatch?.run { + initData() + //TODO:initTags + mViewModel.tags = tags ?: arrayListOf() + val simpleGames = games?.map { game -> game.toGameEntity() } + mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames)) + } + } } override fun onMenuItemClick(item: MenuItem?): Boolean { + item?.run { + if (itemId == R.id.menu_game_collection_post) { + verifyData() + } + } return super.onMenuItemClick(item) } @@ -102,34 +181,139 @@ class GameCollectionEditActivity : ToolBarActivity() { } REQUEST_CODE_IMAGE_CROP -> { val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: "" - initPosterUI(imagePath) + mViewModel.imageUrl = "" + mViewModel.imagePath = imagePath + if (imagePath.isEmpty()) { + mBinding.uploadPictureBtn.text = "点击上传图片" + } else { + mBinding.uploadPictureBtn.text = "图片上传中..." + mViewModel.uploadPoster() + } + initPosterUI() } - REQUEST_CHOOSE_GAMES -> { + REQUEST_CHOOSE_TAG -> { } } } - private fun initPosterUI(imagePath: String) { - mViewModel.imagePath = imagePath - mBinding.placeholderView.goneIf(imagePath.isNotEmpty()) - mBinding.uploadPictureBtn.goneIf(imagePath.isNotEmpty()) - mBinding.posterView.goneIf(imagePath.isEmpty()) - mBinding.changePosterBtn.goneIf(imagePath.isEmpty()) - mBinding.deleteBtn.goneIf(imagePath.isEmpty()) - if (imagePath.isNotEmpty()) { - ImageUtils.display(mBinding.posterView, "file:///$imagePath") + private fun initPosterUI() { + mBinding.placeholderView.goneIf(mViewModel.imageUrl.isNotEmpty()) + mBinding.uploadPictureBtn.goneIf(mViewModel.imageUrl.isNotEmpty()) + mBinding.posterView.goneIf(mViewModel.imageUrl.isEmpty()) + mBinding.changePosterBtn.goneIf(mViewModel.imageUrl.isEmpty()) + mBinding.deleteBtn.goneIf(mViewModel.imageUrl.isEmpty()) + if (mViewModel.imageUrl.isNotEmpty()) { + ImageUtils.display(mBinding.posterView, mViewModel.imageUrl) } } + override fun handleBackPressed(): Boolean { + val patch = mViewModel.gameCollectionPatch + if (patch != null && patch.status.isNotEmpty() && patch.status != "draft") { + DialogHelper.showDialog(this, "温馨提示", "退出将不会保留本次游戏单编辑的内容,是否确定退出", "确定", "取消", { + finish() + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + return true + } else { + val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf() + val title = mBinding.gameCollectionTitleEt.text.toString() + val introduce = mBinding.gameCollectionIntroduceEt.text.toString() + if (mViewModel.imageUrl.isNotEmpty() + || mViewModel.tags.isNotEmpty() + || games.isNotEmpty() + || title.isNotEmpty() + || introduce.isNotEmpty() + ) { + val entity = GamesCollectionEntity() + entity.tags = mViewModel.tags + entity.title = title + entity.intro = introduce + entity.cover = mViewModel.imageUrl + entity.display = if (mBinding.selfOnlyCb.isChecked) "self_only" else "" + entity.games = arrayListOf() + entity.games?.addAll(games.map { it.toSimpleGame() }.toList()) + mViewModel.addDraft(entity) + } + } + return super.handleBackPressed() + } + + private fun verifyData() { + if (mViewModel.imageUrl.isEmpty()) { + toast("请上传游戏单的封面") + return + } + if (mViewModel.tags.isEmpty()) { + toast("请选择游戏单的标签") + return + } + val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf() + + val title = mBinding.gameCollectionTitleEt.text.toString() + if (title.isEmpty()) { + toast("请填写游戏单的标题") + return + } + val introduce = mBinding.gameCollectionIntroduceEt.text.toString() + if (introduce.length < 10) { + toast("介绍至少10个字") + return + } + + val isSelfOnly = mBinding.selfOnlyCb.isChecked + val requestMap = hashMapOf( + "id" to (mViewModel.gameCollectionPatch?.id ?: ""), + "title" to title, + "intro" to introduce, + "tag_ids" to mViewModel.tags, + "cover" to mViewModel.imageUrl, + "display" to if (isSelfOnly) "self_only" else "", + "games" to games.map { + hashMapOf( + "_id" to it.id, + "recommend_star" to it.recommendStar, + "recommend_text" to it.recommendText, + ) + }.toList() + ) + + if (isSelfOnly) { + DialogHelper.showDialog(this, "温馨提示", "游戏单开启“仅自己可见”后,将不会对其他用户展示,是否继续提交", "确定", "取消", { + mViewModel.uploadContent(requestMap) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } else { + if (games.size < 8) { + DialogHelper.showDialog(this, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场,是否在“我的光环-我的游戏单”继续保存为草稿", "继续保存", "添加游戏", { + mViewModel.uploadContent(requestMap) + }, { + startActivity(ChooseGamesActivity.getIntent(this)) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } else { + DialogHelper.showDialog(this, "温馨提示", "游戏单会在1-2个工作日内审核完成,您可以在“我的光环-我的游戏单”查看进度", "继续提交", "取消", { + mViewModel.uploadContent(requestMap) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + } + + companion object { const val REQUEST_CODE_IMAGE = 100 const val REQUEST_CODE_IMAGE_CROP = 101 const val REQUEST_CHOOSE_GAMES = 102 + const val REQUEST_CHOOSE_TAG = 103 @JvmStatic fun getIntent(context: Context): Intent { return Intent(context, GameCollectionEditActivity::class.java) } + + @JvmStatic + fun getIntent(context: Context, entity: GamesCollectionEntity): Intent { + val intent = Intent(context, GameCollectionEditActivity::class.java) + intent.putExtra(GamesCollectionEntity::class.java.name, entity) + return intent + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index 173e63e661..bb97751b11 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -1,8 +1,127 @@ package com.gh.gamecenter.gamecollection.publish +import android.annotation.SuppressLint import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.runOnIoThread +import com.gh.common.util.* +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.mvvm.Resource +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.room.AppDatabase +import com.google.gson.JsonObject +import com.halo.assistant.HaloApp +import retrofit2.HttpException class GameCollectionEditViewModel(application: Application) : AndroidViewModel(application) { var imagePath = "" + var imageUrl = "" + var tags = ArrayList() + var gameCollectionPatch: GamesCollectionEntity? = null + var uploadImageSuccessLiveData = MutableLiveData() + var detailLiveData = MutableLiveData() + var draftLiveData = MutableLiveData() + val postLiveData = MutableLiveData>() + val processDialog = MediatorLiveData() + private val mApi = RetrofitManager.getInstance(HaloApp.getInstance()).api + private val mDraftDao = AppDatabase.getInstance(HaloApp.getInstance()).gameCollectionDraftDao() + + fun uploadPoster() { + if (imagePath.isEmpty()) return + UploadImageUtils.uploadImage(UploadImageUtils.UploadType.poster, imagePath, object : UploadImageUtils.OnUploadImageListener { + override fun onSuccess(imageUrl: String) { + this@GameCollectionEditViewModel.imageUrl = imageUrl + uploadImageSuccessLiveData.postValue(imageUrl) + } + + override fun onError(e: Throwable?) { + uploadImageSuccessLiveData.postValue("") + ToastUtils.showToast("图片上传失败") + } + + override fun onProgress(total: Long, progress: Long) {} + }) + } + + fun uploadContent(requestMap: HashMap) { + val id = requestMap["id"]?.toString() ?: "" + if (id.isNotEmpty()) { + requestMap.remove("id") + } + postContent(requestMap, id) + } + + private fun postContent(requestMap: HashMap, gameCollectionId: String) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) + val requestBody = requestMap.toRequestBody() + val observable = if (gameCollectionId.isEmpty()) { + mApi.createGameCollection(requestBody) + } else { + mApi.patchGameCollection(requestBody, gameCollectionId) + } + observable + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: JsonObject?) { + super.onResponse(response) + val id = response?.get("_id")?.asString + postLiveData.postValue(Resource.success(id)) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + postLiveData.postValue(Resource.error(e)) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + } + }) + } + + fun getGameCollectionDetail(gameCollectionId: String) { + if (gameCollectionId.isEmpty()) return + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", true)) + mApi.getGameCollectionDetail(gameCollectionId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: GamesCollectionEntity?) { + super.onResponse(response) + if (response != null) { + detailLiveData.postValue(response) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + } + }) + } + + @SuppressLint("CheckResult") + fun getDraft() { + mDraftDao.getAllDraft() + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + if (data.isNotEmpty()) { + val draftEntity = data[0] + draftLiveData.postValue(draftEntity) + } + } + }) + } + + fun addDraft(entity: GamesCollectionEntity) { + runOnIoThread { + val drafts = mDraftDao.getDrafts() + mDraftDao.delete(drafts) + mDraftDao.addDraft(entity) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt index 56768c49c9..c5c6091b71 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt @@ -18,6 +18,7 @@ import com.gh.gamecenter.entity.FunctionalGroupEntity import com.gh.gamecenter.entity.FunctionalLinkEntity import com.gh.gamecenter.entity.FunctionalMessageType import com.gh.gamecenter.game.upload.GameSubmissionActivity +import com.gh.gamecenter.gamecollection.MyGameCollectionActivity import com.gh.gamecenter.gamedetail.myrating.MyRatingActivity import com.gh.gamecenter.history.HistoryActivity import com.gh.gamecenter.manager.UserManager @@ -32,8 +33,8 @@ import com.gh.gamecenter.video.videomanager.VideoManagerActivity import com.halo.assistant.HaloApp import com.lightgame.adapter.BaseRecyclerAdapter -class PersonalFunctionAdapter(val context: Context, val groupName: String, var mEntityList: ArrayList) - : BaseRecyclerAdapter(context) { +class PersonalFunctionAdapter(val context: Context, val groupName: String, var mEntityList: ArrayList) : + BaseRecyclerAdapter(context) { private var mDisplayUpdateHint = false private val gameTrendsDao = GameTrendsDao(HaloApp.getInstance().application) @@ -287,6 +288,13 @@ class PersonalFunctionAdapter(val context: Context, val groupName: String, var m "青少年模式" -> { context.startActivity(TeenagerModeActivity.getIntent(context)) } + "我的游戏单" -> { + if (UserManager.getInstance().isLoggedIn) { + context.startActivity(MyGameCollectionActivity.getIntent(context)) + } else { + CheckLoginUtils.checkLogin(context, "我的光环-我的游戏单") { } + } + } else -> { DirectUtils.directToLinkPage(context, linkEntity, "", "我的光环") } diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt index 66d7b945c4..44086bad67 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt @@ -44,7 +44,8 @@ class PersonalViewModel(application: Application) : AndroidViewModel(application Triple("浏览记录", R.drawable.personal_browsing_history, "浏览记录"), Triple("账号安全", R.drawable.personal_account_security, "账号安全"), Triple("模拟器游戏", R.drawable.personal_simulator_game, "模拟器游戏"), - Triple("收货信息", R.drawable.personal_delivery_info, "收货信息") + Triple("收货信息", R.drawable.personal_delivery_info, "收货信息"), + Triple("我的游戏单", R.drawable.icon_game_collection, "我的游戏单") ) private val contentCenterFuncs = arrayListOf( Triple("游戏动态", R.drawable.personal_game_dynamic, "游戏动态"), diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt index f7eb8ac1a8..68731fb867 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt @@ -22,47 +22,47 @@ import kotlinx.android.parcel.Parcelize @Entity @Parcelize data class ArticleEntity( - @PrimaryKey - @SerializedName("_id") - var id: String = "", - var title: String = "", - var brief: String = "", - var active: Boolean = true, - var orderTag: Long = 0, - @TypeConverters(ListStringConverter::class) - var images: List = ArrayList(), - @TypeConverters(ImageInfoConverter::class) - @SerializedName("images_info") - var imagesInfo: List = ArrayList(), - @TypeConverters(CommunityVideoConverter::class) - var videos: List = ArrayList(), - var count: Count = Count(), - var community: CommunityEntity = CommunityEntity(), - var time: TimeEntity? = TimeEntity(), - var user: UserEntity = UserEntity(), - @Ignore - var read: Boolean = true, - @Ignore - var me: MeEntity = MeEntity(), - @Ignore - var commentable: Boolean = true, - @Ignore - var bbs: CommunityEntity = CommunityEntity(), - var des: String = "", - var url: String = "", - @TypeConverters(VideoInfoConverter::class) - @SerializedName("video_info") - var videoInfo: VideoInfo = VideoInfo(), - var poster: String = "", - var length: Long = 0, - @Ignore - var type: String = "", - var status: String = "", - var content: String = "", - @SyncIgnore // questions里的vote当前entity的vote冲突 - @TypeConverters(QuestionsConverter::class) - @SerializedName("question") - var questions: Questions = Questions() + @PrimaryKey + @SerializedName("_id") + var id: String = "", + var title: String = "", + var brief: String = "", + var active: Boolean = true, + var orderTag: Long = 0, + @TypeConverters(ListStringConverter::class) + var images: List = ArrayList(), + @TypeConverters(ImageInfoConverter::class) + @SerializedName("images_info") + var imagesInfo: List = ArrayList(), + @TypeConverters(CommunityVideoConverter::class) + var videos: List = ArrayList(), + var count: Count = Count(), + var community: CommunityEntity = CommunityEntity(), + var time: TimeEntity? = TimeEntity(), + var user: UserEntity = UserEntity(), + @Ignore + var read: Boolean = true, + @Ignore + var me: MeEntity = MeEntity(), + @Ignore + var commentable: Boolean = true, + @Ignore + var bbs: CommunityEntity = CommunityEntity(), + var des: String = "", + var url: String = "", + @TypeConverters(VideoInfoConverter::class) + @SerializedName("video_info") + var videoInfo: VideoInfo = VideoInfo(), + var poster: String = "", + var length: Long = 0, + @Ignore + var type: String = "", + var status: String = "", + var content: String = "", + @SyncIgnore // questions里的vote当前entity的vote冲突 + @TypeConverters(QuestionsConverter::class) + @SerializedName("question") + var questions: Questions = Questions() ) : Parcelable { fun getPassVideos(): List { @@ -147,13 +147,15 @@ data class ArticleEntity( @Parcelize data class Count( - @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_COUNT, SyncFieldConstants.ANSWER_COMMENT_COUNT]) - var comment: Int = 0, - @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_VOTE_COUNT, SyncFieldConstants.ANSWER_VOTE_COUNT]) - var vote: Int = 0, - var favorite: Int = 0, - @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COMMENT_REPLY_COUNT]) - var reply: Int = 0) : Parcelable { + @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_COUNT, SyncFieldConstants.ANSWER_COMMENT_COUNT]) + var comment: Int = 0, + @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_VOTE_COUNT, SyncFieldConstants.ANSWER_VOTE_COUNT]) + var vote: Int = 0, + var favorite: Int = 0, + @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COMMENT_REPLY_COUNT]) + var reply: Int = 0, + var game: Int = 0 +) : Parcelable { @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COUNT]) var answer: Int = 0 @@ -181,7 +183,9 @@ data class Count( } @Parcelize -data class TimeEntity(var create: Long = 0, - var update: Long = 0, - var edit: Long = 0, - var upload: Long = 0) : Parcelable \ No newline at end of file +data class TimeEntity( + var create: Long = 0, + var update: Long = 0, + var edit: Long = 0, + var upload: Long = 0 +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index b57d93d3ea..7e1f8cbff5 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3264,4 +3264,28 @@ public interface ApiService { */ @PUT("api_go/teen_mode/password") Single putTeenModePassword(@Body RequestBody body); + + /** + * 游戏单列表 + */ + @GET("users/{user_id}/game_lists") + Observable> getGameCollectionList(@Path("user_id") String userId); + + /** + * 创建游戏单 + */ + @POST("game_lists") + Observable createGameCollection(@Body RequestBody body); + + /** + * 编辑游戏单 + */ + @PUT("game_lists/{game_list_id}") + Observable patchGameCollection(@Body RequestBody body, @Path("game_list_id") String id); + + /** + * 游戏单详情(用户编辑自己的游戏单时调用) + */ + @GET("game_lists/{game_list_id}?view=draft") + Observable getGameCollectionDetail(@Path("game_list_id") String id); } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java index 71e21a83aa..29501bc569 100644 --- a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java +++ b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java @@ -14,6 +14,7 @@ import com.gh.common.videolog.VideoRecordDao; import com.gh.common.videolog.VideoRecordEntity; import com.gh.gamecenter.entity.CommentDraft; import com.gh.gamecenter.entity.ForumEntity; +import com.gh.gamecenter.entity.GamesCollectionEntity; import com.gh.gamecenter.entity.HomePluggableFilterEntity; import com.gh.gamecenter.entity.SignEntity; import com.gh.gamecenter.entity.SimulatorGameRecordEntity; @@ -21,6 +22,7 @@ import com.gh.gamecenter.qa.entity.AnswerEntity; import com.gh.gamecenter.room.converter.ApkArrayListConverter; import com.gh.gamecenter.room.converter.MeConverter; import com.gh.gamecenter.room.converter.SimpleGameConverter; +import com.gh.gamecenter.room.converter.SimpleGameListConverter; import com.gh.gamecenter.room.converter.SimulatorConverter; import com.gh.gamecenter.room.converter.StringArrayListConverter; import com.gh.gamecenter.room.converter.TagStyleListConverter; @@ -28,6 +30,7 @@ import com.gh.gamecenter.room.converter.VideoInfoConverter; import com.gh.gamecenter.room.dao.AnswerDao; import com.gh.gamecenter.room.dao.CommentDraftDao; import com.gh.gamecenter.room.dao.ForumDao; +import com.gh.gamecenter.room.dao.GameCollectionDraftDao; import com.gh.gamecenter.room.dao.HomePluggableFilterDao; import com.gh.gamecenter.room.dao.SignDao; import com.gh.gamecenter.room.dao.SimulatorGameDao; @@ -45,7 +48,8 @@ import com.gh.gamecenter.video.upload.UploadEntity; HomePluggableFilterEntity.class, VideoRecordEntity.class, SimulatorGameRecordEntity.class, - ForumEntity.class}, version = 20, exportSchema = false) + ForumEntity.class, + GamesCollectionEntity.class}, version = 21, exportSchema = false) @TypeConverters({ StringArrayListConverter.class, TagStyleListConverter.class, @@ -53,7 +57,8 @@ import com.gh.gamecenter.video.upload.UploadEntity; ApkArrayListConverter.class, SimpleGameConverter.class, VideoInfoConverter.class, - MeConverter.class + MeConverter.class, + SimpleGameListConverter.class }) public abstract class AppDatabase extends RoomDatabase { @@ -73,6 +78,8 @@ public abstract class AppDatabase extends RoomDatabase { public abstract ForumDao forumDao(); + public abstract GameCollectionDraftDao gameCollectionDraftDao(); + private static AppDatabase sInstance; private static final String DATABASE_NAME = "gh-db"; @@ -218,6 +225,13 @@ public abstract class AppDatabase extends RoomDatabase { } }; + static final Migration MIGRATION_20_21 = new Migration(20, 21) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE GameCollectionDraft(primaryKey TEXT NOT NULL PRIMARY KEY, tags TEXT DEFAULT '', games TEXT DEFAULT '' , title TEXT NOT NULL DEFAULT '', intro TEXT NOT NULL DEFAULT '', cover TEXT NOT NULL DEFAULT '', display TEXT NOT NULL DEFAULT '')"); + } + }; + private static AppDatabase buildDatabase(Context context) { return Room.databaseBuilder(context, AppDatabase.class, DATABASE_NAME) .addMigrations( @@ -237,7 +251,8 @@ public abstract class AppDatabase extends RoomDatabase { MIGRATION_16_17, MIGRATION_17_18, MIGRATION_18_19, - MIGRATION_19_20 + MIGRATION_19_20, + MIGRATION_20_21 ) // 不允许主线程查询 .allowMainThreadQueries() diff --git a/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameListConverter.kt b/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameListConverter.kt new file mode 100644 index 0000000000..e439911df1 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameListConverter.kt @@ -0,0 +1,20 @@ +package com.gh.gamecenter.room.converter + +import androidx.room.TypeConverter +import com.gh.common.util.toJson +import com.gh.common.util.toObject +import com.gh.gamecenter.entity.ApkEntity +import com.gh.gamecenter.entity.SimpleGame + +class SimpleGameListConverter { + @TypeConverter + fun toSimpleGameString(data: ArrayList?): String { + return data?.toJson() ?: "" + } + + @TypeConverter + fun toSimpleGameEntity(data: String?): ArrayList { + val apkArray = data?.toObject() ?: arrayOf() + return ArrayList(apkArray.toList()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt new file mode 100644 index 0000000000..657a18f3c6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt @@ -0,0 +1,21 @@ +package com.gh.gamecenter.room.dao + +import androidx.room.* +import com.gh.gamecenter.entity.GamesCollectionEntity +import io.reactivex.Single + +@Dao +interface GameCollectionDraftDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addDraft(entity: GamesCollectionEntity) + + @Query("select * from GameCollectionDraft") + fun getAllDraft(): Single> + + @Query("select * from GameCollectionDraft") + fun getDrafts(): List + + @Delete() + fun delete(list: List) +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/ic_choose_game_del.webp b/app/src/main/res/drawable-xxxhdpi/ic_choose_game_del.webp new file mode 100644 index 0000000000000000000000000000000000000000..1f436ab9ee266ee97c2bc991e3402f555b97f099 GIT binary patch literal 1202 zcmV;j1Wo%=Nk&Gh1ONb6MM6+kP&il$0000G0000_002(_06|PpNYeoT00B?IIFh8c zVP+Q(PmotcL?EH2D(aPMIwFzZ&{m^QTxA!lCJYYSNK&M^KZa*!hR#34r`z2Qpf95T zj@&kKq#|ZE@2yfl!CYm{Xt6n*Z%=jS!)7sRG7Bi_&-O3;_0p`LSO`(QdZ0C}>c~8c zhbK5XDK?Li$rT=4YeTDCj=<;Yom*oa_Pkg-qm%@KTa4&fZeHz)GVv4nHBCLp1&i*Qfj+oQy{=MnCqe0#F^_ISc` z2EKC;@txC%?;J^d=Un1DhZEm9p`?N54AsEgm0oMex-c*L-pZ!>=1Vg5(kZS=%%gbd z+#FAe&9kUpISd$D)scme^k@4PQ{PLoeqsS-&3L&zoNrHc=fie6ZZdNP09H^qAg}@e z0FV#>odGIP08juvi9D7^r6ZytGd}15pb-gX2yg*}Couk7_|~|mRr>(vp?|*t_W{1o zpZ`n)c!&2h_X7~ZXr~KZ;NJk(5vT|8A2#n|U$7pK1RMB4h@Of<*p1+&IvDFy5V(AJ zt~Vz_v9BA-qZQ642HQC-IP9Af!uzxJy8pDZay?`yhSS|j7ytnN@yRkqc|I3I{|3c$ zv@0pIuE+@vKlTbd(QWl$0Ta2Z#_Q1jKRy2&bO?4QJ;i*6yvJ4@#UL;AZ1iWqZqCn% zjoDE3pKadIah0>7bb7ZNzY`NLh(VS7SoDhi9T*eu*@msKt^$SD8xTiFcT4#iRvClU zD`@xMQddO3y00C=#w(c{{wP2bPYZeigY!jg)kAuIhm@_%%b9V2ZlLb7RD<)sS-*yv zu*K8F93pi*WNh2uhX3r8U&8i|Bj3K>BSruVCIn2r?f4uGSs#j_# z!-7$_=XkdWt?f1|n|_B};Zfn9*^F?1r>&V^`(3SWo2qC{OtDGEXJwY(w}5*L%`*XA zV0Wan1|a=p@1_2mRSM|Qd)KlhX=7icq4$OQ`FzL)C2T!@Cnu&h4`QVEN>zRG=Fz9)f^pPV?Jg0sCPx1k4Kq5YBO%Dzx34=csdS%esCylI03@GE4FCWD literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_del.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_del.webp new file mode 100644 index 0000000000000000000000000000000000000000..1bc9c423b65892c9a0b9ec539e79654a05a4c3ed GIT binary patch literal 782 zcmV+p1M&P)Nk&En0{{S5MM6+kP&il$0000G0000#002J#06|PpNC^P|009@lIFh8c zS@sOx!_!C+5r$MC-u(~_%uTV1cTNZ=SH=buk|aq|Bq@s-x|qTK|IC!?%;=%rQ$+tK zK>e#shVACzy$y*hf5zbK3vw5XF0Pyw!>yR`0@l=15505+u24z^9D#Z>IDkq@2Q=#K z2I!1z?Oct^o@q6}c=L7ezCEMlg*)#176v}!5%*ycp2610II;ZaA4~R~vbuTz0 zc{woZMj)400JCleivRC&VAPF3E-yNv*1h12X|1>6`v{wR?sSarD zH9%*o0*=5|Z~&F5@B-G1X>s00r?@BR&-Us39R=w9TCD3h$#84th9c&l!PiFHz?fC*WbT zM7uhZ)#z{1$5C)2NC>WTqzYNi72v!v>O4pQ0RH@}oIKtCzt+6qpZdjAcXl)ycj#mj zeTM!i;`_(Cu+_9mpufAj@Jh~OEa>(S%K;69Q;(*f|E0W*rlu%7opFG438KT0g0`Ms zOf#5?B77tDC`QC6{`5Hgd}u1k&sm&!>gJw*-rz4r_%$~@jU{IH-+4cZzxE{=adZC# z9}y@2Ip~72;OC(zRA~Qb|NlR%q3k9HZFbxr_m19|BkKq!8y~bFG>aen#kj(lsLCpc zNi0ThWXve==9tvpi8qR?yyQ9e1^;9%|Bo<5s@ZfX5J|d6ajTM`gT%i0R64lU)(5c9 zk`*jJ^UOzF|JN`-tVQI6QrwSUOqbFCKv zv288qgu(Pyzm^_=?K2`xHteJh0s3tAk;2<-?m!kCBAVp(ytAvkUQDk~49j#!`?Cha M2lEP^SU>;(0QuH+HUIzs literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_draft.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_draft.webp new file mode 100644 index 0000000000000000000000000000000000000000..21687bca5db21d50f2358600db8e36c235f52a3f GIT binary patch literal 1726 zcmV;v20{5!Nk&Gt1^@t8MM6+kP&il$0000G0002D0037206|PpNcjN(00B3lG?Jt^ z%g+5EacCkU7D7)X`Z{#&@Y&iYuSFlC(Z&ssBuR1nYHiHd#GkEg5t`#g<(voE5Jk?-H;$Fu(6GxP0Nt*b$QIIH!S1WBtupGn}J%?5># znRP6k6h@{lg^fbV(3M4fQ!sBeomh1WXt#7Az9^)OKUONNKE!M* z-VcaTy!RDi)!iB~-uV_U%EJUNJ_-i37(DVBTA4hx6IQg(e2P^nuQo_gUJh?K-NM&= zjZ-aeE;^JtzT;OHjJT5bV~i+smI#$H?+%|onb{oBIheV;Jz#UNGQS}*H1g3Bmr_0* zUoc@TpI>!|bQBkDeM93`FT~NX!GfVUoK83}6}N3RV8G0Z^Fh-7J|=;D=einx8LPE? zbC@5`zv#?7l$urz(c}%3I2fe zG5N>(57@tepX49fx}JXG`x}0$mefiJI9C(Xgh|yfp_IxdL9M1Q=f6~A-_A!%NXYzz z%RhT0Wsp8gYCny%63Nlzqh*5CHHMhMt+Zd;4dQO-o<$Gl0^SEPmTnkCLfm2d?ht|h zv)v|aMNuMxX9;qc&eT;uGmLmb;2HotPfmIJUuKI>rf11glzL=b;B6y*pIPagZcdH2 zoduGv4P~uU#r{cJ-CtAX!tyI%0RHH$1pPXIx1a5@=Fae&iksov$tKM8nN@z69fDzr zf#Gtv&1i=q-q}m{yQUQ)&U2xOucuZ?lbrXSR8WxHhT5@tmj?;i8R^AHrP=#G2gSOk zWJEMF2D4yhQM)zJu5qK3n$SZC&pLwgq}RQCsPQTrG>trXRA!#F-xlpS)O%{pVBdj} zmcsck@DN~mzaipnYhiESRnEXo1M>1W0zb9qb@60#`+@>a>Qkja61B{Mm9D4^6e*DV zI_<6{of%!Ty(bf&;P;P@?9xHqRI>jQ=Gb+V@)ATk!1%Mq*AHi5m^LUvh$;)X3ne4G z0LJq0oCHFWVWgM#i$M5c`aBV31mA}yvyGLFE61Klo!ikC*xT(SIBF7g|JI2zV6Nm) zf!2>wPS&#)rjNW{fWG^}gx6EM?okT4q)PUw@H=b(;QOU9qbr?9lQbiNs(&vC!nFvh zJ`aDM7hO3ywSkQjEaG~~Hl5mbw8+X z=$v0DrNPGPYRIrsMB%zsDZTW@icY1u9xV`js#Cac+`5~HWeaEod(Ku7?s%H${5BVd z+tec_6acl_^1RumJA`BlzblvMDwfO+hHw@-+y%lD{2tR$KYQ|rJ)~)4kCA#qfF(!= zVL^HHKh+$5uicHKlQZO^7j5D%7__n&StR}cA_8BBdD|QWAJr9X=>oyL7yXr}VgDXR z?mRWO?2Sp{HD>NKHRN{Q<6hq;-;f+HT$s}C^Y@h?cPS7)-R6myQjFFxnOc3BeB1!T zuO>Sg&YM6~mZ==m)uas&kFG}t|Hek0+&|u|EO#%{q}lXwYM!=M^yNB@DoeC+WyQ?* zWk9e^hdPmPb8JPh!huptE$p>B@L@CeIM$gzv?QrU7z@Ah`?d!4 zy_0cCL23xr+{HBZ-fK;jR|e%pKCLy1Udu`Ui$BTEwyW!!A!+EFBT1hvPhvP|mJqoL UYz7C7L*@rgjmCJpaV+2f0Qcod$N&HU literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_edit.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_edit.webp new file mode 100644 index 0000000000000000000000000000000000000000..10417936578232dea98e23cd1a131029e49a044f GIT binary patch literal 972 zcmV;-12g!t#ymhMV-DvOEQ@U zl1+FZkLaCE3vZHDI1oQu-E>Uynn!xH3t2YqBv(j{aaG;Mh2*!6v{;`vO&O<1js0;?LJ)C2Wb*aP(u2avAzUTECh zcuqnlHdivT?$!i~JfYp3J`yS)WE}Ew=N3;~5`e~`ldW8QUxxjy(YYW1{`w%^ z?H}39?N`=&O>xb!LSmY<5{ISyaPur=!;RVcBOvTlboT`i5~35|y}oTOg5R5;9Xn_9 zEBu6g!q+OUo^`SB91%!dPoW1suTH_FvvV^Q1O6`t~9abQ>hY|heIaO;0XoA=yt<;l1dmws}i-;eV zYf27^QvBJZ-Hge~ldYU8yE8t|35KnS&XSSgC&xzBwJ7~>KXGdgPpChzPQ-k|paN0u zI9sJt$M2PRly*gF(sQ#_00oo(t85U|1|uY_+7+dV(opd&91DD5RbMIqA#es0)1$m3U>7BwklbHa zOwdv8&eeCInesuX6VK;=0T-W(vg|R3^56cL?um#MMQL40Xts(B^AW|2fQk0m_^E{J u3mDJBdh)r#%y0W$r-Vgf((Wq1EbuASO04w8f9oVZf`a9-y?u^~*Z=^W+TAk% literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_fail.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_fail.webp new file mode 100644 index 0000000000000000000000000000000000000000..96b7d50a9ec871f20785da922fdd0001854587e9 GIT binary patch literal 3394 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_pending.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_pending.webp new file mode 100644 index 0000000000000000000000000000000000000000..03c38aeb9e85e0c51e2fc58a21e03ea18a718a88 GIT binary patch literal 3208 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_menu_game_collection_square.webp b/app/src/main/res/drawable-xxxhdpi/ic_menu_game_collection_square.webp new file mode 100644 index 0000000000000000000000000000000000000000..f7f219813a0deeb67f610ff1fc545717f516364e GIT binary patch literal 2176 zcmV-`2!HodNk&F^2mkfOBuK}6)`=a6RzadPr(-gA^&6kF~EL0e?V*7Hbav1Hizx$nwt2= zwr$(C?V0u1wr#EbMR!-Vt*ZJ@8JQ6o)knK05&fS4{O`WCAZjm&f+-37=FyFYrXr~= z32HBhC=niM@Ys>x9BhoMG$Im2M7c2CcHOu|KiZcRBq9<-1RDo_z4o0&2_r`g=|Z`% zJNxSLKWs@}xdw5Wb#zJ8j$S7bucN!1wqn!~*jk^*EV+iJ^>o3yqc#lL1HX&eDVfWl z z_kUsuTaVAoQuN);l9{>E+#I-uX2I&Z_rnx{Q6z|nqRfB4dGX%FSeMWwSpI7_MQ$_^ z&UB)VgGWXVT-F=xvKO{9DUw5N6zaj7S}ZHnAyw#lyr3w@6EJ1NtguEbLGc>aOQEVr z(?IXTlN-F%rz)!KV^ZZ+ix%E6Xu@7^`9Vc?aol{KCR>mo>NKvC3R{mAd16u!TUUWJ z1u=?QGXM3k{khJHX7f%q31D(_eZKZW4+kdSF_DIf_(SoIZxsVSMh$?t3BV)j;YdS; zJrkCX$fYv}xVkB*!vXBFuov9Ddt9W11r&W|g!FU~&)}DNkyplMN-?C4~?8x4jPe zlpd)v!sLe(cA@>hJ_u=s$r344Li#}LOJ-e-PfRLeAKDc}{Q$9sw5zHy13+?i$O@q- zgmyujBtO6e076CelAWb6l#&^C2+J_L=a?8vX67OocLA}36!jI>R3CZ*-_1xE ztU#I(E6uP8FvW`$O#MD631xDlB_Ot~7t{$c z0HP_{>B0a&D%q5b%W-+*iGAt%n_bIh!u7G2Vi?U2Br3){v8J_X-+va|pTgNe!Db@= zbXJTld*e@1fRuug;QfFjoBY7*A&P|StMg5y2~y|6@D(_xL;07X7b6wuaBAN}m!u$x z_JW8Ae*gTHqswa58$;)3Hdmm}(bNZ}>4J#bz}8<^0Km8{P(L?Rcz?bM7&AT-R=@0Y zAr@*s2(HgzdfU2yi{z~g>n;jPCKEvpX~m(+Ke3Ehh@fA`9%Br8h76*Y}X z&C_$*1+ueJq5bBB4>*72hoHH7$t*e4N^~7OuzOlEj6d52U+-;Rv#_N`5i_%7=5i>j z{8 z=#jp=50w++JjT*ZjV?@2HuM1fMj>@#R_x0nDFdvc>Dru)YU_IxD$h-hp$las_&_;P zP{j~)C_wEZD(Jja=-@6zj(EwYR7J>y0j*(stZR*HoFs_TQM0XuGTDS#iz6_zR{bK= za32S@i@v}~-Apo67wjx8GGTBz%r z^@FzS&k(zz(`rFExm zvd}I0At2IyFfMg2$oRIh?A+{g>|-@LtneJbP|k%#esu7psR=cQ?7f0--SaR6P4GiE zwWLSK&_Q&`{s^Wk|CZ@d;WK|e2$1Ijj846F9vUwMb2dr9mXD+SwU2JLtgu)bFl*}! zrK0tAJxKZ~t%?i!5tmbT)?rqEradc{oJx{IT#o4HH+AF)A>XY+GPdG!{@0-_P;(Ei zkmuu>nef@ZOWVMv{EVI_TGAZqLyv^lF+x}_3IKA@^eT%%_f;wNIRoR`!%g$>G%gV~ zU1_UD{^;Kna79aYq}6T=4axW5x6fzG>>n*iufYgwg{RC)FDk!A9cwhZrxLe(0002P C_8K$* literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/bg_my_game_collection_btn.xml b/app/src/main/res/drawable/bg_my_game_collection_btn.xml new file mode 100644 index 0000000000..f4e094865a --- /dev/null +++ b/app/src/main/res/drawable/bg_my_game_collection_btn.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_game_collection_edit.xml b/app/src/main/res/layout/activity_game_collection_edit.xml index 5c75cdb64b..bc46d99ced 100644 --- a/app/src/main/res/layout/activity_game_collection_edit.xml +++ b/app/src/main/res/layout/activity_game_collection_edit.xml @@ -187,9 +187,8 @@ android:layout_marginTop="16dp" android:layout_marginRight="16dp" android:background="@null" - android:hint="为游戏单起一个 20字以内 的标题吧 ~" + android:hint="为游戏单起一个20字以内的标题吧~" android:inputType="text" - android:maxLength="20" android:textColor="@color/text_333333" android:textColorHint="@color/text_cccccc" android:textSize="16sp" /> @@ -214,7 +213,6 @@ android:gravity="top|left" android:hint="详细的介绍可以为你的游戏单加分哦(至少10个字)" android:inputType="textMultiLine" - android:maxLength="200" android:textColor="@color/text_333333" android:textColorHint="@color/text_cccccc" android:textSize="16sp" /> @@ -256,7 +254,7 @@ android:textSize="12sp" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_choose_games.xml b/app/src/main/res/layout/item_choose_games.xml index 3cd59056e4..026f1b19ae 100644 --- a/app/src/main/res/layout/item_choose_games.xml +++ b/app/src/main/res/layout/item_choose_games.xml @@ -22,6 +22,7 @@ android:layout_width="20dp" android:layout_height="20dp" android:layout_marginLeft="10dp" + android:src="@drawable/ic_choose_game_del" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -32,7 +33,6 @@ android:layout_marginLeft="12dp" android:layout_marginTop="2dp" android:includeFontPadding="false" - android:text="御龙正版授权" android:textColor="@color/text_333333" android:textStyle="bold" app:layout_constraintStart_toEndOf="@+id/gameIcon" diff --git a/app/src/main/res/layout/item_my_game_collection.xml b/app/src/main/res/layout/item_my_game_collection.xml new file mode 100644 index 0000000000..0c4722fb2f --- /dev/null +++ b/app/src/main/res/layout/item_my_game_collection.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_my_game_collection.xml b/app/src/main/res/menu/menu_my_game_collection.xml new file mode 100644 index 0000000000..cf96d0c461 --- /dev/null +++ b/app/src/main/res/menu/menu_my_game_collection.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file From 1f025f3556db68a927878044978e1d25d0e79fe2 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Fri, 12 Nov 2021 12:19:36 +0800 Subject: [PATCH 09/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=88=91=E7=9A=84=E5=85=89=E7=8E=AF-?= =?UTF-8?q?=E6=88=91=E7=9A=84=E6=B8=B8=E6=88=8F=E5=8D=95=20https://git.ghz?= =?UTF-8?q?s.com/pm/halo-app-issues/-/issues/1603?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/GamesCollectionEntity.kt | 20 +- .../com/gh/gamecenter/entity/TagInfoEntity.kt | 12 + .../gamecollection/MyGameCollectionAdapter.kt | 28 +- .../MyGameCollectionFragment.kt | 38 ++- .../MyGameCollectionViewHolder.kt | 82 ++++++ .../MyGameCollectionViewModel.kt | 45 ++- .../publish/GameCollectionEditActivity.kt | 27 +- .../publish/GameCollectionEditViewModel.kt | 27 +- .../retrofit/service/ApiService.java | 20 +- .../com/gh/gamecenter/room/AppDatabase.java | 4 +- .../room/converter/TagInfoListConverter.kt | 21 ++ .../room/dao/GameCollectionDraftDao.kt | 2 +- .../fragment_my_game_collection_list.xml | 2 +- app/src/main/res/layout/item_choose_games.xml | 2 + .../layout/item_game_collection_flex_tag.xml | 26 ++ .../res/layout/item_my_game_collection.xml | 275 ++++++++++-------- 16 files changed, 463 insertions(+), 168 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/room/converter/TagInfoListConverter.kt create mode 100644 app/src/main/res/layout/item_game_collection_flex_tag.xml diff --git a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt index 2d2e7d4749..9b312b7669 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt @@ -5,10 +5,11 @@ import androidx.room.Entity import androidx.room.Ignore import androidx.room.PrimaryKey import androidx.room.TypeConverters +import com.gh.gamecenter.R import com.gh.gamecenter.qa.entity.Count import com.gh.gamecenter.qa.entity.TimeEntity -import com.gh.gamecenter.room.converter.ListStringConverter import com.gh.gamecenter.room.converter.SimpleGameListConverter +import com.gh.gamecenter.room.converter.TagInfoListConverter import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize @@ -20,8 +21,8 @@ class GamesCollectionEntity( var id: String = "", @PrimaryKey var primaryKey: String = "", // db key - @TypeConverters(ListStringConverter::class) - var tags: ArrayList? = null, + @TypeConverters(TagInfoListConverter::class) + var tags: ArrayList? = null, @TypeConverters(SimpleGameListConverter::class) var games: ArrayList? = null, var title: String = "", @@ -40,4 +41,15 @@ class GamesCollectionEntity( var user: User? = null, @Ignore var me: MeEntity? = null, -) : Parcelable \ No newline at end of file +) : Parcelable { + + fun getStatusLabelRes(): Int { + return when { + display == "self_only" && status == "draft" -> R.drawable.ic_game_collection_private + status == "draft" -> R.drawable.ic_game_collection_draft + status == "pending" -> R.drawable.ic_game_collection_pending + status == "failed" -> R.drawable.ic_game_collection_fail + else -> -1 + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt new file mode 100644 index 0000000000..f4c3e67bb0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt @@ -0,0 +1,12 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class TagInfoEntity( + @SerializedName("_id") + var id: String = "", + var name: String = "" +) : Parcelable diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt index 2fee46c81a..d508cc2ce5 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt @@ -4,17 +4,15 @@ import android.content.Context import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import com.gh.base.BaseRecyclerViewHolder import com.gh.common.constant.ItemViewType import com.gh.common.util.DisplayUtils import com.gh.common.util.toBinding import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.databinding.ItemMyGameCollectionBinding import com.gh.gamecenter.entity.GamesCollectionEntity -class MyGameCollectionAdapter(context: Context) : ListAdapter(context) { +class MyGameCollectionAdapter(context: Context, val mViewModel: MyGameCollectionViewModel) : ListAdapter(context) { override fun getItemViewType(position: Int): Int { return if (position == itemCount - 1) { @@ -26,16 +24,30 @@ class MyGameCollectionAdapter(context: Context) : ListAdapter MyGameCollectionViewHolder(parent.toBinding()) - ItemViewType.ITEM_FOOTER -> FooterViewHolder(parent.toBinding()) - else -> MyGameCollectionViewHolder(parent.toBinding()) + ItemViewType.ITEM_BODY -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel) + ItemViewType.ITEM_FOOTER -> { + FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + else -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel) } } + override fun areItemsTheSame(oldItem: GamesCollectionEntity?, newItem: GamesCollectionEntity?): Boolean { + return oldItem == newItem || oldItem?.id == newItem?.id + } + + override fun areContentsTheSame(oldItem: GamesCollectionEntity?, newItem: GamesCollectionEntity?): Boolean { + return oldItem?.id == newItem?.id + && oldItem?.title == newItem?.title + && oldItem?.intro == newItem?.intro + && oldItem?.cover == newItem?.cover + && oldItem?.display == newItem?.display + } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is MyGameCollectionViewHolder -> { - + val entity = mEntityList[position] + holder.bindGameCollectionItem(entity) } is FooterViewHolder -> { holder.initItemPadding() @@ -52,6 +64,4 @@ class MyGameCollectionAdapter(context: Context) : ListAdapter(binding.root) } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt index 828d06eb37..3af8c8823a 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt @@ -3,20 +3,26 @@ package com.gh.gamecenter.gamecollection import android.os.Bundle import android.view.MenuItem import android.view.View +import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.showRegulationTestDialogIfNeeded +import com.gh.common.view.VerticalItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.databinding.FragmentMyGameCollectionListBinding import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode class MyGameCollectionFragment : ListFragment() { private var mAdapter: MyGameCollectionAdapter? = null private lateinit var mBinding: FragmentMyGameCollectionListBinding - override fun getLayoutId(): Int = R.layout.fragment_my_game_collection_list + override fun getLayoutId(): Int = 0 override fun getInflatedLayout(): View { return FragmentMyGameCollectionListBinding.inflate(layoutInflater, null, false).apply { @@ -34,7 +40,7 @@ class MyGameCollectionFragment : ListFragment { - return mAdapter ?: MyGameCollectionAdapter(requireContext()).apply { + return mAdapter ?: MyGameCollectionAdapter(requireContext(), mListViewModel).apply { mAdapter = this } } @@ -44,6 +50,22 @@ class MyGameCollectionFragment : ListFragment(binding.root) { + + fun bindGameCollectionItem(entity: GamesCollectionEntity) { + binding.entity = entity + initTagsUI(entity.tags ?: arrayListOf()) + val statusLabelRes = entity.getStatusLabelRes() + if (statusLabelRes > 0) { + binding.statusView.setImageResource(statusLabelRes) + } + + binding.statusView.setOnClickListener { + when { + entity.display == "self_only" && entity.status == "draft" -> { + DialogHelper.showDialog(binding.root.context, "仅自己可见", "游戏单开启“仅自己可见”后,将不会对其他用户展示,您可以通过关闭仅自己可见或者投稿分享游戏单", "我知道了", "", { + + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + entity.status == "pending" -> { + DialogHelper.showDialog(binding.root.context, "审核中", "游戏单会在1-2个工作日内审核完成,请您耐心等候", "我知道了", "", { + + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + entity.status == "failed" -> { + DialogHelper.showDialog(binding.root.context, "未通过", "您的游戏单可能含有违规内容或语意不明确,请您仔细检查后再次尝试提交", "我知道了", "", { + + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + } + binding.root.setOnClickListener { + if (entity.status == "pass" || (entity.display == "self_only" && entity.status == "draft")) { + //TODO:进入游戏单详情 + + } else { + binding.statusView.performClick() + } + } + + binding.publishBtn.setOnClickListener { + if ((entity.count?.game ?: 0) >= 8) { + DialogHelper.showDialog(binding.root.context, "温馨提示", "投稿通过后,将自动关闭“仅自己可见”,所有用户都能浏览到游戏单,确定投稿?", "确定", "取消", { + mViewModel.publishGameCollection(entity) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } else { + DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场哦~", "添加游戏", "我知道了", { + binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity)) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + binding.editBtn.setOnClickListener { + binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity)) + } + binding.deleteBtn.setOnClickListener { + DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单删除后将无法恢复,是否确认删除", "确定", "取消", { + mViewModel.deleteGameCollection(entity) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + + private fun initTagsUI(tags: ArrayList) { + binding.gameCollectionTagsContainer.removeAllViews() + tags.forEachIndexed { index, tag -> + val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(binding.root.context), null, false) + tagBinding.tagNameTv.text = tag.name + tagBinding.divider.goneIf(index == tags.size - 1) + binding.gameCollectionTagsContainer.addView(tagBinding.root) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt index 46bc427ca0..a10588beaf 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewModel.kt @@ -1,21 +1,60 @@ package com.gh.gamecenter.gamecollection import android.app.Application +import androidx.lifecycle.MutableLiveData +import com.gh.common.util.ToastUtils +import com.gh.common.util.observableToMain import com.gh.gamecenter.baselist.ListViewModel import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.Observable +import okhttp3.ResponseBody +import retrofit2.HttpException class MyGameCollectionViewModel(application: Application) : ListViewModel(application) { - + private val mApi = RetrofitManager.getInstance(HaloApp.getInstance()).api + val deleteLiveData = MutableLiveData() + val publishLiveData = MutableLiveData() override fun provideDataObservable(page: Int): Observable>? { - return RetrofitManager.getInstance(HaloApp.getInstance()).api - .getGameCollectionList(UserManager.getInstance().userId) + return mApi.getGameCollectionList(UserManager.getInstance().userId) } override fun mergeResultLiveData() { mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } } + + fun deleteGameCollection(entity: GamesCollectionEntity) { + mApi.deleteGameCollection(entity.id) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + deleteLiveData.postValue(entity) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + ToastUtils.showToast("删除失败") + } + }) + } + + fun publishGameCollection(entity: GamesCollectionEntity){ + mApi.deleteGameCollection(entity.id) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + publishLiveData.postValue(entity) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + ToastUtils.showToast("投稿失败") + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index bbcf2d5f20..0ef1aea648 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -14,6 +14,8 @@ import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.SimpleGame +import com.gh.gamecenter.entity.TagInfoEntity +import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel import com.gh.gamecenter.gamecollection.square.GameCollectionTagSelectActivity @@ -21,6 +23,7 @@ import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.qa.editor.LocalMediaActivity import com.zhihu.matisse.Matisse import com.zhihu.matisse.internal.utils.PathUtils +import org.greenrobot.eventbus.EventBus class GameCollectionEditActivity : ToolBarActivity() { @@ -87,22 +90,24 @@ class GameCollectionEditActivity : ToolBarActivity() { } } - private fun initData() { + private fun initData(isFirst: Boolean = true) { mBinding.gameCollectionTitleEt.filters = arrayOf(TextHelper.getFilter(20, "最多输入20个字")) mBinding.gameCollectionIntroduceEt.filters = arrayOf(TextHelper.getFilter(200, "最多输入200个字")) mViewModel.gameCollectionPatch?.run { + mViewModel.imageUrl = cover if (status.isNotEmpty() && status != "draft") { setNavigationTitle("编辑游戏单") } - mViewModel.imageUrl = cover initPosterUI() mBinding.gameCollectionTitleEt.setText(title) mBinding.gameCollectionTitleEt.setSelection(title.length) mBinding.gameCollectionIntroduceEt.setText(intro) mBinding.gameCollectionIntroduceEt.setSelection(intro.length) mBinding.selfOnlyCb.isChecked = display == "self_only" - mViewModel.getGameCollectionDetail(id) + if (isFirst) { + mViewModel.getGameCollectionDetail(id) + } } ?: mViewModel.getDraft() } @@ -132,6 +137,7 @@ class GameCollectionEditActivity : ToolBarActivity() { mViewModel.postLiveData.observe(this) { if (it.status == Status.SUCCESS) { toast("提交成功") + EventBus.getDefault().post(EBReuse("createGameCollectionSuccess")) finish() } else { ErrorHelper.handleError(this, it.exception?.response()?.errorBody()?.string()) @@ -141,6 +147,7 @@ class GameCollectionEditActivity : ToolBarActivity() { mViewModel.gameCollectionPatch = it mViewModel.gameCollectionPatch?.run { //TODO:initTags + initData(false) mViewModel.tags = tags ?: arrayListOf() val simpleGames = games?.map { game -> game.toGameEntity() }?.toList() mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames)) @@ -149,7 +156,7 @@ class GameCollectionEditActivity : ToolBarActivity() { mViewModel.draftLiveData.observe(this) { mViewModel.gameCollectionPatch = it mViewModel.gameCollectionPatch?.run { - initData() + initData(false) //TODO:initTags mViewModel.tags = tags ?: arrayListOf() val simpleGames = games?.map { game -> game.toGameEntity() } @@ -161,7 +168,9 @@ class GameCollectionEditActivity : ToolBarActivity() { override fun onMenuItemClick(item: MenuItem?): Boolean { item?.run { if (itemId == R.id.menu_game_collection_post) { - verifyData() + debounceActionWithInterval(R.id.menu_game_collection_post, 3000L) { + verifyData() + } } } return super.onMenuItemClick(item) @@ -209,7 +218,7 @@ class GameCollectionEditActivity : ToolBarActivity() { override fun handleBackPressed(): Boolean { val patch = mViewModel.gameCollectionPatch - if (patch != null && patch.status.isNotEmpty() && patch.status != "draft") { + if (patch != null && patch.status.isNotEmpty() && (patch.status != "draft" || patch.display == "self_only")) { DialogHelper.showDialog(this, "温馨提示", "退出将不会保留本次游戏单编辑的内容,是否确定退出", "确定", "取消", { finish() }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) @@ -239,6 +248,10 @@ class GameCollectionEditActivity : ToolBarActivity() { } private fun verifyData() { + //TODO:测试数据 + mViewModel.tags.clear() + mViewModel.tags.add(TagInfoEntity(id = "6184ec6d92109de55a4276eb")) + if (mViewModel.imageUrl.isEmpty()) { toast("请上传游戏单的封面") return @@ -265,7 +278,7 @@ class GameCollectionEditActivity : ToolBarActivity() { "id" to (mViewModel.gameCollectionPatch?.id ?: ""), "title" to title, "intro" to introduce, - "tag_ids" to mViewModel.tags, + "tag_ids" to mViewModel.tags.map { it.id }.toList(), "cover" to mViewModel.imageUrl, "display" to if (isSelfOnly) "self_only" else "", "games" to games.map { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index bb97751b11..566b3ad07c 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -9,19 +9,21 @@ import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.runOnIoThread import com.gh.common.util.* import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.mvvm.Resource import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.room.AppDatabase -import com.google.gson.JsonObject import com.halo.assistant.HaloApp +import okhttp3.ResponseBody +import org.json.JSONObject import retrofit2.HttpException class GameCollectionEditViewModel(application: Application) : AndroidViewModel(application) { var imagePath = "" var imageUrl = "" - var tags = ArrayList() + var tags = ArrayList() var gameCollectionPatch: GamesCollectionEntity? = null var uploadImageSuccessLiveData = MutableLiveData() var detailLiveData = MutableLiveData() @@ -50,9 +52,7 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a fun uploadContent(requestMap: HashMap) { val id = requestMap["id"]?.toString() ?: "" - if (id.isNotEmpty()) { - requestMap.remove("id") - } + requestMap.remove("id") postContent(requestMap, id) } @@ -66,12 +66,16 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a } observable .compose(observableToMain()) - .subscribe(object : Response() { - override fun onResponse(response: JsonObject?) { + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { super.onResponse(response) - val id = response?.get("_id")?.asString + val data = response?.string() + val id = if (!data.isNullOrEmpty()) { + JSONObject(data).optString("_id") + } else "" postLiveData.postValue(Resource.success(id)) processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + deleteAllDraft() } override fun onFailure(e: HttpException?) { @@ -124,4 +128,11 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a mDraftDao.addDraft(entity) } } + + fun deleteAllDraft(){ + runOnIoThread { + val drafts = mDraftDao.getDrafts() + mDraftDao.delete(drafts) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 7e1f8cbff5..89a56ca2e1 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3266,26 +3266,38 @@ public interface ApiService { Single putTeenModePassword(@Body RequestBody body); /** - * 游戏单列表 + * 游戏单列表(返回所有数据) */ - @GET("users/{user_id}/game_lists") + @GET("users/{user_id}/game_lists?view=draft") Observable> getGameCollectionList(@Path("user_id") String userId); /** * 创建游戏单 */ @POST("game_lists") - Observable createGameCollection(@Body RequestBody body); + Observable createGameCollection(@Body RequestBody body); /** * 编辑游戏单 */ @PUT("game_lists/{game_list_id}") - Observable patchGameCollection(@Body RequestBody body, @Path("game_list_id") String id); + Observable patchGameCollection(@Body RequestBody body, @Path("game_list_id") String id); /** * 游戏单详情(用户编辑自己的游戏单时调用) */ @GET("game_lists/{game_list_id}?view=draft") Observable getGameCollectionDetail(@Path("game_list_id") String id); + + /** + * 删除单条游戏单数据 + */ + @DELETE("game_lists/{game_list_id}") + Observable deleteGameCollection(@Path("game_list_id") String id); + + /** + * 提交审核 (投稿) + */ + @POST("game_lists/{game_list_id}:submit") + Observable publishGameCollection(@Path("game_list_id") String id); } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java index 29501bc569..18b7fcb714 100644 --- a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java +++ b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java @@ -25,6 +25,7 @@ import com.gh.gamecenter.room.converter.SimpleGameConverter; import com.gh.gamecenter.room.converter.SimpleGameListConverter; import com.gh.gamecenter.room.converter.SimulatorConverter; import com.gh.gamecenter.room.converter.StringArrayListConverter; +import com.gh.gamecenter.room.converter.TagInfoListConverter; import com.gh.gamecenter.room.converter.TagStyleListConverter; import com.gh.gamecenter.room.converter.VideoInfoConverter; import com.gh.gamecenter.room.dao.AnswerDao; @@ -58,7 +59,8 @@ import com.gh.gamecenter.video.upload.UploadEntity; SimpleGameConverter.class, VideoInfoConverter.class, MeConverter.class, - SimpleGameListConverter.class + SimpleGameListConverter.class, + TagInfoListConverter.class }) public abstract class AppDatabase extends RoomDatabase { diff --git a/app/src/main/java/com/gh/gamecenter/room/converter/TagInfoListConverter.kt b/app/src/main/java/com/gh/gamecenter/room/converter/TagInfoListConverter.kt new file mode 100644 index 0000000000..9f07ce30c4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/converter/TagInfoListConverter.kt @@ -0,0 +1,21 @@ +package com.gh.gamecenter.room.converter + +import androidx.room.TypeConverter +import com.gh.common.util.toJson +import com.gh.common.util.toObject +import com.gh.gamecenter.entity.ApkEntity +import com.gh.gamecenter.entity.SimpleGame +import com.gh.gamecenter.entity.TagInfoEntity + +class TagInfoListConverter { + @TypeConverter + fun toTagInfoString(data: ArrayList?): String { + return data?.toJson() ?: "" + } + + @TypeConverter + fun toTagInfoEntity(data: String?): ArrayList { + val array = data?.toObject() ?: arrayOf() + return ArrayList(array.toList()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt index 657a18f3c6..deb4344d53 100644 --- a/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt +++ b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt @@ -16,6 +16,6 @@ interface GameCollectionDraftDao { @Query("select * from GameCollectionDraft") fun getDrafts(): List - @Delete() + @Delete fun delete(list: List) } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_my_game_collection_list.xml b/app/src/main/res/layout/fragment_my_game_collection_list.xml index 5761da8e5e..aaa8d28c1a 100644 --- a/app/src/main/res/layout/fragment_my_game_collection_list.xml +++ b/app/src/main/res/layout/fragment_my_game_collection_list.xml @@ -35,7 +35,7 @@ android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" - android:visibility="visible"> + android:visibility="gone"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_my_game_collection.xml b/app/src/main/res/layout/item_my_game_collection.xml index 0c4722fb2f..d448043b36 100644 --- a/app/src/main/res/layout/item_my_game_collection.xml +++ b/app/src/main/res/layout/item_my_game_collection.xml @@ -1,143 +1,162 @@ - + - + + + + + + + + - - + android:paddingLeft="16dp" + android:paddingRight="16dp"> - + - + - + - + - + - - + - + - - + + - + - - - + + - \ No newline at end of file + + + + + + + + \ No newline at end of file From 58b37f8922e7a434c1cf94b489b8df5315ce8dc8 Mon Sep 17 00:00:00 2001 From: lyr <15622190878@163.com> Date: Sun, 14 Nov 2021 11:47:15 +0800 Subject: [PATCH 10/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E4=B8=BB=E9=A1=B5=EF=BC=88=E5=A4=A7=E8=87=B4=E5=AE=8C?= =?UTF-8?q?=E6=88=90=EF=BC=89=20https://git.ghzs.com/pm/halo-app-issues/-/?= =?UTF-8?q?issues/1591=EF=BC=8C=20=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E6=88=91?= =?UTF-8?q?=E7=9A=84=E6=94=B6=E8=97=8F=EF=BC=88=E5=A4=A7=E8=87=B4=E5=AE=8C?= =?UTF-8?q?=E6=88=90=EF=BC=89https://git.ghzs.com/pm/halo-app-issues/-/iss?= =?UTF-8?q?ues/1592?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameCollectionItemViewHolder.kt | 6 + .../collection/CollectionWrapperFragment.java | 22 +- .../collection/GamesCollectionAdapter.kt | 100 +++++++ .../collection/GamesCollectionFragment.kt | 62 ++++ .../collection/GamesCollectionViewModel.kt | 33 +++ .../home/game/UserGameFragment.kt | 109 ++++--- .../retrofit/service/ApiService.java | 6 + .../game_collection_comment.webp | Bin 0 -> 768 bytes .../game_collection_vote.webp | Bin 0 -> 544 bytes .../main/res/drawable-xxxhdpi/ic_chosen.webp | Bin 0 -> 3744 bytes .../res/drawable-xxxhdpi/ic_chosen_big.webp | Bin 0 -> 3920 bytes .../ic_game_collection_des.webp | Bin 0 -> 268 bytes .../ic_game_collection_des_blue.webp | Bin 0 -> 670 bytes .../ic_game_collection_des_gray.webp | Bin 0 -> 318 bytes ...ame_collection_detail_share_bottom_bar.png | Bin 0 -> 2745 bytes .../ic_game_collection_more.webp | Bin 0 -> 262 bytes .../ic_game_collection_square.webp | Bin 0 -> 1474 bytes .../res/drawable-xxxhdpi/ic_official.webp | Bin 0 -> 3060 bytes .../res/drawable-xxxhdpi/ic_official_big.webp | Bin 0 -> 3176 bytes .../pic_game_collection_only_myself.webp | Bin 0 -> 2940 bytes ...ckground_shape_white_alpha_10_radius_2.xml | 6 + .../res/drawable/bg_border_blue_radius_2.xml | 13 + .../bg_shape_f2_alpha_10_radius_8.xml | 6 + .../res/drawable/bg_shape_f5_radius_2.xml | 6 + .../res/drawable/button_round_1a2496ff.xml | 9 + .../drawable/button_round_black_alpha_30.xml | 9 + .../drawable/button_round_white_alpha_10.xml | 9 + .../game_collection_detail_poster_mask.xml | 8 + .../drawable/game_collection_poster_mask.xml | 11 + .../game_collection_vote_selector.xml | 6 + .../progressbar_played_game_blue_style.xml | 28 ++ .../progressbar_played_game_style.xml | 28 ++ .../layout/fragment_collection_wrapper.xml | 35 +++ .../main/res/layout/fragment_user_game.xml | 25 +- .../main/res/layout/game_collection_item.xml | 275 ++++++++++++++++++ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 1 + 37 files changed, 766 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameCollectionItemViewHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt create mode 100644 app/src/main/res/drawable-xxxhdpi/game_collection_comment.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/game_collection_vote.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_chosen.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_chosen_big.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_des.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_des_blue.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_des_gray.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_detail_share_bottom_bar.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_more.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_square.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_official.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_official_big.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/pic_game_collection_only_myself.webp create mode 100644 app/src/main/res/drawable/background_shape_white_alpha_10_radius_2.xml create mode 100644 app/src/main/res/drawable/bg_border_blue_radius_2.xml create mode 100644 app/src/main/res/drawable/bg_shape_f2_alpha_10_radius_8.xml create mode 100644 app/src/main/res/drawable/bg_shape_f5_radius_2.xml create mode 100644 app/src/main/res/drawable/button_round_1a2496ff.xml create mode 100644 app/src/main/res/drawable/button_round_black_alpha_30.xml create mode 100644 app/src/main/res/drawable/button_round_white_alpha_10.xml create mode 100644 app/src/main/res/drawable/game_collection_detail_poster_mask.xml create mode 100644 app/src/main/res/drawable/game_collection_poster_mask.xml create mode 100644 app/src/main/res/drawable/game_collection_vote_selector.xml create mode 100644 app/src/main/res/drawable/progressbar_played_game_blue_style.xml create mode 100644 app/src/main/res/drawable/progressbar_played_game_style.xml create mode 100644 app/src/main/res/layout/fragment_collection_wrapper.xml create mode 100644 app/src/main/res/layout/game_collection_item.xml diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameCollectionItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameCollectionItemViewHolder.kt new file mode 100644 index 0000000000..24ebf431eb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameCollectionItemViewHolder.kt @@ -0,0 +1,6 @@ +package com.gh.gamecenter.adapter.viewholder + +import com.gh.base.BaseRecyclerViewHolder +import com.gh.gamecenter.databinding.GameCollectionItemBinding + +class GameCollectionItemViewHolder(var binding: GameCollectionItemBinding) : BaseRecyclerViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java index b0b4fcaae2..d0ce7b7a93 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java @@ -3,8 +3,10 @@ package com.gh.gamecenter.collection; import android.os.Bundle; import com.gh.base.fragment.BaseFragment_TabLayout; +import com.gh.common.util.EntranceUtils; import com.gh.common.util.MtaHelper; import com.gh.gamecenter.R; +import com.gh.gamecenter.manager.UserManager; import java.util.List; @@ -29,27 +31,41 @@ public class CollectionWrapperFragment extends BaseFragment_TabLayout { return fragment; } + @Override + protected int getLayoutId() { + return R.layout.fragment_collection_wrapper; + } + @Override protected void initTabTitleList(List tabTitleList) { tabTitleList.add(getString(R.string.answer)); tabTitleList.add(getString(R.string.collection_article)); + tabTitleList.add(getString(R.string.video)); + tabTitleList.add(getString(R.string.game_collection)); tabTitleList.add(getString(R.string.collection_toolkit)); tabTitleList.add(getString(R.string.collection_info)); - tabTitleList.add(getString(R.string.video)); } @Override protected void initFragmentList(List fragments) { fragments.add(new AnswerFragment().with(getArguments())); fragments.add(new CommunityArticleFragment().with(getArguments())); - fragments.add(new ToolsFragment().with(getArguments())); - fragments.add(new ArticleFragment().with(getArguments())); Bundle arguments = getArguments(); if (arguments != null) arguments.putString("videoStyle", VideoFragment.VideoStyle.COLLECT.getValue()); fragments.add(new VideoFragment().with(arguments)); + Bundle gameCollectionArguments = getArguments(); + if (gameCollectionArguments != null) { + gameCollectionArguments.putString(EntranceUtils.KEY_USER_ID, UserManager.getInstance().getUserId()); + gameCollectionArguments.putString(EntranceUtils.KEY_TYPE, GamesCollectionFragment.TYPE_COLLECT); + } + fragments.add(new GamesCollectionFragment().with(arguments)); + + fragments.add(new ToolsFragment().with(getArguments())); + fragments.add(new ArticleFragment().with(getArguments())); + } @Override diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt new file mode 100644 index 0000000000..9f6251eaa5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt @@ -0,0 +1,100 @@ +package com.gh.gamecenter.collection + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.ItemViewType +import com.gh.common.util.DialogHelper +import com.gh.common.util.goneIf +import com.gh.common.util.safelyGetInRelease +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.adapter.viewholder.GameCollectionItemViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.manager.UserManager + +class GamesCollectionAdapter(context: Context, + private val mViewModel: GamesCollectionViewModel +): ListAdapter(context) { + + override fun getItemCount(): Int { + return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1 + } + + override fun getItemViewType(position: Int): Int { + return if (position == itemCount - 1) { + ItemViewType.ITEM_FOOTER + } else { + ItemViewType.ITEM_BODY + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_BODY -> GameCollectionItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.game_collection_item, parent, false)) + + else -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GameCollectionItemViewHolder -> { + val itemEntity = mEntityList[position] + holder.binding.run { + entity = itemEntity + executePendingBindings() + + moreIv.goneIf(itemEntity.user?.id != UserManager.getInstance().userId) + + when (itemEntity.stamp) { + "special_choice" -> { + tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + } + "official" -> { + tagIv.setBackgroundResource(R.drawable.ic_official_big) + } + } + + when (mViewModel.type) { + GamesCollectionFragment.TYPE_COLLECT -> { + userIcon.visibility = View.VISIBLE + userName.visibility = View.VISIBLE + timeTv.visibility = View.GONE + } + + GamesCollectionFragment.TYPE_USER -> { + userIcon.visibility = View.GONE + userName.visibility = View.GONE + timeTv.visibility = View.VISIBLE + } + } + + myselfTag.setOnClickListener { + DialogHelper.showDialog(mContext, "仅自己可见", "游戏单开启“仅自己可见”后,将不会对其他用户展示,您可以通过关闭仅自己可见或者投稿分享游戏单", "我知道了", "", { + + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + + gameOne.setOnClickListener { + GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(0)?.id, "") + } + gameTwo.setOnClickListener { + GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(1)?.id, "") + } + gameThree.setOnClickListener { + GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(2)?.id, "") + } + } + } + + is FooterViewHolder -> holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt new file mode 100644 index 0000000000..083307b33a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt @@ -0,0 +1,62 @@ +package com.gh.gamecenter.collection + +import android.os.Bundle +import android.view.View +import androidx.core.content.ContextCompat +import com.gh.common.util.* +import com.gh.common.util.EntranceUtils.KEY_TYPE +import com.gh.common.util.EntranceUtils.KEY_USER_ID +import com.gh.common.view.SpacingItemDecoration +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.entity.GamesCollectionEntity + +class GamesCollectionFragment: ListFragment() { + + private var mUserId = "" + private var mType = "" + private var mAdapter: GamesCollectionAdapter? = null + + override fun provideListViewModel() = viewModelProvider( + GamesCollectionViewModel.Factory(mUserId, mType) + ) + + override fun provideListAdapter() = mAdapter ?: GamesCollectionAdapter(requireContext(), mListViewModel).apply { mAdapter = this } + + override fun getItemDecoration() = SpacingItemDecoration(notDecorateTheFirstItem = mType == TYPE_USER, top = 16F.dip2px(), ) + + override fun onCreate(savedInstanceState: Bundle?) { + mUserId = arguments?.getString(KEY_USER_ID, "") ?: "" + mType = arguments?.getString(KEY_TYPE, "") ?: "" + super.onCreate(savedInstanceState) + mListRv?.run { + overScrollMode = View.OVER_SCROLL_NEVER + if (mType == TYPE_USER) setBackgroundColor(R.color.white.toColor()) + } + + } + + override fun onResume() { + if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged() + super.onResume() + } + + override fun onLoadEmpty() { + super.onLoadEmpty() + // RecyclerView 被隐藏的话会导致不能 AppBar 不能滑动 + if (mType == TYPE_USER) mListRv.visibility = View.VISIBLE + } + + override fun onLoadError() { + super.onLoadError() + if (mType == TYPE_USER) { + mListRv.visibility = View.VISIBLE + mReuseNoConn?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background)) + } + } + + companion object { + const val TYPE_COLLECT = "collect" + const val TYPE_USER = "user" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt new file mode 100644 index 0000000000..950e2719c1 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt @@ -0,0 +1,33 @@ +package com.gh.gamecenter.collection + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.Single + +class GamesCollectionViewModel(application: Application, var userId: String, var type: String) : + ListViewModel(application) { + + override fun provideDataObservable(page: Int) = null + + override fun provideDataSingle(page: Int): Single> { + return RetrofitManager.getInstance(getApplication()).api.getUserGameCollectionList(userId) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { + mResultLiveData.postValue(it) + } + } + + class Factory(private val mUserId: String, private val mType: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return GamesCollectionViewModel(HaloApp.getInstance().application, mUserId, mType) as T + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt index 2d12705a54..98a090f821 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt @@ -10,9 +10,9 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import com.gh.common.util.* -import com.gh.common.util.EntranceUtils.KEY_COMMENT_COUNT -import com.gh.common.util.EntranceUtils.KEY_USER_ID +import com.gh.common.util.EntranceUtils.* import com.gh.gamecenter.R +import com.gh.gamecenter.collection.GamesCollectionFragment import com.gh.gamecenter.databinding.FragmentUserGameBinding import com.gh.gamecenter.normal.NormalFragment @@ -20,12 +20,13 @@ class UserGameFragment: NormalFragment() { private var mBinding: FragmentUserGameBinding? = null private var mViewModel: UserGameViewModel? = null + private var mGamesCollectionFragment: GamesCollectionFragment? = null private var mPlayedGameFragment: UserPlayedGameFragment? = null private var mCommentFragment: UserCommentHistoryFragment? = null private var mUserId: String = "" private var mFilter: String = "全部" private var mCommentCount = 0 - private var mCurrentType = TYPE_PLAYED_GAME + private var mCurrentType = TYPE_GAME_COLLECTION override fun getLayoutId() = 0 @@ -36,7 +37,7 @@ class UserGameFragment: NormalFragment() { mUserId = arguments?.getString(KEY_USER_ID, "") ?: "" mCommentCount = arguments?.getInt(KEY_COMMENT_COUNT, 0) ?: 0 mViewModel = viewModelProvider(UserGameViewModel.Factory(mUserId)) - changeType(TYPE_PLAYED_GAME) + changeType(TYPE_GAME_COLLECTION) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -49,18 +50,20 @@ class UserGameFragment: NormalFragment() { mBinding?.run { commentType.text = "评论 $mCommentCount" - gameType.setOnClickListener { - if (mCurrentType == TYPE_PLAYED_GAME) return@setOnClickListener - mCurrentType = TYPE_PLAYED_GAME - updateTypeView(TYPE_PLAYED_GAME) - changeType(TYPE_PLAYED_GAME) - } + val typePairList = arrayListOf( + Pair(TYPE_GAME_COLLECTION, gameCollectionType), + Pair(TYPE_PLAYED_GAME, gameType), + Pair(TYPE_COMMENT, commentType) + ) - commentType.setOnClickListener { - if (mCurrentType == TYPE_COMMENT) return@setOnClickListener - mCurrentType = TYPE_COMMENT - updateTypeView(TYPE_COMMENT) - changeType(TYPE_COMMENT) + typePairList.forEach { pair -> + pair.second.setOnClickListener { + if (mCurrentType != pair.first) { + mCurrentType = pair.first + updateTypeView(pair.first) + changeType(pair.first) + } + } } commentSubType.setOnClickListener { @@ -71,35 +74,62 @@ class UserGameFragment: NormalFragment() { private fun updateTypeView(type: Int) { mBinding?.run { - if (type == TYPE_PLAYED_GAME) { - commentSubType.visibility = View.GONE - arrowIv.visibility = View.GONE - gameType.setBackgroundResource(R.drawable.button_round_f0f8ff) - gameType.setTextColor(R.color.theme_font.toColor()) - commentType.setBackgroundResource(R.drawable.button_round_fafafa) - commentType.setTextColor(R.color.text_333333.toColor()) - } else { - commentSubType.visibility = View.VISIBLE - arrowIv.visibility = View.VISIBLE - gameType.setBackgroundResource(R.drawable.button_round_fafafa) - gameType.setTextColor(R.color.text_333333.toColor()) - commentType.setBackgroundResource(R.drawable.button_round_f0f8ff) - commentType.setTextColor(R.color.theme_font.toColor()) + when (type) { + TYPE_GAME_COLLECTION -> { + commentSubType.visibility = View.GONE + arrowIv.visibility = View.GONE + gameCollectionType.setBackgroundResource(R.drawable.button_round_f0f8ff) + gameCollectionType.setTextColor(R.color.theme_font.toColor()) + gameType.setBackgroundResource(R.drawable.button_round_fafafa) + gameType.setTextColor(R.color.text_333333.toColor()) + commentType.setBackgroundResource(R.drawable.button_round_fafafa) + commentType.setTextColor(R.color.text_333333.toColor()) + } + TYPE_PLAYED_GAME -> { + commentSubType.visibility = View.GONE + arrowIv.visibility = View.GONE + gameCollectionType.setBackgroundResource(R.drawable.button_round_fafafa) + gameCollectionType.setTextColor(R.color.text_333333.toColor()) + gameType.setBackgroundResource(R.drawable.button_round_f0f8ff) + gameType.setTextColor(R.color.theme_font.toColor()) + commentType.setBackgroundResource(R.drawable.button_round_fafafa) + commentType.setTextColor(R.color.text_333333.toColor()) + } + else -> { + commentSubType.visibility = View.VISIBLE + arrowIv.visibility = View.VISIBLE + gameCollectionType.setBackgroundResource(R.drawable.button_round_fafafa) + gameCollectionType.setTextColor(R.color.text_333333.toColor()) + gameType.setBackgroundResource(R.drawable.button_round_fafafa) + gameType.setTextColor(R.color.text_333333.toColor()) + commentType.setBackgroundResource(R.drawable.button_round_f0f8ff) + commentType.setTextColor(R.color.theme_font.toColor()) + } } } } private fun changeType(type: Int) { - if (type == TYPE_PLAYED_GAME) { - mPlayedGameFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? UserPlayedGameFragment + when (type) { + TYPE_GAME_COLLECTION -> { + mGamesCollectionFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? GamesCollectionFragment + ?: GamesCollectionFragment() + mGamesCollectionFragment?.arguments = + bundleOf(KEY_USER_ID to mUserId, KEY_TYPE to GamesCollectionFragment.TYPE_USER) + childFragmentManager.beginTransaction().replace(R.id.contentContainer, mGamesCollectionFragment!!, GamesCollectionFragment::class.java.simpleName).commitAllowingStateLoss() + } + TYPE_PLAYED_GAME -> { + mPlayedGameFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? UserPlayedGameFragment ?: UserPlayedGameFragment() - mPlayedGameFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) - childFragmentManager.beginTransaction().replace(R.id.contentContainer, mPlayedGameFragment!!, UserPlayedGameFragment::class.java.simpleName).commitAllowingStateLoss() - } else { - mCommentFragment = childFragmentManager.findFragmentByTag(UserCommentHistoryFragment::class.java.simpleName) as? UserCommentHistoryFragment + mPlayedGameFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) + childFragmentManager.beginTransaction().replace(R.id.contentContainer, mPlayedGameFragment!!, UserPlayedGameFragment::class.java.simpleName).commitAllowingStateLoss() + } + else -> { + mCommentFragment = childFragmentManager.findFragmentByTag(UserCommentHistoryFragment::class.java.simpleName) as? UserCommentHistoryFragment ?: UserCommentHistoryFragment() - mCommentFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) - childFragmentManager.beginTransaction().replace(R.id.contentContainer, mCommentFragment!!, UserCommentHistoryFragment::class.java.simpleName).commitAllowingStateLoss() + mCommentFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) + childFragmentManager.beginTransaction().replace(R.id.contentContainer, mCommentFragment!!, UserCommentHistoryFragment::class.java.simpleName).commitAllowingStateLoss() + } } } @@ -154,8 +184,9 @@ class UserGameFragment: NormalFragment() { } companion object { - private const val TYPE_PLAYED_GAME = 100 - private const val TYPE_COMMENT = 101 + private const val TYPE_GAME_COLLECTION = 100 + private const val TYPE_PLAYED_GAME = 101 + private const val TYPE_COMMENT = 102 fun getInstance(userId: String, commentCount: Int): UserGameFragment { return UserGameFragment().apply { diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 89a56ca2e1..6262bcdfa4 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3300,4 +3300,10 @@ public interface ApiService { */ @POST("game_lists/{game_list_id}:submit") Observable publishGameCollection(@Path("game_list_id") String id); + + /** + * 用户游戏单列表 + */ + @GET("users/{user_id}/game_lists") + Single> getUserGameCollectionList(@Path("user_id") String userId); } \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/game_collection_comment.webp b/app/src/main/res/drawable-xxxhdpi/game_collection_comment.webp new file mode 100644 index 0000000000000000000000000000000000000000..de1f77774485a340ea9ffabb6668797282871e0b GIT binary patch literal 768 zcmV+b1ONO|Nk&Ha0ssJ4MM6+kP&il$0000G0000#002J#06|PpNPPkT00EGNZQI#M zU$>dHZQHhYGi%$ncgOb3(b~3c+qP}H?#y?PFZusJ|FwG@5fcFat=9Sx8?U_k<+Xi$ zw13915@yRaWYc$Kd^Nk4i3-fJBiB1!Oj2Zl6S<#nXod++6uPsNs2sad^sjben!H7W zfA>yoSF>Q6oRwP-nf}ZGCI^UU>qcGA$`)zBaHX(^I*6;&aBJZ}+O?<{uv>?})4`(( zrv`tKK35F*rVp`EIG<0sUq~j$T7i4@Hx!QHVZDsx-{v4P?|I78j6(>L_NE{<%}xTp z=3uJmBAmV;I>QYDXE0qxawv!|{E2WCV`zcoUNB<=uLjpK>>KDb?PgQhaJF&&UXyPbn z)hL0g-XP+OMQ<$;N%)EI)63!2U^%^4L~|3tIijl`lIaOmglb?l5zGRlqWbajQxyYA z#C$#`okXmas0XoAqVB{liN+D@B#I>#O0<{=lIRlAL!KPTL`go1>KDc6rz&i{QTq(| zs8JkMwpGMDPJTT6m~z{Q_VOg5)|G*i%0p$z{+n10oD|iM#ZOgOW}r5?3@;P5N+C9a zEO|bnHl?C0X^$hh79`1V49S;SCCPLKNl!hHqsCpNLqH0jXb;jmQpw=c?+)s2Qoww9 zgWsY4N-GE|)|z?n(nn}?o&|(hJ03_up`W`0PXl?wUZdcxxj;m-&2AL@s3&ly9-!zW z?JOWhZ1SyH6+s}=mjkSO6We74u{LAp`lkWYpEdA%L1mE9SLfE4Od#pdUx?1N4M5_I zO&Vo_e>4DAP&gn|0000`1OS}@DnI~006sAmi9;eGAr=GxAOi%nfB-H!ukN^p3UQ&k yok6YzTmS(6`5K@9+&};Q!ax81-`@Kd|NhiO6+i#v%7LT*{!F#M|MF!(0001yQDt8M literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/game_collection_vote.webp b/app/src/main/res/drawable-xxxhdpi/game_collection_vote.webp new file mode 100644 index 0000000000000000000000000000000000000000..a828f98d66bca26610d0548919947c62ce27490f GIT binary patch literal 544 zcmV+*0^j{oNk&E(0ssJ4MM6+kP&il$0000G0000#002J#06|PpNSpxx00EF|+qP*- z>)E!A$hM1-ZJVclz*P}5r?zb)vTfV7N@t(-=4$Q55itSOT8$jH_U7Afk5A9*;HDya z@prbrYkA@|zhLt#HE8X=v(q1=vEtsb;7q9HEIJOz`@PK1N}M7VzUl+hR*I*<k;>I6fJr264>mFkRBAXSS~FO>@8x^loK{+cew_BuTk=muo1pDioVLTHZH%^`QQG#A@ec#ve|#Z^n>{dS z|4(~_@8M?04Q^x*W!ym6Mds5C`164SpBZtmgKQJjSAGs?9o%$s8vh>bijO1e_zmiH z4hi=70`$C$MDwn|98Dl$@3D6wF7$LEd5?y<$8LRWfe&~0OfR99 ibKL*psY~vNw>kg+`-lJkxJUp0+xq|jWB>n?DgXfT$p>lx literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_chosen.webp b/app/src/main/res/drawable-xxxhdpi/ic_chosen.webp new file mode 100644 index 0000000000000000000000000000000000000000..5e7f593a0005638dd1a1362f25439a18bc89acb5 GIT binary patch literal 3744 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_des.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_des.webp new file mode 100644 index 0000000000000000000000000000000000000000..c55413d099c95a8f5c8aeced1ce6092384566124 GIT binary patch literal 268 zcmV+n0rUP+Nk&El0RRA3MM6+kP&il$0000G0000t001`t06|PpNT&b*00DrSZF{ro zqsUyigboY*o*AlS0(6+Aqoa_hpi!NS)!$IgaS<^A^l!|hx|rk_C0S5$GN8H$VA{ZH z<|hrX^ixR6?}BvkGtl4Ol2g?BI-Q`%^>p=EBHg+khcDA1{xx zuKV#N4&u*q$wp-(bI~jlT~H1eB^gC~%n17KIu8i@;5haPd}7ir-)eDX&~*8;2kNT?XG|hiLe8>vICGP0e*U=9IvJjF2l14XMAPh3dT=f z3K#k>rj%oC3h`t(ns|&$7Ei(0<)wH+|9U!rtIyDp0@~A5h2Y9h`HbMTUN7s91g|CH zp>>PUS6cOohmjqC`!$LB7N`ImO-tMdyni?;k*~bBJs`0+Y&^p4X0v?4(ba_s`Bw*4 zP&go(0RR9H4gj42DmVZ*06sAihC-qt3K?JwX4 zT2}ynRr_oDiTVNf0lWeH1K0=Zuc!~$4_6=14zLf{4!RFO57R$H@Aw|%pS54I-r>L9 zfB%9l2FnJC4UF73l^I+-O|AS42isjB^)Ld^ zzuXC{6OiZp&{@wQtfS9t2Y?NS$cRzbZGdmh(ZcU^B6OQSAiAQJvLY@eU&cwUM4vC= z$Rq7^X8DNMB-dA|)(+m5`yV-E1C&tbo*BF?u6fTybusW&IlkmA60+a@dcO|0XaE2J E07!~IS^xk5 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_des_gray.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_des_gray.webp new file mode 100644 index 0000000000000000000000000000000000000000..5629d8cffd11c08e43f23ea653fdad0c1b51fb7b GIT binary patch literal 318 zcmV-E0m1%KNk&FC0RRA3MM6+kP&il$0000G0000t001`t06|PpNU8t;00D4YZF{Qx zNPVSsDpt4)u!YyGzye_(hXGPjQ-%R3-7WG3?IL0V=x^*WzB(IUapovxi!-Rm6dZAe z6^{(04F5H2q#hr zaR!lG(8EFedREqOYnUy literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_detail_share_bottom_bar.png b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_detail_share_bottom_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..309f81d3b3e87473757e2a8a642cd86f2b273d88 GIT binary patch literal 2745 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_official.webp b/app/src/main/res/drawable-xxxhdpi/ic_official.webp new file mode 100644 index 0000000000000000000000000000000000000000..8d0ac71c1784c718afac2820f248baab927549a1 GIT binary patch literal 3060 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

+)l;tq{XaU*ts+uKTBVR_n3-`gcmIrNPF zW-Z1PkM+_edCZfS-8T`&WiC4Q(>1!oPSsyS3o{mt{Rb zO?Mw4Tn2-hb)VysPv(sW%_9b~ebG}r(Ct2Q%i~gfUEN#Lzs0{DUj*Ac7d9VLzyJUV C)&tl8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_official_big.webp b/app/src/main/res/drawable-xxxhdpi/ic_official_big.webp new file mode 100644 index 0000000000000000000000000000000000000000..d17b07f8c513d95a1f62f0a32c7050c70b14542c GIT binary patch literal 3176 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 Ovew@+j^56+00022<3V=- literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/pic_game_collection_only_myself.webp b/app/src/main/res/drawable-xxxhdpi/pic_game_collection_only_myself.webp new file mode 100644 index 0000000000000000000000000000000000000000..2c6000b27c59231ad24f2cc152b97dfee7019de8 GIT binary patch literal 2940 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/background_shape_white_alpha_10_radius_2.xml b/app/src/main/res/drawable/background_shape_white_alpha_10_radius_2.xml new file mode 100644 index 0000000000..4bdd4e68eb --- /dev/null +++ b/app/src/main/res/drawable/background_shape_white_alpha_10_radius_2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_border_blue_radius_2.xml b/app/src/main/res/drawable/bg_border_blue_radius_2.xml new file mode 100644 index 0000000000..a32c663798 --- /dev/null +++ b/app/src/main/res/drawable/bg_border_blue_radius_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_f2_alpha_10_radius_8.xml b/app/src/main/res/drawable/bg_shape_f2_alpha_10_radius_8.xml new file mode 100644 index 0000000000..4fc1c93493 --- /dev/null +++ b/app/src/main/res/drawable/bg_shape_f2_alpha_10_radius_8.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_f5_radius_2.xml b/app/src/main/res/drawable/bg_shape_f5_radius_2.xml new file mode 100644 index 0000000000..2aa26231b8 --- /dev/null +++ b/app/src/main/res/drawable/bg_shape_f5_radius_2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_1a2496ff.xml b/app/src/main/res/drawable/button_round_1a2496ff.xml new file mode 100644 index 0000000000..59dd042258 --- /dev/null +++ b/app/src/main/res/drawable/button_round_1a2496ff.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_black_alpha_30.xml b/app/src/main/res/drawable/button_round_black_alpha_30.xml new file mode 100644 index 0000000000..d69c5b6616 --- /dev/null +++ b/app/src/main/res/drawable/button_round_black_alpha_30.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_white_alpha_10.xml b/app/src/main/res/drawable/button_round_white_alpha_10.xml new file mode 100644 index 0000000000..20b04b477d --- /dev/null +++ b/app/src/main/res/drawable/button_round_white_alpha_10.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/game_collection_detail_poster_mask.xml b/app/src/main/res/drawable/game_collection_detail_poster_mask.xml new file mode 100644 index 0000000000..2ef9289b3b --- /dev/null +++ b/app/src/main/res/drawable/game_collection_detail_poster_mask.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/game_collection_poster_mask.xml b/app/src/main/res/drawable/game_collection_poster_mask.xml new file mode 100644 index 0000000000..43f2df4e50 --- /dev/null +++ b/app/src/main/res/drawable/game_collection_poster_mask.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/game_collection_vote_selector.xml b/app/src/main/res/drawable/game_collection_vote_selector.xml new file mode 100644 index 0000000000..a39bd3fab7 --- /dev/null +++ b/app/src/main/res/drawable/game_collection_vote_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_played_game_blue_style.xml b/app/src/main/res/drawable/progressbar_played_game_blue_style.xml new file mode 100644 index 0000000000..f68bfa3c16 --- /dev/null +++ b/app/src/main/res/drawable/progressbar_played_game_blue_style.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_played_game_style.xml b/app/src/main/res/drawable/progressbar_played_game_style.xml new file mode 100644 index 0000000000..7361e015ba --- /dev/null +++ b/app/src/main/res/drawable/progressbar_played_game_style.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_collection_wrapper.xml b/app/src/main/res/layout/fragment_collection_wrapper.xml new file mode 100644 index 0000000000..75f6966603 --- /dev/null +++ b/app/src/main/res/layout/fragment_collection_wrapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_game.xml b/app/src/main/res/layout/fragment_user_game.xml index c718980a23..a196bd0068 100644 --- a/app/src/main/res/layout/fragment_user_game.xml +++ b/app/src/main/res/layout/fragment_user_game.xml @@ -1,7 +1,6 @@ @@ -19,17 +18,31 @@ android:paddingBottom="16dp" android:background="@color/white"> + + + android:text="玩过" + android:textColor="@color/text_333333" + android:textSize="12sp" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 67cdbc0027..4aac4429e8 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -129,6 +129,7 @@ #66000000 #80000000 + #1AFFFFFF #33FFFFFF #66FFFFFF #80FFFFFF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6e1403884e..8d7e8d5eac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -344,6 +344,7 @@ 帖子 资讯 视频 + 游戏单 今天开服 明天开服 后续开服 From 32bc1a4a6f2a1ca8e287c1e42016a6894c010345 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Mon, 15 Nov 2021 09:36:26 +0800 Subject: [PATCH 11/49] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gh/common/util/PatternUtils.java | 20 +++++++ .../com/gh/gamecenter/entity/GameEntity.kt | 14 +++-- .../choose/ChooseGamesAdapter.kt | 15 +++++ .../publish/GameCollectionEditActivity.kt | 55 +++++++++++++++++-- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/gh/common/util/PatternUtils.java b/app/src/main/java/com/gh/common/util/PatternUtils.java index 4459437029..8beb323628 100644 --- a/app/src/main/java/com/gh/common/util/PatternUtils.java +++ b/app/src/main/java/com/gh/common/util/PatternUtils.java @@ -55,4 +55,24 @@ public class PatternUtils { return newText; } + /** + * 判断字符串中是否有连续2个以上的换行 + */ + public static boolean isHasWrap(String text){ + String pattern = "[\\n]{2,}"; + Regex regex = new Regex(pattern); + return regex.find(text, 0) != null; + } + + /** + * 替换字符串中连续2个以上的换行为一个换行 + */ + public static String replaceWrap(String text) { + String pattern = "[\\n]{2,}"; + String newText = text; + if (isHasSpace(text)) { + newText = text.replaceAll(pattern, "\n"); + } + return newText; + } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt index eaed9e11ed..12f088d9d9 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt @@ -563,10 +563,10 @@ data class GameEntity( simpleGame.nameSuffix = nameSuffix simpleGame.mIcon = mIcon simpleGame.mRawIcon = mRawIcon - simpleGame.active=active - simpleGame.iconSubscript=iconSubscript - simpleGame.recommendStar=recommendStar - simpleGame.recommendText=recommendText + simpleGame.active = active + simpleGame.iconSubscript = iconSubscript + simpleGame.recommendStar = recommendStar + simpleGame.recommendText = recommendText return simpleGame } @@ -792,6 +792,10 @@ data class SimpleGame( //游戏单推荐分数 @SerializedName("recommend_star") var recommendStar: Int = 5, + @SerializedName("mirror_status") + var mirrorStatus: String? = "", + @SerializedName("mirror_data") + var mirrorData: SimpleGame? = null, //游戏单推荐理由 @SerializedName("recommend_text") var recommendText: String = "", @@ -815,6 +819,8 @@ data class SimpleGame( gameEntity.icon = mIcon gameEntity.rawIcon = mRawIcon gameEntity.iconSubscript = iconSubscript + gameEntity.mirrorStatus = mirrorStatus + gameEntity.mirrorData = mirrorData?.toGameEntity() gameEntity.recommendStar = recommendStar gameEntity.recommendText = recommendText return gameEntity diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt index 5786168d44..5deb3cccf1 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesAdapter.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.core.widget.doOnTextChanged import androidx.recyclerview.widget.RecyclerView import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.PatternUtils import com.gh.common.util.TextHelper import com.gh.common.util.consume import com.gh.common.util.toBinding @@ -39,6 +40,20 @@ class ChooseGamesAdapter(context: Context, val dragListener: ItemDragListener) : override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is ChooseGamesViewHolder) { val gameEntity = mEntityList[position] + holder.binding.recommendReasonEt.run { + doOnTextChanged { text, start, _, _ -> + if(PatternUtils.isHasWrap(text.toString())){ + setText(PatternUtils.replaceWrap(text.toString())) + setSelection(start) + return@doOnTextChanged + } + if (PatternUtils.isHasSpace(text.toString())) { + setText(PatternUtils.replaceSpace(text.toString())) + setSelection(start) + return@doOnTextChanged + } + } + } holder.binding.gameNameTv.text = gameEntity.name holder.binding.gameIcon.displayGameIcon(gameEntity) holder.binding.recommendReasonEt.setText(gameEntity.recommendText) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index 0ef1aea648..a892f1be79 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -13,7 +13,6 @@ import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding import com.gh.gamecenter.entity.GamesCollectionEntity -import com.gh.gamecenter.entity.SimpleGame import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity @@ -32,6 +31,7 @@ class GameCollectionEditActivity : ToolBarActivity() { private lateinit var mViewModel: GameCollectionEditViewModel private lateinit var mChooseGamesViewModel: ChooseGamesViewModel private var mProcessingDialog: WaitingDialogFragment? = null + private var mPatchCommitCount = 0 override fun getLayoutId(): Int = R.layout.activity_game_collection_edit @@ -51,6 +51,34 @@ class GameCollectionEditActivity : ToolBarActivity() { } private fun initListener() { + mBinding.gameCollectionTitleEt.run { + doOnTextChanged { text, start, _, _ -> + if (text?.contains("\n") == true) { + setText(text.toString().replace("\n", "")) + setSelection(start) + return@doOnTextChanged + } + if (PatternUtils.isHasSpace(text.toString())) { + setText(PatternUtils.replaceSpace(text.toString())) + setSelection(start) + return@doOnTextChanged + } + } + } + mBinding.gameCollectionIntroduceEt.run { + doOnTextChanged { text, start, _, _ -> + if(PatternUtils.isHasWrap(text.toString())){ + setText(PatternUtils.replaceWrap(text.toString())) + setSelection(start) + return@doOnTextChanged + } + if (PatternUtils.isHasSpace(text.toString())) { + setText(PatternUtils.replaceSpace(text.toString())) + setSelection(start) + return@doOnTextChanged + } + } + } mBinding.uploadPictureBtn.setOnClickListener { PermissionHelper.checkStoragePermissionBeforeAction( this, @@ -96,7 +124,7 @@ class GameCollectionEditActivity : ToolBarActivity() { mViewModel.gameCollectionPatch?.run { mViewModel.imageUrl = cover - if (status.isNotEmpty() && status != "draft") { + if (status.isNotEmpty() && (status != "draft" || display == "self_only")) { setNavigationTitle("编辑游戏单") } initPosterUI() @@ -262,12 +290,12 @@ class GameCollectionEditActivity : ToolBarActivity() { } val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf() - val title = mBinding.gameCollectionTitleEt.text.toString() + val title = mBinding.gameCollectionTitleEt.text.toString().trim() if (title.isEmpty()) { toast("请填写游戏单的标题") return } - val introduce = mBinding.gameCollectionIntroduceEt.text.toString() + val introduce = mBinding.gameCollectionIntroduceEt.text.toString().trim() if (introduce.length < 10) { toast("介绍至少10个字") return @@ -290,6 +318,25 @@ class GameCollectionEditActivity : ToolBarActivity() { }.toList() ) + val patch = mViewModel.gameCollectionPatch + if (patch != null + && patch.status.isNotEmpty() + && patch.status != "draft" + && games.size < 8 + ) { + if (mPatchCommitCount < 2) { + toast("游戏单收录的游戏不能少于8款") + mPatchCommitCount++ + return + } else { + if (!mBinding.selfOnlyCb.isChecked) { + toast("没想好收录的游戏,开启仅自己可见试试") + mPatchCommitCount++ + return + } + } + } + if (isSelfOnly) { DialogHelper.showDialog(this, "温馨提示", "游戏单开启“仅自己可见”后,将不会对其他用户展示,是否继续提交", "确定", "取消", { mViewModel.uploadContent(requestMap) From 4e78162d7ff25cd83cfa5023c51d283f101e3d19 Mon Sep 17 00:00:00 2001 From: leafwai Date: Mon, 15 Nov 2021 17:27:41 +0800 Subject: [PATCH 12/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(UI)https://git.ghzs.com/pm/halo-app-issues/-/issues/1?= =?UTF-8?q?598=20=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9=E6=89=8BV5.5.0?= =?UTF-8?q?=E3=80=91=E9=A6=96=E9=A1=B5=E9=A1=B6=E9=83=A8tab-=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=8D=95=E5=B9=BF=E5=9C=BA(UI)https://git.ghzs.com/pm?= =?UTF-8?q?/halo-app-issues/-/issues/1599=20=E3=80=90=E5=85=89=E7=8E=AF?= =?UTF-8?q?=E5=8A=A9=E6=89=8BV5.5.0=E3=80=91=E9=A6=96=E9=A1=B5/=E7=89=88?= =?UTF-8?q?=E5=9D=97=E5=86=85=E5=AE=B9=E5=88=97=E8=A1=A8-=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=8D=95=E5=B9=BF=E5=9C=BA(UI)https://git.ghzs.com/pm?= =?UTF-8?q?/halo-app-issues/-/issues/1600?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 +- .../com/gh/common/view/StackRecyclerView.kt | 23 + .../view/stacklayoutmanager/StackAnimation.kt | 22 + .../view/stacklayoutmanager/StackLayout.kt | 34 ++ .../stacklayoutmanager/StackLayoutManager.kt | 544 ++++++++++++++++++ .../square/GameCollectionAmwayAdapter.kt | 51 ++ .../square/GameCollectionAmwayViewHolder.kt | 111 ++++ .../square/GameCollectionSquareActivity.kt | 23 + .../square/GameCollectionSquareAdapter.kt | 128 +++++ .../square/GameCollectionSquareFragment.kt | 313 ++++++++++ .../square/GameCollectionSquareViewModel.kt | 31 + .../GameCollectionStackAnimation.kt | 144 +++++ .../GameCollectionStackLayout.kt | 110 ++++ .../HomeGameCollectionAdapter.kt | 65 +++ .../HomeGameCollectionViewHolder.kt | 46 ++ .../game_collection_rg_button_selector.xml | 5 + .../game_collection_square_head.webp | Bin 0 -> 101002 bytes .../ic_game_collection_hot.webp | Bin 0 -> 874 bytes .../ic_game_collection_square_amway_go.webp | Bin 0 -> 10126 bytes .../bg_game_collection_hot_container.xml | 5 + .../drawable/bg_game_collection_square.xml | 7 + .../bg_game_collection_user_container.xml | 5 + .../drawable/progressbar_game_collection.xml | 20 + .../fragment_game_collection_square.xml | 229 ++++++++ .../fragment_game_collection_square_al.xml | 52 ++ .../game_collection_amway_content_item.xml | 38 ++ .../game_collection_square_amway_item.xml | 58 ++ .../layout/game_collection_square_item.xml | 280 +++++++++ .../layout/home_game_collection_card_item.xml | 196 +++++++ ...item.xml => home_game_collection_item.xml} | 17 +- 30 files changed, 2551 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/gh/common/view/StackRecyclerView.kt create mode 100644 app/src/main/java/com/gh/common/view/stacklayoutmanager/StackAnimation.kt create mode 100644 app/src/main/java/com/gh/common/view/stacklayoutmanager/StackLayout.kt create mode 100644 app/src/main/java/com/gh/common/view/stacklayoutmanager/StackLayoutManager.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackAnimation.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackLayout.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionViewHolder.kt create mode 100644 app/src/main/res/color/game_collection_rg_button_selector.xml create mode 100644 app/src/main/res/drawable-xxxhdpi/game_collection_square_head.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_hot.webp create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_amway_go.webp create mode 100644 app/src/main/res/drawable/bg_game_collection_hot_container.xml create mode 100644 app/src/main/res/drawable/bg_game_collection_square.xml create mode 100644 app/src/main/res/drawable/bg_game_collection_user_container.xml create mode 100644 app/src/main/res/drawable/progressbar_game_collection.xml create mode 100644 app/src/main/res/layout/fragment_game_collection_square.xml create mode 100644 app/src/main/res/layout/fragment_game_collection_square_al.xml create mode 100644 app/src/main/res/layout/game_collection_amway_content_item.xml create mode 100644 app/src/main/res/layout/game_collection_square_amway_item.xml create mode 100644 app/src/main/res/layout/game_collection_square_item.xml create mode 100644 app/src/main/res/layout/home_game_collection_card_item.xml rename app/src/main/res/layout/{game_collection_tag_body_item.xml => home_game_collection_item.xml} (58%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7c76517d5..e260be198b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -742,12 +742,12 @@ android:name=".gamecollection.MyGameCollectionActivity" android:screenOrientation="portrait" /> - - - + , + layout: Class) : RecyclerView.LayoutManager() { + private enum class FlingOrientation{NONE, LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP} + + enum class ScrollOrientation{LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP} + + private var mVisibleItemCount = visibleCount + + private var mScrollOrientation = scrollOrientation + + private var mScrollOffset: Int + + var isFlinging = false + + private lateinit var mOnScrollListener: OnScrollListener + private lateinit var mOnFlingListener: OnFlingListener + + //做动画的组件,支持自定义 + private var mAnimation: StackAnimation? = null + //做布局的组件,支持自定义 + private var mLayout: StackLayout? = null + + //是否是翻页效果 + private var mPagerMode = true + + //触发翻页效果的最低 Fling速度 + private var mPagerFlingVelocity = 0 + + //标志当前滚动是否是调用scrollToCenter之后触发的滚动 + private var mFixScrolling = false + + //fling的方向,用来判断是前翻还是后翻 + private var mFlingOrientation = FlingOrientation.NONE + + //当前所处item对应的位置 + private var itemPosition = 0 + + //判断item位置是否发生了改变 + private var isItemPositionChanged = false + + //item 位置发生改变的回调 + private var itemChangedListener: ItemChangedListener? = null + + interface ItemChangedListener { + fun onItemChanged(position: Int) + } + + /** + * 设置是否为ViewPager 式翻页模式. + *

+ * 当设置为 true 的时候,可以配合[StackLayoutManager.setPagerFlingVelocity]设置触发翻页的最小速度. + * @param isPagerMode 这个值默认是 false,当设置为 true 的时候,会有 viewPager 翻页效果. + */ + fun setPagerMode(isPagerMode: Boolean) { + mPagerMode = isPagerMode + } + + /** + * @return 当前是否为ViewPager翻页模式. + */ + fun getPagerMode(): Boolean { + return mPagerMode + } + + /** + * 设置触发ViewPager翻页效果的最小速度. + *

+ * 该值仅在 [StackLayoutManager.getPagerMode] == true的时候有效. + * @param velocity 默认值是2000. + */ + fun setPagerFlingVelocity(@androidx.annotation.IntRange(from = 0, to = Int.MAX_VALUE.toLong()) velocity: Int) { + mPagerFlingVelocity = Int.MAX_VALUE.coerceAtMost(0.coerceAtLeast(velocity)) + } + + /** + * @return 当前触发翻页的最小 fling 速度. + */ + fun getPagerFlingVelocity(): Int { + return mPagerFlingVelocity + } + + /** + * 设置recyclerView 静止时候可见的itemView 个数. + * @param count 可见 itemView,默认为3 + */ + fun setVisibleItemCount(@androidx.annotation.IntRange(from = 1, to = Long.MAX_VALUE)count: Int) { + mVisibleItemCount = (itemCount - 1).coerceAtMost(1.coerceAtLeast(count)) + mAnimation?.setVisibleCount(mVisibleItemCount) + } + + /** + * 获取recyclerView 静止时候可见的itemView 个数. + * @return 静止时候可见的itemView 个数,默认为3. + */ + fun getVisibleItemCount(): Int { + return mVisibleItemCount + } + + /** + * 设置 item 偏移值,即第 i 个 item 相对于 第 i-1个 item 在水平方向的偏移值,默认是40px. + * @param offset 每个 item 相对于前一个的偏移值. + */ + fun setItemOffset(offset: Int) { + mLayout?.setItemOffset(offset) + } + + /** + * 获取每个 item 相对于前一个的水平偏移值. + * @return 每个 item 相对于前一个的水平偏移值. + */ + fun getItemOffset(): Int { + return if (mLayout == null) { + 0 + } else { + mLayout!!.getItemOffset() + } + } + + /** + * 设置item 移动动画. + * @param animation item 移动动画. + */ + fun setAnimation(animation: StackAnimation) { + mAnimation = animation + } + + /** + * 获取 item 移动动画. + * @return item 移动动画. + */ + fun getAnimation(): StackAnimation? { + return mAnimation + } + + /** + * 获取StackLayoutManager 的滚动方向. + * @return StackLayoutManager 的滚动方向. + */ + fun getScrollOrientation(): ScrollOrientation { + return mScrollOrientation + } + + /** + * 返回第一个可见 itemView 的位置. + * @return 返回第一个可见 itemView 的位置. + */ + fun getFirstVisibleItemPosition(): Int { + if (width == 0 || height == 0) { + return 0 + } + return when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT -> floor((mScrollOffset * 1.0 / width)).toInt() + ScrollOrientation.LEFT_TO_RIGHT -> itemCount - 1 - ceil((mScrollOffset * 1.0 / width)).toInt() + ScrollOrientation.BOTTOM_TO_TOP -> floor((mScrollOffset * 1.0 / height)).toInt() + ScrollOrientation.TOP_TO_BOTTOM -> itemCount - 1 - ceil((mScrollOffset * 1.0 / height)).toInt() + } + } + + /** + * 设置 item 位置改变时触发的回调 + */ + fun setItemChangedListener(listener: ItemChangedListener) { + itemChangedListener = listener + } + +// constructor(scrollOrientation: ScrollOrientation) : this(scrollOrientation, 3, DefaultAnimation::class.java, DefaultLayout::class.java) +// +// constructor(scrollOrientation: ScrollOrientation, visibleCount: Int) : this(scrollOrientation, visibleCount, DefaultAnimation::class.java, DefaultLayout::class.java) + +// constructor() : this(ScrollOrientation.RIGHT_TO_LEFT) + + init { + mScrollOffset = when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.BOTTOM_TO_TOP -> 0 + else -> Int.MAX_VALUE + } + + if (StackAnimation::class.java.isAssignableFrom(animation)) { + try { + val cla = animation.getDeclaredConstructor(ScrollOrientation::class.java, Int::class.javaPrimitiveType) + mAnimation = cla.newInstance(scrollOrientation, visibleCount) as StackAnimation + } catch (e: Exception) { + e.printStackTrace() + } + } + if (StackLayout::class.java.isAssignableFrom(layout)) { + try { + val cla = layout.getDeclaredConstructor(ScrollOrientation::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType) + mLayout = cla.newInstance(scrollOrientation, visibleCount, 30) as StackLayout + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + override fun generateDefaultLayoutParams(): LayoutParams { + return LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT) + } + + override fun onAttachedToWindow(view: RecyclerView) { + super.onAttachedToWindow(view) + mOnFlingListener = object : OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + if (mPagerMode) { + when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.LEFT_TO_RIGHT -> { + mFlingOrientation = when { + velocityX > mPagerFlingVelocity -> FlingOrientation.RIGHT_TO_LEFT + velocityX < -mPagerFlingVelocity -> FlingOrientation.LEFT_TO_RIGHT + else -> FlingOrientation.NONE + } + if (mScrollOffset in 1 until width * (itemCount - 1)) { //边界不需要滚动 + mFixScrolling = true + } + } + else -> { + mFlingOrientation = when { + velocityY > mPagerFlingVelocity -> FlingOrientation.BOTTOM_TO_TOP + velocityY < -mPagerFlingVelocity -> FlingOrientation.TOP_TO_BOTTOM + else -> FlingOrientation.NONE + } + if (mScrollOffset in 1 until width * (itemCount - 1)) { //边界不需要滚动 + mFixScrolling = true + } + } + } + calculateAndScrollToTarget(view) + } + else { + Utils.log("$velocityX $velocityY") + } + return mPagerMode + } + } + view.onFlingListener = mOnFlingListener + + mOnScrollListener = object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (newState == SCROLL_STATE_IDLE) { + isFlinging = false + if (!mFixScrolling) { + mFixScrolling = true + calculateAndScrollToTarget(view) + } else { + //表示此次 IDLE 是由修正位置结束触发的 + mFixScrolling = false + } + } else if (newState == SCROLL_STATE_DRAGGING) { + mFixScrolling = false + isFlinging = false + } else if (newState == SCROLL_STATE_SETTLING) { + isFlinging = true + } + } + } + view.addOnScrollListener(mOnScrollListener) + } + + override fun onDetachedFromWindow(view: RecyclerView?, recycler: RecyclerView.Recycler?) { + super.onDetachedFromWindow(view, recycler) + if (view?.onFlingListener == mOnFlingListener) { + view.onFlingListener = null + } + view?.removeOnScrollListener(mOnScrollListener) + } + + override fun canScrollHorizontally(): Boolean { + if (itemCount == 0) { + return false + } + return when (mScrollOrientation) { + ScrollOrientation.LEFT_TO_RIGHT, ScrollOrientation.RIGHT_TO_LEFT -> true + else -> false + } + } + + override fun canScrollVertically(): Boolean { + if (itemCount == 0) { + return false + } + return when (mScrollOrientation) { + ScrollOrientation.TOP_TO_BOTTOM, ScrollOrientation.BOTTOM_TO_TOP -> true + else -> false + } + } + + override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: State) { + + mLayout?.requestLayout() + + removeAndRecycleAllViews(recycler) + + if (itemCount > 0) { + mScrollOffset = getValidOffset(mScrollOffset) + loadItemView(recycler) + } + } + + override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler, state: State): Int { + return handleScrollBy(dx, recycler) + } + + override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler, state: State?): Int { + return handleScrollBy(dy, recycler) + } + + override fun scrollToPosition(position: Int) { + if (position < 0 || position >= itemCount) { + throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]") + } + if (position < 0) { + throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]") + } + mScrollOffset = getPositionOffset(position) + requestLayout() + } + + override fun smoothScrollToPosition(recyclerView: RecyclerView, state: State?, position: Int) { + if (position < 0 || position >= itemCount) { + throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]") + } + if (position < 0) { + throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]") + } + mFixScrolling = true + scrollToCenter(position, recyclerView, true) + } + + private fun updatePositionRecordAndNotify(position: Int) { + if (itemChangedListener == null) { + return + } + if (position != itemPosition) { + isItemPositionChanged = true + itemPosition = position + itemChangedListener?.onItemChanged(itemPosition) + } else { + isItemPositionChanged = false + } + } + + private fun handleScrollBy(offset: Int, recycler: RecyclerView.Recycler): Int { + //期望值,不得超过最大最小值,所以期望值不一定等于实际值 + val expectOffset = mScrollOffset + offset + //实际值 + mScrollOffset = getValidOffset(expectOffset) + + //实际偏移,超过最大最小值之后的偏移都应该是0,该值作为返回值,否则在极限位置进行滚动的时候不会出现弹性阴影 + val exactMove = mScrollOffset - expectOffset + offset + + if (exactMove == 0) { + //itemViews 位置都不会改变,直接 return + return 0 + } + + detachAndScrapAttachedViews(recycler) + + loadItemView(recycler) + + return offset + } + + private fun loadItemView(recycler: RecyclerView.Recycler) { + val firstVisiblePosition = getFirstVisibleItemPosition() + val lastVisiblePosition = getLastVisibleItemPosition() + + //位移百分比 + val movePercent = getFirstVisibleItemMovePercent() + + for (i in lastVisiblePosition downTo firstVisiblePosition) { + val view = recycler.getViewForPosition(i) + view.layoutParams = view.layoutParams.apply { width = DisplayUtils.getScreenWidth() - 50F.dip2px() } + //添加到recycleView 中 + addView(view) + //测量 + measureChild(view, 0, 0) + //布局 + mLayout?.doLayout(this, mScrollOffset, movePercent, view, i - firstVisiblePosition) + //做动画 + mAnimation?.doAnimation(movePercent, view, i - firstVisiblePosition) + } + + //尝试更新当前item的位置并通知外界 + updatePositionRecordAndNotify(firstVisiblePosition) + + //重用 + if (firstVisiblePosition - 1 >= 0) { + val view = recycler.getViewForPosition(firstVisiblePosition - 1) + resetViewAnimateProperty(view) + removeAndRecycleView(view, recycler) + } + if (lastVisiblePosition + 1 < itemCount) { + val view = recycler.getViewForPosition(lastVisiblePosition + 1) + resetViewAnimateProperty(view) + removeAndRecycleView(view, recycler) + } + } + + private fun resetViewAnimateProperty(view: View) { + view.rotationY = 0f + view.rotationX = 0f + view.scaleX = 1f + view.scaleY = 1f + view.alpha = 1f + } + + private fun calculateAndScrollToTarget(view: RecyclerView) { + val targetPosition = calculateCenterPosition(getFirstVisibleItemPosition()) + scrollToCenter(targetPosition, view, true) + } + + private fun scrollToCenter(targetPosition: Int, recyclerView: RecyclerView, animation: Boolean) { + val targetOffset = getPositionOffset(targetPosition) + when(mScrollOrientation) { + ScrollOrientation.LEFT_TO_RIGHT, ScrollOrientation.RIGHT_TO_LEFT -> { + if (animation) { + recyclerView.smoothScrollBy(targetOffset - mScrollOffset, 0) + } else { + recyclerView.scrollBy(targetOffset - mScrollOffset, 0) + } + } + else -> { + if (animation) { + recyclerView.smoothScrollBy(0, targetOffset - mScrollOffset) + } else { + recyclerView.scrollBy(0, targetOffset - mScrollOffset) + } + } + } + } + + private fun getValidOffset(expectOffset: Int): Int { + val offset = (width * (itemCount - 1)).coerceAtMost(expectOffset).coerceAtLeast(0) + return when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.LEFT_TO_RIGHT -> offset + else -> (height * (itemCount - 1)).coerceAtMost(expectOffset).coerceAtLeast(0) + } + } + + private fun getPositionOffset(position: Int): Int { + return when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT -> position * width + ScrollOrientation.LEFT_TO_RIGHT -> (itemCount - 1 - position) * width + ScrollOrientation.BOTTOM_TO_TOP -> position * height + ScrollOrientation.TOP_TO_BOTTOM -> (itemCount - 1 - position) * height + } + } + + private fun getLastVisibleItemPosition(): Int { + val firstVisiblePosition = getFirstVisibleItemPosition() + return if (firstVisiblePosition + mVisibleItemCount > itemCount - 1) { + itemCount - 1 + } else { + firstVisiblePosition + mVisibleItemCount + } + } + + private fun getFirstVisibleItemMovePercent(): Float { + if (width == 0 || height == 0) { + return 0f + } + return when (mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT -> (mScrollOffset % width) * 1.0f / width + ScrollOrientation.LEFT_TO_RIGHT -> { + val targetPercent = 1 - (mScrollOffset % width) * 1.0f / width + return if (targetPercent == 1f) { + 0f + } else { + targetPercent + } + } + ScrollOrientation.BOTTOM_TO_TOP -> (mScrollOffset % height) * 1.0f / height + ScrollOrientation.TOP_TO_BOTTOM -> { + val targetPercent = 1 - (mScrollOffset % height) * 1.0f / height + return if (targetPercent == 1f) { + 0f + } else { + targetPercent + } + } + } + } + + private fun calculateCenterPosition(position: Int): Int { + //当是 Fling 触发的时候 + val triggerOrientation = mFlingOrientation + mFlingOrientation = FlingOrientation.NONE + when(mScrollOrientation) { + ScrollOrientation.RIGHT_TO_LEFT -> { + if (triggerOrientation == FlingOrientation.RIGHT_TO_LEFT) { + return position + 1 + } else if (triggerOrientation == FlingOrientation.LEFT_TO_RIGHT) { + return position + } + } + ScrollOrientation.LEFT_TO_RIGHT -> { + if (triggerOrientation == FlingOrientation.LEFT_TO_RIGHT) { + return position + 1 + } else if (triggerOrientation == FlingOrientation.RIGHT_TO_LEFT) { + return position + } + } + ScrollOrientation.BOTTOM_TO_TOP -> { + if (triggerOrientation == FlingOrientation.BOTTOM_TO_TOP) { + return position + 1 + } else if (triggerOrientation == FlingOrientation.TOP_TO_BOTTOM) { + return position + } + } + ScrollOrientation.TOP_TO_BOTTOM -> { + if (triggerOrientation == FlingOrientation.TOP_TO_BOTTOM) { + return position + 1 + } else if (triggerOrientation == FlingOrientation.BOTTOM_TO_TOP) { + return position + } + } + } + + //当不是 fling 触发的时候 + val percent = getFirstVisibleItemMovePercent() + //向左移动超过50% position(firstVisibleItemPosition)++ + //否 position不变 + return if (percent < 0.5) { + position + } else { + position + 1 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt new file mode 100644 index 0000000000..dd8ec0761b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt @@ -0,0 +1,51 @@ +package com.gh.gamecenter.gamecollection.square + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.databinding.GameCollectionAmwayContentItemBinding +import com.gh.gamecenter.entity.AmwayCommentEntity +import com.lightgame.adapter.BaseRecyclerAdapter + +class GameCollectionAmwayAdapter(context: Context) : + BaseRecyclerAdapter(context) { + + private var mAmwayList = ArrayList() + + fun setAmwayList(amwayList: ArrayList) { + mAmwayList = amwayList + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + GameCollectionAmwayContentItemViewHolder(GameCollectionAmwayContentItemBinding.inflate(mLayoutInflater, parent, false)) + + override fun getItemCount(): Int = if (getRealCount() > 1) getRealCount() + INCREASE_COUNT else getRealCount() + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is GameCollectionAmwayContentItemViewHolder) { + holder.binding.entity = mAmwayList[getRealPosition(position)] + } + } + + class GameCollectionAmwayContentItemViewHolder(var binding: GameCollectionAmwayContentItemBinding) : + RecyclerView.ViewHolder(binding.root) + + fun getRealCount(): Int = mAmwayList.size + + fun getRealPosition(position: Int) = when (position) { + 0 -> { + getRealCount() - 1 + } + getRealCount() + 1 -> { + 0 + } + else -> { + position - 1 + } + } + + companion object{ + const val INCREASE_COUNT = 2 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt new file mode 100644 index 0000000000..479a51ad92 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt @@ -0,0 +1,111 @@ +package com.gh.gamecenter.gamecollection.square + +import android.animation.Animator +import android.animation.TimeInterpolator +import android.animation.ValueAnimator +import android.view.animation.AccelerateDecelerateInterpolator +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.gh.common.util.DirectUtils +import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding +import java.lang.ref.WeakReference + +class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBinding) : RecyclerView.ViewHolder(binding.root) { + val mAdapter = GameCollectionAmwayAdapter(binding.root.context).apply { registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + if (itemCount <= 1) { + stop() + } else { + start() + } + } + }) } + val mLoopTask = object : Runnable { + private val reference: WeakReference = WeakReference(binding.amwayVp) + override fun run() { + val vp: ViewPager2? = reference.get() + if (vp != null) { + val count = mAdapter.itemCount + if (count == 0) return + val next = (vp.currentItem + 1) % count + vp.setCurrentItem(next, 1000) + vp.postDelayed(this, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + } + } + } + + + fun bindAmway() { + binding.amwayVp.run { + if (adapter is GameCollectionAmwayAdapter) return + isUserInputEnabled = false + adapter = mAdapter + orientation = ViewPager2.ORIENTATION_VERTICAL + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + private var mTempPosition = GameCollectionSquareAdapter.INVALID_VALUE + private var isScrolled = false + + override fun onPageSelected(position: Int) { + if (isScrolled) { + mTempPosition = position + } + } + + override fun onPageScrollStateChanged(state: Int) { + //手势滑动中,代码执行滑动中 + if (state == ViewPager2.SCROLL_STATE_DRAGGING || state == ViewPager2.SCROLL_STATE_SETTLING) { + isScrolled = true + } else if (state == ViewPager2.SCROLL_STATE_IDLE) { + //滑动闲置或滑动结束 + isScrolled = false + if (mTempPosition != GameCollectionSquareAdapter.INVALID_VALUE) { + if (mTempPosition == 0) { + setCurrentItem(mAdapter.getRealCount(), false) + } else if (mTempPosition == mAdapter.itemCount - 1) { + setCurrentItem(1, false) + } + } + } + } + }) + setCurrentItem(1, false) + postDelayed(mLoopTask, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + } + binding.root.setOnClickListener { DirectUtils.directToAmway(binding.root.context) } + } + + fun start() { + stop() + binding.amwayVp.postDelayed(mLoopTask, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + } + + fun stop() { + binding.amwayVp.removeCallbacks(mLoopTask) + } + + fun ViewPager2.setCurrentItem( + item: Int, + duration: Long, + interpolator: TimeInterpolator = AccelerateDecelerateInterpolator(), + pagePxHeight: Int = height + ) { + val pxToDrag: Int = pagePxHeight * (item - currentItem) + val animator = ValueAnimator.ofInt(0, pxToDrag) + var previousValue = 0 + animator.addUpdateListener { valueAnimator -> + val currentValue = valueAnimator.animatedValue as Int + val currentPxToDrag = (currentValue - previousValue).toFloat() + fakeDragBy(-currentPxToDrag) + previousValue = currentValue + } + animator.addListener(object : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator?) { beginFakeDrag() } + override fun onAnimationEnd(animation: Animator?) { endFakeDrag() } + override fun onAnimationCancel(animation: Animator?) { } + override fun onAnimationRepeat(animation: Animator?) { } + }) + animator.interpolator = interpolator + animator.duration = duration + animator.start() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareActivity.kt new file mode 100644 index 0000000000..d61d327fba --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareActivity.kt @@ -0,0 +1,23 @@ +package com.gh.gamecenter.gamecollection.square + +import android.os.Bundle +import com.gh.base.BaseActivity +import com.gh.common.util.DisplayUtils +import com.gh.gamecenter.R + +class GameCollectionSquareActivity : BaseActivity() { + override fun getLayoutId(): Int { + return R.layout.activity_amway + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + DisplayUtils.transparentStatusBar(this) + + val containerFragment = supportFragmentManager.findFragmentByTag( + GameCollectionSquareFragment::class.java.simpleName) + ?: GameCollectionSquareFragment().with(intent.extras) + supportFragmentManager.beginTransaction().replace(R.id.placeholder, containerFragment, GameCollectionSquareFragment::class.java.simpleName).commitAllowingStateLoss() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt new file mode 100644 index 0000000000..98d2c8e3df --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt @@ -0,0 +1,128 @@ +package com.gh.gamecenter.gamecollection.square + +import android.content.Context +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.ItemViewType +import com.gh.common.exposure.ExposureEvent +import com.gh.common.exposure.ExposureSource +import com.gh.common.exposure.IExposable +import com.gh.common.util.DisplayUtils +import com.gh.common.util.toDrawable +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding +import com.gh.gamecenter.databinding.GameCollectionSquareItemBinding +import com.gh.gamecenter.entity.GamesCollectionEntity + +class GameCollectionSquareAdapter( + context: Context, + private val isHome: Boolean = false, + private val mViewModel: GameCollectionSquareViewModel, + private var basicExposureSource: List +) : + ListAdapter(context), IExposable { + + override fun getItemViewType(position: Int): Int { + return if (isHome && position == 0) ItemViewType.ITEM_HEADER else if (position == itemCount - 1) ItemViewType.ITEM_FOOTER else ItemViewType.ITEM_BODY + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_HEADER -> { + GameCollectionAmwayViewHolder( + GameCollectionSquareAmwayItemBinding.bind( + mLayoutInflater.inflate( + R.layout.game_collection_square_amway_item, + parent, + false + ) + ) + ) + } + + ItemViewType.ITEM_BODY -> { + GameCollectionSquareItemViewHolder( + GameCollectionSquareItemBinding.bind( + mLayoutInflater.inflate( + R.layout.game_collection_square_item, + parent, + false + ) + ) + ) + } + + ItemViewType.ITEM_FOOTER -> FooterViewHolder( + mLayoutInflater.inflate( + R.layout.refresh_footerview, + parent, + false + ) + ) + + else -> GameCollectionSquareItemViewHolder( + GameCollectionSquareItemBinding.bind( + mLayoutInflater.inflate( + R.layout.game_collection_square_item, + parent, + false + ) + ) + ) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GameCollectionAmwayViewHolder -> { + holder.bindAmway() + } + is GameCollectionSquareItemViewHolder -> { + val realPosition = if (isHome) position - 1 else position + holder.bindGameCollection(mEntityList[realPosition]) + } + is FooterViewHolder -> { + holder.initItemPadding() + holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + holder.hint.setTextColor(ContextCompat.getColor(mContext, R.color.text_B3B3B3)) + val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams + lp.height = DisplayUtils.dip2px(48F) + lp.width = ViewGroup.LayoutParams.MATCH_PARENT + holder.itemView.layoutParams = lp + } + } + } + + override fun getItemCount() = if (mEntityList.isNullOrEmpty()) 0 else if (isHome) mEntityList.size + 2 else mEntityList.size + 1 + + class GameCollectionSquareItemViewHolder(var binding: GameCollectionSquareItemBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bindGameCollection(gamesCollectionEntity: GamesCollectionEntity) { + binding.run { + entity = gamesCollectionEntity + stampIv.setImageDrawable(if (gamesCollectionEntity.stamp == "offical") R.drawable.ic_official.toDrawable() else R.drawable.ic_chosen.toDrawable()) + } + } + } + + companion object { + const val INVALID_VALUE = -1 + + // 安利墙卡片轮播时间 + const val AMWAY_LOOP_TIME = 5000L + } + + override fun getEventByPosition(pos: Int): ExposureEvent? { +// return mEntityList[pos].exposureEvent + return null + } + + override fun getEventListByPosition(pos: Int): List? { +// return mEntityList[pos].exposureEventList + return null + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt new file mode 100644 index 0000000000..f662d4c641 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -0,0 +1,313 @@ +package com.gh.gamecenter.gamecollection.square + +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.TimeElapsedHelper +import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.ExposureSource +import com.gh.common.util.* +import com.gh.common.view.VerticalItemDecoration +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LazyListFragment +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.FragmentGameCollectionSquareAlBinding +import com.gh.gamecenter.databinding.FragmentGameCollectionSquareBinding +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.eventbus.EBReuse +import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity +import com.gh.gamecenter.personal.PersonalFragment +import com.google.android.material.appbar.AppBarLayout +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import kotlin.math.abs + +class GameCollectionSquareFragment : LazyListFragment() { + private lateinit var mViewModel: GameCollectionSquareViewModel + private val mElapsedHelper by lazy { TimeElapsedHelper() } + private lateinit var mExposureListener: ExposureListener + + private lateinit var mDefaultBinding: FragmentGameCollectionSquareBinding + private lateinit var mAlternativeBinding: FragmentGameCollectionSquareAlBinding + + private var mAdapter: GameCollectionSquareAdapter? = null + + private var mUseAlternativeLayout = false + + private val dataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { +// mAdapter?.notifyItemByDownload(downloadEntity) + +// if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { +// showUnzipFailureDialog(downloadEntity) +// } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + mUseAlternativeLayout = arguments?.getBoolean(EntranceUtils.KEY_IS_HOME, false) ?: false + super.onCreate(savedInstanceState) + } + + override fun getLayoutId() = R.layout.fragment_stub + + override fun getRealLayoutId(): Int { + return if (mUseAlternativeLayout) R.layout.fragment_game_collection_square_al else R.layout.fragment_game_collection_square + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mViewModel.initData() + mViewModel.entrance = mEntrance + } + + override fun onRealLayoutInflated(inflatedView: View) { + super.onRealLayoutInflated(inflatedView) + if (mUseAlternativeLayout) { + mAlternativeBinding = FragmentGameCollectionSquareAlBinding.bind(inflatedView) + } else { + mDefaultBinding = FragmentGameCollectionSquareBinding.bind(inflatedView) + } + } + + override fun initRealView() { + super.initRealView() + + mExposureListener = ExposureListener(this, mAdapter!!) + mListRv.addOnScrollListener(mExposureListener) + + if (!mUseAlternativeLayout) { + initDefaultLayout() + } else { + initAlternativeLayout() + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_SELECT_TAG) { + val tagName = data?.getStringExtra("tagName") + mDefaultBinding.tagTv.text = tagName ?: "标签筛选" + } + } + + override fun provideListViewModel(): GameCollectionSquareViewModel { + mViewModel = viewModelProvider() + return mViewModel + } + + override fun getItemDecoration() = VerticalItemDecoration(context, 16F, false, R.color.white) + + override fun provideListAdapter(): ListAdapter<*> { + if (mAdapter == null) { + val basicExposureSource = arrayListOf().apply { + arguments?.getParcelable(EntranceUtils.KEY_EXPOSURE_SOURCE)?.let { + add(it) + } + add(ExposureSource("游戏单广场", "")) + } + mAdapter = GameCollectionSquareAdapter(requireContext(), mUseAlternativeLayout, mViewModel, basicExposureSource) + } + return mAdapter!! + } + + override fun isAutomaticLoad() = false + + private fun initDefaultLayout() { + // toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding + // [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits] + ViewCompat.setOnApplyWindowInsetsListener(mDefaultBinding.appbar) { _, insets -> + (mDefaultBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop + insets.consumeSystemWindowInsets() + } + + val collapsingTrigger = 66F.dip2px() + DisplayUtils.getStatusBarHeight(context?.resources) + + mDefaultBinding.toolbar.setNavigationOnClickListener { requireActivity().finish() } + + mDefaultBinding.collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger + mDefaultBinding.collapsingToolbar.scrimShownAction = { + DisplayUtils.setLightStatusBar(requireActivity(), it) + if (it) { + mDefaultBinding.titleTv.alpha = 1F + mDefaultBinding.titleTv.visibility = View.VISIBLE + mDefaultBinding.titleTv.setTextColor(R.color.black.toColor()) + mDefaultBinding.toolbar.navigationIcon = R.drawable.ic_bar_back.toDrawable() + } else { + mDefaultBinding.titleTv.visibility = View.GONE + mDefaultBinding.toolbar.navigationIcon = R.drawable.ic_bar_back_light.toDrawable() + } + } + + mDefaultBinding.titleTv.setOnClickListener { + if (ClickUtils.isFastDoubleClick(mDefaultBinding.titleTv.id, 300)) { + scrollToTop() + } + } + + mDefaultBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + val absOffset = abs(verticalOffset) + val invisibleOffset = DisplayUtils.dip2px(30F) + + if (absOffset <= invisibleOffset) { + mDefaultBinding.titleTv.alpha = 1 - (absOffset.toFloat() / invisibleOffset) + } else { + mDefaultBinding.titleTv.alpha = 0F + } + + mListRefresh?.isEnabled = absOffset <= 2 + }) + + mDefaultBinding.orderRg.setOnCheckedChangeListener { _, checkedId -> + if (checkedId == R.id.hotRb) { + // 热门排序 + // TODO 热门排序 + } else { + // 最新排序 + // TODO 最新排序 + } + } + + mDefaultBinding.fab.setOnClickListener { + // 创建游戏单 + ifLogin(mEntrance) { + showRegulationTestDialogIfNeeded { + startActivity(GameCollectionEditActivity.getIntent(requireContext())) + } + } + } + + mDefaultBinding.tagFilter.setOnClickListener { + startActivityForResult(GameCollectionTagSelectActivity.getIntent(requireContext(), true), REQUEST_SELECT_TAG) + } + + mListRefresh?.setProgressViewOffset(false, 0, 118F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources)) +// mSkeletonScreen = Skeleton.bind(mDefaultBinding?.skeletonPlaceholder).shimmer(false).load(R.layout.fragment_amway_skeleton).show() + } + + private fun initAlternativeLayout() { + mAlternativeBinding.fab.setOnClickListener { + // 创建游戏单 + ifLogin(mEntrance) { + showRegulationTestDialogIfNeeded { + startActivity(GameCollectionEditActivity.getIntent(requireContext())) + } + } + } +// mAlternativeBinding.listRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { +// override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { +// super.onScrolled(recyclerView, dx, dy) +// val scrollY = recyclerView.computeVerticalScrollOffset() +// } +// }) + +// mSkeletonScreen = Skeleton.bind(mAlternativeBinding?.skeletonPlaceholder).shimmer(false).load( +// R.layout.fragment_amway_skeleton_al).show() + } + + override fun onFragmentPause() { + super.onPause() + DownloadManager.getInstance(context).removeObserver(dataWatcher) + +// mElapsedHelper.pauseCounting() + } + + override fun onResume() { +// if (isEverPause) mAdapter?.notifyDataSetChanged() + super.onResume() + } + + override fun onFragmentResume() { + super.onFragmentResume() + DownloadManager.getInstance(context).addObserver(dataWatcher) + +// mElapsedHelper.resetCounting() +// mElapsedHelper.resumeCounting() + } + + override fun onLoadEmpty() { + super.onLoadEmpty() + if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.GONE + } + + override fun onLoadDone() { + super.onLoadDone() + if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.VISIBLE + } + + override fun onLoadError() { + super.onLoadError() + if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.GONE + } + + override fun onLoadRefresh() { + super.onLoadRefresh() + if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.GONE + } + + private fun scrollToTop() { + val firstItemPosition = mLayoutManager.findFirstVisibleItemPosition() + if (firstItemPosition >= 10) { + mListRv.scrollToPosition(6) + } + mListRv.smoothScrollToPosition(0) + mDefaultBinding.appbar.setExpanded(true) + } + + + //下载被删除事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + if ("delete" == status.status) { +// mAdapter?.notifyItemAndRemoveDownload(status) + } + } + + //安装、卸载事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(busFour: EBPackage) { +// val data = mAdapter?.getGameEntityByPackage(busFour.packageName) ?: arrayListOf() +// for (gameAndPosition in data) { +// mAdapter?.notifyChildItem(gameAndPosition.position) +// } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(reuse: EBReuse) { + if ("Refresh" == reuse.type) { +// mAdapter?.notifyDataSetChanged() + } else if (reuse.type == PersonalFragment.LOGIN_TAG) { // 登入 + scrollToTop() + onRefresh() +// mViewModel.initData(false) + } + } + + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { +// val data = mAdapter?.getGameEntityByPackage(downloadEntity.packageName) ?: return +// for (gameAndPosition in data) { +// val targetView = mLayoutManager.findViewByPosition(gameAndPosition.position) +// if (targetView != null) { +// if (targetView is RecyclerView) { +// // todo 如果时竖向专题该怎么判断? +// } else { +// DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) +// return +// } +// } +// } + } + + companion object { + const val REQUEST_SELECT_TAG = 100 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareViewModel.kt new file mode 100644 index 0000000000..82d5a8ce9a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareViewModel.kt @@ -0,0 +1,31 @@ +package com.gh.gamecenter.gamecollection.square + +import android.app.Application +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.Observable +import io.reactivex.Single + +class GameCollectionSquareViewModel(application: Application) : + ListViewModel(application) { + + var entrance: String? = null + + init { + initData() + } + + fun initData() { + + } + +// override fun provideDataSingle(page: Int): Single> = RetrofitManager.getInstance(getApplication()) +// .api.getGameCollectionSquareList(page) + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + } + + override fun provideDataObservable(page: Int): Observable>? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackAnimation.kt b/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackAnimation.kt new file mode 100644 index 0000000000..010e20c8b3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackAnimation.kt @@ -0,0 +1,144 @@ +package com.gh.gamecenter.home.gamecollection + +import android.view.View +import com.gh.common.util.DisplayUtils +import com.gh.common.view.stacklayoutmanager.StackAnimation +import com.gh.common.view.stacklayoutmanager.StackLayoutManager +import com.gh.gamecenter.R +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import kotlin.math.pow + +class GameCollectionStackAnimation( + scrollOrientation: StackLayoutManager.ScrollOrientation, + visibleCount: Int +) : StackAnimation(scrollOrientation, visibleCount) { + private var mScale = 0.9F + private var mOutScale = 1.0F + private var mOutRotation = 0 + + /** + * 设置 item 缩放比例. + * @param scale 缩放比例 + */ + fun setItemScaleRate(scale: Float) { + mScale = scale + } + + /** + * 获取item缩放比例. + * @return item缩放比例 + */ + fun getItemScaleRate(): Float { + return mScale + } + + /** + * 设置 itemView 离开屏幕时候的缩放比例. + * @param scale 缩放比例 + */ + fun setOutScale(scale: Float) { + mOutScale = scale + } + + /** + * 获取 itemView 离开屏幕时候的缩放比例. + * @return 缩放比例 + */ + fun getOutScale(): Float { + return mOutScale + } + + /** + * 设置 itemView 离开屏幕时候的旋转角度. + * @param rotation 旋转角度 + */ + fun setOutRotation(rotation: Int) { + mOutRotation = rotation + } + + /** + * 获取 itemView 离开屏幕时候的旋转角度 + * @return 旋转角度 + */ + fun getOutRotation(): Int { + return mOutRotation + } + + override fun doAnimation(firstMovePercent: Float, itemView: View, position: Int) { + val cover = itemView.findViewById(R.id.cover) + val content = itemView.findViewById(R.id.content) + val scale: Float + var alpha = 1.0F + val rotation: Float + if (position == 0) { + // 顶层item透明度变化 + cover.alpha = 0F + content.alpha = 1F + if (firstMovePercent > 0.5) alpha = 1F - 2 * (firstMovePercent - 0.5F) + scale = 1 - ((1 - mOutScale) * firstMovePercent) + rotation = mOutRotation * firstMovePercent + } else { + val minScale = (mScale.toDouble().pow(position.toDouble())).toFloat() + val maxScale = (mScale.toDouble().pow((position - 1).toDouble())).toFloat() + scale = minScale + (maxScale - minScale) * firstMovePercent + when (position) { + 1 -> { + cover.alpha = 0.3F - 0.3F * firstMovePercent + content.alpha = firstMovePercent + } + 2 -> cover.alpha = 0.6F - 0.3F * firstMovePercent + 3 -> { + cover.alpha = 1F - 0.4F * firstMovePercent + // 如果不改变会出现残留边框 +// alpha = if (firstMovePercent == 0F) 0F else 1F + } + } + rotation = 0F + } + + setItemPivotXY(mScrollOrientation, itemView) + rotationFirstVisibleItem(mScrollOrientation, itemView, rotation) + itemView.scaleX = scale + itemView.scaleY = scale + itemView.alpha = alpha + } + + private fun setItemPivotXY( + scrollOrientation: StackLayoutManager.ScrollOrientation, + view: View + ) { + when (scrollOrientation) { + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> { + view.pivotX = view.measuredWidth.toFloat() + view.pivotY = view.measuredHeight.toFloat() / 2 + } + StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> { + view.pivotX = 0F + view.pivotY = view.measuredHeight.toFloat() / 2 + } + StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> { + view.pivotX = view.measuredWidth.toFloat() / 2 + view.pivotY = view.measuredHeight.toFloat() + } + StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> { + view.pivotX = view.measuredWidth.toFloat() / 2 + view.pivotY = 0F + } + } + } + + private fun rotationFirstVisibleItem( + scrollOrientation: StackLayoutManager.ScrollOrientation, + view: View, + rotation: Float + ) { + when (scrollOrientation) { + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> view.rotationY = rotation + StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> view.rotationY = -rotation + StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> view.rotationX = -rotation + StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> view.rotationX = rotation + } + } + +} diff --git a/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackLayout.kt b/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackLayout.kt new file mode 100644 index 0000000000..67fd5e3bfc --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/gamecollection/GameCollectionStackLayout.kt @@ -0,0 +1,110 @@ +package com.gh.gamecenter.home.gamecollection + +import android.view.View +import com.gh.common.util.dip2px +import com.gh.common.view.stacklayoutmanager.StackLayout +import com.gh.common.view.stacklayoutmanager.StackLayoutManager + +class GameCollectionStackLayout( + scrollOrientation: StackLayoutManager.ScrollOrientation, + visibleCount: Int, + perItemOffset: Int +) : StackLayout(scrollOrientation, visibleCount, perItemOffset) { + + private var mHasMeasureItemSize = false + private var mWidthSpace = 0 + private var mHeightSpace = 0 + private var mStartMargin = 0 + + private var mWidth = 0 + private var mHeight = 0 + private var mScrollOffset = 0 + + override fun doLayout( + stackLayoutManager: StackLayoutManager, + scrollOffset: Int, + firstMovePercent: Float, + itemView: View, + position: Int + ) { + mWidth = stackLayoutManager.width + mHeight = stackLayoutManager.height + mScrollOffset = scrollOffset + if (!mHasMeasureItemSize) { + mWidthSpace = + mWidth - stackLayoutManager.getDecoratedMeasuredWidth(itemView) - 18F.dip2px() + mHeightSpace = mHeight - stackLayoutManager.getDecoratedMeasuredHeight(itemView) + mStartMargin = 16F.dip2px() + mHasMeasureItemSize = true + } + val left: Int + val top: Int + if (position == 0) { + left = getFirstVisibleItemLeft() + top = getFirstVisibleItemTop() + } else { + left = getAfterFirstVisibleItemLeft(position, firstMovePercent) + top = getAfterFirstVisibleItemTop(position, firstMovePercent) + } + + val right = left + stackLayoutManager.getDecoratedMeasuredWidth(itemView) + val bottom = top + stackLayoutManager.getDecoratedMeasuredHeight(itemView) + + stackLayoutManager.layoutDecorated(itemView, left, top, right, bottom) + } + + override fun requestLayout() { + mHasMeasureItemSize = false //表示尺寸可能发生了改变 + } + + private fun getFirstVisibleItemLeft(): Int { + return when (mScrollOrientation) { + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> mStartMargin - mScrollOffset % mWidth + StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> { + return if (mScrollOffset % mWidth == 0) { + mStartMargin + } else { + mStartMargin + (mWidth - mScrollOffset % mWidth) + } + } + else -> mWidthSpace / 2 + } + } + + private fun getFirstVisibleItemTop(): Int { + return when (mScrollOrientation) { + StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> mStartMargin - mScrollOffset % mHeight + StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> { + return if (mScrollOffset % mHeight == 0) { + mStartMargin + } else { + mStartMargin + (mHeight - mScrollOffset % mHeight) + } + } + else -> mHeightSpace / 2 + } + } + + private fun getAfterFirstVisibleItemLeft(visiblePosition: Int, movePercent: Float): Int { + return when (mScrollOrientation) { + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> (mStartMargin + mPerItemOffset * (visiblePosition - movePercent)).toInt() + StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> (mStartMargin - mPerItemOffset * (visiblePosition - movePercent)).toInt() + else -> mWidthSpace / 2 + } + } + + private fun getAfterFirstVisibleItemTop(visiblePosition: Int, movePercent: Float): Int { + return when (mScrollOrientation) { + StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> (mStartMargin + mPerItemOffset * (visiblePosition - movePercent)).toInt() + StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> (mStartMargin - mPerItemOffset * (visiblePosition - movePercent)).toInt() + else -> mHeightSpace / 2 + } + } + + private fun getStartMargin(): Int { + return when (mScrollOrientation) { + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT, StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> mWidthSpace / 2 + else -> mHeightSpace / 2 + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionAdapter.kt new file mode 100644 index 0000000000..52c3f38716 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionAdapter.kt @@ -0,0 +1,65 @@ +package com.gh.gamecenter.home.gamecollection + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.HomeGameCollectionCardItemBinding +import com.gh.gamecenter.entity.GamesCollectionEntity +import com.lightgame.adapter.BaseRecyclerAdapter + +class HomeGameCollectionAdapter(context: Context) : + BaseRecyclerAdapter(context) { + + private var mGameCollectionList = ArrayList() + + fun setGameCollectionList(gameCollectionList: ArrayList) { + mGameCollectionList = gameCollectionList + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = HomeGameCollectionCardViewHolder( + HomeGameCollectionCardItemBinding.bind( + mLayoutInflater.inflate( + R.layout.home_game_collection_card_item, + parent, + false + ) + ) + ) + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is HomeGameCollectionCardViewHolder) { + var realPosition: Int + when (position) { + 0 -> { + realPosition = getRealCount() - 2 + } + 1 -> { + realPosition = getRealCount() - 1 + } + itemCount - 2 -> { + realPosition = 0 + } + itemCount - 1 -> { + realPosition = 1 + } + else -> { + realPosition = position - 2 + } + } + holder.bindGameListCard(position, mGameCollectionList[realPosition]) + } + } + + override fun getItemCount() = getRealCount() + 4 + + fun getRealCount() = mGameCollectionList.size + + class HomeGameCollectionCardViewHolder(val binding: HomeGameCollectionCardItemBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bindGameListCard(itemPosition: Int, entity: GamesCollectionEntity) { + binding.entity = entity + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionViewHolder.kt new file mode 100644 index 0000000000..167ebfc8d1 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/gamecollection/HomeGameCollectionViewHolder.kt @@ -0,0 +1,46 @@ +package com.gh.gamecenter.home.gamecollection + +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.dip2px +import com.gh.common.view.stacklayoutmanager.StackLayoutManager +import com.gh.gamecenter.databinding.HomeGameCollectionItemBinding +import com.gh.gamecenter.entity.GamesCollectionEntity + +class HomeGameCollectionViewHolder(val binding: HomeGameCollectionItemBinding) : + BaseRecyclerViewHolder(binding.root) { + fun bindGameCollectionList(gameCollectionList: ArrayList) { + if (binding.recyclerView.adapter is HomeGameCollectionAdapter) { + return + } + val adapter = HomeGameCollectionAdapter(binding.root.context) + val manager = StackLayoutManager( + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT, + 3, + GameCollectionStackAnimation::class.java, + GameCollectionStackLayout::class.java + ) + manager.setItemOffset(9F.dip2px()) + manager.setItemChangedListener(object : StackLayoutManager.ItemChangedListener { + override fun onItemChanged(position: Int) { + when (position) { + 0 -> binding.recyclerView.scrollToPosition(adapter.itemCount - 3) + // 最后一个item跳转 + adapter.itemCount - 3 -> binding.recyclerView.scrollToPosition(1) + } + } + }) + binding.recyclerView.layoutManager = manager + binding.recyclerView.adapter = adapter + binding.recyclerView.isNestedScrollingEnabled = false + + adapter.setGameCollectionList(gameCollectionList) + binding.recyclerView.post { + // 定位到实际的第一个item + manager.scrollToPosition(FIRST_ITEM_POSITION) + } + } + + companion object { + const val FIRST_ITEM_POSITION = 2 + } +} \ No newline at end of file diff --git a/app/src/main/res/color/game_collection_rg_button_selector.xml b/app/src/main/res/color/game_collection_rg_button_selector.xml new file mode 100644 index 0000000000..948223d8e9 --- /dev/null +++ b/app/src/main/res/color/game_collection_rg_button_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/game_collection_square_head.webp b/app/src/main/res/drawable-xxxhdpi/game_collection_square_head.webp new file mode 100644 index 0000000000000000000000000000000000000000..5ad47779859f52f0fb5cbef83db48424fc298174 GIT binary patch literal 101002 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_hot.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_hot.webp new file mode 100644 index 0000000000000000000000000000000000000000..1ac9abd47de6f62e65b4d2ee8e132fc1c24ff831 GIT binary patch literal 874 zcmWIYbaP8$W?%?+bqWXzusjua zciZfG&$N%mALcPKfB*->41R^21N+10Gzjo4PBAe0k?hCG7T)-`TCiMJ@sGkYgBTX> zBaR|$ZR?mf`aWP#ne5BZuubLmvRv){+}qo7r^)y!zv8{nT$2xiRuZ(dXaa zns@M7kkH3_SKi&Ze))F_!#bUnKXM$e%ls&H&i8%HRrK-fqrNp+x9(ot!FcjVU7-28 zyi>L2E$PbF>%H$d+->{NJ@ro9ysmfGM0NG=OI=&~;#E}TRQIg&^3Qd0e?NLF8zH|# zw!nLBK}-Lwq}q9>nYPZK_GTT=J(&ZhxqGZN&N|y}f90;Xj^$gzpY^$g7H5Au&UN|U zaL{()>RT!IPCVOCZ)|hTKsbyN*Qrjm>mmOI3 zVUyD~wcC21W+hzV?v(!H_GGK(&)$Q}ek?aXTz0?i^(?Wq-O6Gq$K5t4Pm1DJc2hg5 zw{q*hQ|~^m(B9l@tS&Hn>SZb64BIJrclfh5&b3@!$rybpCa&9d(q^lW=vrHgvmN@c zc={0++>+1V! tAM@G&xqI?F%jw@Ig(nBIUq82eLF#Y)eJ}t0&foXsz$eG&8{cO!0swbUvWfrz literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_amway_go.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_amway_go.webp new file mode 100644 index 0000000000000000000000000000000000000000..650b241e801ea5793bb4cb0df9b8e6a72a6768aa GIT binary patch literal 10126 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/bg_game_collection_hot_container.xml b/app/src/main/res/drawable/bg_game_collection_hot_container.xml new file mode 100644 index 0000000000..ed2cefd0ca --- /dev/null +++ b/app/src/main/res/drawable/bg_game_collection_hot_container.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_square.xml b/app/src/main/res/drawable/bg_game_collection_square.xml new file mode 100644 index 0000000000..3349ffadb0 --- /dev/null +++ b/app/src/main/res/drawable/bg_game_collection_square.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_user_container.xml b/app/src/main/res/drawable/bg_game_collection_user_container.xml new file mode 100644 index 0000000000..4bfcfd216a --- /dev/null +++ b/app/src/main/res/drawable/bg_game_collection_user_container.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_game_collection.xml b/app/src/main/res/drawable/progressbar_game_collection.xml new file mode 100644 index 0000000000..f36ccf6bc0 --- /dev/null +++ b/app/src/main/res/drawable/progressbar_game_collection.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_square.xml b/app/src/main/res/layout/fragment_game_collection_square.xml new file mode 100644 index 0000000000..1c49ee7234 --- /dev/null +++ b/app/src/main/res/layout/fragment_game_collection_square.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_square_al.xml b/app/src/main/res/layout/fragment_game_collection_square_al.xml new file mode 100644 index 0000000000..08a4f98669 --- /dev/null +++ b/app/src/main/res/layout/fragment_game_collection_square_al.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_amway_content_item.xml b/app/src/main/res/layout/game_collection_amway_content_item.xml new file mode 100644 index 0000000000..68fbd48317 --- /dev/null +++ b/app/src/main/res/layout/game_collection_amway_content_item.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_square_amway_item.xml b/app/src/main/res/layout/game_collection_square_amway_item.xml new file mode 100644 index 0000000000..2bc937e1b9 --- /dev/null +++ b/app/src/main/res/layout/game_collection_square_amway_item.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_square_item.xml b/app/src/main/res/layout/game_collection_square_item.xml new file mode 100644 index 0000000000..79781dbb6c --- /dev/null +++ b/app/src/main/res/layout/game_collection_square_item.xml @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_collection_card_item.xml b/app/src/main/res/layout/home_game_collection_card_item.xml new file mode 100644 index 0000000000..3cc9a68141 --- /dev/null +++ b/app/src/main/res/layout/home_game_collection_card_item.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_tag_body_item.xml b/app/src/main/res/layout/home_game_collection_item.xml similarity index 58% rename from app/src/main/res/layout/game_collection_tag_body_item.xml rename to app/src/main/res/layout/home_game_collection_item.xml index 402bc80584..b81cada242 100644 --- a/app/src/main/res/layout/game_collection_tag_body_item.xml +++ b/app/src/main/res/layout/home_game_collection_item.xml @@ -1,20 +1,17 @@ + android:paddingTop="16dp"> - - \ No newline at end of file From 772aa7ca03e1e2001c2f95772f4b45a30cd6fa4b Mon Sep 17 00:00:00 2001 From: leafwai Date: Mon, 15 Nov 2021 17:38:13 +0800 Subject: [PATCH 13/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(=E9=80=89=E6=8B=A9=E6=A0=87=E7=AD=BEUI)https://git.gh?= =?UTF-8?q?zs.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/GameCollectionTagEntity.kt | 23 ++++ .../publish/GameCollectionEditActivity.kt | 2 +- .../square/GameCollectionTagSelectFragment.kt | 93 --------------- .../GameCollectionTagAdapter.kt | 109 ++++++++---------- .../GameCollectionTagSelectActivity.kt | 7 +- .../tag/GameCollectionTagSelectFragment.kt | 82 +++++++++++++ .../tag/GameCollectionTagViewModel.kt | 33 ++++++ .../game_collection_tag_header_item.xml | 19 --- .../res/layout/game_collection_tag_item.xml | 34 ++++++ 9 files changed, 223 insertions(+), 179 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt delete mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectFragment.kt rename app/src/main/java/com/gh/gamecenter/gamecollection/{square => tag}/GameCollectionTagAdapter.kt (50%) rename app/src/main/java/com/gh/gamecenter/gamecollection/{square => tag}/GameCollectionTagSelectActivity.kt (74%) create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt delete mode 100644 app/src/main/res/layout/game_collection_tag_header_item.xml create mode 100644 app/src/main/res/layout/game_collection_tag_item.xml diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt new file mode 100644 index 0000000000..4cb615d52b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt @@ -0,0 +1,23 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class GameCollectionTagEntity( + @SerializedName("category_id") + val categoryId: String = "", + @SerializedName("name") + val categoryName: String = "", + val tagList: List = ArrayList() +) : Parcelable { + + @Parcelize + data class GameCollectionTag( + @SerializedName("tag_id") + val tagId: String = "", + @SerializedName("name") + val tagName: String = "" + ) : Parcelable +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index a892f1be79..e2dcf6bed5 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -17,7 +17,7 @@ import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel -import com.gh.gamecenter.gamecollection.square.GameCollectionTagSelectActivity +import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.qa.editor.LocalMediaActivity import com.zhihu.matisse.Matisse diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectFragment.kt deleted file mode 100644 index 2ae23e018b..0000000000 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectFragment.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.gh.gamecenter.gamecollection.square - -import android.content.Intent -import android.os.Bundle -import android.view.MenuItem -import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.gh.gamecenter.R -import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding -import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding -import com.gh.gamecenter.normal.NormalFragment - -class GameCollectionTagSelectFragment : NormalFragment() { - - private var mBinding: FragmentGameCollectionTagSelectBinding? = null - private var mAdapter: GameCollectionTagAdapter? = null - private var firstVisibleItemPosition = 0 - - private val updateSelectedTagView: (() -> Unit) = { - mAdapter?.multipleTagList?.let { list -> - mBinding?.selectedTagContainer?.removeAllViews() - for (name in list) { - val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(layoutInflater) - selectedTagView.run { - tagTv.text = name - root.setOnClickListener { - list.remove(name) - mBinding?.selectedTagContainer?.removeView(selectedTagView.root) - mAdapter?.notifyDataSetChanged() - mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE - } - } - mBinding?.selectedTagContainer?.addView(selectedTagView.root) - } - mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE - } - } - - override fun getLayoutId(): Int = 0 - - override fun getInflatedLayout() = - FragmentGameCollectionTagSelectBinding.inflate(layoutInflater).apply { - mBinding = this - }.root - - override fun onMenuItemClick(menuItem: MenuItem?) { -// if (menuItem?.itemId == R.id.layout_menu_save) { -// requireActivity().setResult( -// GameCollectionSquareFragment.TAG_SELECT_REQUEST, -// Intent().putExtra("tagName", mAdapter?.singleTagName) -// ) -// requireActivity().finish() -// } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val data = arrayListOf( - "游戏玩法", - "body", - "游戏题材", - "body", - "游戏题材", - "body", - "游戏玩法", - "body", - "游戏玩法", - "body", - "游戏玩法", - "body", - "游戏玩法", - "body" - ) - mAdapter = GameCollectionTagAdapter(requireContext(), true, data, updateSelectedTagView) - mBinding?.tagRv?.run { - layoutManager = LinearLayoutManager(requireContext()) - adapter = mAdapter - addOnScrollListener(object :RecyclerView.OnScrollListener(){ - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - when (newState) { - RecyclerView.SCROLL_STATE_IDLE -> firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - RecyclerView.SCROLL_STATE_DRAGGING -> firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - } - mBinding?.selectedTagScrollView?.visibility = if (firstVisibleItemPosition == 0 || mAdapter?.multipleTagList?.isEmpty() == true) View.GONE else View.VISIBLE - } - }) - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt similarity index 50% rename from app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt index 65b9c8a3e0..a79e11dc25 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.gamecollection.square +package com.gh.gamecenter.gamecollection.tag import android.content.Context import android.view.View @@ -7,38 +7,36 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DisplayUtils import com.gh.common.util.dip2px import com.gh.gamecenter.databinding.* +import com.gh.gamecenter.entity.GameCollectionTagEntity import com.lightgame.adapter.BaseRecyclerAdapter class GameCollectionTagAdapter( context: Context, val singleChoice: Boolean = true, - val data: ArrayList, private val updateCallback: (() -> Unit) ) : BaseRecyclerAdapter(context) { private var mTagViewList = arrayListOf() - var singleTagName = "" - var multipleTagList = arrayListOf() + var mTagList = arrayListOf() + var singleTag: GameCollectionTagEntity.GameCollectionTag? = null + var multipleTagList = arrayListOf() + fun setTagList(tagList: ArrayList) { + mTagList = tagList + notifyDataSetChanged() + } override fun getItemViewType(position: Int): Int { - return if (singleChoice) { - if (position % 2 == 0) TAG_HEADER else TAG_BODY - } else { - if (position == 0) SELECTED_TAGS else if (position % 2 != 0) TAG_HEADER else TAG_BODY - } + return if (!singleChoice && position == 0) SELECTED_TAGS else TAG_ITEM } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType){ SELECTED_TAGS -> GameCollectionSelectedTagViewHolder( GameCollectionSelectedTagItemBinding.inflate(mLayoutInflater, parent, false) ) - TAG_HEADER -> GameCollectionTagHeaderViewHolder( - GameCollectionTagHeaderItemBinding.inflate(mLayoutInflater, parent, false) - ) - else -> GameCollectionTagBodyViewHolder( - GameCollectionTagBodyItemBinding.inflate(mLayoutInflater, parent, false) + else -> GameCollectionTagItemViewHolder( + GameCollectionTagItemBinding.inflate(mLayoutInflater, parent, false) ) } @@ -47,13 +45,13 @@ class GameCollectionTagAdapter( is GameCollectionSelectedTagViewHolder -> { holder.binding.selectedTagFlexbox.removeAllViews() holder.binding.hintTv.visibility = if (multipleTagList.size == 0) View.VISIBLE else View.GONE - for (name in multipleTagList) { + for (tag in multipleTagList) { val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(mLayoutInflater) .apply { - tagTv.text = name + tagTv.text = tag.tagName root.setOnClickListener { - multipleTagList.remove(name) + multipleTagList.remove(tag) updateCallback.invoke() notifyDataSetChanged() } @@ -61,87 +59,70 @@ class GameCollectionTagAdapter( holder.binding.selectedTagFlexbox.addView(selectedTagView) } } - is GameCollectionTagHeaderViewHolder -> { - holder.binding.headerTv.text = data[position] - if (position == 0) { - holder.binding.root.setPadding( - 16F.dip2px(), - 40F.dip2px(), - 16F.dip2px(), - 10F.dip2px() - ) - } - } - is GameCollectionTagBodyViewHolder -> { - val dataList = arrayListOf( - "ghgg", - "das5555554a44", - "dasda54544", - "dsdasdasdas", - "dasdsa8787", - "58748sss" - ) + is GameCollectionTagItemViewHolder -> { + val data = mTagList[if (singleChoice) position else position - 1] holder.binding.tagFlexbox.removeAllViews() - for (name in dataList) { - val tag = if(singleChoice) getSingleTag(name) else getMultipleTag(name) + holder.binding.titleTv.text = data.categoryName + holder.binding.titleTv.setPadding( + 16F.dip2px(), + if (position == 0) 40F.dip2px() else 10F.dip2px(), + 16F.dip2px(), + 10F.dip2px() + ) + holder.binding.tagFlexbox.setPadding( + 12F.dip2px(), + 5F.dip2px(), + 12F.dip2px(), + if (position == itemCount - 1) 97F.dip2px() else 15F.dip2px() + ) + for (tagEntity in data.tagList) { + val tag = if(singleChoice) getSingleTag(tagEntity) else getMultipleTag(tagEntity) mTagViewList.add(tag) holder.binding.tagFlexbox.addView(tag.root) } - if (position == data.size - 1) { - holder.binding.root.setPadding( - 12F.dip2px(), - 5F.dip2px(), - 12F.dip2px(), - 97F.dip2px() - ) - } } } } - private fun getSingleTag(name: String) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { - tagTv.text = name + private fun getSingleTag(tag: GameCollectionTagEntity.GameCollectionTag) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { + tagTv.text = tag.tagName tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } root.setOnClickListener { - for (tag in mTagViewList) { - if (tag.tagTv != tagTv) tag.tagTv.isChecked = false + for (tagView in mTagViewList) { + if (tagView.tagTv != tagTv) tagView.tagTv.isChecked = false } tagTv.isChecked = !tagTv.isChecked - if (tagTv.isChecked) singleTagName = tagTv.text.toString() + if (tagTv.isChecked) singleTag = tag } } - private fun getMultipleTag(name: String) = + private fun getMultipleTag(tag: GameCollectionTagEntity.GameCollectionTag) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } - tagTv.text = name - if (multipleTagList.contains(name)) tagTv.isChecked = true + tagTv.text = tag.tagName + if (multipleTagList.contains(tag)) tagTv.isChecked = true root.setOnClickListener { tagTv.isChecked = !tagTv.isChecked if (tagTv.isChecked) { - multipleTagList.add(name) + multipleTagList.add(tag) } else { - if (multipleTagList.contains(name)) multipleTagList.remove(name) + if (multipleTagList.contains(tag)) multipleTagList.remove(tag) } updateCallback.invoke() notifyItemChanged(0) } } - override fun getItemCount() = data.size + override fun getItemCount() = if (singleChoice) mTagList.size else mTagList.size + 1 class GameCollectionSelectedTagViewHolder(var binding: GameCollectionSelectedTagItemBinding) : RecyclerView.ViewHolder(binding.root) - class GameCollectionTagBodyViewHolder(var binding: GameCollectionTagBodyItemBinding) : - RecyclerView.ViewHolder(binding.root) - - class GameCollectionTagHeaderViewHolder(var binding: GameCollectionTagHeaderItemBinding) : + class GameCollectionTagItemViewHolder(var binding: GameCollectionTagItemBinding) : RecyclerView.ViewHolder(binding.root) companion object { const val SELECTED_TAGS = 100 - const val TAG_HEADER = 101 - const val TAG_BODY = 102 + const val TAG_ITEM = 101 } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectActivity.kt similarity index 74% rename from app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectActivity.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectActivity.kt index 1f2fc94203..740baf832b 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionTagSelectActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectActivity.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.gamecollection.square +package com.gh.gamecenter.gamecollection.tag import android.content.Context import android.content.Intent @@ -15,9 +15,12 @@ class GameCollectionTagSelectActivity : NormalActivity() { } companion object { + const val KEY_IS_SINGLE_CHOICE = "single_choice" + @JvmStatic - fun getIntent(context: Context): Intent { + fun getIntent(context: Context, singleChoice: Boolean = false): Intent { val bundle = Bundle() + bundle.putBoolean(KEY_IS_SINGLE_CHOICE, singleChoice) return getTargetIntent( context, GameCollectionTagSelectActivity::class.java, diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt new file mode 100644 index 0000000000..7d22ace58b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt @@ -0,0 +1,82 @@ +package com.gh.gamecenter.gamecollection.tag + +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding +import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding +import com.gh.gamecenter.entity.GameCollectionTagEntity +import com.gh.gamecenter.normal.NormalFragment + +class GameCollectionTagSelectFragment : NormalFragment() { + + private var mSingleChoice = false + private lateinit var mBinding: FragmentGameCollectionTagSelectBinding + private lateinit var mAdapter: GameCollectionTagAdapter + private lateinit var mViewModel: GameCollectionTagViewModel + private var firstVisibleItemPosition = 0 + + private val updateSelectedTagView: (() -> Unit) = { + mAdapter.multipleTagList.let { list -> + mBinding.selectedTagContainer.removeAllViews() + for (tag in list) { + val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(layoutInflater) + selectedTagView.run { + tagTv.text = tag.tagName + root.setOnClickListener { + list.remove(tag) + mBinding.selectedTagContainer.removeView(selectedTagView.root) + mAdapter.notifyDataSetChanged() + mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE + } + } + mBinding.selectedTagContainer.addView(selectedTagView.root) + } + mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE + } + } + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout() = + FragmentGameCollectionTagSelectBinding.inflate(layoutInflater).apply { + mBinding = this + }.root + + override fun onMenuItemClick(menuItem: MenuItem?) { +// if (menuItem?.itemId == R.id.layout_menu_save) { +// requireActivity().setResult( +// GameCollectionSquareFragment.REQUEST_SELECT_TAG, +// Intent().putExtra("tagName", mAdapter.singleTag?.tagName) +// ) +// requireActivity().finish() +// } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mSingleChoice = arguments?.getBoolean(GameCollectionTagSelectActivity.KEY_IS_SINGLE_CHOICE) ?: false + mAdapter = GameCollectionTagAdapter(requireContext(), mSingleChoice, updateSelectedTagView) + mViewModel = viewModelProvider() + mBinding.tagRv.run { + layoutManager = LinearLayoutManager(requireContext()) + adapter = mAdapter + addOnScrollListener(object :RecyclerView.OnScrollListener() { + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || mAdapter.multipleTagList.isEmpty()) View.GONE else View.VISIBLE + } + }) + } + +// mViewModel.tagListLiveData.observe(viewLifecycleOwner) { +// mAdapter.setTagList(it) +// } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt new file mode 100644 index 0000000000..22a8297e3e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt @@ -0,0 +1,33 @@ +package com.gh.gamecenter.gamecollection.tag + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import com.gh.gamecenter.entity.GameCollectionTagEntity +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers + +class GameCollectionTagViewModel(application: Application) : AndroidViewModel(application) { + + var tagListLiveData = MutableLiveData>() + private val mApi = RetrofitManager.getInstance(getApplication()).api + + init { +// getGameCollectionTagList() + } + +// @SuppressLint("CheckResult") +// fun getGameCollectionTagList() { +// mApi.gameCollectionTagList +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(object: BiResponse>(){ +// override fun onSuccess(data: ArrayList) { +// tagListLiveData.postValue(data) +// } +// }) +// } +} \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_tag_header_item.xml b/app/src/main/res/layout/game_collection_tag_header_item.xml deleted file mode 100644 index 65e105061b..0000000000 --- a/app/src/main/res/layout/game_collection_tag_header_item.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_tag_item.xml b/app/src/main/res/layout/game_collection_tag_item.xml new file mode 100644 index 0000000000..7316f7c7fc --- /dev/null +++ b/app/src/main/res/layout/game_collection_tag_item.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file From 49067706b7a0b94f699f61ffc112a89df59c8a3f Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Tue, 16 Nov 2021 15:08:49 +0800 Subject: [PATCH 14/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E7=A4=BE?= =?UTF-8?q?=E5=8C=BA=E7=9B=B8=E5=85=B3=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1595?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 14 ++-- .../com/gh/base/BaseRichEditorActivity.kt | 23 +++++-- .../com/gh/common/util/EntranceUtils.java | 1 + .../collection/GamesCollectionAdapter.kt | 64 +++++++++++++----- .../collection/GamesCollectionFragment.kt | 13 ++-- .../collection/GamesCollectionViewModel.kt | 12 ++-- .../publish/GameCollectionEditActivity.kt | 9 ++- .../InsertGameCollectionWrapperActivity.kt | 48 +++++++++++++ .../qa/editor/OnLinkClickListener.kt | 51 ++++++++------ .../qa/entity/EditorInsertEntity.kt | 28 ++++++-- .../retrofit/service/ApiService.java | 2 +- .../icon_editor_link_game_collection.webp | Bin 0 -> 792 bytes .../activity_tablayout_no_title_viewpager.xml | 2 +- .../res/layout/editor_insert_container.xml | 25 +++++++ 14 files changed, 224 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/qa/editor/InsertGameCollectionWrapperActivity.kt create mode 100644 app/src/main/res/drawable-xxhdpi/icon_editor_link_game_collection.webp diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7c76517d5..62c29feef6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,7 +45,7 @@ com.google.android.exoplayer2, tv.danmaku.ijk.media.exo2, pl.droidsonroids.gif, - com.lzf.easyfloat"/> + com.lzf.easyfloat" /> + android:screenOrientation="portrait" /> - - - + + + + + : ToolBarAct mRichEditor.insertCustomStyleLink(insertData) } } + INSERT_GAME_COLLECTION_CODE -> { + val gameCollectionEntity = data?.getParcelableExtra(GamesCollectionEntity::class.java.simpleName) + if (gameCollectionEntity != null) { + mRichEditor.focusEditor() + insertData = EditorInsertEntity.transform(gameCollectionEntity) + mRichEditor.insertCustomStyleLink(insertData) + } + } REQUEST_CODE_IMAGE -> { if (data != null) mViewModel.uploadPic(data) } @@ -248,7 +254,7 @@ abstract class BaseRichEditorActivity : ToolBarAct R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container, R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article, R.id.editor_link_game, R.id.editor_link_video, R.id.uploadVideoGuideClose, - R.id.originalTipsClose + R.id.originalTipsClose, R.id.editor_link_game_collection ) fun onRichClick(view: View) { when (view.id) { @@ -354,6 +360,12 @@ abstract class BaseRichEditorActivity : ToolBarAct INSERT_GAME_CODE ) } + R.id.editor_link_game_collection -> { + startActivityForResult( + InsertGameCollectionWrapperActivity.getIntent(this), + INSERT_GAME_COLLECTION_CODE + ) + } R.id.editor_link_video -> { chooseVideo() } @@ -714,6 +726,7 @@ abstract class BaseRichEditorActivity : ToolBarAct const val INSERT_ANSWER_CODE = 411 const val INSERT_ARTICLE_CODE = 412 const val INSERT_GAME_CODE = 413 + const val INSERT_GAME_COLLECTION_CODE = 414 const val MAX_INPUT_TEXT_NUM = 10000 const val MAX_MEDIA_COUNT = 20 diff --git a/app/src/main/java/com/gh/common/util/EntranceUtils.java b/app/src/main/java/com/gh/common/util/EntranceUtils.java index 6a061d2211..159bd168c1 100644 --- a/app/src/main/java/com/gh/common/util/EntranceUtils.java +++ b/app/src/main/java/com/gh/common/util/EntranceUtils.java @@ -251,6 +251,7 @@ public class EntranceUtils { public static final String KEY_PARENT_TAG = "parent_tag"; public static final String KEY_BLOCK_ID = "block_id"; public static final String KEY_BLOCK_NAME = "block_name"; + public static final String KEY_INSERT_GAME_COLLECTION = "insert_game_collection"; public static void jumpActivity(Context context, Bundle bundle) { bundle.putBoolean(KEY_REQUIRE_REDIRECT, true); diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt index 9f6251eaa5..83b08f317e 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt @@ -1,8 +1,11 @@ package com.gh.gamecenter.collection +import android.app.Activity import android.content.Context +import android.content.Intent import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType @@ -17,9 +20,10 @@ import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.manager.UserManager -class GamesCollectionAdapter(context: Context, - private val mViewModel: GamesCollectionViewModel -): ListAdapter(context) { +class GamesCollectionAdapter( + context: Context, + private val mViewModel: GamesCollectionViewModel +) : ListAdapter(context) { override fun getItemCount(): Int { return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1 @@ -27,15 +31,22 @@ class GamesCollectionAdapter(context: Context, override fun getItemViewType(position: Int): Int { return if (position == itemCount - 1) { - ItemViewType.ITEM_FOOTER + ItemViewType.ITEM_FOOTER } else { - ItemViewType.ITEM_BODY + ItemViewType.ITEM_BODY } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { - ItemViewType.ITEM_BODY -> GameCollectionItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.game_collection_item, parent, false)) + ItemViewType.ITEM_BODY -> GameCollectionItemViewHolder( + DataBindingUtil.inflate( + mLayoutInflater, + R.layout.game_collection_item, + parent, + false + ) + ) else -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) } @@ -59,19 +70,24 @@ class GamesCollectionAdapter(context: Context, tagIv.setBackgroundResource(R.drawable.ic_official_big) } } + if (mViewModel.mIsInsertGameCollection) { + when (mViewModel.type) { + GamesCollectionFragment.TYPE_COLLECT -> { + userIcon.visibility = View.VISIBLE + userName.visibility = View.VISIBLE + timeTv.visibility = View.GONE + } - when (mViewModel.type) { - GamesCollectionFragment.TYPE_COLLECT -> { - userIcon.visibility = View.VISIBLE - userName.visibility = View.VISIBLE - timeTv.visibility = View.GONE - } - - GamesCollectionFragment.TYPE_USER -> { - userIcon.visibility = View.GONE - userName.visibility = View.GONE - timeTv.visibility = View.VISIBLE + GamesCollectionFragment.TYPE_USER -> { + userIcon.visibility = View.GONE + userName.visibility = View.GONE + timeTv.visibility = View.VISIBLE + } } + } else { + userIcon.visibility = View.VISIBLE + userName.visibility = View.VISIBLE + timeTv.visibility = View.GONE } myselfTag.setOnClickListener { @@ -89,6 +105,20 @@ class GamesCollectionAdapter(context: Context, gameThree.setOnClickListener { GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(2)?.id, "") } + root.setOnClickListener { + if (mViewModel.mIsInsertGameCollection) { + val entity = GamesCollectionEntity().apply { + id = itemEntity.id + title = itemEntity.title + intro = itemEntity.intro + } + val intent = Intent().apply { + putExtra(GamesCollectionEntity::class.java.simpleName, entity) + } + (mContext as AppCompatActivity).setResult(Activity.RESULT_OK, intent) + (mContext as AppCompatActivity).finish() + } + } } } diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt index 083307b33a..84bfc64f50 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt @@ -4,34 +4,35 @@ import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat import com.gh.common.util.* -import com.gh.common.util.EntranceUtils.KEY_TYPE -import com.gh.common.util.EntranceUtils.KEY_USER_ID +import com.gh.common.util.EntranceUtils.* import com.gh.common.view.SpacingItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.entity.GamesCollectionEntity -class GamesCollectionFragment: ListFragment() { +class GamesCollectionFragment : ListFragment() { private var mUserId = "" private var mType = "" + private var mIsInsertGameCollection = false private var mAdapter: GamesCollectionAdapter? = null override fun provideListViewModel() = viewModelProvider( - GamesCollectionViewModel.Factory(mUserId, mType) + GamesCollectionViewModel.Factory(mUserId, mType, mIsInsertGameCollection) ) override fun provideListAdapter() = mAdapter ?: GamesCollectionAdapter(requireContext(), mListViewModel).apply { mAdapter = this } - override fun getItemDecoration() = SpacingItemDecoration(notDecorateTheFirstItem = mType == TYPE_USER, top = 16F.dip2px(), ) + override fun getItemDecoration() = SpacingItemDecoration(notDecorateTheFirstItem = mType == TYPE_USER, top = 16F.dip2px()) override fun onCreate(savedInstanceState: Bundle?) { mUserId = arguments?.getString(KEY_USER_ID, "") ?: "" mType = arguments?.getString(KEY_TYPE, "") ?: "" + mIsInsertGameCollection = arguments?.getBoolean(KEY_INSERT_GAME_COLLECTION, false) ?: false super.onCreate(savedInstanceState) mListRv?.run { overScrollMode = View.OVER_SCROLL_NEVER - if (mType == TYPE_USER) setBackgroundColor(R.color.white.toColor()) + if (!mIsInsertGameCollection && mType == TYPE_USER) setBackgroundColor(R.color.white.toColor()) } } diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt index 950e2719c1..556313fd51 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt @@ -9,13 +9,16 @@ import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.Single -class GamesCollectionViewModel(application: Application, var userId: String, var type: String) : +class GamesCollectionViewModel(application: Application, var userId: String, var type: String, val mIsInsertGameCollection: Boolean = false) : ListViewModel(application) { override fun provideDataObservable(page: Int) = null override fun provideDataSingle(page: Int): Single> { - return RetrofitManager.getInstance(getApplication()).api.getUserGameCollectionList(userId) + val map = if (mIsInsertGameCollection) { + hashMapOf("filter" to "display") + } else mapOf() + return RetrofitManager.getInstance(getApplication()).api.getUserGameCollectionList(userId, map) } override fun mergeResultLiveData() { @@ -24,9 +27,10 @@ class GamesCollectionViewModel(application: Application, var userId: String, var } } - class Factory(private val mUserId: String, private val mType: String) : ViewModelProvider.NewInstanceFactory() { + class Factory(private val mUserId: String, private val mType: String, private val mIsInsertGameCollection: Boolean) : + ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return GamesCollectionViewModel(HaloApp.getInstance().application, mUserId, mType) as T + return GamesCollectionViewModel(HaloApp.getInstance().application, mUserId, mType, mIsInsertGameCollection) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index a892f1be79..a91b3b5f4d 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -67,7 +67,7 @@ class GameCollectionEditActivity : ToolBarActivity() { } mBinding.gameCollectionIntroduceEt.run { doOnTextChanged { text, start, _, _ -> - if(PatternUtils.isHasWrap(text.toString())){ + if (PatternUtils.isHasWrap(text.toString())) { setText(PatternUtils.replaceWrap(text.toString())) setSelection(start) return@doOnTextChanged @@ -172,7 +172,12 @@ class GameCollectionEditActivity : ToolBarActivity() { } } mViewModel.detailLiveData.observe(this) { - mViewModel.gameCollectionPatch = it + mViewModel.gameCollectionPatch?.apply { + games = it.games + tags = it.tags + title = it.title + intro = it.intro + } mViewModel.gameCollectionPatch?.run { //TODO:initTags initData(false) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/InsertGameCollectionWrapperActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/InsertGameCollectionWrapperActivity.kt new file mode 100644 index 0000000000..0baf3555ef --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/InsertGameCollectionWrapperActivity.kt @@ -0,0 +1,48 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.content.Intent +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import com.gh.base.BaseActivity_TabLayout +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.collection.GamesCollectionFragment +import com.gh.gamecenter.manager.UserManager + +class InsertGameCollectionWrapperActivity : BaseActivity_TabLayout() { + + override fun getLayoutId(): Int = R.layout.activity_tablayout_no_title_viewpager + + override fun initFragmentList(fragments: MutableList?) { + fragments?.add( + GamesCollectionFragment().with( + bundleOf( + EntranceUtils.KEY_USER_ID to UserManager.getInstance().userId, + EntranceUtils.KEY_TYPE to GamesCollectionFragment.TYPE_USER, + EntranceUtils.KEY_INSERT_GAME_COLLECTION to true + ) + ) + ) + fragments?.add( + GamesCollectionFragment().with( + bundleOf( + EntranceUtils.KEY_USER_ID to UserManager.getInstance().userId, + EntranceUtils.KEY_TYPE to GamesCollectionFragment.TYPE_COLLECT, + EntranceUtils.KEY_INSERT_GAME_COLLECTION to true + ) + ) + ) + } + + override fun initTabTitleList(tabTitleList: MutableList?) { + tabTitleList?.add("我的游戏单") + tabTitleList?.add("收藏游戏单") + } + + companion object { + fun getIntent(context: Context): Intent { + return Intent(context, InsertGameCollectionWrapperActivity::class.java) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt index ef565f6dff..3d755a4073 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt @@ -18,12 +18,14 @@ import com.lightgame.utils.Utils /** * 富文本编辑框的链接回调 */ -class OnLinkClickListener(val context: Context, - val title: String = "", - val status: String = "", - val entrance: String, - val path: String, - val mtaEvent: MtaEvent? = null) { +class OnLinkClickListener( + val context: Context, + val title: String = "", + val status: String = "", + val entrance: String, + val path: String, + val mtaEvent: MtaEvent? = null +) { @JavascriptInterface fun onClick(content: String) { AppExecutor.uiExecutor.execute { @@ -31,25 +33,34 @@ class OnLinkClickListener(val context: Context, val insertEntity = GsonUtils.fromJson(content, EditorInsertEntity::class.java) when (insertEntity.type) { "answer" -> { - val intent = SimpleAnswerDetailActivity.getIntent(context, - insertEntity.id ?: "", - entrance, - "$path-链接") + val intent = SimpleAnswerDetailActivity.getIntent( + context, + insertEntity.id ?: "", + entrance, + "$path-链接" + ) context.startActivity(intent) } "community_article" -> { val community = CommunityEntity(insertEntity.communityId!!, "") - val intent = ArticleDetailActivity.getIntent(context, - community, - insertEntity.id!!, - entrance, - "$path-链接") + val intent = ArticleDetailActivity.getIntent( + context, + community, + insertEntity.id!!, + entrance, + "$path-链接" + ) context.startActivity(intent) } "game" -> { - GameDetailActivity.startGameDetailActivity(context, - insertEntity.id, - BaseActivity.mergeEntranceAndPath(entrance, "$path-链接")) + GameDetailActivity.startGameDetailActivity( + context, + insertEntity.id, + BaseActivity.mergeEntranceAndPath(entrance, "$path-链接") + ) + } + "game_collection" -> { + //TODO:跳转游戏单详情 } } } @@ -62,7 +73,7 @@ class OnLinkClickListener(val context: Context, tryWithDefaultCatch { if (mtaEvent != null) MtaHelper.onEvent(mtaEvent.name, mtaEvent.key, mtaEvent.value) val videoEntity = GsonUtils.fromJson(content, MyVideoEntity::class.java) - clickToastByStatus(videoEntity.status){ + clickToastByStatus(videoEntity.status) { FullScreenVideoActivity.start(context, title, videoEntity.url, videoEntity.poster) } } @@ -71,7 +82,7 @@ class OnLinkClickListener(val context: Context, @JavascriptInterface fun onVideoClick(url: String, poster: String) { - clickToastByStatus(status){ + clickToastByStatus(status) { FullScreenVideoActivity.start(context, title, url, poster) } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/EditorInsertEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/EditorInsertEntity.kt index cae2ca5aa5..5d4ae3ff93 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/EditorInsertEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/EditorInsertEntity.kt @@ -3,16 +3,19 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable import com.gh.common.util.eliminateDoubleQuote import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.GamesCollectionEntity import kotlinx.android.parcel.Parcelize @Parcelize -data class EditorInsertEntity(var id: String? = "", - var communityId: String? = "", - var type: String? = "", - var title: String? = "", - var brief: String? = "", - var icon: String? = "", - var tags: List? = null) : Parcelable { +data class EditorInsertEntity( + var id: String? = "", + var communityId: String? = "", + var type: String? = "", + var title: String? = "", + var brief: String? = "", + var icon: String? = "", + var tags: List? = null +) : Parcelable { companion object { @JvmStatic @@ -48,5 +51,16 @@ data class EditorInsertEntity(var id: String? = "", entity.icon = game.icon return entity } + + @JvmStatic + fun transform(game: GamesCollectionEntity): EditorInsertEntity { + val entity = EditorInsertEntity() + entity.id = game.id + entity.type = "game_collection" + entity.title = game.title + entity.brief = game.intro + entity.icon = "https://static-web.ghzs.com/website-static/images/icon_game_collection.png" + return entity + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 6262bcdfa4..118978596b 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3305,5 +3305,5 @@ public interface ApiService { * 用户游戏单列表 */ @GET("users/{user_id}/game_lists") - Single> getUserGameCollectionList(@Path("user_id") String userId); + Single> getUserGameCollectionList(@Path("user_id") String userId, @QueryMap Map params); } \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/icon_editor_link_game_collection.webp b/app/src/main/res/drawable-xxhdpi/icon_editor_link_game_collection.webp new file mode 100644 index 0000000000000000000000000000000000000000..4fe08c4894143ab79a82f40ba18daecbe4d908b6 GIT binary patch literal 792 zcmV+z1LyowNk&Ex0{{S5MM6+kP&il$0000G0000x002P%06|PpNNE88009@nux;D0 z!AW+!Eh6F(Gr^Rs$N+}Pdi3{1cP=Jpi%56@F-AYaNgI_Qhy(z1ZQB$juN^2It8`sg$j8yzA5;Pmy?aTGkd^w0Eat$K6 zIRLQSk+^{VOqD=)*#K?=MVNe_K>OiZd;2P~Z{~h{&lc#<-Uu9-DUWXh#y)|kDWm!B z__j=}3Gk@VHXr|o!(g!}()8#>1{2_eT1i{!a^ckmN)Q0B}rYPxDA6 z3}9hIWfeex6MX}KO)6KKYx(*+p_P&gof0RRB7 z2>_h|Dm(x}06vjCoJl34BOxm?OR%sK31s@C|7clx7w zP9=WXK%Tep7--gLa%P(d<)tiH{V$~pH8w`f=}G9bWn1dDN+N_ebVguC$Vn?REm$J% z1sr+<3St0YsH+-5F)=Cz+-t>O)@DJpf$W-#MEzFOtP5~O=Am1#s-bb#12I)&`F*r+ zE0^RZ5RKdG!a&D{if)O6d=LQg+RDyg6L@0|V;<2=k{+BUT02%>#512OUM`OVe|7i! zAFbKPtz>7&XoO9OzeuUG3vBl4nIbQg=R!&pDeCcjrRV1 zWTX&tIxDU=78Q6qO%D$KI43W2M1KdL)bHU=4b WH6M$5>aroT`lf*GnLuwL2mk<8sAqTp literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_tablayout_no_title_viewpager.xml b/app/src/main/res/layout/activity_tablayout_no_title_viewpager.xml index 128a96300a..62b06767a4 100644 --- a/app/src/main/res/layout/activity_tablayout_no_title_viewpager.xml +++ b/app/src/main/res/layout/activity_tablayout_no_title_viewpager.xml @@ -18,7 +18,7 @@ diff --git a/app/src/main/res/layout/editor_insert_container.xml b/app/src/main/res/layout/editor_insert_container.xml index a4228dd546..226f336008 100644 --- a/app/src/main/res/layout/editor_insert_container.xml +++ b/app/src/main/res/layout/editor_insert_container.xml @@ -275,6 +275,31 @@ android:textSize="11sp" /> + + + + + + + Date: Tue, 16 Nov 2021 18:21:18 +0800 Subject: [PATCH 15/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BA(=E9=80=89=E6=8B=A9=E6=A0=87=E7=AD=BE)https://git.ghzs?= =?UTF-8?q?.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/GameCollectionTagEntity.kt | 15 ++----- .../tag/GameCollectionTagAdapter.kt | 32 ++++++++------- .../tag/GameCollectionTagSelectFragment.kt | 41 ++++++++++++------- .../tag/GameCollectionTagViewModel.kt | 24 +++++------ .../retrofit/service/ApiService.java | 12 ++++++ 5 files changed, 71 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt index 4cb615d52b..693dc99b9e 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionTagEntity.kt @@ -6,18 +6,9 @@ import kotlinx.android.parcel.Parcelize @Parcelize data class GameCollectionTagEntity( - @SerializedName("category_id") + @SerializedName("_id") val categoryId: String = "", @SerializedName("name") val categoryName: String = "", - val tagList: List = ArrayList() -) : Parcelable { - - @Parcelize - data class GameCollectionTag( - @SerializedName("tag_id") - val tagId: String = "", - @SerializedName("name") - val tagName: String = "" - ) : Parcelable -} \ No newline at end of file + val tags: List = ArrayList() +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt index a79e11dc25..a6460d7b09 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagAdapter.kt @@ -6,8 +6,10 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DisplayUtils import com.gh.common.util.dip2px +import com.gh.common.util.goneIf import com.gh.gamecenter.databinding.* import com.gh.gamecenter.entity.GameCollectionTagEntity +import com.gh.gamecenter.entity.TagInfoEntity import com.lightgame.adapter.BaseRecyclerAdapter class GameCollectionTagAdapter( @@ -19,8 +21,8 @@ class GameCollectionTagAdapter( private var mTagViewList = arrayListOf() var mTagList = arrayListOf() - var singleTag: GameCollectionTagEntity.GameCollectionTag? = null - var multipleTagList = arrayListOf() + var selectedTagEntity: TagInfoEntity? = null + var selectedTagEntityList = arrayListOf() fun setTagList(tagList: ArrayList) { mTagList = tagList @@ -44,14 +46,14 @@ class GameCollectionTagAdapter( when (holder) { is GameCollectionSelectedTagViewHolder -> { holder.binding.selectedTagFlexbox.removeAllViews() - holder.binding.hintTv.visibility = if (multipleTagList.size == 0) View.VISIBLE else View.GONE - for (tag in multipleTagList) { + holder.binding.hintTv.goneIf(selectedTagEntityList.size != 0) + for (tag in selectedTagEntityList) { val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(mLayoutInflater) .apply { - tagTv.text = tag.tagName + tagTv.text = tag.name root.setOnClickListener { - multipleTagList.remove(tag) + selectedTagEntityList.remove(tag) updateCallback.invoke() notifyDataSetChanged() } @@ -75,7 +77,7 @@ class GameCollectionTagAdapter( 12F.dip2px(), if (position == itemCount - 1) 97F.dip2px() else 15F.dip2px() ) - for (tagEntity in data.tagList) { + for (tagEntity in data.tags) { val tag = if(singleChoice) getSingleTag(tagEntity) else getMultipleTag(tagEntity) mTagViewList.add(tag) holder.binding.tagFlexbox.addView(tag.root) @@ -84,29 +86,29 @@ class GameCollectionTagAdapter( } } - private fun getSingleTag(tag: GameCollectionTagEntity.GameCollectionTag) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { - tagTv.text = tag.tagName + private fun getSingleTag(tag: TagInfoEntity) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { + tagTv.text = tag.name tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } root.setOnClickListener { for (tagView in mTagViewList) { if (tagView.tagTv != tagTv) tagView.tagTv.isChecked = false } tagTv.isChecked = !tagTv.isChecked - if (tagTv.isChecked) singleTag = tag + if (tagTv.isChecked) selectedTagEntity = tag } } - private fun getMultipleTag(tag: GameCollectionTagEntity.GameCollectionTag) = + private fun getMultipleTag(tag: TagInfoEntity) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply { tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 } - tagTv.text = tag.tagName - if (multipleTagList.contains(tag)) tagTv.isChecked = true + tagTv.text = tag.name + if (selectedTagEntityList.contains(tag)) tagTv.isChecked = true root.setOnClickListener { tagTv.isChecked = !tagTv.isChecked if (tagTv.isChecked) { - multipleTagList.add(tag) + selectedTagEntityList.add(tag) } else { - if (multipleTagList.contains(tag)) multipleTagList.remove(tag) + if (selectedTagEntityList.contains(tag)) selectedTagEntityList.remove(tag) } updateCallback.invoke() notifyItemChanged(0) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt index 7d22ace58b..fdbdf7f983 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt @@ -1,14 +1,17 @@ package com.gh.gamecenter.gamecollection.tag +import android.content.Intent import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding -import com.gh.gamecenter.entity.GameCollectionTagEntity +import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment import com.gh.gamecenter.normal.NormalFragment class GameCollectionTagSelectFragment : NormalFragment() { @@ -20,12 +23,12 @@ class GameCollectionTagSelectFragment : NormalFragment() { private var firstVisibleItemPosition = 0 private val updateSelectedTagView: (() -> Unit) = { - mAdapter.multipleTagList.let { list -> + mAdapter.selectedTagEntityList.let { list -> mBinding.selectedTagContainer.removeAllViews() for (tag in list) { val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(layoutInflater) selectedTagView.run { - tagTv.text = tag.tagName + tagTv.text = tag.name root.setOnClickListener { list.remove(tag) mBinding.selectedTagContainer.removeView(selectedTagView.root) @@ -47,13 +50,20 @@ class GameCollectionTagSelectFragment : NormalFragment() { }.root override fun onMenuItemClick(menuItem: MenuItem?) { -// if (menuItem?.itemId == R.id.layout_menu_save) { -// requireActivity().setResult( -// GameCollectionSquareFragment.REQUEST_SELECT_TAG, -// Intent().putExtra("tagName", mAdapter.singleTag?.tagName) -// ) -// requireActivity().finish() -// } + if (menuItem?.itemId == R.id.layout_menu_save) { + if (mSingleChoice) { + requireActivity().setResult( + GameCollectionSquareFragment.REQUEST_SELECT_TAG, + Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntity) + ) + } else { + requireActivity().setResult( + GameCollectionEditActivity.REQUEST_CHOOSE_TAG, + Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntityList) + ) + } + requireActivity().finish() + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -69,14 +79,17 @@ class GameCollectionTagSelectFragment : NormalFragment() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || mAdapter.multipleTagList.isEmpty()) View.GONE else View.VISIBLE + mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || mAdapter.selectedTagEntityList.isEmpty()) View.GONE else View.VISIBLE } }) } -// mViewModel.tagListLiveData.observe(viewLifecycleOwner) { -// mAdapter.setTagList(it) -// } + mViewModel.tagListLiveData.observe(viewLifecycleOwner) { + mAdapter.setTagList(it) + } + } + companion object { + const val SELECTED_TAG = "selected_tag" } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt index 22a8297e3e..f4c26d2804 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagViewModel.kt @@ -16,18 +16,18 @@ class GameCollectionTagViewModel(application: Application) : AndroidViewModel(ap private val mApi = RetrofitManager.getInstance(getApplication()).api init { -// getGameCollectionTagList() + getGameCollectionTagList() } -// @SuppressLint("CheckResult") -// fun getGameCollectionTagList() { -// mApi.gameCollectionTagList -// .subscribeOn(Schedulers.io()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(object: BiResponse>(){ -// override fun onSuccess(data: ArrayList) { -// tagListLiveData.postValue(data) -// } -// }) -// } + @SuppressLint("CheckResult") + fun getGameCollectionTagList() { + mApi.gameCollectionTagList + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object: BiResponse>(){ + override fun onSuccess(data: ArrayList) { + tagListLiveData.postValue(data) + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 118978596b..1eeb599303 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3306,4 +3306,16 @@ public interface ApiService { */ @GET("users/{user_id}/game_lists") Single> getUserGameCollectionList(@Path("user_id") String userId, @QueryMap Map params); + + /** + * 获取游戏单广场列表 + */ + @GET("game_lists") + Single> getGameCollectionSquareList(@Query("view") String view, @Query("tag_id") String tagId, @Query("page") int page); + + /** + * 获取游戏单标签列表 + */ + @GET("game_lists/tags") + Single> getGameCollectionTagList(); } \ No newline at end of file From cff7f4a9f5a7c32c366b3005592663caa1462c2e Mon Sep 17 00:00:00 2001 From: lyr <15622190878@163.com> Date: Tue, 16 Nov 2021 18:30:28 +0800 Subject: [PATCH 16/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E8=AF=A6?= =?UTF-8?q?=E6=83=85-=E9=A1=B6=E9=83=A8=E5=8C=BA=E5=9F=9F/=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=88=97=E8=A1=A8=EF=BC=88=E4=B8=80=E3=80=81=E5=A4=A7?= =?UTF-8?q?=E5=9B=BE=E6=A0=B7=E5=BC=8F-=E9=A1=B6=E9=83=A8=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=EF=BC=89https://git.ghzs.com/pm/halo-app-issues/-/iss?= =?UTF-8?q?ues/1601?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 8 + .../com/gh/common/util/EntranceUtils.java | 1 + .../collection/GamesCollectionAdapter.kt | 76 ++- .../collection/GamesCollectionFragment.kt | 22 +- .../collection/GamesCollectionViewModel.kt | 42 ++ .../entity/GamesCollectionDetailEntity.kt | 25 + .../detail/GameCollectionDetailActivity.kt | 28 ++ .../detail/GameCollectionDetailAdapter.kt | 47 ++ .../detail/GameCollectionDetailFragment.kt | 233 +++++++++ .../detail/GameCollectionDetailGameAdapter.kt | 117 +++++ .../detail/GameCollectionDetailViewModel.kt | 152 ++++++ .../detail/GameCollectionPosterActivity.kt | 28 ++ .../detail/GameCollectionPosterFragment.kt | 107 ++++ .../detail/GameCollectionPosterViewModel.kt | 48 ++ .../detail/GameCollectionVideoView.kt | 464 ++++++++++++++++++ .../publish/GameCollectionEditViewModel.kt | 2 +- .../gh/gamecenter/qa/entity/ArticleEntity.kt | 4 +- .../retrofit/service/ApiService.java | 8 +- .../ic_game_collection_square.webp | Bin 1474 -> 1990 bytes .../ic_game_collection_square_light.webp | Bin 0 -> 1474 bytes .../fragment_game_collection_detail.xml | 291 +++++++++++ .../fragment_game_collection_poster.xml | 157 ++++++ .../game_collection_detail_image_item.xml | 171 +++++++ .../game_collection_detail_none_game_item.xml | 26 + .../game_collection_detail_video_item.xml | 242 +++++++++ .../res/layout/game_collection_game_item.xml | 148 ++++++ .../main/res/layout/game_collection_item.xml | 8 +- .../res/layout/layout_game_collection_tag.xml | 25 + .../layout/piece_article_input_container.xml | 37 +- 30 files changed, 2504 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/GamesCollectionDetailEntity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailGameAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionVideoView.kt create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_light.webp create mode 100644 app/src/main/res/layout/fragment_game_collection_detail.xml create mode 100644 app/src/main/res/layout/fragment_game_collection_poster.xml create mode 100644 app/src/main/res/layout/game_collection_detail_image_item.xml create mode 100644 app/src/main/res/layout/game_collection_detail_none_game_item.xml create mode 100644 app/src/main/res/layout/game_collection_detail_video_item.xml create mode 100644 app/src/main/res/layout/game_collection_game_item.xml create mode 100644 app/src/main/res/layout/layout_game_collection_tag.xml diff --git a/app/build.gradle b/app/build.gradle index c9ab5428e5..9cbb0d98a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -487,6 +487,8 @@ andResGuard { "R.id.vote", "R.id.watermark_hint", "R.id.watermark_sb", + "R.id.bottomShareIv", + "R.id.bottomShareTv", ] compressFilePattern = [ "*.png", diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9244c9b1bf..85af5f4787 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -683,6 +683,14 @@ android:name=".game.commoncollection.detail.CommonCollectionDetailActivity" android:screenOrientation="portrait" /> + + + + { - tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + tagIv.setBackgroundResource(R.drawable.ic_chosen) } "official" -> { - tagIv.setBackgroundResource(R.drawable.ic_official_big) + tagIv.setBackgroundResource(R.drawable.ic_official) } } if (mViewModel.mIsInsertGameCollection) { @@ -90,6 +98,10 @@ class GamesCollectionAdapter( timeTv.visibility = View.GONE } + moreIv.setOnClickListener { + showOptionPopupWindow(moreIv, itemEntity) + } + myselfTag.setOnClickListener { DialogHelper.showDialog(mContext, "仅自己可见", "游戏单开启“仅自己可见”后,将不会对其他用户展示,您可以通过关闭仅自己可见或者投稿分享游戏单", "我知道了", "", { @@ -105,6 +117,7 @@ class GamesCollectionAdapter( gameThree.setOnClickListener { GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(2)?.id, "") } + root.setOnClickListener { if (mViewModel.mIsInsertGameCollection) { val entity = GamesCollectionEntity().apply { @@ -117,6 +130,8 @@ class GamesCollectionAdapter( } (mContext as AppCompatActivity).setResult(Activity.RESULT_OK, intent) (mContext as AppCompatActivity).finish() + } else { + mContext.startActivity(GameCollectionDetailActivity.getIntent(mContext, itemEntity.id)) } } } @@ -126,5 +141,62 @@ class GamesCollectionAdapter( } } + private fun showOptionPopupWindow(view: View, entity: GamesCollectionEntity) { + val contentList = arrayListOf("编辑", "投稿", "删除") + val inflater = LayoutInflater.from(mContext) + val layout = inflater.inflate(R.layout.layout_popup_container, null) + val popupWindow = PopupWindow( + layout, + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + popupWindow.apply { + setBackgroundDrawable(ColorDrawable(0)) + isTouchable = true + isFocusable = true + isOutsideTouchable = true + } + + val container = layout.findViewById(R.id.container) + for (text in contentList) { + val item = inflater.inflate(R.layout.layout_popup_option_item, container, false) + container.addView(item) + + val hitText = item.findViewById(R.id.hint_text) + hitText.text = text + + item.setOnClickListener { + dealOption(text, entity) + popupWindow.dismiss() + } + } + popupWindow.showAutoOrientation(view) + } + + private fun dealOption(content: String, entity: GamesCollectionEntity) { + when (content) { + "编辑" -> { + mContext.startActivity(GameCollectionEditActivity.getIntent(mContext, entity)) + } + + "投稿" -> { + if ((entity.count?.game ?: 0) >= 8) { + DialogHelper.showDialog(mContext, "温馨提示", "投稿通过后,将自动关闭“仅自己可见”,所有用户都能浏览到游戏单,确定投稿?", "确定", "取消", { + mViewModel.publishGameCollection(entity) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } else { + DialogHelper.showDialog(mContext, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场哦~", "添加游戏", "我知道了", { + mContext.startActivity(GameCollectionEditActivity.getIntent(mContext, entity)) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + + "删除" -> { + DialogHelper.showDialog(mContext, "温馨提示", "游戏单删除后将无法恢复,是否确认删除", "确定", "取消", { + mViewModel.deleteGameCollection(entity) + }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt index 84bfc64f50..c2826597ad 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt @@ -8,6 +8,7 @@ import com.gh.common.util.EntranceUtils.* import com.gh.common.view.SpacingItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.entity.GamesCollectionEntity class GamesCollectionFragment : ListFragment() { @@ -37,9 +38,24 @@ class GamesCollectionFragment : ListFragment(application) { + private val mApi = RetrofitManager.getInstance(getApplication()).api + val deleteLiveData = MutableLiveData() + val publishLiveData = MutableLiveData() + override fun provideDataObservable(page: Int) = null override fun provideDataSingle(page: Int): Single> { @@ -27,6 +37,38 @@ class GamesCollectionViewModel(application: Application, var userId: String, var } } + fun deleteGameCollection(entity: GamesCollectionEntity) { + mApi.deleteGameCollection(entity.id) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + deleteLiveData.postValue(entity) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + ToastUtils.showToast("删除失败") + } + }) + } + + fun publishGameCollection(entity: GamesCollectionEntity){ + mApi.deleteGameCollection(entity.id) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + publishLiveData.postValue(entity) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + ToastUtils.showToast("投稿失败") + } + }) + } + class Factory(private val mUserId: String, private val mType: String, private val mIsInsertGameCollection: Boolean) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionDetailEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionDetailEntity.kt new file mode 100644 index 0000000000..3be42c21fb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionDetailEntity.kt @@ -0,0 +1,25 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.gh.gamecenter.qa.entity.Count +import com.gh.gamecenter.qa.entity.TimeEntity +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Parcelize +class GamesCollectionDetailEntity( + @SerializedName("_id") + var id: String = "", + var tags: ArrayList? = null, + var games: ArrayList? = null, + var title: String = "", + var intro: String = "", + var cover: String = "", + var video: VideoEntity? = null, + var display: String = "",//self_only: 仅自己可见 + var time: TimeEntity? = null, + var stamp: String = "",//special_choice: 精选 official: 官方 + var count: Count? = null, + var user: UserEntity? = null, + var me: MeEntity? = null, +) : Parcelable diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailActivity.kt new file mode 100644 index 0000000000..a8ccc5d976 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailActivity.kt @@ -0,0 +1,28 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R + +class GameCollectionDetailActivity : NormalActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + hideToolbar(true) + DisplayUtils.setStatusBarColor(this, R.color.transparent, false) + } + + companion object { + @JvmStatic + fun getIntent(context: Context, gameCollectionId: String, isFromSquare: Boolean = false): Intent { + val bundle = Bundle() + bundle.putString(EntranceUtils.KEY_ID, gameCollectionId) + bundle.putBoolean(EntranceUtils.KEY_IS_FROM_SQUARE, isFromSquare) + return getTargetIntent(context, GameCollectionDetailActivity::class.java, GameCollectionDetailFragment::class.java, bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt new file mode 100644 index 0000000000..f366fac421 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt @@ -0,0 +1,47 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.ItemViewType +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.adapter.viewholder.GameCollectionItemViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.entity.GamesCollectionEntity + +class GameCollectionDetailAdapter(context: Context, + private val mViewModel: GameCollectionDetailViewModel +): ListAdapter(context) { + + override fun getItemCount(): Int { + return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1 + } + + override fun getItemViewType(position: Int): Int { + return if (position == itemCount - 1) { + ItemViewType.ITEM_FOOTER + } else { + ItemViewType.ITEM_BODY + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_BODY -> GameCollectionItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.game_collection_item, parent, false)) + + else -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GameCollectionItemViewHolder -> { + + } + + is FooterViewHolder -> holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt new file mode 100644 index 0000000000..442208839f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -0,0 +1,233 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.graphics.Color +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import butterknife.OnClick +import com.gh.common.util.* +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.databinding.FragmentGameCollectionDetailBinding +import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.entity.GamesCollectionDetailEntity +import com.gh.gamecenter.manager.UserManager +import com.google.android.material.appbar.AppBarLayout +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import kotlin.math.abs + +class GameCollectionDetailFragment : + ListFragment() { + + private var mBinding: FragmentGameCollectionDetailBinding? = null + private var mAdapter: GameCollectionDetailAdapter? = null + private var mGameAdapter: GameCollectionDetailGameAdapter? = null + private var mEntity = GamesCollectionDetailEntity() + private var mGameCollectionId = "" + private var mFromSquare = false + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + mGameAdapter?.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } + } + } + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = + FragmentGameCollectionDetailBinding.inflate(layoutInflater).apply { mBinding = this }.root + + override fun provideListViewModel() = + viewModelProvider(GameCollectionDetailViewModel.Factory(mGameCollectionId)) + + override fun provideListAdapter() = mAdapter ?: GameCollectionDetailAdapter(requireContext(), mListViewModel).apply { mAdapter = this } + + override fun onCreate(savedInstanceState: Bundle?) { + mGameCollectionId = arguments?.getString(EntranceUtils.KEY_ID) ?: "" + super.onCreate(savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mListViewModel.gameCollectionLiveData.observe(viewLifecycleOwner, { + mEntity = it + initTopView() + }) + + mBinding?.appbar?.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + if (!isAdded) return@OnOffsetChangedListener + + val absVerticalOffset = abs(verticalOffset) + mListRefresh?.isEnabled = absVerticalOffset <= 2 + + mBinding?.run { + if (mEntity.video == null) { + val bgOffset = imageItem.root.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px() + + if (absVerticalOffset >= bgOffset) { + DisplayUtils.setStatusBarColor(requireActivity(), R.color.white, true) + toolbarContainer.setBackgroundColor(Color.WHITE) + backIv.setImageResource(R.drawable.ic_bar_back) + toolbarUserContainer.visibility = View.VISIBLE + toolbarLightUserContainer.visibility = View.GONE + toolbarFollowTv.setBackgroundResource(R.drawable.button_round_1a2496ff) + toolbarFollowTv.setTextColor(R.color.theme.toColor()) + squareIv.setImageResource(R.drawable.ic_game_collection_square) + } else { + DisplayUtils.setStatusBarColor(requireActivity(), R.color.transparent, false) + toolbarContainer.setBackgroundColor(Color.TRANSPARENT) + backIv.setImageResource(R.drawable.ic_bar_back_light) + toolbarUserContainer.visibility = View.GONE + toolbarLightUserContainer.visibility = View.VISIBLE + toolbarFollowTv.setBackgroundResource(R.drawable.button_round_black_alpha_30) + toolbarFollowTv.setTextColor(Color.WHITE) + squareIv.setImageResource(R.drawable.ic_game_collection_square_light) + } + } + } + + }) + } + + override fun onResume() { + if (isEverPause && mAdapter != null) mGameAdapter?.notifyDataSetChanged() + super.onResume() + DownloadManager.getInstance(context).addObserver(mDataWatcher) + } + + override fun onPause() { + super.onPause() + DownloadManager.getInstance(context).removeObserver(mDataWatcher) + } + + private fun initTopView() { + mBinding?.run { + mReuseNoConn?.setVisibility(View.GONE) + mReuseNoData?.setVisibility(View.GONE) + mListLoading?.setVisibility(View.GONE) + + if (mEntity.video == null) { + imageItem.root.visibility = View.VISIBLE + videoItem.root.visibility = View.GONE + initImageItemView() + } else { + imageItem.root.visibility = View.GONE + videoItem.root.visibility = View.VISIBLE + videoItem.entity = mEntity + } + + if (mEntity.games?.size ?: 0 > 0) { + noneGameContainer.root.visibility = View.GONE + gameListRv.run { + visibility = View.VISIBLE + layoutManager = LinearLayoutManager(requireContext()) + mGameAdapter = GameCollectionDetailGameAdapter(requireContext(), mEntity.games ?: listOf()) + adapter = mGameAdapter + } + } else { + noneGameContainer.root.visibility = View.VISIBLE + gameListRv.visibility = View.GONE + } + } + } + + private fun initImageItemView() { + mBinding?.run { + entity = mEntity + executePendingBindings() + + squareIv.goneIf(mFromSquare) + } + mBinding?.imageItem?.run { + entity = mEntity + executePendingBindings() + + desTv.text = "游戏单简介:${mEntity.intro}" + + when (mEntity.stamp) { + "special_choice" -> { + tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + } + "official" -> { + tagIv.setBackgroundResource(R.drawable.ic_official_big) + } + } + + if (!UserManager.getInstance().isLoggedIn) { + mEntity.tags?.forEachIndexed { index, tag -> + tagContainer.addView(getTagView(tag.name, index == mEntity.tags!!.size - 1)) + } + } + } + } + + private fun getTagView(content: String, isLast: Boolean): View { + return LayoutGameCollectionTagBinding.inflate(layoutInflater).apply { + divider.goneIf(isLast) + contentTv.text = content + }.root + } + + override fun onLoadDone() { + super.onLoadDone() + } + + override fun onLoadEmpty() { + super.onLoadEmpty() + mListRv.visibility = View.VISIBLE + } + + override fun onLoadError() { + super.onLoadError() + mListRv.visibility = View.VISIBLE + } + + @OnClick(R.id.desIv, R.id.toolbarLightUserContainer, R.id.toolbarUserContainer, R.id.toolbarFollowTv, R.id.squareIv) + override fun onClick(v: View) { + when (v.id) { + R.id.desIv -> { + startActivity(GameCollectionPosterActivity.getIntent(requireContext(), mEntity)) + } + + R.id.toolbarLightUserContainer -> { + DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-导航栏") + } + + R.id.toolbarUserContainer -> { + DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-导航栏") + } + + R.id.toolbarFollowTv -> { + ifLogin("游戏单详情") { + mListViewModel?.followingCommand(mEntity.user?.id ?: "", mEntity.me?.isFollower == false) + } + } + + R.id.squareIv -> { + + } + } + } + + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mGameAdapter?.positionAndPackageMap ?: return + for (gameAndPosition in data) { + if (gameAndPosition.key.contains(downloadEntity.packageName)) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value) + if (targetView != null) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailGameAdapter.kt new file mode 100644 index 0000000000..6e155f718a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailGameAdapter.kt @@ -0,0 +1,117 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.* +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.GameCollectionGameItemBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.lightgame.adapter.BaseRecyclerAdapter +import com.lightgame.download.DownloadEntity +import java.util.HashMap + +class GameCollectionDetailGameAdapter( + context: Context, + private var mList: List, +) : BaseRecyclerAdapter(context) { + + val positionAndPackageMap = HashMap() + private var countAndKey: Pair? = null + + init { + var dataIds = "" + mList.forEach { + dataIds += it.id + } + if (dataIds.isNotEmpty()) countAndKey = Pair(mList.size, dataIds) + + positionAndPackageMap.clear() + // 记录游戏位置 + for (i in mList.indices) { + val gameEntity = mList[i] + var packages = gameEntity.id + for (apkEntity in gameEntity.getApk()) { + packages += apkEntity.packageName + } + positionAndPackageMap[packages + i] = i + } + } + + override fun getItemCount() = mList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameCollectionGameItemViewHolder { + return GameCollectionGameItemViewHolder(GameCollectionGameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_collection_game_item, parent, false))) + } + + override fun onBindViewHolder(holder: GameCollectionGameItemViewHolder, position: Int) { + val gameEntity = mList[position] + holder.binding.run { + game = gameEntity + executePendingBindings() + + root.setOnClickListener { + GameDetailActivity.startGameDetailActivity( + mContext, + gameEntity.id, + "", + ) + } + + DownloadItemUtils.setOnClickListener( + mContext, + holder.binding.downloadBtn, + gameEntity, + position, + this@GameCollectionDetailGameAdapter, + "", + "", + null) + + DownloadItemUtils.updateDownloadButton(mContext, downloadBtn, gameEntity, false) + } + } + + fun notifyItemByDownload(download: DownloadEntity) { + for (key in positionAndPackageMap.keys) { + if (key.contains(download.packageName) && key.contains(download.gameId)) { + val position = positionAndPackageMap[key] + if (position != null && position < mList.size) { + mList[position].getEntryMap()[download.platform] = download + notifyItemChanged(position) + } + } + } + } + + fun notifyItemAndRemoveDownload(status: EBDownloadStatus) { + for (key in positionAndPackageMap.keys) { + if (key.contains(status.packageName) && key.contains(status.gameId)) { + val position = positionAndPackageMap[key] + if (position != null && position < mList.size) { + mList[position].getEntryMap().remove(status.platform) + notifyItemChanged(position) + } + } + } + } + + fun checkResetData(update: List) { + var dataIds = "" + update.forEach { dataIds += it.id } + + mList = update + if (countAndKey?.first == update.size && countAndKey?.second != dataIds) { // 数量不变,内容发生改变 + notifyItemRangeChanged(0, itemCount) + } else if (countAndKey?.first != update.size) { // 数量发生改变 + notifyDataSetChanged() + } + + // 重新刷新数据标识 + countAndKey = Pair(update.size, dataIds) + } + + inner class GameCollectionGameItemViewHolder(val binding: GameCollectionGameItemBinding): RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt new file mode 100644 index 0000000000..41c1ad259b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt @@ -0,0 +1,152 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.entity.GamesCollectionDetailEntity +import com.gh.gamecenter.eventbus.EBUserFollow +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import org.greenrobot.eventbus.EventBus +import retrofit2.HttpException + +class GameCollectionDetailViewModel(application: Application, + val gameCollectionId: String) : + ListViewModel(application) { + + private val mApi = RetrofitManager.getInstance(getApplication()).api + val gameCollectionLiveData = MutableLiveData() + var followLiveData = MutableLiveData() + + init { + getGameCollectionDetail() + } + + @SuppressLint("CheckResult") + fun getGameCollectionDetail() { + mApi.getGameCollectionDetail(gameCollectionId) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: GamesCollectionDetailEntity) { + gameCollectionLiveData.postValue(data) + } + }) + } + + override fun provideDataObservable(page: Int): Observable>? { +// val map = hashMapOf() +//// if (answerId.isNotEmpty()) { +//// map["top_comment_id"] = answerId +//// } +// return mApi.getQuestionComment(questionId, currentSortType.value, page, map) + return Observable.create { + it.onNext(mutableListOf()) + } + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { + mResultLiveData.postValue(it) + } +// mergeListData(it, displayFloor = true) } + } + + fun followingCommand(userId: String, isFollow: Boolean) { + val observable = if (isFollow) { + RetrofitManager.getInstance(getApplication()).api.postFollowing(userId) + } else { + RetrofitManager.getInstance(getApplication()).api.deleteFollowing(userId) + } + observable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + if (isFollow) { + Utils.toast(getApplication(), R.string.concern_success) + } else { + Utils.toast(getApplication(), R.string.concern_cancel) + } + followLiveData.postValue(isFollow) + EventBus.getDefault().post(EBUserFollow(userId, isFollow)) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + Utils.toast(getApplication(), R.string.loading_failed_hint) + } + }) + } + +// override fun hideCommentSuccess() { +// questionDetail?.count?.answer = (questionDetail?.count?.answer ?: 0) - 1 +// loadResultLiveData.postValue(LoadResult.SUCCESS) +// } +// +// fun follow() { +// followingCommand(true, questionDetail?.user?.id!!) +// } +// +// fun unFollow() { +// followingCommand(false, questionDetail?.user?.id!!) +// } +// +// private fun followingCommand(isFollow: Boolean, targetUserId: String) { +// val observable = if (isFollow) { +// mApi.postFollowing(targetUserId) +// } else { +// mApi.deleteFollowing(targetUserId) +// } +// observable +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(object : Response() { +// override fun onResponse(response: ResponseBody?) { +// super.onResponse(response) +// if (isFollow) { +// // 关注成功 +// mFollowLiveData.postValue(true) +// } else { +// // 取消关注成功 +// mFollowLiveData.postValue(false) +// } +// +// SyncPageRepository.postSyncData( +// SyncDataEntity( +// questionDetail?.id ?: "", +// SyncFieldConstants.IS_FOLLOWER, +// isFollow, +// checkFieldEntity = true +// ) +// ) +// EventBus.getDefault().post(EBUserFollow(targetUserId, isFollow)) +// } +// +// override fun onFailure(e: HttpException?) { +// super.onFailure(e) +// Utils.toast(getApplication(), R.string.loading_failed_hint) +// } +// }) +// } + + class Factory(private val gameCollectionId: String) : ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return GameCollectionDetailViewModel(HaloApp.getInstance().application, gameCollectionId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterActivity.kt new file mode 100644 index 0000000000..491c030e9c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterActivity.kt @@ -0,0 +1,28 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.GamesCollectionDetailEntity + +class GameCollectionPosterActivity : NormalActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + hideToolbar(true) + DisplayUtils.setStatusBarColor(this, R.color.transparent, false) + } + + companion object { + @JvmStatic + fun getIntent(context: Context, entity: GamesCollectionDetailEntity): Intent { + val bundle = Bundle() + bundle.putParcelable(EntranceUtils.KEY_DATA, entity) + return getTargetIntent(context, GameCollectionPosterActivity::class.java, GameCollectionPosterFragment::class.java, bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt new file mode 100644 index 0000000000..9582ebab30 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt @@ -0,0 +1,107 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.graphics.Bitmap +import android.os.Build +import android.os.Bundle +import android.view.View +import androidx.annotation.RequiresApi +import butterknife.OnClick +import com.gh.common.runOnUiThread +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentGameCollectionPosterBinding +import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding +import com.gh.gamecenter.entity.GamesCollectionDetailEntity +import com.gh.gamecenter.normal.NormalFragment + +class GameCollectionPosterFragment : NormalFragment() { + + private var mBinding: FragmentGameCollectionPosterBinding? = null + private var mViewModel: GameCollectionPosterViewModel? = null + private var mEntity = GamesCollectionDetailEntity() + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = + FragmentGameCollectionPosterBinding.inflate(layoutInflater).apply { mBinding = this }.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + mViewModel = viewModelProvider() + mEntity = arguments?.getParcelable(EntranceUtils.KEY_DATA) ?: GamesCollectionDetailEntity() + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mViewModel?.followLiveData?.observeNonNull(viewLifecycleOwner) { + setFollowStatus(it) + } + + mBinding?.apply { + mEntity.apply { + entity = this + executePendingBindings() + + ImageUtils.getBitmap(cover, object : BiCallback { + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + override fun onFirst(first: Bitmap) { + val blurBitmap = BitmapUtils.getBlurBitmap(requireContext(), first, 16) + runOnUiThread { + posterBg.setImageBitmap(blurBitmap) + } + } + + override fun onSecond(second: Boolean) {} + }) + + userIcon.display(user?.border, user?.icon, user?.auth?.icon) + + tags?.forEachIndexed { index, tag -> + tagContainer.addView(getTagView(tag.name, index == tags!!.size - 1)) + } + + setFollowStatus(me?.isFollower == true) + } + } + } + + private fun setFollowStatus(isFollow: Boolean) { + mBinding?.followTv?.run { + setBackgroundResource(if (isFollow) R.drawable.button_round_white_alpha_10 else R.drawable.button_round_white_alpha_20) + setTextColor(if (isFollow) R.color.white_alpha_60.toColor() else R.color.white.toColor()) + text = if (isFollow) "已关注" else "关注" + } + } + + private fun getTagView(content: String, isLast: Boolean): View { + return LayoutGameCollectionTagBinding.inflate(layoutInflater).apply { + root.setPadding(0, 12F.dip2px(), 0, 0) + divider.goneIf(isLast) + contentTv.text = content + }.root + } + + @OnClick(R.id.backIv, R.id.userIcon, R.id.userName, R.id.followTv) + override fun onClick(view: View) { + when (view.id) { + R.id.backIv -> requireActivity().finish() + + R.id.userIcon -> { + DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-封面页") + } + + R.id.userName -> { + DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-封面页") + } + + R.id.followTv -> { + ifLogin("游戏单详情-封面页") { + mViewModel?.followingCommand(mEntity?.user?.id ?: "", mEntity?.me?.isFollower == false) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterViewModel.kt new file mode 100644 index 0000000000..bc37e692d1 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterViewModel.kt @@ -0,0 +1,48 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import com.gh.gamecenter.R +import com.gh.gamecenter.eventbus.EBUserFollow +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import org.greenrobot.eventbus.EventBus +import retrofit2.HttpException + +class GameCollectionPosterViewModel(application: Application) : AndroidViewModel(application) { + + var followLiveData = MutableLiveData() + + fun followingCommand(userId: String, isFollow: Boolean) { + val observable = if (isFollow) { + RetrofitManager.getInstance(getApplication()).api.postFollowing(userId) + } else { + RetrofitManager.getInstance(getApplication()).api.deleteFollowing(userId) + } + observable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + if (isFollow) { + Utils.toast(getApplication(), R.string.concern_success) + } else { + Utils.toast(getApplication(), R.string.concern_cancel) + } + followLiveData.postValue(isFollow) + EventBus.getDefault().post(EBUserFollow(userId, isFollow)) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + Utils.toast(getApplication(), R.string.loading_failed_hint) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionVideoView.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionVideoView.kt new file mode 100644 index 0000000000..ebfbdac975 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionVideoView.kt @@ -0,0 +1,464 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.content.Context +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.Surface +import android.view.View +import android.widget.ImageView +import android.widget.SeekBar +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import com.gh.common.observer.MuteCallback +import com.gh.common.observer.VolumeObserver +import com.gh.common.runOnIoThread +import com.gh.common.runOnUiThread +import com.gh.common.util.* +import com.gh.download.cache.ExoCacheManager +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.GameDetailEntity +import com.gh.gamecenter.gamedetail.GameDetailViewModel +import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.video.detail.CustomManager +import com.lightgame.utils.Utils +import com.shuyu.gsyvideoplayer.utils.CommonUtil +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge +import com.squareup.picasso.Picasso +import io.reactivex.disposables.Disposable +import kotlinx.android.synthetic.main.layout_game_detail_video_portrait.view.* +import kotlinx.android.synthetic.main.piece_video_control.view.* +import kotlinx.android.synthetic.main.piece_video_replay.view.* +import java.util.* + +class GameCollectionVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { + + private var mMuteCallback: MuteCallback + private var mVolumeObserver: VolumeObserver + + var gameName = "" + var video: GameDetailEntity.Video? = null + var viewModel: GameDetailViewModel? = null + var uuid = UUID.randomUUID().toString() + private var mMuteDisposable: Disposable? = null + private var mContentLength = 0.0 + private var mIsAutoPlay = false + + val combinedTitleAndId: String + get() = StringUtils.combineTwoString(video?.title, video?.videoId) + + init { + post { + gestureDetector = GestureDetector(getContext().applicationContext, object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (!mChangePosition && !mChangeVolume && !mBrightness) { + onClickUiToggle(e) + } + return super.onSingleTapConfirmed(e) + } + }) + + if (mIfCurrentIsFullscreen) { + showBackBtn() + } else { + hideBackBtn() + } + + volume.setOnClickListener { toggleMute() } + } + + mMuteCallback = object : MuteCallback { + override fun onMute(isMute: Boolean) { + if (isMute) { + mute() + } else { + unMute() + } + } + } + + mVolumeObserver = VolumeObserver(mMuteCallback) + + setBackFromFullScreenListener { + if (it.id == R.id.fullscreen) { + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-退出全屏", combinedTitleAndId) + } else if (it.id == R.id.back) { + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击返回", combinedTitleAndId) + } + clearFullscreenLayout() + } + + errorBtn?.setOnClickListener { + debounceActionWithInterval(errorBtn.id, 1000) { + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络异常,请检查手机网络状态") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + return@debounceActionWithInterval + } + startPlayLogic(false) + } + } + } + + //这个必须配置最上面的构造才能生效 + override fun getLayoutId(): Int { + return R.layout.layout_game_detail_video_portrait + } + + fun observeVolume(fragment: Fragment?) { + fragment?.context?.applicationContext?.contentResolver?.registerContentObserver( + android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver) + + fragment?.fragmentManager?.registerFragmentLifecycleCallbacks( + object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + if (f === fragment) { + fragment.context?.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver) + fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this) + } + } + }, false) + } + + fun startPlayLogic(isAutoPlay: Boolean) { + mIsAutoPlay = isAutoPlay + violenceUpdateMuteStatus() + if (isAutoPlay) { + MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "自动播放") + MtaHelper.onEvent("游戏详情_顶部视频", "顶部视频-自动播放", combinedTitleAndId) + } else { + MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "手动播放") + } + if (isAutoPlay) { + val seekTime = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video?.url)) + seekOnStart = seekTime + } + startPlayLogic() + } + + fun violenceUpdateMuteStatus() { + if (mMuteDisposable != null) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + mMuteDisposable = rxTimer(25) { + if (it >= 400) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + updateMuteStatus() + } + } + + fun disposableTimer() { + if (mMuteDisposable != null && !mMuteDisposable!!.isDisposed) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + } + + private fun toggleMute() { + if (viewModel?.videoIsMuted == true) { + unMute(true) + } else { + mute(true) + } + } + + fun updateMuteStatus() { + if (viewModel?.videoIsMuted == true) { + mute() + } else { + unMute() + } + } + + private fun mute(isManual: Boolean = false) { + viewModel?.videoIsMuted = true + volume.setImageResource(R.drawable.ic_game_detail_volume_off) + CustomManager.getCustomManager(getKey()).isNeedMute = true + if (isManual) { + Utils.toast(context, "当前处于静音状态") + uploadVideoStreamingPlaying("点击静音") + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击静音", combinedTitleAndId) + } + } + + private fun unMute(isManual: Boolean = false) { + viewModel?.videoIsMuted = false + volume.setImageResource(R.drawable.ic_game_detail_volume_on) + CustomManager.getCustomManager(getKey()).isNeedMute = false + if (isManual) { + uploadVideoStreamingPlaying("取消静音") + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-解除静音", combinedTitleAndId) + } + } + + override fun onSeekComplete() { + super.onSeekComplete() + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-拖动进度条", combinedTitleAndId) + } + + // 重载以减少横竖屏切换的时间 + override fun checkoutState() { + removeCallbacks(mCheckoutTask) + postDelayed(mCheckoutTask, 300) + } + + override fun clearFullscreenLayout() { + super.clearFullscreenLayout() + updateMuteStatus() + hideBackBtn() + } + + fun updateThumb(url: String) { + Picasso.with(context).load(url).fit().into(findViewById(R.id.thumbImage)) + } + + override fun touchDoubleUp(e: MotionEvent?) { + // we do not need double click to play or pause + } + + // 不需要弹弹窗,直接播放 + override fun showWifiDialog() { + startPlayLogic(false) + //val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false) + //if (trafficVideo) { + // 不延迟的话 isCacheFile 可能直接返回 false + postDelayed({ + // 这个库的 NetworkUtils.isWifiConnected 可能会触发空指针,这里换为我们自己的 + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "网络异常,请检查手机网络状态") + } else if (!NetworkUtils.isWifiConnected(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "当前为非Wi-Fi环境,请注意流量消耗") + } + }, 100) + // } + } + + override fun getGSYVideoManager(): GSYVideoViewBridge { + CustomManager.getCustomManager(getKey()).initContext(context.applicationContext) + return CustomManager.getCustomManager(getKey()) + } + + fun getKey(): String { + return uuid + } + + override fun onAutoCompletion() { + // 这个方法在内核被释放时也会被回调,所以可以用来统计播放量和播放时长 + val playedTime = (gsyVideoManager.currentPosition / 1000).toInt() + + MtaHelper.onEventWithTime("视频播放量_按位置", playedTime, "游戏详情-顶部视频", combinedTitleAndId) + MtaHelper.onEventWithTime("视频播放量_游戏加位置", playedTime, gameName, "游戏详情-顶部视频") + + //播放完成后判断是否已缓冲完毕,没有完成显示播放错误状态 + if (mBufferPoint != 0 && mBufferPoint != 100 && isShown) { + gsyVideoManager.releaseMediaPlayer() + changeUiToPreparingShow() + postDelayed({ + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络错误,视频播放失败") + changeUiToError() + } + }, 10 * 1000) + } + uploadVideoStreamingPlaying("播放完毕") + super.onAutoCompletion() + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + super.onStopTrackingTouch(seekBar) + uploadVideoStreamingPlaying("拖动") + } + + override fun isShowNetConfirm(): Boolean { + return (!mOriginUrl.startsWith("file") && !mOriginUrl.startsWith("android.resource") && !CommonUtil.isWifiConnected(context) + && mNeedShowWifiTip) + } + + override fun updateStartImage() { + if (mStartButton is ImageView) { + val imageView = mStartButton as ImageView + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.ic_game_detail_pause) + GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.ic_game_detail_play) + else -> imageView.setImageResource(R.drawable.ic_game_detail_play) + } + } + } + + override fun setStateAndUi(state: Int) { + super.setStateAndUi(state) + + if (state == CURRENT_STATE_AUTO_COMPLETE) { + hideAllWidget() + replayContainer.visibility = View.VISIBLE + mTopContainer.visibility = View.VISIBLE + replayIv.setOnClickListener { + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击重新播放", combinedTitleAndId) + startButton.performClick() + violenceUpdateMuteStatus() + uploadVideoStreamingPlaying("重新播放") + } + updateThumb(video!!.poster) + } else { + replayContainer.visibility = View.GONE + } + } + + private fun showBackBtn() { + mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg) + back.visibility = View.VISIBLE + } + + private fun hideBackBtn() { + mTopContainer?.setBackgroundResource(0) + back.visibility = View.GONE + } + + override fun getEnlargeImageRes(): Int { + return R.drawable.ic_game_detail_enter_full_screen + } + + override fun getShrinkImageRes(): Int { + return R.drawable.ic_game_detail_exit_full_screen + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (!isIfCurrentIsFullscreen) { + parent.requestDisallowInterceptTouchEvent(true) + } + return super.onInterceptTouchEvent(ev) + } + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + uploadVideoStreamingPlaying("开始播放") + } + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + /********************************各类UI的状态显示*********************************************/ + + override fun changeUiToNormal() { + super.changeUiToNormal() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPreparingShow() { + super.changeUiToPreparingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPauseShow() { + super.changeUiToPauseShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToCompleteShow() { + super.changeUiToCompleteShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToError() { + super.changeUiToError() + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun netWorkErrorLogic() { + super.netWorkErrorLogic() + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + //监控播放错误 + override fun onError(what: Int, extra: Int) { + super.onError(what, extra) + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun releaseVideos() { + uploadVideoStreamingPlaying("结束播放") + CustomManager.releaseAllVideos(getKey()) + } + + override fun onVideoPause() { + super.onVideoPause() + uploadVideoStreamingPlaying("暂停播放") + } + + + override fun onClick(v: View) { + when (v.id) { + R.id.start -> { + if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) { + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击暂停", combinedTitleAndId) + } else { + MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击播放", combinedTitleAndId) + } + super.onClick(v) + } + else -> super.onClick(v) + } + } + + private fun getMtaKeyPrefix() = if (mIfCurrentIsFullscreen) "顶部视频(全屏)" else "顶部视频" + + fun getCurrentPosition(): Long { + return mCurrentPosition + } + + fun uploadVideoStreamingPlaying(action: String) { + if (video == null || video?.url.isNullOrEmpty()) return + runOnIoThread { + val isLandscape = mOrientationUtils != null + val videoPlayModel = if (!isLandscape) { + if (mIsAutoPlay) "自动播放" else "点击播放" + } else "全屏播放" + val videoPlayTs = currentPositionWhenPlaying / 1000 + val videoTotalTime = duration / 1000 + val progress = if (videoTotalTime != 0) videoPlayTs.toFloat() / videoTotalTime.toFloat() * 100 else 0f + if (mContentLength == 0.0) { + mContentLength = ExoCacheManager.getContentLength(video!!.url) / 1024.0 / 1024.0 + } + + //https://exoplayer.dev/hello-world.html#a-note-on-threading + runOnUiThread { + LogUtils.uploadTopVideoStreamingPlaying(action, video?.videoId, video?.title, viewModel?.game?.id, viewModel?.game?.name, + videoPlayModel, videoPlayStatus(), mContentLength, videoTotalTime, videoPlayTs, progress.toInt()) + } + } + } + + private fun videoPlayStatus(): String { + return when (mCurrentState) { + CURRENT_STATE_PLAYING, CURRENT_STATE_PREPAREING, CURRENT_STATE_PLAYING_BUFFERING_START -> "play" + GSYVideoView.CURRENT_STATE_PAUSE -> "pause" + else -> "" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index 566b3ad07c..383fce7474 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -89,7 +89,7 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a fun getGameCollectionDetail(gameCollectionId: String) { if (gameCollectionId.isEmpty()) return processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", true)) - mApi.getGameCollectionDetail(gameCollectionId) + mApi.getGameCollectionDetailForDraft(gameCollectionId) .compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: GamesCollectionEntity?) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt index 68731fb867..5113661e69 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt @@ -154,7 +154,9 @@ data class Count( var favorite: Int = 0, @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COMMENT_REPLY_COUNT]) var reply: Int = 0, - var game: Int = 0 + var game: Int = 0, + @SerializedName("game_played") + var playedGame: Int = 0 ) : Parcelable { @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COUNT]) diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 118978596b..eb88165e32 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3287,7 +3287,7 @@ public interface ApiService { * 游戏单详情(用户编辑自己的游戏单时调用) */ @GET("game_lists/{game_list_id}?view=draft") - Observable getGameCollectionDetail(@Path("game_list_id") String id); + Observable getGameCollectionDetailForDraft(@Path("game_list_id") String id); /** * 删除单条游戏单数据 @@ -3306,4 +3306,10 @@ public interface ApiService { */ @GET("users/{user_id}/game_lists") Single> getUserGameCollectionList(@Path("user_id") String userId, @QueryMap Map params); + + /** + * 游戏单详情 + */ + @GET("game_lists/{game_list_id}") + Single getGameCollectionDetail(@Path("game_list_id") String id); } \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square.webp index 831b141f807d0190a995c88543fef8805ab9ace4..3d451223ccbd6f939d3f8a181f003bb576ce831b 100644 GIT binary patch delta 671 zcmV;Q0$}~Z3&sxyQb|Tez6Y@eHU)o%0ssIo6#$(9DqsL$06rxSg+if+$HE2xku5EJ zD3%7O55>=RVqr}FgTLT3gg?9S;mia1FZ!>tzLr{me+B+q{$u)=<1c$}*neOT&;tnu z{4Y+Wo)|~f+#Yw7kRPo0f8ISGt4xuCDfQ=@&-j)1Rth3bsQzmh`Llhn?jC;#sCSiA z(YI*1$L(yi4p2xZL33)qQ&NWGHir=`?D0#ncY4vyvq(BK$d@mYzRq7si~OiG_y7R@ z|Nc%SpYAj36gBnv`%IPXh~3HZ_PeY;maCu$j1`Lbp7zyjbPk|b2X0_~)!z zK9+FTBBJEU_9sTvs%3rg^KF3DFsSNsfll%nnR^Z{s`{RmhZLvF$*ngDcQs*~)~8pt z6jF$Y5zUxYvrqVTNLbt2ie!Z}1raU6t6IcbS|ouEt7GS#Fd z;1=Wb-G+#K^t=L(Iy|hs2A}pZ9Pz2eb-nc9dfx$5G}09;fEO(|g5iQEt}$e>X*6UP zfVR7nLv9(Hp}O3Mq#%DNf-G_HYqaFZ{^c3ciuGfH(Iz-EBzDRpR`mUnNwp!nOM|pH z&-)?{bXrCywb{&{r^|o;#brB~TSJs$*$+zrn_ho@>Smfw0fqoR5_vpZ{iU^Xm76u>ccdTBtYzCAwLYm&? zhbEq>L@;*a?r_)ile548{`osw_U)*@%alLu{gA)-VR`+(>oiaQ|D+x=KW_*9|F`x4 F006NJMYsR} diff --git a/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_light.webp b/app/src/main/res/drawable-xxxhdpi/ic_game_collection_square_light.webp new file mode 100644 index 0000000000000000000000000000000000000000..831b141f807d0190a995c88543fef8805ab9ace4 GIT binary patch literal 1474 zcmV;z1wHywNk&Gx1pok7MM6+kP&il$0000G0001A003VA06|PpND>7A009{>4H+6Q z5&e(Dj5z{-%5bZgm%2h#;a|cn#$AitzIwHo&~P=YCtHib_D3i!Y4Z;nf9#efTdc;@W^~%TZ>B z8|lV#+%|Q!!LJT3UB$@`b9n;NzyqHa$@YV$Irirb^HOC`e`Br; zPTZQ>KXWGmlS`ldUN-bzV;TjAJ`9aaOw9n-->VBG8~3uq%v`504-T%R0PE(JS4xhy zhx9n}p|#64=twxQk_626KpOS?CGxeOja2hF&;s09hO%N$z@$eiRLH)S9>HusR{`5D zELZpI zQ>CXVvWHButF6tE{QVGI2M#$!f)_t)6Ts(4W?$}x!ewVm7N4&}2k!8@M6#`;2V6W? zQkx(51UbPzNC{r&?Udx?z@Dof?O#;8=HHPq19_QVut|c1HK{*81fb}Op{s8>tih2X z;^~+q(PT^3wDTeFJ$X=_8oQ7uA*pEbR2G#99Uq?$_pbllq)&Jv3Bz-|L`E7yPr&Pq zeOGEDQ(%492R-5Dmy}B48d8hw>J0B4p&TpGAbb)>PuQ;(REVm9H4vn%ClFR+h00b3 zQk$qSMS~*?`&!NB75%*MGz}~ZhDz)tLh?POBq(?H+Z%0QHLxyU=aY#d9VO2~x}f_3PbG1MU^~kOjEq1%|K9gK zMavX~&s5m&Eo)o|Iy2#&6$siKSxT|DZXsx$php;E8OU2Tbe6xgN|UcuY@2$GTeO57 zPTn1do?ttPunf09cYcB$X6Dj3bi05gBx$L%rU4c*xv&=p8f$BFv{7K^`;L?{MU#h8M$fGjg{P$q@%9DpB$|61Bx)i!!-BRpg z`TDH;f<(pe_^Gz?M<)6$Ybmd)ZaM81s1AqzDfj;qu%f!WwlNw=&n+tQp&%8?YyU8S zp*Q><*4N@NJ6x6}PCt#pDbT;Xm76u>ccdTBtYzCAwLYm&?hbEq>L@;*a?r_)ile548{`osw_U)*@%alLu c{gA)-VR`+(>oiaQ|D+x=KX3j2xAp)40D95BlK=n! literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/fragment_game_collection_detail.xml b/app/src/main/res/layout/fragment_game_collection_detail.xml new file mode 100644 index 0000000000..c631cb6d61 --- /dev/null +++ b/app/src/main/res/layout/fragment_game_collection_detail.xml @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_poster.xml b/app/src/main/res/layout/fragment_game_collection_poster.xml new file mode 100644 index 0000000000..f986c9c40a --- /dev/null +++ b/app/src/main/res/layout/fragment_game_collection_poster.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_detail_image_item.xml b/app/src/main/res/layout/game_collection_detail_image_item.xml new file mode 100644 index 0000000000..5019e0052c --- /dev/null +++ b/app/src/main/res/layout/game_collection_detail_image_item.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_detail_none_game_item.xml b/app/src/main/res/layout/game_collection_detail_none_game_item.xml new file mode 100644 index 0000000000..80b35ce952 --- /dev/null +++ b/app/src/main/res/layout/game_collection_detail_none_game_item.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_detail_video_item.xml b/app/src/main/res/layout/game_collection_detail_video_item.xml new file mode 100644 index 0000000000..21bfb14d31 --- /dev/null +++ b/app/src/main/res/layout/game_collection_detail_video_item.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_game_item.xml b/app/src/main/res/layout/game_collection_game_item.xml new file mode 100644 index 0000000000..ed25814e2d --- /dev/null +++ b/app/src/main/res/layout/game_collection_game_item.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_item.xml b/app/src/main/res/layout/game_collection_item.xml index 31fec19ee2..66ba66257e 100644 --- a/app/src/main/res/layout/game_collection_item.xml +++ b/app/src/main/res/layout/game_collection_item.xml @@ -69,7 +69,7 @@ android:layout_height="20dp" android:layout_marginTop="18dp" android:layout_marginRight="6dp" - tools:src="@drawable/ic_chosen_big" + tools:src="@drawable/ic_chosen" app:layout_goneMarginRight="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@+id/moreIv" /> @@ -91,7 +91,7 @@ android:layout_height="32dp" android:layout_marginLeft="16dp" android:layout_marginTop="12dp" - game="@{entity.games[0].toGameEntity}" + game="@{entity.games[0].toGameEntity()}" app:gameIconBorderWidth="1dp" app:gameIconBorderColor="@color/white" app:layout_constraintTop_toBottomOf="@+id/nameTv" @@ -103,7 +103,7 @@ android:layout_width="32dp" android:layout_height="32dp" android:layout_marginLeft="6dp" - game="@{entity.games[1].toGameEntity}" + game="@{entity.games[1].toGameEntity()}" app:gameIconBorderWidth="1dp" app:gameIconBorderColor="@color/white" app:layout_constraintTop_toTopOf="@+id/gameOne" @@ -116,7 +116,7 @@ android:layout_width="32dp" android:layout_height="32dp" android:layout_marginLeft="6dp" - game="@{entity.games[2].toGameEntity}" + game="@{entity.games[2].toGameEntity()}" app:gameIconBorderWidth="1dp" app:gameIconBorderColor="@color/white" app:layout_constraintTop_toTopOf="@+id/gameTwo" diff --git a/app/src/main/res/layout/layout_game_collection_tag.xml b/app/src/main/res/layout/layout_game_collection_tag.xml new file mode 100644 index 0000000000..7558227384 --- /dev/null +++ b/app/src/main/res/layout/layout_game_collection_tag.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/piece_article_input_container.xml b/app/src/main/res/layout/piece_article_input_container.xml index 28027de83e..c7b2199691 100644 --- a/app/src/main/res/layout/piece_article_input_container.xml +++ b/app/src/main/res/layout/piece_article_input_container.xml @@ -31,6 +31,38 @@ app:layout_constraintTop_toTopOf="parent" tools:text="说点什么吧" /> + + + + + + app:layout_constraintRight_toLeftOf="@+id/bottomShareIv" + app:layout_constraintTop_toTopOf="parent" /> Date: Wed, 17 Nov 2021 11:17:37 +0800 Subject: [PATCH 17/49] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95=E9=A1=B5=E9=9D=A2=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E6=A0=87=E7=AD=BEUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyGameCollectionViewHolder.kt | 4 +- .../publish/GameCollectionEditActivity.kt | 47 ++++++++++++++++--- .../tag/GameCollectionTagSelectFragment.kt | 4 +- .../qa/editor/OnLinkClickListener.kt | 5 +- .../layout/activity_game_collection_edit.xml | 17 ++++--- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewHolder.kt index 98d3119eb1..8f8668d849 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionViewHolder.kt @@ -8,6 +8,7 @@ import com.gh.gamecenter.databinding.ItemGameCollectionFlexTagBinding import com.gh.gamecenter.databinding.ItemMyGameCollectionBinding import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.TagInfoEntity +import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity class MyGameCollectionViewHolder(val binding: ItemMyGameCollectionBinding, val mViewModel: MyGameCollectionViewModel) : @@ -42,8 +43,7 @@ class MyGameCollectionViewHolder(val binding: ItemMyGameCollectionBinding, val m } binding.root.setOnClickListener { if (entity.status == "pass" || (entity.display == "self_only" && entity.status == "draft")) { - //TODO:进入游戏单详情 - + binding.root.context.startActivity(GameCollectionDetailActivity.getIntent(binding.root.context,entity.id)) } else { binding.statusView.performClick() } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index 85144d91d7..53c77c1213 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -4,7 +4,9 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.LayoutInflater import android.view.MenuItem +import android.view.View import androidx.core.widget.doOnTextChanged import com.gh.base.ToolBarActivity import com.gh.base.fragment.WaitingDialogFragment @@ -12,12 +14,14 @@ import com.gh.common.util.* import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding +import com.gh.gamecenter.databinding.ItemGameCollectionFlexTagBinding import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity +import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectFragment import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.qa.editor.LocalMediaActivity import com.zhihu.matisse.Matisse @@ -121,6 +125,7 @@ class GameCollectionEditActivity : ToolBarActivity() { private fun initData(isFirst: Boolean = true) { mBinding.gameCollectionTitleEt.filters = arrayOf(TextHelper.getFilter(20, "最多输入20个字")) mBinding.gameCollectionIntroduceEt.filters = arrayOf(TextHelper.getFilter(200, "最多输入200个字")) + initTagsUI(mViewModel.tags) mViewModel.gameCollectionPatch?.run { mViewModel.imageUrl = cover @@ -179,9 +184,9 @@ class GameCollectionEditActivity : ToolBarActivity() { intro = it.intro } mViewModel.gameCollectionPatch?.run { - //TODO:initTags initData(false) mViewModel.tags = tags ?: arrayListOf() + initTagsUI(mViewModel.tags) val simpleGames = games?.map { game -> game.toGameEntity() }?.toList() mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames)) } @@ -190,8 +195,8 @@ class GameCollectionEditActivity : ToolBarActivity() { mViewModel.gameCollectionPatch = it mViewModel.gameCollectionPatch?.run { initData(false) - //TODO:initTags mViewModel.tags = tags ?: arrayListOf() + initTagsUI(mViewModel.tags) val simpleGames = games?.map { game -> game.toGameEntity() } mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames)) } @@ -234,6 +239,9 @@ class GameCollectionEditActivity : ToolBarActivity() { initPosterUI() } REQUEST_CHOOSE_TAG -> { + val tags = data.getParcelableArrayListExtra(GameCollectionTagSelectFragment.SELECTED_TAG) ?: arrayListOf() + mViewModel.tags = tags + initTagsUI(tags) } } } @@ -249,6 +257,37 @@ class GameCollectionEditActivity : ToolBarActivity() { } } + private fun initTagsUI(tags: ArrayList) { + mBinding.gameCollectionTagsContainer.removeAllViews() + mBinding.labelTipTv.goneIf(tags.isNotEmpty()) + if (tags.isEmpty()) { + val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(this), null, false) + tagBinding.tagNameTv.run { + text = "选择标签" + setTextColor(R.color.text_333333.toColor()) + textSize = 14f + } + tagBinding.divider.visibility = View.GONE + mBinding.gameCollectionTagsContainer.addView(tagBinding.root) + } else { + tags.forEachIndexed { index, tag -> + val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(this), null, false) + tagBinding.tagNameTv.run { + text = tag.name + setTextColor(R.color.text_333333.toColor()) + textSize = 14f + } + tagBinding.divider.run { + alpha = 0.2f + setBackgroundColor(R.color.text_333333.toColor()) + goneIf(index == tags.size - 1) + setPadding(8f.dip2px(), 0, 8f.dip2px(), 0) + } + mBinding.gameCollectionTagsContainer.addView(tagBinding.root) + } + } + } + override fun handleBackPressed(): Boolean { val patch = mViewModel.gameCollectionPatch if (patch != null && patch.status.isNotEmpty() && (patch.status != "draft" || patch.display == "self_only")) { @@ -281,10 +320,6 @@ class GameCollectionEditActivity : ToolBarActivity() { } private fun verifyData() { - //TODO:测试数据 - mViewModel.tags.clear() - mViewModel.tags.add(TagInfoEntity(id = "6184ec6d92109de55a4276eb")) - if (mViewModel.imageUrl.isEmpty()) { toast("请上传游戏单的封面") return diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt index fdbdf7f983..dd931789e8 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.gamecollection.tag +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.MenuItem @@ -10,7 +11,6 @@ import com.gh.common.util.viewModelProvider import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding -import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment import com.gh.gamecenter.normal.NormalFragment @@ -58,7 +58,7 @@ class GameCollectionTagSelectFragment : NormalFragment() { ) } else { requireActivity().setResult( - GameCollectionEditActivity.REQUEST_CHOOSE_TAG, + Activity.RESULT_OK, Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntityList) ) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt index 3d755a4073..ad169d2a6d 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt @@ -4,16 +4,15 @@ import android.content.Context import android.webkit.JavascriptInterface import com.gh.base.BaseActivity import com.gh.common.AppExecutor -import com.gh.common.DefaultJsApi import com.gh.common.util.* import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.entity.CommunityEntity import com.gh.gamecenter.entity.MtaEvent import com.gh.gamecenter.entity.MyVideoEntity +import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity import com.gh.gamecenter.qa.answer.detail.SimpleAnswerDetailActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.EditorInsertEntity -import com.lightgame.utils.Utils /** * 富文本编辑框的链接回调 @@ -60,7 +59,7 @@ class OnLinkClickListener( ) } "game_collection" -> { - //TODO:跳转游戏单详情 + context.startActivity(GameCollectionDetailActivity.getIntent(context, insertEntity.id ?: "")) } } } diff --git a/app/src/main/res/layout/activity_game_collection_edit.xml b/app/src/main/res/layout/activity_game_collection_edit.xml index bc46d99ced..99b77030e5 100644 --- a/app/src/main/res/layout/activity_game_collection_edit.xml +++ b/app/src/main/res/layout/activity_game_collection_edit.xml @@ -90,17 +90,20 @@ android:gravity="center_vertical" android:orientation="horizontal"> - + + + app:flexDirection="row" + app:flexWrap="wrap" /> Date: Wed, 17 Nov 2021 14:45:31 +0800 Subject: [PATCH 18/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E7=A6=81?= =?UTF-8?q?=E8=A8=80=E7=9B=B8=E5=85=B3=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1597?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../com/gh/common/constant/Constants.java | 3 ++ .../java/com/gh/common/util/ErrorHelper.kt | 39 ++++++++++++++++++- .../{ => mine}/MyGameCollectionActivity.kt | 2 +- .../{ => mine}/MyGameCollectionAdapter.kt | 2 +- .../{ => mine}/MyGameCollectionFragment.kt | 12 +++++- .../{ => mine}/MyGameCollectionViewHolder.kt | 2 +- .../{ => mine}/MyGameCollectionViewModel.kt | 2 +- .../personal/PersonalFunctionAdapter.kt | 2 +- 9 files changed, 56 insertions(+), 10 deletions(-) rename app/src/main/java/com/gh/gamecenter/gamecollection/{ => mine}/MyGameCollectionActivity.kt (92%) rename app/src/main/java/com/gh/gamecenter/gamecollection/{ => mine}/MyGameCollectionAdapter.kt (98%) rename app/src/main/java/com/gh/gamecenter/gamecollection/{ => mine}/MyGameCollectionFragment.kt (85%) rename app/src/main/java/com/gh/gamecenter/gamecollection/{ => mine}/MyGameCollectionViewHolder.kt (99%) rename app/src/main/java/com/gh/gamecenter/gamecollection/{ => mine}/MyGameCollectionViewModel.kt (98%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 85af5f4787..970b3d2e58 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -747,7 +747,7 @@ android:screenOrientation="portrait" /> handleErrorWithCommentBannedDialog(context, errorEntity) + 403200, + 403201, + 403202 -> handleErrorWithGameCollectionBannedDialog(context) 403001 -> Utils.toast(context, "标签名称太长了") 403002 -> Utils.toast(context, "已经被邀请了") @@ -160,6 +164,25 @@ object ErrorHelper { } } + private fun handleErrorWithGameCollectionBannedDialog(context: Context) { + val dialogContext = DialogUtils.checkDialogContext(context) + DialogHelper.showDialog( + dialogContext, + "提示", + "你因违反《游戏单管理规则》,已被禁言,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})", + "去看看", "关闭", { + dialogContext.startActivity( + WebActivity.getWebIntent( + dialogContext, + "游戏单管理规则", + Constants.GAME_COLLECTION_RULE + ) + ) + }, + extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) + ) + } + private fun handleErrorWithCommentBannedDialog(context: Context, errorEntity: ErrorEntity?) { val bannedType = if (errorEntity?.data?.alwaysBlock == true) { "" @@ -172,7 +195,13 @@ object ErrorHelper { "提示", "你因违反《光环助手评论规则》,已被禁言$bannedType,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})", "去看看", "关闭", { - dialogContext.startActivity(WebActivity.getWebIntent(dialogContext, dialogContext.getString(R.string.comment_rules_title), dialogContext.getString(R.string.comment_rules_url))) + dialogContext.startActivity( + WebActivity.getWebIntent( + dialogContext, + dialogContext.getString(R.string.comment_rules_title), + dialogContext.getString(R.string.comment_rules_url) + ) + ) }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) ) @@ -190,7 +219,13 @@ object ErrorHelper { "提示", "你因违反《问答版块规则》,已被禁言$bannedType,如有疑问,请联系客服(QQ:1562479331)", "去看看", "关闭", { - dialogContext.startActivity(WebActivity.getWebIntent(dialogContext, dialogContext.getString(R.string.community_rule_title), dialogContext.getString(R.string.community_rule_url))) + dialogContext.startActivity( + WebActivity.getWebIntent( + dialogContext, + dialogContext.getString(R.string.community_rule_title), + dialogContext.getString(R.string.community_rule_url) + ) + ) }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) ) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionActivity.kt similarity index 92% rename from app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionActivity.kt index f894d8df15..2836609f67 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionActivity.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.gamecollection +package com.gh.gamecenter.gamecollection.mine import android.content.Context import android.content.Intent diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt similarity index 98% rename from app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt index d508cc2ce5..134f8e642e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.gamecollection +package com.gh.gamecenter.gamecollection.mine import android.content.Context import android.view.ViewGroup diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt similarity index 85% rename from app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt rename to app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt index 3af8c8823a..26b255ca70 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/MyGameCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt @@ -1,12 +1,15 @@ -package com.gh.gamecenter.gamecollection +package com.gh.gamecenter.gamecollection.mine +import android.content.Intent import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.Constants import com.gh.common.util.showRegulationTestDialogIfNeeded import com.gh.common.view.VerticalItemDecoration import com.gh.gamecenter.R +import com.gh.gamecenter.WebActivity import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.baselist.LoadType @@ -14,6 +17,7 @@ import com.gh.gamecenter.databinding.FragmentMyGameCollectionListBinding import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -34,7 +38,7 @@ class MyGameCollectionFragment : ListFragment Date: Wed, 17 Nov 2021 14:55:04 +0800 Subject: [PATCH 19/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E7=A6=81?= =?UTF-8?q?=E8=A8=80=E7=9B=B8=E5=85=B3=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1597?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/gh/common/util/ErrorHelper.kt | 4 ++-- .../gamecollection/mine/MyGameCollectionFragment.kt | 2 +- .../gamecollection/publish/GameCollectionEditActivity.kt | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/gh/common/util/ErrorHelper.kt b/app/src/main/java/com/gh/common/util/ErrorHelper.kt index 9a77eacaf2..a26d7e84ac 100644 --- a/app/src/main/java/com/gh/common/util/ErrorHelper.kt +++ b/app/src/main/java/com/gh/common/util/ErrorHelper.kt @@ -169,12 +169,12 @@ object ErrorHelper { DialogHelper.showDialog( dialogContext, "提示", - "你因违反《游戏单管理规则》,已被禁言,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})", + "你因违反《游戏单管理规范》,已被禁言,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})", "去看看", "关闭", { dialogContext.startActivity( WebActivity.getWebIntent( dialogContext, - "游戏单管理规则", + "游戏单管理规范", Constants.GAME_COLLECTION_RULE ) ) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt index 26b255ca70..8acebe162e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt @@ -81,7 +81,7 @@ class MyGameCollectionFragment : ListFragment Date: Wed, 17 Nov 2021 15:34:16 +0800 Subject: [PATCH 20/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF?= =?UTF-8?q?=E5=9C=BAhttps://git.ghzs.com/pm/halo-app-issues/-/issues/1598?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../square/GameCollectionSquareAdapter.kt | 52 ++++++++++- .../square/GameCollectionSquareFragment.kt | 88 +++---------------- .../square/GameCollectionSquareViewModel.kt | 15 ++-- .../tag/GameCollectionTagSelectFragment.kt | 3 +- .../gh/gamecenter/qa/entity/ArticleEntity.kt | 3 +- .../fragment_game_collection_square.xml | 6 +- .../fragment_game_collection_square_al.xml | 7 +- .../layout/game_collection_square_item.xml | 23 ++--- 8 files changed, 87 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt index 98d2c8e3df..e1c7f447b3 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt @@ -1,21 +1,27 @@ package com.gh.gamecenter.gamecollection.square +import android.annotation.SuppressLint import android.content.Context +import android.graphics.Color +import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toDrawable import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureSource import com.gh.common.exposure.IExposable -import com.gh.common.util.DisplayUtils -import com.gh.common.util.toDrawable +import com.gh.common.util.* import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding import com.gh.gamecenter.databinding.GameCollectionSquareItemBinding import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity class GameCollectionSquareAdapter( context: Context, @@ -100,10 +106,52 @@ class GameCollectionSquareAdapter( class GameCollectionSquareItemViewHolder(var binding: GameCollectionSquareItemBinding) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") fun bindGameCollection(gamesCollectionEntity: GamesCollectionEntity) { binding.run { + val context = root.context entity = gamesCollectionEntity stampIv.setImageDrawable(if (gamesCollectionEntity.stamp == "offical") R.drawable.ic_official.toDrawable() else R.drawable.ic_chosen.toDrawable()) + tagContainer.removeAllViews() + if (!gamesCollectionEntity.tags.isNullOrEmpty()) { + for ((index, tagEntity) in gamesCollectionEntity.tags!!.withIndex()) { + // 添加分隔线 + if (index != 0) tagContainer.addView(View(context).apply { + layoutParams = ViewGroup.LayoutParams(1F.dip2px(), 14F.dip2px()) + setBackgroundColor(Color.parseColor("#33FFFFFF")) + }) + // 添加标签 + tagContainer.addView(TextView(context).apply { + layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) + setPadding(if (index == 0) 0 else 6F.dip2px(), 0, 6F.dip2px(), 0) + text = tagEntity.name + setTextColor(R.color.white.toColor()) + textSize = 10F + }) + } + } + tagContainer.goneIf(CheckLoginUtils.isLogin()) + playedGamesContainer.goneIf(!CheckLoginUtils.isLogin()) + iconIvOne.setOnClickListener { gamesCollectionEntity.games?.get(0)?.id?.let { id -> + DirectUtils.directToGameDetail(context, + id, "游戏单广场") + } } + iconIvTwo.setOnClickListener { gamesCollectionEntity.games?.get(1)?.id?.let { id -> + DirectUtils.directToGameDetail(context, + id, "游戏单广场") + } } + iconIvThree.setOnClickListener { gamesCollectionEntity.games?.get(2)?.id?.let { id -> + DirectUtils.directToGameDetail(context, + id, "游戏单广场") + } } + playedGameProgress.progress = gamesCollectionEntity.count?.game?.let { + gamesCollectionEntity.count?.playedGame?.div( + it + ) + } ?: 0 + playedGameTv.text = "玩过 ${gamesCollectionEntity.count?.playedGame} / ${gamesCollectionEntity.count?.game} 款" + userContainer.setOnClickListener { DirectUtils.directToHomeActivity(context, gamesCollectionEntity.user?.id, 0, "游戏单广场") } + root.setOnClickListener { context.startActivity(GameCollectionDetailActivity.getIntent(context, gamesCollectionEntity.id, true)) } } } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt index f662d4c641..9efa358816 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -1,32 +1,29 @@ package com.gh.gamecenter.gamecollection.square +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat -import androidx.recyclerview.widget.RecyclerView import com.gh.common.TimeElapsedHelper import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource import com.gh.common.util.* import com.gh.common.view.VerticalItemDecoration -import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LazyListFragment import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.FragmentGameCollectionSquareAlBinding import com.gh.gamecenter.databinding.FragmentGameCollectionSquareBinding import com.gh.gamecenter.entity.GamesCollectionEntity -import com.gh.gamecenter.eventbus.EBDownloadStatus -import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity +import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectFragment import com.gh.gamecenter.personal.PersonalFragment import com.google.android.material.appbar.AppBarLayout -import com.lightgame.download.DataWatcher -import com.lightgame.download.DownloadEntity import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import kotlin.math.abs @@ -43,16 +40,6 @@ class GameCollectionSquareFragment : LazyListFragment(GameCollectionTagSelectFragment.SELECTED_TAG) + mDefaultBinding.tagTv.text = tagInfoEntity?.name ?: "标签筛选" + mViewModel.tagId = tagInfoEntity?.id ?: "" + onLoadRefresh() } } @@ -120,8 +109,6 @@ class GameCollectionSquareFragment : LazyListFragment - if (checkedId == R.id.hotRb) { - // 热门排序 - // TODO 热门排序 - } else { - // 最新排序 - // TODO 最新排序 - } + mViewModel.view = if (checkedId == R.id.hotRb) "hot" else "new" + onLoadRefresh() } mDefaultBinding.fab.setOnClickListener { @@ -203,12 +185,6 @@ class GameCollectionSquareFragment : LazyListFragment(application) { var entrance: String? = null + var tagId = "" + var view = "hot" - init { - initData() - } - - fun initData() { - - } - -// override fun provideDataSingle(page: Int): Single> = RetrofitManager.getInstance(getApplication()) -// .api.getGameCollectionSquareList(page) + override fun provideDataSingle(page: Int): Single> = RetrofitManager.getInstance(getApplication()) + .api.getGameCollectionSquareList(view, tagId, page) override fun mergeResultLiveData() { mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt index dd931789e8..ceb08c9f50 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/tag/GameCollectionTagSelectFragment.kt @@ -11,7 +11,6 @@ import com.gh.common.util.viewModelProvider import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding -import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment import com.gh.gamecenter.normal.NormalFragment class GameCollectionTagSelectFragment : NormalFragment() { @@ -53,7 +52,7 @@ class GameCollectionTagSelectFragment : NormalFragment() { if (menuItem?.itemId == R.id.layout_menu_save) { if (mSingleChoice) { requireActivity().setResult( - GameCollectionSquareFragment.REQUEST_SELECT_TAG, + Activity.RESULT_OK, Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntity) ) } else { diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt index 5113661e69..b551adac40 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt @@ -156,7 +156,8 @@ data class Count( var reply: Int = 0, var game: Int = 0, @SerializedName("game_played") - var playedGame: Int = 0 + var playedGame: Int = 0, + var hot: Int = 0 ) : Parcelable { @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COUNT]) diff --git a/app/src/main/res/layout/fragment_game_collection_square.xml b/app/src/main/res/layout/fragment_game_collection_square.xml index 1c49ee7234..f372f2aab1 100644 --- a/app/src/main/res/layout/fragment_game_collection_square.xml +++ b/app/src/main/res/layout/fragment_game_collection_square.xml @@ -186,10 +186,10 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - + android:layout_height="wrap_content" + android:layout_gravity="center"/> diff --git a/app/src/main/res/layout/game_collection_square_item.xml b/app/src/main/res/layout/game_collection_square_item.xml index 79781dbb6c..dac3cae5b9 100644 --- a/app/src/main/res/layout/game_collection_square_item.xml +++ b/app/src/main/res/layout/game_collection_square_item.xml @@ -180,9 +180,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" + android:text="@{``+entity.count.hot}" android:textColor="@color/white" android:textSize="@dimen/secondary_size" - tools:text="10000" /> + tools:text="999" /> @@ -194,6 +195,7 @@ android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" + android:background="@drawable/bg_game_collection_user_container" android:gravity="center_vertical" android:orientation="horizontal" android:paddingEnd="6dp" @@ -233,25 +235,18 @@ app:layout_constraintBottom_toBottomOf="@+id/userContainer" app:layout_constraintEnd_toStartOf="@+id/userContainer" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/userContainer"> - - - + app:layout_constraintTop_toTopOf="@+id/userContainer" /> @@ -265,10 +260,10 @@ From 10973002117fb26f70b47ecad635e62c7a097931 Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Wed, 17 Nov 2021 16:16:58 +0800 Subject: [PATCH 21/49] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E6=84=8F=E8=A7=81=E5=8F=8D=E9=A6=88=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gamecenter/gamecollection/choose/AddSearchGameFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt index 7f166d03bb..a1799ffd54 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/AddSearchGameFragment.kt @@ -40,7 +40,7 @@ class AddSearchGameFragment : GameFragment() { } mBinding.applyGameTv.setOnClickListener { - SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.gameCollect, "求游戏:") + SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.gameCollect, "【游戏单添加游戏】") } } From 68db40c4c80b46763cac72d0ce4eac8ae5e74c2d Mon Sep 17 00:00:00 2001 From: lyr <15622190878@163.com> Date: Thu, 18 Nov 2021 10:49:14 +0800 Subject: [PATCH 22/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E6=88=91?= =?UTF-8?q?=E7=9A=84=E6=94=B6=E8=97=8F=EF=BC=88=E5=AF=B9=E6=8E=A5=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=89=20https://git.ghzs.com/pm/halo-app-issues/-/?= =?UTF-8?q?issues/1592=EF=BC=8C=20=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E8=AF=A6?= =?UTF-8?q?=E6=83=85-=E9=A1=B6=E9=83=A8=E5=8C=BA=E5=9F=9F/=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=88=97=E8=A1=A8=EF=BC=88=E4=BA=8C=E3=80=81=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=A0=B7=E5=BC=8F-=E9=A1=B6=E9=83=A8=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=EF=BC=88=E8=A7=86=E9=A2=91=E6=95=B0=E6=8D=AE=E6=9C=AA?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=EF=BC=89=EF=BC=89https://git.ghzs.com/pm/hal?= =?UTF-8?q?o-app-issues/-/issues/1601?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collection/GamesCollectionAdapter.kt | 40 +- .../collection/GamesCollectionViewModel.kt | 45 ++- .../java/com/gh/gamecenter/entity/MeEntity.kt | 4 +- .../detail/GameCollectionDetailFragment.kt | 369 ++++++++++++++---- .../detail/GameCollectionDetailViewModel.kt | 90 ++++- .../detail/GameCollectionPosterFragment.kt | 6 +- .../retrofit/service/ApiService.java | 30 ++ .../drawable/button_round_black_alpha_10.xml | 9 + .../fragment_game_collection_detail.xml | 1 - .../game_collection_detail_image_item.xml | 7 +- .../game_collection_detail_video_item.xml | 37 +- .../main/res/layout/game_collection_item.xml | 3 +- app/src/main/res/values/colors.xml | 1 + 13 files changed, 541 insertions(+), 101 deletions(-) create mode 100644 app/src/main/res/drawable/button_round_black_alpha_10.xml diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt index 9c307cc32c..e2a271e4de 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionAdapter.kt @@ -14,10 +14,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType -import com.gh.common.util.DialogHelper -import com.gh.common.util.goneIf -import com.gh.common.util.safelyGetInRelease -import com.gh.common.util.showAutoOrientation +import com.gh.common.util.* import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder @@ -78,6 +75,7 @@ class GamesCollectionAdapter( tagIv.setBackgroundResource(R.drawable.ic_official) } } + if (mViewModel.mIsInsertGameCollection) { when (mViewModel.type) { GamesCollectionFragment.TYPE_COLLECT -> { @@ -134,6 +132,40 @@ class GamesCollectionAdapter( mContext.startActivity(GameCollectionDetailActivity.getIntent(mContext, itemEntity.id)) } } + + voteCountContainer.setOnClickListener { + debounceActionWithInterval(R.id.vote_count_container, 1000) { + mViewModel.postVoteGameCollection(itemEntity.id, !voteIcon.isChecked) { + if (!voteIcon.isChecked) { + voteCount.setTextColor(R.color.theme_font.toColor()) + voteIcon.isChecked = true + voteIcon.visibility = View.GONE + voteAnimation.visibility = View.VISIBLE + voteAnimation.setAnimation("lottie/community_vote.json") + voteAnimation.playAnimation() + voteAnimation.doOnAnimationEnd { + voteAnimation.visibility = View.GONE + voteIcon.visibility = View.VISIBLE + } + + itemEntity.me?.vote = true + itemEntity.count?.run { + vote++ + voteCount.text = vote.toString() + } + } else { + voteIcon.isChecked = false + itemEntity.me?.vote = false + itemEntity.count?.run { + vote-- + if (vote < 0) vote = 0 + voteCount.setTextColor(R.color.text_999999.toColor()) + voteCount.text = vote.toString() + } + } + } + } + } } } diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt index 9a31217aa0..47a6adcc17 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionViewModel.kt @@ -1,13 +1,18 @@ package com.gh.gamecenter.collection +import android.annotation.SuppressLint import android.app.Application import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.gh.common.util.ErrorHelper import com.gh.common.util.ToastUtils import com.gh.common.util.observableToMain +import com.gh.common.util.singleToMain import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.collection.GamesCollectionFragment.Companion.TYPE_COLLECT import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp @@ -25,10 +30,14 @@ class GamesCollectionViewModel(application: Application, var userId: String, var override fun provideDataObservable(page: Int) = null override fun provideDataSingle(page: Int): Single> { - val map = if (mIsInsertGameCollection) { - hashMapOf("filter" to "display") - } else mapOf() - return RetrofitManager.getInstance(getApplication()).api.getUserGameCollectionList(userId, map) + return if (type == TYPE_COLLECT) { + mApi.getFavoriteGameCollectionList(userId) + } else { + val map = if (mIsInsertGameCollection) { + hashMapOf("filter" to "display") + } else mapOf() + mApi.getUserGameCollectionList(userId, map) + } } override fun mergeResultLiveData() { @@ -69,6 +78,34 @@ class GamesCollectionViewModel(application: Application, var userId: String, var }) } + @SuppressLint("CheckResult") + fun postVoteGameCollection(gameCollectionId: String, isVote: Boolean, successCallback: (() -> Unit)) { + val single = if (isVote) { + mApi.voteGameCollection(gameCollectionId) + } else { + mApi.unVoteGameCollection(gameCollectionId) + } + single.compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + ToastUtils.toast(if (isVote) "点赞成功" else "取消点赞") + successCallback.invoke() + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + + if (exception is HttpException) { + ErrorHelper.handleError( + getApplication(), + exception.response()?.errorBody()?.string() + ) + } + } + }) + } + + class Factory(private val mUserId: String, private val mType: String, private val mIsInsertGameCollection: Boolean) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt index e9ac7b7617..9a774510e2 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt @@ -116,7 +116,9 @@ class MeEntity(@SerializedName("is_community_voted") var questionDraft: QuestionDraftEntity? = null,//问题详情可能返回草稿 @SerializedName("is_follow_bbs") - var isFollowForum: Boolean = false + var isFollowForum: Boolean = false, + + var vote: Boolean = false ) : Parcelable @Parcelize diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt index 442208839f..a77e94a62e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -3,6 +3,7 @@ package com.gh.gamecenter.gamecollection.detail import android.graphics.Color import android.os.Bundle import android.view.View +import androidx.annotation.ColorRes import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick import com.gh.common.util.* @@ -15,10 +16,14 @@ import com.gh.gamecenter.databinding.FragmentGameCollectionDetailBinding import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.entity.GamesCollectionDetailEntity +import com.gh.gamecenter.eventbus.EBUserFollow import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel import com.google.android.material.appbar.AppBarLayout import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode import kotlin.math.abs class GameCollectionDetailFragment : @@ -27,9 +32,10 @@ class GameCollectionDetailFragment : private var mBinding: FragmentGameCollectionDetailBinding? = null private var mAdapter: GameCollectionDetailAdapter? = null private var mGameAdapter: GameCollectionDetailGameAdapter? = null - private var mEntity = GamesCollectionDetailEntity() + private var mEntity: GamesCollectionDetailEntity? = null private var mGameCollectionId = "" private var mFromSquare = false + private var mIsLight = false private val mDataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mGameAdapter?.notifyItemByDownload(downloadEntity) @@ -58,44 +64,9 @@ class GameCollectionDetailFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mListViewModel.gameCollectionLiveData.observe(viewLifecycleOwner, { - mEntity = it - initTopView() - }) - - mBinding?.appbar?.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> - if (!isAdded) return@OnOffsetChangedListener - - val absVerticalOffset = abs(verticalOffset) - mListRefresh?.isEnabled = absVerticalOffset <= 2 - - mBinding?.run { - if (mEntity.video == null) { - val bgOffset = imageItem.root.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px() - - if (absVerticalOffset >= bgOffset) { - DisplayUtils.setStatusBarColor(requireActivity(), R.color.white, true) - toolbarContainer.setBackgroundColor(Color.WHITE) - backIv.setImageResource(R.drawable.ic_bar_back) - toolbarUserContainer.visibility = View.VISIBLE - toolbarLightUserContainer.visibility = View.GONE - toolbarFollowTv.setBackgroundResource(R.drawable.button_round_1a2496ff) - toolbarFollowTv.setTextColor(R.color.theme.toColor()) - squareIv.setImageResource(R.drawable.ic_game_collection_square) - } else { - DisplayUtils.setStatusBarColor(requireActivity(), R.color.transparent, false) - toolbarContainer.setBackgroundColor(Color.TRANSPARENT) - backIv.setImageResource(R.drawable.ic_bar_back_light) - toolbarUserContainer.visibility = View.GONE - toolbarLightUserContainer.visibility = View.VISIBLE - toolbarFollowTv.setBackgroundResource(R.drawable.button_round_black_alpha_30) - toolbarFollowTv.setTextColor(Color.WHITE) - squareIv.setImageResource(R.drawable.ic_game_collection_square_light) - } - } - } - - }) + initView() + initObserver() + initAppbarListener() } override fun onResume() { @@ -109,28 +80,117 @@ class GameCollectionDetailFragment : DownloadManager.getInstance(context).removeObserver(mDataWatcher) } + private fun initView() { + mBinding?.run { + inputContainer.run { + bottomShareGroup.visibility = View.VISIBLE + replyTv.setBackgroundResource(R.drawable.button_round_f5f5f5) + replyTv.text = "说点什么吧" + replyTv.setDebouncedClickListener { + startCommentActivity() + } + } + } + } + + private fun initObserver() { + mListViewModel.loadResultLiveData.observeNonNull(this) { + when (it) { + BaseCommentViewModel.LoadResult.SUCCESS -> { + mEntity = mListViewModel.gameCollectionDetail + updateView() + } + } + } + + mListViewModel.followLiveData.observeNonNull(this) { + updateFollowView() + } + + mListViewModel.favoriteLiveData.observeNonNull(this) { + updateStartView() + } + + mListViewModel.likeLiveData.observeNonNull(this) { + updateLikeView() + } + } + + private fun initAppbarListener() { + mBinding?.appbar?.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + if (!isAdded) return@OnOffsetChangedListener + + val absVerticalOffset = abs(verticalOffset) + mListRefresh?.isEnabled = absVerticalOffset <= 2 + + mBinding?.run { + mEntity?.run { + val bgOffset = if (video == null) { + imageItem.root.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px() + } else { + videoItem.player.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px() + } + mIsLight = absVerticalOffset < bgOffset + updateFollowView() + if (mIsLight) { + DisplayUtils.setStatusBarColor(requireActivity(), R.color.transparent, false) + toolbarContainer.setBackgroundColor(Color.TRANSPARENT) + backIv.setImageResource(R.drawable.ic_bar_back_light) + squareIv.setImageResource(R.drawable.ic_game_collection_square_light) + toolbarUserContainer.visibility = View.GONE + if (video == null) { + toolbarLightUserContainer.visibility = View.VISIBLE + } + } else { + DisplayUtils.setStatusBarColor(requireActivity(), R.color.white, true) + toolbarContainer.setBackgroundColor(Color.WHITE) + backIv.setImageResource(R.drawable.ic_bar_back) + squareIv.setImageResource(R.drawable.ic_game_collection_square) + toolbarUserContainer.visibility = View.VISIBLE + if (video == null) { + toolbarLightUserContainer.visibility = View.GONE + } + } + } + } + }) + } + + private fun updateView() { + initTopView() + updateFollowView() + updateStartView() + updateLikeView() + } + private fun initTopView() { mBinding?.run { mReuseNoConn?.setVisibility(View.GONE) mReuseNoData?.setVisibility(View.GONE) mListLoading?.setVisibility(View.GONE) - if (mEntity.video == null) { + mBinding?.run { + entity = mEntity + executePendingBindings() + squareIv.goneIf(mFromSquare) + } + + if (mEntity?.video == null) { imageItem.root.visibility = View.VISIBLE videoItem.root.visibility = View.GONE - initImageItemView() + initImageTypeView() } else { imageItem.root.visibility = View.GONE videoItem.root.visibility = View.VISIBLE - videoItem.entity = mEntity + initVideoTypeView() } - if (mEntity.games?.size ?: 0 > 0) { + if (mEntity?.games?.size ?: 0 > 0) { noneGameContainer.root.visibility = View.GONE gameListRv.run { visibility = View.VISIBLE layoutManager = LinearLayoutManager(requireContext()) - mGameAdapter = GameCollectionDetailGameAdapter(requireContext(), mEntity.games ?: listOf()) + mGameAdapter = GameCollectionDetailGameAdapter(requireContext(), mEntity?.games ?: listOf()) adapter = mGameAdapter } } else { @@ -140,43 +200,170 @@ class GameCollectionDetailFragment : } } - private fun initImageItemView() { - mBinding?.run { - entity = mEntity - executePendingBindings() - - squareIv.goneIf(mFromSquare) - } + private fun initImageTypeView() { mBinding?.imageItem?.run { - entity = mEntity - executePendingBindings() + mEntity?.run { + entity = this + executePendingBindings() - desTv.text = "游戏单简介:${mEntity.intro}" + desTv.text = "游戏单简介:${intro}" - when (mEntity.stamp) { - "special_choice" -> { - tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + when (stamp) { + "special_choice" -> { + tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + } + "official" -> { + tagIv.setBackgroundResource(R.drawable.ic_official_big) + } } - "official" -> { - tagIv.setBackgroundResource(R.drawable.ic_official_big) - } - } - if (!UserManager.getInstance().isLoggedIn) { - mEntity.tags?.forEachIndexed { index, tag -> - tagContainer.addView(getTagView(tag.name, index == mEntity.tags!!.size - 1)) + if (!UserManager.getInstance().isLoggedIn) { + tags?.forEachIndexed { index, tag -> + tagList.addView( + getTagView( + tag.name, + index == tags!!.size - 1, + R.color.white, + R.color.white_alpha_20 + ) + ) + } } } } } - private fun getTagView(content: String, isLast: Boolean): View { + private fun initVideoTypeView() { + mBinding?.videoItem?.run { + mEntity?.run { + entity = this + executePendingBindings() + + desTv.text = "游戏单简介:${intro}" + + when (stamp) { + "special_choice" -> { + tagIv.setBackgroundResource(R.drawable.ic_chosen_big) + } + "official" -> { + tagIv.setBackgroundResource(R.drawable.ic_official_big) + } + } + + if (!UserManager.getInstance().isLoggedIn) { + tags?.forEachIndexed { index, tag -> + tagList.addView( + getTagView( + tag.name, + index == tags!!.size - 1, + R.color.theme, + R.color.theme_alpha_20 + ) + ) + } + } + + userIcon.display(user?.border, user?.icon, user?.auth?.icon) + } + } + } + + private fun getTagView( + content: String, + isLast: Boolean, + @ColorRes tvColorRes: Int, + @ColorRes dividerColorRes: Int + ): View { return LayoutGameCollectionTagBinding.inflate(layoutInflater).apply { divider.goneIf(isLast) + divider.setBackgroundColor(dividerColorRes.toColor()) contentTv.text = content + contentTv.setTextColor(tvColorRes.toColor()) }.root } + private fun updateFollowView() { + if (mEntity?.video == null) { + updateImageItemFollowView() + } else { + updateVideoItemFollowView() + } + } + + private fun updateImageItemFollowView() { + mBinding?.toolbarFollowTv?.run { + val isFollow = mEntity?.me?.isFollower ?: false + visibility = View.VISIBLE + text = if (isFollow) "已关注" else "关注" + if (mIsLight) { + setBackgroundResource(if (isFollow) R.drawable.button_round_black_alpha_10 else R.drawable.button_round_black_alpha_30) + setTextColor(if (isFollow) R.color.white_alpha_60.toColor() else R.color.white.toColor()) + } else { + setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff) + setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor()) + } + } + } + + private fun updateVideoItemFollowView() { + val isFollow = mEntity?.me?.isFollower ?: false + mBinding?.videoItem?.videoItemFollowTv?.run { + text = if (isFollow) "已关注" else "关注" + setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff) + setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor()) + } + mBinding?.toolbarFollowTv?.run { + if (mIsLight) { + visibility = View.GONE + } else { + visibility = View.VISIBLE + text = if (isFollow) "已关注" else "关注" + setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff) + setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor()) + } + } + } + + private fun updateStartView() { + mBinding?.inputContainer?.run { + bottomStarTv.text = mListViewModel.getStarText() + if (mEntity?.me?.isFavorite == true) { + bottomStarIv.setImageResource(R.drawable.ic_article_detail_stared_bottom_bar) + bottomStarTv.setTextColor(R.color.theme_font.toColor()) + } else { + bottomStarIv.setImageResource(R.drawable.ic_article_detail_star_bottom_bar) + bottomStarTv.setTextColor(R.color.text_666666.toColor()) + } + } + } + + private fun updateLikeView() { + mBinding?.inputContainer?.run { + bottomLikeTv.text = mListViewModel.getLikeText() + if (mEntity?.me?.vote == true) { + bottomLikeIv.setImageResource(R.drawable.ic_article_detail_liked_bottom_bar) + bottomLikeTv.setTextColor(R.color.theme_font.toColor()) + } else { + bottomLikeIv.setImageResource(R.drawable.ic_article_detail_like_bottom_bar) + bottomLikeTv.setTextColor(R.color.text_666666.toColor()) + } + } + } + + private fun startCommentActivity() { + mEntity?.run { +// val intent = CommentActivity.getQuestionCommentIntent( +// requireContext(), +// it.id ?: "", +// it.count.answer, +// true, +// it.community.id, +// true +// ) +// startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } + } + override fun onLoadDone() { super.onLoadDone() } @@ -191,30 +378,70 @@ class GameCollectionDetailFragment : mListRv.visibility = View.VISIBLE } - @OnClick(R.id.desIv, R.id.toolbarLightUserContainer, R.id.toolbarUserContainer, R.id.toolbarFollowTv, R.id.squareIv) + @Subscribe(threadMode = ThreadMode.MAIN) + fun onUserFollow(change: EBUserFollow) { + mEntity?.me?.isFollower = change.isFollow + updateFollowView() + } + + @OnClick(R.id.backIv, R.id.imageDesIv, R.id.videoItemDesIv, R.id.toolbarLightUserContainer, R.id.toolbarUserContainer, + R.id.videoItemUserContainer, R.id.toolbarFollowTv, R.id.videoItemFollowTv, R.id.squareIv, R.id.bottomStarIv, + R.id.bottomLikeIv) override fun onClick(v: View) { when (v.id) { - R.id.desIv -> { - startActivity(GameCollectionPosterActivity.getIntent(requireContext(), mEntity)) + R.id.backIv -> requireActivity().finish() + + R.id.imageDesIv -> { + startActivity(GameCollectionPosterActivity.getIntent(requireContext(), mEntity ?: GamesCollectionDetailEntity())) + } + + R.id.videoItemDesIv -> { + startActivity(GameCollectionPosterActivity.getIntent(requireContext(), mEntity ?: GamesCollectionDetailEntity())) } R.id.toolbarLightUserContainer -> { - DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-导航栏") + DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-导航栏") } R.id.toolbarUserContainer -> { - DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-导航栏") + DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-导航栏") + } + + R.id.videoItemUserContainer -> { + DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-导航栏") } R.id.toolbarFollowTv -> { ifLogin("游戏单详情") { - mListViewModel?.followingCommand(mEntity.user?.id ?: "", mEntity.me?.isFollower == false) + mListViewModel?.followingCommand(mEntity?.user?.id ?: "", mEntity?.me?.isFollower == false) + } + } + + R.id.videoItemFollowTv -> { + ifLogin("游戏单详情") { + mListViewModel?.followingCommand(mEntity?.user?.id ?: "", mEntity?.me?.isFollower == false) } } R.id.squareIv -> { } + + R.id.bottomStarIv -> { + debounceActionWithInterval(v.id) { + ifLogin("游戏单详情") { + mListViewModel?.postFavoriteGameCollection() + } + } + } + + R.id.bottomLikeIv -> { + debounceActionWithInterval(v.id) { + ifLogin("游戏单详情") { + mListViewModel?.postVoteGameCollection() + } + } + } } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt index 41c1ad259b..715c3e0b7d 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt @@ -11,6 +11,8 @@ import com.gh.gamecenter.baselist.ListViewModel import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.entity.GamesCollectionDetailEntity import com.gh.gamecenter.eventbus.EBUserFollow +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager @@ -28,8 +30,11 @@ class GameCollectionDetailViewModel(application: Application, ListViewModel(application) { private val mApi = RetrofitManager.getInstance(getApplication()).api - val gameCollectionLiveData = MutableLiveData() + val loadResultLiveData = MutableLiveData() var followLiveData = MutableLiveData() + val favoriteLiveData = MutableLiveData() + val likeLiveData = MutableLiveData() + var gameCollectionDetail: GamesCollectionDetailEntity? = null init { getGameCollectionDetail() @@ -41,7 +46,19 @@ class GameCollectionDetailViewModel(application: Application, .compose(singleToMain()) .subscribe(object : BiResponse() { override fun onSuccess(data: GamesCollectionDetailEntity) { - gameCollectionLiveData.postValue(data) + gameCollectionDetail = data + loadResultLiveData.postValue(BaseCommentViewModel.LoadResult.SUCCESS) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + if (exception is HttpException) { + if (exception.code().toString().startsWith("404")) { + loadResultLiveData.postValue(BaseCommentViewModel.LoadResult.DELETED) + } else { + loadResultLiveData.postValue(BaseCommentViewModel.LoadResult.NETWORK_ERROR) + } + } } }) } @@ -81,6 +98,8 @@ class GameCollectionDetailViewModel(application: Application, } else { Utils.toast(getApplication(), R.string.concern_cancel) } + gameCollectionDetail?.me?.isFollower = + gameCollectionDetail?.me?.isFollower != true followLiveData.postValue(isFollow) EventBus.getDefault().post(EBUserFollow(userId, isFollow)) } @@ -92,6 +111,73 @@ class GameCollectionDetailViewModel(application: Application, }) } + @SuppressLint("CheckResult") + fun postFavoriteGameCollection() { + if (gameCollectionDetail == null) return + val single = if (gameCollectionDetail?.me?.isFavorite == true) { + mApi.deleteFavoriteGameCollection(UserManager.getInstance().userId, gameCollectionId) + } else { + mApi.favoriteGameCollection(UserManager.getInstance().userId, gameCollectionId) + } + single.compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + if (gameCollectionDetail?.me?.isFavorite == true) { + ToastUtils.showToast("取消收藏") + } else { + ToastUtils.showToast("收藏成功") + } + gameCollectionDetail?.me?.isFavorite = + gameCollectionDetail?.me?.isFavorite != true + favoriteLiveData.postValue(true) + } + }) + } + + @SuppressLint("CheckResult") + fun postVoteGameCollection() { + if (gameCollectionDetail == null) return + val single = if (gameCollectionDetail?.me?.vote == true) { + mApi.unVoteGameCollection(gameCollectionId) + } else { + mApi.voteGameCollection(gameCollectionId) + } + single.compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + if (gameCollectionDetail?.me?.vote == true) { + gameCollectionDetail!!.count!!.vote-- + ToastUtils.showToast("取消点赞") + } else { + gameCollectionDetail!!.count!!.vote++ + ToastUtils.showToast("点赞成功") + } + gameCollectionDetail?.me?.vote = + gameCollectionDetail?.me?.vote != true + likeLiveData.postValue(true) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + + if (exception is HttpException) { + ErrorHelper.handleError( + getApplication(), + exception.response()?.errorBody()?.string() + ) + } + } + }) + } + + fun getLikeText(): String { + return if (gameCollectionDetail?.count?.vote == 0) "赞同" else "${gameCollectionDetail?.count?.vote}" + } + + fun getStarText(): String { + return if (gameCollectionDetail?.count?.favorite == 0) "收藏" else "${gameCollectionDetail?.count?.favorite}" + } + // override fun hideCommentSuccess() { // questionDetail?.count?.answer = (questionDetail?.count?.answer ?: 0) - 1 // loadResultLiveData.postValue(LoadResult.SUCCESS) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt index 9582ebab30..ba0f6484cc 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt @@ -90,16 +90,16 @@ class GameCollectionPosterFragment : NormalFragment() { R.id.backIv -> requireActivity().finish() R.id.userIcon -> { - DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-封面页") + DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-封面页") } R.id.userName -> { - DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-封面页") + DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-封面页") } R.id.followTv -> { ifLogin("游戏单详情-封面页") { - mViewModel?.followingCommand(mEntity?.user?.id ?: "", mEntity?.me?.isFollower == false) + mViewModel?.followingCommand(mEntity.user?.id ?: "", mBinding?.followTv?.text == "关注") } } } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index ac7f3f3336..b5e4a5b990 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3324,4 +3324,34 @@ public interface ApiService { */ @GET("game_lists/tags") Single> getGameCollectionTagList(); + + /** + * 获取用户收藏的游戏单列表 + */ + @GET("users/{user_id}/favorites/game_list") + Single> getFavoriteGameCollectionList(@Path("user_id") String userId); + + /** + * 添加游戏单收藏 + */ + @POST("users/{user_id}/favorites/game_list/{game_list_id}") + Single favoriteGameCollection(@Path("user_id") String userId, @Path("game_list_id") String id); + + /** + * 取消游戏单收藏 + */ + @DELETE("users/{user_id}/favorites/game_list/{game_list_id}") + Single deleteFavoriteGameCollection(@Path("user_id") String userId, @Path("game_list_id") String id); + + /** + * 点赞游戏单 + */ + @POST("game_lists/{game_list_id}:vote") + Single voteGameCollection(@Path("game_list_id") String id); + + /** + * 取消点赞游戏单 + */ + @POST("game_lists/{game_list_id}:unvote") + Single unVoteGameCollection(@Path("game_list_id") String id); } \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_black_alpha_10.xml b/app/src/main/res/drawable/button_round_black_alpha_10.xml new file mode 100644 index 0000000000..b2dd803ce5 --- /dev/null +++ b/app/src/main/res/drawable/button_round_black_alpha_10.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_detail.xml b/app/src/main/res/layout/fragment_game_collection_detail.xml index c631cb6d61..204d9bfaa9 100644 --- a/app/src/main/res/layout/fragment_game_collection_detail.xml +++ b/app/src/main/res/layout/fragment_game_collection_detail.xml @@ -163,7 +163,6 @@ + + + @@ -118,11 +132,12 @@ android:layout_height="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp" + android:gravity="center_vertical" android:orientation="horizontal" /> @@ -208,7 +222,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:layout_marginLeft="12dp" + android:layout_marginLeft="5dp" android:includeFontPadding="false" android:textColor="@color/text_cccccc" android:textSize="10sp" @@ -220,15 +234,16 @@ @@ -236,7 +251,7 @@ android:layout_width="match_parent" android:layout_height="8dp" android:background="@color/text_f5f5f5" - app:layout_constraintTop_toBottomOf="@+id/userContainer" /> + app:layout_constraintTop_toBottomOf="@+id/videoItemUserContainer" /> \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_item.xml b/app/src/main/res/layout/game_collection_item.xml index 66ba66257e..2775743f98 100644 --- a/app/src/main/res/layout/game_collection_item.xml +++ b/app/src/main/res/layout/game_collection_item.xml @@ -248,6 +248,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" + android:checked="@{entity.me.vote}" android:src="@drawable/game_collection_vote_selector" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 4aac4429e8..ebd673bd84 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,6 +4,7 @@ #2496FF + #332496FF #FFA142 From 924f83dacfa9e2c2437d972a8292aa239c52cc6c Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Thu, 18 Nov 2021 11:21:28 +0800 Subject: [PATCH 23/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=9F=8B=E7=82=B9=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1549?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gh/common/util/NewLogUtils.kt | 21 +++++++++++++++++++ .../mine/MyGameCollectionAdapter.kt | 12 ++++++++--- .../mine/MyGameCollectionFragment.kt | 10 +++++++-- .../mine/MyGameCollectionViewHolder.kt | 13 ++++++++---- .../publish/GameCollectionEditActivity.kt | 15 ++++++++++--- .../square/GameCollectionSquareFragment.kt | 4 ++-- .../gh/gamecenter/mygame/MyGameActivity.kt | 2 +- 7 files changed, 62 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/gh/common/util/NewLogUtils.kt b/app/src/main/java/com/gh/common/util/NewLogUtils.kt index cb03b1dc8a..5f540c25cc 100644 --- a/app/src/main/java/com/gh/common/util/NewLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewLogUtils.kt @@ -1615,4 +1615,25 @@ object NewLogUtils { } log(json, "event", false) } + + //进入我的游戏单 + fun logEnterMyGameCollection() { + val json = json { + "event" to "enter_game_collect_self_location" + "timestamp" to System.currentTimeMillis() / 1000 + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + //进入创建游戏单 + fun logEnterGameCollectionEdit(entrance: String) { + val json = json { + "event" to "enter_game_collect_create_location" + "entrance" to entrance + "timestamp" to System.currentTimeMillis() / 1000 + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt index 134f8e642e..a2af9647f8 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionAdapter.kt @@ -12,7 +12,12 @@ import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.entity.GamesCollectionEntity -class MyGameCollectionAdapter(context: Context, val mViewModel: MyGameCollectionViewModel) : ListAdapter(context) { +class MyGameCollectionAdapter( + context: Context, + val mViewModel: MyGameCollectionViewModel, + val entrance: String, + val path: String +) : ListAdapter(context) { override fun getItemViewType(position: Int): Int { return if (position == itemCount - 1) { @@ -24,13 +29,14 @@ class MyGameCollectionAdapter(context: Context, val mViewModel: MyGameCollection override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { - ItemViewType.ITEM_BODY -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel) + ItemViewType.ITEM_BODY -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel, entrance, path) ItemViewType.ITEM_FOOTER -> { FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) } - else -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel) + else -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel, entrance, path) } } + override fun areItemsTheSame(oldItem: GamesCollectionEntity?, newItem: GamesCollectionEntity?): Boolean { return oldItem == newItem || oldItem?.id == newItem?.id } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt index 8acebe162e..a6bd76620e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/mine/MyGameCollectionFragment.kt @@ -6,6 +6,7 @@ import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Constants +import com.gh.common.util.NewLogUtils import com.gh.common.util.showRegulationTestDialogIfNeeded import com.gh.common.view.VerticalItemDecoration import com.gh.gamecenter.R @@ -44,11 +45,16 @@ class MyGameCollectionFragment : ListFragment { - return mAdapter ?: MyGameCollectionAdapter(requireContext(), mListViewModel).apply { + return mAdapter ?: MyGameCollectionAdapter(requireContext(), mListViewModel, mEntrance, "我的游戏单").apply { mAdapter = this } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NewLogUtils.logEnterMyGameCollection() + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setNavigationTitle("我的游戏单") @@ -72,7 +78,7 @@ class MyGameCollectionFragment : ListFragment(binding.root) { fun bindGameCollectionItem(entity: GamesCollectionEntity) { @@ -43,7 +48,7 @@ class MyGameCollectionViewHolder(val binding: ItemMyGameCollectionBinding, val m } binding.root.setOnClickListener { if (entity.status == "pass" || (entity.display == "self_only" && entity.status == "draft")) { - binding.root.context.startActivity(GameCollectionDetailActivity.getIntent(binding.root.context,entity.id)) + binding.root.context.startActivity(GameCollectionDetailActivity.getIntent(binding.root.context, entity.id)) } else { binding.statusView.performClick() } @@ -56,12 +61,12 @@ class MyGameCollectionViewHolder(val binding: ItemMyGameCollectionBinding, val m }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) } else { DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场哦~", "添加游戏", "我知道了", { - binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity)) + binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity, entrance, path)) }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)) } } binding.editBtn.setOnClickListener { - binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity)) + binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity, entrance, path)) } binding.deleteBtn.setOnClickListener { DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单删除后将无法恢复,是否确认删除", "确定", "取消", { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index c69e93b5a9..78a408d0d9 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -50,6 +50,10 @@ class GameCollectionEditActivity : ToolBarActivity() { mMenuPost = getMenuItem(R.id.menu_game_collection_post) setNavigationTitle("创建游戏单") mViewModel.gameCollectionPatch = intent.getParcelableExtra(GamesCollectionEntity::class.java.name) + val path = intent.getStringExtra(EntranceUtils.KEY_PATH) ?: "" + if (path.isNotEmpty()) { + NewLogUtils.logEnterGameCollectionEdit(path) + } initData() observeData() @@ -410,14 +414,19 @@ class GameCollectionEditActivity : ToolBarActivity() { const val REQUEST_CHOOSE_TAG = 103 @JvmStatic - fun getIntent(context: Context): Intent { - return Intent(context, GameCollectionEditActivity::class.java) + fun getIntent(context: Context, entrance: String = "", path: String = ""): Intent { + val intent = Intent(context, GameCollectionEditActivity::class.java) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_PATH, path) + return intent } @JvmStatic - fun getIntent(context: Context, entity: GamesCollectionEntity): Intent { + fun getIntent(context: Context, entity: GamesCollectionEntity, entrance: String = "", path: String = ""): Intent { val intent = Intent(context, GameCollectionEditActivity::class.java) intent.putExtra(GamesCollectionEntity::class.java.name, entity) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_PATH, path) return intent } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt index 9efa358816..e2ca7dd304 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -163,7 +163,7 @@ class GameCollectionSquareFragment : LazyListFragment Date: Thu, 18 Nov 2021 16:48:57 +0800 Subject: [PATCH 24/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95-=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E8=AE=B0=E5=BD=95=20https://git.ghzs.com/pm/halo-app-?= =?UTF-8?q?issues/-/issues/1593?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gh/common/history/HistoryDatabase.kt | 17 +++++- .../com/gh/common/history/HistoryHelper.kt | 30 ++++++++++ .../collection/CollectionWrapperFragment.java | 2 +- .../collection/GamesCollectionFragment.kt | 1 + .../collection/GamesCollectionViewModel.kt | 26 ++++++--- .../gamecenter/entity/GameCollectionDraft.kt | 55 +++++++++++++++++++ .../entity/GamesCollectionEntity.kt | 25 ++++----- .../detail/GameCollectionDetailFragment.kt | 8 +++ .../detail/GameCollectionDetailViewModel.kt | 11 ++++ .../publish/GameCollectionEditActivity.kt | 3 +- .../publish/GameCollectionEditViewModel.kt | 11 ++-- .../history/HistoryWrapperFragment.kt | 13 +++-- .../com/gh/gamecenter/room/AppDatabase.java | 4 +- .../room/dao/GameCollectionDraftDao.kt | 10 ++-- .../gamecenter/room/dao/GamesCollectionDao.kt | 18 ++++++ ...agment_no_padding_tablayout_viewpager.xml} | 0 16 files changed, 193 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt create mode 100644 app/src/main/java/com/gh/gamecenter/room/dao/GamesCollectionDao.kt rename app/src/main/res/layout/{fragment_collection_wrapper.xml => fragment_no_padding_tablayout_viewpager.xml} (100%) diff --git a/app/src/main/java/com/gh/common/history/HistoryDatabase.kt b/app/src/main/java/com/gh/common/history/HistoryDatabase.kt index 0dd544fd51..477dabf9a5 100644 --- a/app/src/main/java/com/gh/common/history/HistoryDatabase.kt +++ b/app/src/main/java/com/gh/common/history/HistoryDatabase.kt @@ -6,6 +6,7 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.HistoryGameEntity import com.gh.gamecenter.entity.MyVideoEntity import com.gh.gamecenter.entity.NewsEntity @@ -15,7 +16,7 @@ import com.gh.gamecenter.room.converter.* import com.gh.gamecenter.room.dao.* import com.halo.assistant.HaloApp -@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 9, exportSchema = false) +@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class, GamesCollectionEntity::class], version = 10, exportSchema = false) @TypeConverters(CountConverter::class, CommunityConverter::class, TimeConverter::class, @@ -28,7 +29,10 @@ import com.halo.assistant.HaloApp UserConverter::class, ImageInfoConverter::class, VideoInfoConverter::class, - QuestionsConverter::class) + QuestionsConverter::class, + MeConverter::class, + SimpleGameListConverter::class, + TagInfoListConverter::class) abstract class HistoryDatabase : RoomDatabase() { @@ -37,6 +41,7 @@ abstract class HistoryDatabase : RoomDatabase() { abstract fun newsDao(): NewsHistoryDao abstract fun gameDao(): GameDao abstract fun videoHistoryDao(): VideoHistoryDao + abstract fun gamesCollectionDao(): GamesCollectionDao companion object { @@ -101,6 +106,13 @@ abstract class HistoryDatabase : RoomDatabase() { } } + val MIGRATION_9_10: Migration = object : Migration(9, 10) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE GamesCollectionEntity (id TEXT NOT NULL PRIMARY KEY, tags TEXT DEFAULT '', games TEXT DEFAULT '', title TEXT NOT NULL DEFAULT '', intro TEXT NOT NULL DEFAULT '', cover TEXT NOT NULL DEFAULT '', display TEXT NOT NULL DEFAULT '', stamp TEXT NOT NULL DEFAULT '', count TEXT NOT NULL DEFAULT '', user TEXT NOT NULL DEFAULT '', me TEXT NOT NULL DEFAULT '', orderTag INTEGER NOT NULL DEFAULT 0)" + ) + } + } + val instance by lazy { Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE") .addMigrations(MIGRATION_2_3) @@ -110,6 +122,7 @@ abstract class HistoryDatabase : RoomDatabase() { .addMigrations(MIGRATION_6_7) .addMigrations(MIGRATION_7_8) .addMigrations(MIGRATION_8_9) + .addMigrations(MIGRATION_9_10) .build() } } diff --git a/app/src/main/java/com/gh/common/history/HistoryHelper.kt b/app/src/main/java/com/gh/common/history/HistoryHelper.kt index 9c096fd90f..3ea1cbb49a 100644 --- a/app/src/main/java/com/gh/common/history/HistoryHelper.kt +++ b/app/src/main/java/com/gh/common/history/HistoryHelper.kt @@ -24,6 +24,11 @@ object HistoryHelper { runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.articleDao().addArticle(articleEntity) } } } + fun insertGamesCollectionEntity(gamesCollectionDetailEntity: GamesCollectionDetailEntity) { + val gamesCollectionEntity = convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity) + runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gamesCollectionDao().addGamesCollection(gamesCollectionEntity) } } + } + @JvmStatic fun insertGameEntity(gameEntity: GameEntity) { val historyGameEntity = convertGameEntityToHistoryGameEntity(gameEntity) @@ -145,4 +150,29 @@ object HistoryHelper { return answerEntity } + private fun convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity: GamesCollectionDetailEntity): GamesCollectionEntity { + val gamesCollectionEntity = GamesCollectionEntity() + + gamesCollectionEntity.id = gamesCollectionDetailEntity.id + gamesCollectionEntity.tags = gamesCollectionDetailEntity.tags + gamesCollectionEntity.games = ArrayList(gamesCollectionDetailEntity.games?.map { it.toSimpleGame() }) + gamesCollectionEntity.title = gamesCollectionDetailEntity.title + gamesCollectionEntity.intro = gamesCollectionDetailEntity.intro + gamesCollectionEntity.cover = gamesCollectionDetailEntity.cover + gamesCollectionEntity.display = gamesCollectionDetailEntity.display + gamesCollectionEntity.orderTag = System.currentTimeMillis() + gamesCollectionEntity.stamp = gamesCollectionDetailEntity.stamp + gamesCollectionEntity.count = gamesCollectionDetailEntity.count + gamesCollectionDetailEntity.user?.run { + gamesCollectionEntity.user = User( + id = id ?: "", + name = name ?: "", + icon = icon ?: "", + badge = badge) + } + gamesCollectionEntity.me = gamesCollectionDetailEntity.me + + return gamesCollectionEntity + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java index d0ce7b7a93..2df04ac545 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java @@ -33,7 +33,7 @@ public class CollectionWrapperFragment extends BaseFragment_TabLayout { @Override protected int getLayoutId() { - return R.layout.fragment_collection_wrapper; + return R.layout.fragment_no_padding_tablayout_viewpager; } @Override diff --git a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt index c2826597ad..bab8713915 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/GamesCollectionFragment.kt @@ -73,6 +73,7 @@ class GamesCollectionFragment : ListFragment> { - return if (type == TYPE_COLLECT) { - mApi.getFavoriteGameCollectionList(userId) - } else { - val map = if (mIsInsertGameCollection) { - hashMapOf("filter" to "display") - } else mapOf() - mApi.getUserGameCollectionList(userId, map) + return when (type) { + TYPE_COLLECT -> mApi.getFavoriteGameCollectionList(userId) + + TYPE_HISTORY -> { + if (page > 5) { + Single.create { it.onSuccess(arrayListOf()) } + } else { + HistoryDatabase.instance.gamesCollectionDao().getGamesCollectionWithOffset(20, (page - 1) * 20) + } + } + + else -> { + val map = if (mIsInsertGameCollection) { + hashMapOf("filter" to "display") + } else mapOf() + mApi.getUserGameCollectionList(userId, map) + } } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt new file mode 100644 index 0000000000..1fbaf8690a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt @@ -0,0 +1,55 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import androidx.room.TypeConverters +import com.gh.gamecenter.R +import com.gh.gamecenter.qa.entity.Count +import com.gh.gamecenter.qa.entity.TimeEntity +import com.gh.gamecenter.room.converter.SimpleGameListConverter +import com.gh.gamecenter.room.converter.TagInfoListConverter +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Entity +@Parcelize +class GameCollectionDraft( + @Ignore + @SerializedName("_id") + var id: String = "", + @PrimaryKey + var primaryKey: String = "", // db key + @TypeConverters(TagInfoListConverter::class) + var tags: ArrayList? = null, + @TypeConverters(SimpleGameListConverter::class) + var games: ArrayList? = null, + var title: String = "", + var intro: String = "", + var cover: String = "", + var display: String = "",//self_only: 仅自己可见 + @Ignore + var time: TimeEntity? = null, + @Ignore + var stamp: String = "",//special_choice: 精选 offical: 官方 + @Ignore + var count: Count? = null, + @Ignore + var status: String = "",// draft/pending/pass/failed + @Ignore + var user: User? = null, + @Ignore + var me: MeEntity? = null, +) : Parcelable { + + fun getStatusLabelRes(): Int { + return when { + display == "self_only" && status == "draft" -> R.drawable.ic_game_collection_private + status == "draft" -> R.drawable.ic_game_collection_draft + status == "pending" -> R.drawable.ic_game_collection_pending + status == "failed" -> R.drawable.ic_game_collection_fail + else -> -1 + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt index 9b312b7669..f72c1747e7 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GamesCollectionEntity.kt @@ -8,19 +8,16 @@ import androidx.room.TypeConverters import com.gh.gamecenter.R import com.gh.gamecenter.qa.entity.Count import com.gh.gamecenter.qa.entity.TimeEntity -import com.gh.gamecenter.room.converter.SimpleGameListConverter -import com.gh.gamecenter.room.converter.TagInfoListConverter +import com.gh.gamecenter.room.converter.* import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize -@Entity(tableName = "GameCollectionDraft") +@Entity @Parcelize class GamesCollectionEntity( - @Ignore + @PrimaryKey @SerializedName("_id") var id: String = "", - @PrimaryKey - var primaryKey: String = "", // db key @TypeConverters(TagInfoListConverter::class) var tags: ArrayList? = null, @TypeConverters(SimpleGameListConverter::class) @@ -29,18 +26,18 @@ class GamesCollectionEntity( var intro: String = "", var cover: String = "", var display: String = "",//self_only: 仅自己可见 + var stamp: String = "",//special_choice: 精选 offical: 官方 + @TypeConverters(CountConverter::class) + var count: Count? = null, + @TypeConverters(UserConverter::class) + var user: User? = null, + @TypeConverters(MeConverter::class) + var me: MeEntity? = null, + var orderTag: Long = 0, @Ignore var time: TimeEntity? = null, @Ignore - var stamp: String = "",//special_choice: 精选 offical: 官方 - @Ignore - var count: Count? = null, - @Ignore var status: String = "",// draft/pending/pass/failed - @Ignore - var user: User? = null, - @Ignore - var me: MeEntity? = null, ) : Parcelable { fun getStatusLabelRes(): Int { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt index a77e94a62e..12fcc89471 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -6,6 +6,7 @@ import android.view.View import androidx.annotation.ColorRes import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick +import com.gh.common.history.HistoryHelper import com.gh.common.util.* import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -80,6 +81,13 @@ class GameCollectionDetailFragment : DownloadManager.getInstance(context).removeObserver(mDataWatcher) } + override fun onDestroyView() { + super.onDestroyView() + mEntity?.run { + HistoryHelper.insertGamesCollectionEntity(this) + } + } + private fun initView() { mBinding?.run { inputContainer.run { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt index 715c3e0b7d..2d4c49589c 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailViewModel.kt @@ -131,6 +131,17 @@ class GameCollectionDetailViewModel(application: Application, gameCollectionDetail?.me?.isFavorite != true favoriteLiveData.postValue(true) } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + + if (exception is HttpException) { + ErrorHelper.handleError( + getApplication(), + exception.response()?.errorBody()?.string() + ) + } + } }) } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index 78a408d0d9..dd01ee3051 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding import com.gh.gamecenter.databinding.ItemGameCollectionFlexTagBinding +import com.gh.gamecenter.entity.GameCollectionDraft import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.eventbus.EBReuse @@ -314,7 +315,7 @@ class GameCollectionEditActivity : ToolBarActivity() { || title.isNotEmpty() || introduce.isNotEmpty() ) { - val entity = GamesCollectionEntity() + val entity = GameCollectionDraft() entity.tags = mViewModel.tags entity.title = title entity.intro = introduce diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index 383fce7474..e24617b769 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.runOnIoThread import com.gh.common.util.* +import com.gh.gamecenter.entity.GameCollectionDraft import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.entity.TagInfoEntity import com.gh.gamecenter.mvvm.Resource @@ -24,10 +25,10 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a var imagePath = "" var imageUrl = "" var tags = ArrayList() - var gameCollectionPatch: GamesCollectionEntity? = null + var gameCollectionPatch: GameCollectionDraft? = null var uploadImageSuccessLiveData = MutableLiveData() var detailLiveData = MutableLiveData() - var draftLiveData = MutableLiveData() + var draftLiveData = MutableLiveData() val postLiveData = MutableLiveData>() val processDialog = MediatorLiveData() private val mApi = RetrofitManager.getInstance(HaloApp.getInstance()).api @@ -111,8 +112,8 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a fun getDraft() { mDraftDao.getAllDraft() .compose(singleToMain()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { if (data.isNotEmpty()) { val draftEntity = data[0] draftLiveData.postValue(draftEntity) @@ -121,7 +122,7 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a }) } - fun addDraft(entity: GamesCollectionEntity) { + fun addDraft(entity: GameCollectionDraft) { runOnIoThread { val drafts = mDraftDao.getDrafts() mDraftDao.delete(drafts) diff --git a/app/src/main/java/com/gh/gamecenter/history/HistoryWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/history/HistoryWrapperFragment.kt index 85f5205222..971dcfc6e5 100644 --- a/app/src/main/java/com/gh/gamecenter/history/HistoryWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/history/HistoryWrapperFragment.kt @@ -6,15 +6,16 @@ import com.gh.base.fragment.BaseFragment_TabLayout import com.gh.common.util.EntranceUtils import com.gh.common.util.MtaHelper import com.gh.gamecenter.R -import com.gh.gamecenter.collection.AnswerFragment -import com.gh.gamecenter.collection.ArticleFragment -import com.gh.gamecenter.collection.CommunityArticleFragment -import com.gh.gamecenter.collection.VideoFragment +import com.gh.gamecenter.collection.* +import com.gh.gamecenter.manager.UserManager class HistoryWrapperFragment : BaseFragment_TabLayout() { + override fun getLayoutId() = R.layout.fragment_no_padding_tablayout_viewpager + override fun initTabTitleList(tabTitleList: MutableList) { tabTitleList.add(getString(R.string.main_game)) + tabTitleList.add(getString(R.string.game_collection)) tabTitleList.add(getString(R.string.video)) tabTitleList.add(getString(R.string.answer)) tabTitleList.add(getString(R.string.collection_article)) @@ -23,6 +24,10 @@ class HistoryWrapperFragment : BaseFragment_TabLayout() { override fun initFragmentList(fragments: MutableList) { fragments.add(HistoryGameListFragment().with(arguments)) + fragments.add(GamesCollectionFragment().with(arguments?.apply { + putString(EntranceUtils.KEY_USER_ID, UserManager.getInstance().userId) + putString(EntranceUtils.KEY_TYPE, GamesCollectionFragment.TYPE_HISTORY) + })) fragments.add(VideoFragment().with(arguments?.apply { putString("videoStyle", VideoFragment.VideoStyle.BROWSING_HISTORY.value) })) diff --git a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java index 18b7fcb714..55eab163bb 100644 --- a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java +++ b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java @@ -14,7 +14,7 @@ import com.gh.common.videolog.VideoRecordDao; import com.gh.common.videolog.VideoRecordEntity; import com.gh.gamecenter.entity.CommentDraft; import com.gh.gamecenter.entity.ForumEntity; -import com.gh.gamecenter.entity.GamesCollectionEntity; +import com.gh.gamecenter.entity.GameCollectionDraft; import com.gh.gamecenter.entity.HomePluggableFilterEntity; import com.gh.gamecenter.entity.SignEntity; import com.gh.gamecenter.entity.SimulatorGameRecordEntity; @@ -50,7 +50,7 @@ import com.gh.gamecenter.video.upload.UploadEntity; VideoRecordEntity.class, SimulatorGameRecordEntity.class, ForumEntity.class, - GamesCollectionEntity.class}, version = 21, exportSchema = false) + GameCollectionDraft.class}, version = 21, exportSchema = false) @TypeConverters({ StringArrayListConverter.class, TagStyleListConverter.class, diff --git a/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt index deb4344d53..8c9365cd08 100644 --- a/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt +++ b/app/src/main/java/com/gh/gamecenter/room/dao/GameCollectionDraftDao.kt @@ -1,21 +1,21 @@ package com.gh.gamecenter.room.dao import androidx.room.* -import com.gh.gamecenter.entity.GamesCollectionEntity +import com.gh.gamecenter.entity.GameCollectionDraft import io.reactivex.Single @Dao interface GameCollectionDraftDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun addDraft(entity: GamesCollectionEntity) + fun addDraft(entity: GameCollectionDraft) @Query("select * from GameCollectionDraft") - fun getAllDraft(): Single> + fun getAllDraft(): Single> @Query("select * from GameCollectionDraft") - fun getDrafts(): List + fun getDrafts(): List @Delete - fun delete(list: List) + fun delete(list: List) } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/dao/GamesCollectionDao.kt b/app/src/main/java/com/gh/gamecenter/room/dao/GamesCollectionDao.kt new file mode 100644 index 0000000000..d827f41249 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/dao/GamesCollectionDao.kt @@ -0,0 +1,18 @@ +package com.gh.gamecenter.room.dao + +import androidx.room.* +import com.gh.gamecenter.entity.GamesCollectionEntity +import io.reactivex.Single + +@Dao +interface GamesCollectionDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addGamesCollection(gamesCollection: GamesCollectionEntity) + + @Query("select * from GamesCollectionEntity order by orderTag desc limit :pageSize offset :offset") + fun getGamesCollectionWithOffset(pageSize: Int, offset: Int): Single> + + @Delete + fun deleteGamesCollection(gamesCollection: GamesCollectionEntity) +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_collection_wrapper.xml b/app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml similarity index 100% rename from app/src/main/res/layout/fragment_collection_wrapper.xml rename to app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml From 020c8d445664970c957b1ac39094767ce73c11ea Mon Sep 17 00:00:00 2001 From: jack <1484288157@qq.com> Date: Thu, 18 Nov 2021 17:47:29 +0800 Subject: [PATCH 25/49] =?UTF-8?q?1.=E5=A4=84=E7=90=86=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8D=95=E9=97=AA=E9=80=80=E9=97=AE=E9=A2=98?= =?UTF-8?q?=202.=E5=A4=84=E7=90=86=E9=80=89=E6=8B=A9=E6=B8=B8=E6=88=8F?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=86=85=E5=AD=98=E6=B3=84=E9=9C=B2=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gamecenter/entity/GameCollectionDraft.kt | 30 ++++------ .../choose/ChooseGamesFragment.kt | 59 +++++++++++-------- .../publish/GameCollectionEditActivity.kt | 2 +- .../publish/GameCollectionEditViewModel.kt | 2 +- libraries/LGLibrary | 2 +- 5 files changed, 46 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt index 1fbaf8690a..aae0ec76fa 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameCollectionDraft.kt @@ -29,27 +29,17 @@ class GameCollectionDraft( var intro: String = "", var cover: String = "", var display: String = "",//self_only: 仅自己可见 - @Ignore - var time: TimeEntity? = null, - @Ignore - var stamp: String = "",//special_choice: 精选 offical: 官方 - @Ignore - var count: Count? = null, - @Ignore - var status: String = "",// draft/pending/pass/failed - @Ignore - var user: User? = null, - @Ignore - var me: MeEntity? = null, ) : Parcelable { - fun getStatusLabelRes(): Int { - return when { - display == "self_only" && status == "draft" -> R.drawable.ic_game_collection_private - status == "draft" -> R.drawable.ic_game_collection_draft - status == "pending" -> R.drawable.ic_game_collection_pending - status == "failed" -> R.drawable.ic_game_collection_fail - else -> -1 - } + fun convertGameCollectionEntity(): GamesCollectionEntity { + val entity = GamesCollectionEntity() + entity.id = id + entity.tags = tags + entity.games = games + entity.title = title + entity.intro = intro + entity.cover = cover + entity.display = display + return entity } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt index d987182fe3..ee4cf9d765 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/ChooseGamesFragment.kt @@ -22,32 +22,7 @@ class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListene private lateinit var mViewModel: ChooseGamesViewModel private lateinit var mAdapter: ChooseGamesAdapter - private val mItemTouchCallback = object : ItemTouchHelper.Callback() { - override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { - return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) - } - - override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { - mAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) - Collections.swap(mAdapter.entityList, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) - val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf() - Collections.swap(games, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - - } - - override fun canDropOver(recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { - return true - } - - override fun isLongPressDragEnabled(): Boolean { - return false - } - - } + private val mItemTouchCallback = CustomItemTouchHelper(this) private val mItemTouchHelper = ItemTouchHelper(mItemTouchCallback) override fun getLayoutId(): Int = 0 @@ -131,4 +106,36 @@ class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListene return super.onBackPressed() } + class CustomItemTouchHelper(val fragment: ChooseGamesFragment) : ItemTouchHelper.Callback() { + + var fragmentReference: WeakReference = WeakReference(fragment) + + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + } + + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + val fragment = fragmentReference.get() + fragment?.run { + mAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + Collections.swap(mAdapter.entityList, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf() + Collections.swap(games, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + } + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + + } + + override fun canDropOver(recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + return true + } + + override fun isLongPressDragEnabled(): Boolean { + return false + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index dd01ee3051..6dd1d5fa42 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -202,7 +202,7 @@ class GameCollectionEditActivity : ToolBarActivity() { } } mViewModel.draftLiveData.observe(this) { - mViewModel.gameCollectionPatch = it + mViewModel.gameCollectionPatch = it.convertGameCollectionEntity() mViewModel.gameCollectionPatch?.run { initData(false) mViewModel.tags = tags ?: arrayListOf() diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt index e24617b769..2e3e2f061a 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditViewModel.kt @@ -25,7 +25,7 @@ class GameCollectionEditViewModel(application: Application) : AndroidViewModel(a var imagePath = "" var imageUrl = "" var tags = ArrayList() - var gameCollectionPatch: GameCollectionDraft? = null + var gameCollectionPatch: GamesCollectionEntity? = null var uploadImageSuccessLiveData = MutableLiveData() var detailLiveData = MutableLiveData() var draftLiveData = MutableLiveData() diff --git a/libraries/LGLibrary b/libraries/LGLibrary index 7b7d13d471..36c7ab24ef 160000 --- a/libraries/LGLibrary +++ b/libraries/LGLibrary @@ -1 +1 @@ -Subproject commit 7b7d13d47190581c24ae5f89b02a2d7e78ccde7f +Subproject commit 36c7ab24efb4b718ef3d85593a155b8268e9f37a From e2140f501f06f054e1ce28228a2e3bf298abc2c7 Mon Sep 17 00:00:00 2001 From: leafwai Date: Thu, 18 Nov 2021 18:26:40 +0800 Subject: [PATCH 26/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E9=A6=96=E9=A1=B5=E9=A1=B6=E9=83=A8tab?= =?UTF-8?q?-=E6=B8=B8=E6=88=8F=E5=8D=95=E5=B9=BF=E5=9C=BA=20https://git.gh?= =?UTF-8?q?zs.com/pm/halo-app-issues/-/issues/1599?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gh/common/util/DirectUtils.kt | 15 +++++++++ .../com/gh/common/util/EntranceUtils.java | 3 ++ .../square/GameCollectionAmwayAdapter.kt | 6 ++-- .../square/GameCollectionAmwayViewHolder.kt | 26 +++++++-------- .../square/GameCollectionSquareAdapter.kt | 22 +++++++------ .../square/GameCollectionSquareFragment.kt | 15 +++++++++ .../square/GameCollectionSquareViewModel.kt | 33 +++++++++++++++++-- .../retrofit/service/ApiService.java | 6 ++++ .../game_collection_amway_content_item.xml | 10 +++--- .../layout/game_collection_square_item.xml | 5 ++- 10 files changed, 106 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt index c90bfd057b..c18247d085 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -39,6 +39,7 @@ import com.gh.gamecenter.gamedetail.GameDetailFragment import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity import com.gh.gamecenter.gamedetail.history.HistoryApkListActivity import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mygame.PlayedGameActivity import com.gh.gamecenter.personalhome.UserHomeActivity @@ -1643,4 +1644,18 @@ object DirectUtils { bundle.putString(KEY_COLLECTION_ID, collectionId) jumpActivity(context, bundle) } + + /** + * 跳转至游戏单广场 + */ + @JvmStatic + fun directToGameCollectionSquare(context: Context, entrance: String = "", forumName: String = "", gameCollectionTitle: String = "", gameCollectionId: String = "") { + val bundle = Bundle() + bundle.putString(KEY_TO, GameCollectionSquareActivity::class.java.name) + bundle.putString(KEY_ENTRANCE, entrance) + bundle.putString(KEY_FORUM_NAME, forumName) + bundle.putString(KEY_GAME_COLLECTION_TITLE, gameCollectionTitle) + bundle.putString(KEY_GAME_COLLECTION_ID, gameCollectionId) + jumpActivity(context, bundle) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/EntranceUtils.java b/app/src/main/java/com/gh/common/util/EntranceUtils.java index b14e8c2c90..a22498e824 100644 --- a/app/src/main/java/com/gh/common/util/EntranceUtils.java +++ b/app/src/main/java/com/gh/common/util/EntranceUtils.java @@ -253,6 +253,9 @@ public class EntranceUtils { public static final String KEY_BLOCK_NAME = "block_name"; public static final String KEY_INSERT_GAME_COLLECTION = "insert_game_collection"; public static final String KEY_IS_FROM_SQUARE = "is_from_square"; + public static final String KEY_FORUM_NAME = "forum_name";//版块名称 + public static final String KEY_GAME_COLLECTION_TITLE = "game_collection_title";//游戏单标题 + public static final String KEY_GAME_COLLECTION_ID = "game_collection_id";//游戏单ID public static void jumpActivity(Context context, Bundle bundle) { bundle.putBoolean(KEY_REQUIRE_REDIRECT, true); diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt index dd8ec0761b..0a5d504f94 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayAdapter.kt @@ -10,9 +10,9 @@ import com.lightgame.adapter.BaseRecyclerAdapter class GameCollectionAmwayAdapter(context: Context) : BaseRecyclerAdapter(context) { - private var mAmwayList = ArrayList() + private var mAmwayList = listOf() - fun setAmwayList(amwayList: ArrayList) { + fun setAmwayList(amwayList: List) { mAmwayList = amwayList notifyDataSetChanged() } @@ -20,7 +20,7 @@ class GameCollectionAmwayAdapter(context: Context) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GameCollectionAmwayContentItemViewHolder(GameCollectionAmwayContentItemBinding.inflate(mLayoutInflater, parent, false)) - override fun getItemCount(): Int = if (getRealCount() > 1) getRealCount() + INCREASE_COUNT else getRealCount() + override fun getItemCount() = if (getRealCount() > 1) getRealCount() + INCREASE_COUNT else getRealCount() override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is GameCollectionAmwayContentItemViewHolder) { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt index 479a51ad92..60e7326ee9 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionAmwayViewHolder.kt @@ -8,18 +8,11 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import com.gh.common.util.DirectUtils import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding +import com.gh.gamecenter.entity.AmwayCommentEntity import java.lang.ref.WeakReference class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBinding) : RecyclerView.ViewHolder(binding.root) { - val mAdapter = GameCollectionAmwayAdapter(binding.root.context).apply { registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - if (itemCount <= 1) { - stop() - } else { - start() - } - } - }) } + val mAdapter = GameCollectionAmwayAdapter(binding.root.context) val mLoopTask = object : Runnable { private val reference: WeakReference = WeakReference(binding.amwayVp) override fun run() { @@ -29,13 +22,13 @@ class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBi if (count == 0) return val next = (vp.currentItem + 1) % count vp.setCurrentItem(next, 1000) - vp.postDelayed(this, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + vp.postDelayed(this, AMWAY_LOOP_TIME) } } } - - fun bindAmway() { + fun bindAmway(amwayList: List) { + mAdapter.setAmwayList(amwayList) binding.amwayVp.run { if (adapter is GameCollectionAmwayAdapter) return isUserInputEnabled = false @@ -69,14 +62,14 @@ class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBi } }) setCurrentItem(1, false) - postDelayed(mLoopTask, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + postDelayed(mLoopTask, AMWAY_LOOP_TIME) } binding.root.setOnClickListener { DirectUtils.directToAmway(binding.root.context) } } fun start() { stop() - binding.amwayVp.postDelayed(mLoopTask, GameCollectionSquareAdapter.AMWAY_LOOP_TIME) + binding.amwayVp.postDelayed(mLoopTask, AMWAY_LOOP_TIME) } fun stop() { @@ -108,4 +101,9 @@ class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBi animator.duration = duration animator.start() } + + companion object { + // 安利墙卡片轮播时间 + const val AMWAY_LOOP_TIME = 5000L + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt index e1c7f447b3..b09f8ee67b 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareAdapter.kt @@ -15,11 +15,13 @@ import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureSource import com.gh.common.exposure.IExposable import com.gh.common.util.* +import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding import com.gh.gamecenter.databinding.GameCollectionSquareItemBinding +import com.gh.gamecenter.entity.AmwayCommentEntity import com.gh.gamecenter.entity.GamesCollectionEntity import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity @@ -31,6 +33,15 @@ class GameCollectionSquareAdapter( ) : ListAdapter(context), IExposable { + private var mAmwayList = listOf() + + fun setAmwayList(amwayList: List) { + if (isHome) { + mAmwayList = amwayList + notifyItemChanged(0) + } + } + override fun getItemViewType(position: Int): Int { return if (isHome && position == 0) ItemViewType.ITEM_HEADER else if (position == itemCount - 1) ItemViewType.ITEM_FOOTER else ItemViewType.ITEM_BODY } @@ -84,7 +95,7 @@ class GameCollectionSquareAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is GameCollectionAmwayViewHolder -> { - holder.bindAmway() + holder.bindAmway(mAmwayList) } is GameCollectionSquareItemViewHolder -> { val realPosition = if (isHome) position - 1 else position @@ -144,12 +155,6 @@ class GameCollectionSquareAdapter( DirectUtils.directToGameDetail(context, id, "游戏单广场") } } - playedGameProgress.progress = gamesCollectionEntity.count?.game?.let { - gamesCollectionEntity.count?.playedGame?.div( - it - ) - } ?: 0 - playedGameTv.text = "玩过 ${gamesCollectionEntity.count?.playedGame} / ${gamesCollectionEntity.count?.game} 款" userContainer.setOnClickListener { DirectUtils.directToHomeActivity(context, gamesCollectionEntity.user?.id, 0, "游戏单广场") } root.setOnClickListener { context.startActivity(GameCollectionDetailActivity.getIntent(context, gamesCollectionEntity.id, true)) } } @@ -158,9 +163,6 @@ class GameCollectionSquareAdapter( companion object { const val INVALID_VALUE = -1 - - // 安利墙卡片轮播时间 - const val AMWAY_LOOP_TIME = 5000L } override fun getEventByPosition(pos: Int): ExposureEvent? { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt index e2ca7dd304..f623c1df81 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.gamecollection.square +import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.os.Bundle @@ -39,9 +40,15 @@ class GameCollectionSquareFragment : LazyListFragment(application) { @@ -14,13 +19,35 @@ class GameCollectionSquareViewModel(application: Application) : var entrance: String? = null var tagId = "" var view = "hot" + var isHome = false - override fun provideDataSingle(page: Int): Single> = RetrofitManager.getInstance(getApplication()) - .api.getGameCollectionSquareList(view, tagId, page) + val mAmwayCommentList = MutableLiveData>() + + init { + getAmwayCommentList() + } + + override fun provideDataSingle(page: Int): Single> = + if (isHome) RetrofitManager.getInstance(getApplication()) + .api.getHomeGameCollectionSquareList(UUID.randomUUID().toString(), page) + else RetrofitManager.getInstance(getApplication()) + .api.getGameCollectionSquareList(view, tagId, page) override fun mergeResultLiveData() { mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } } override fun provideDataObservable(page: Int): Observable>? = null + + @SuppressLint("CheckResult") + fun getAmwayCommentList() { + RetrofitManager.getInstance(getApplication()).api + .getAmwayCommentList(1, 10) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + mAmwayCommentList.postValue(data) + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index b5e4a5b990..184ee5ae1c 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -3354,4 +3354,10 @@ public interface ApiService { */ @POST("game_lists/{game_list_id}:unvote") Single unVoteGameCollection(@Path("game_list_id") String id); + + /** + * 获取首页tab游戏单广场列表 + */ + @GET("game_lists?view=hot") + Single> getHomeGameCollectionSquareList(@Query("random") String id, @Query("page") int page); } \ No newline at end of file diff --git a/app/src/main/res/layout/game_collection_amway_content_item.xml b/app/src/main/res/layout/game_collection_amway_content_item.xml index 68fbd48317..64f2975c85 100644 --- a/app/src/main/res/layout/game_collection_amway_content_item.xml +++ b/app/src/main/res/layout/game_collection_amway_content_item.xml @@ -1,5 +1,6 @@ @@ -16,10 +17,11 @@ android:orientation="horizontal"> + android:layout_height="16dp" + app:gameIconCornerRadius="4dp" /> diff --git a/app/src/main/res/layout/game_collection_square_item.xml b/app/src/main/res/layout/game_collection_square_item.xml index dac3cae5b9..b9d01c3e24 100644 --- a/app/src/main/res/layout/game_collection_square_item.xml +++ b/app/src/main/res/layout/game_collection_square_item.xml @@ -204,6 +204,7 @@ @@ -255,7 +256,8 @@ style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="100dp" android:layout_height="10dp" - android:max="100" + android:max="@{entity.count.game}" + android:progress="@{entity.count.playedGame}" android:progressDrawable="@drawable/progressbar_game_collection" /> From c64e0da8c77e7fe2f6bd5c99be8804fd5f4c3209 Mon Sep 17 00:00:00 2001 From: lyr <15622190878@163.com> Date: Fri, 19 Nov 2021 09:44:40 +0800 Subject: [PATCH 27/49] =?UTF-8?q?=E3=80=90=E5=85=89=E7=8E=AF=E5=8A=A9?= =?UTF-8?q?=E6=89=8BV5.5.0=E3=80=91=E6=B8=B8=E6=88=8F=E5=8D=95=E8=AF=A6?= =?UTF-8?q?=E6=83=85web=E9=A1=B5=EF=BC=88=E5=AE=8C=E6=88=90=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E9=9D=A2=E6=9D=BF=EF=BC=89https://git.ghzs.com/pm/hal?= =?UTF-8?q?o-app-issues/-/issues/1546?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gh/common/util/ShareUtils.java | 3 +- ...rumShareEntity.kt => NormalShareEntity.kt} | 2 +- .../detail/GameCollectionDetailFragment.kt | 32 ++- .../detail/GameCollectionShareDialog.kt | 111 +++++++++ .../qa/answer/detail/AnswerDetailFragment.kt | 4 +- .../article/detail/ArticleDetailFragment.kt | 5 +- .../qa/dialog/MoreFunctionPanelDialog.kt | 6 +- .../detail/QuestionsDetailFragment.kt | 6 +- .../newdetail/NewQuestionDetailFragment.kt | 6 +- .../video/detail/ForumVideoDetailFragment.kt | 4 +- .../res/drawable-xxhdpi/ic_share_copy.webp | Bin 0 -> 3922 bytes .../main/res/drawable-xxhdpi/ic_share_qq.webp | Bin 0 -> 2442 bytes .../res/drawable-xxhdpi/ic_share_qq_zone.webp | Bin 0 -> 2850 bytes .../res/drawable-xxhdpi/ic_share_sms.webp | Bin 0 -> 2706 bytes .../res/drawable-xxhdpi/ic_share_wechat.webp | Bin 0 -> 2978 bytes .../ic_share_wechat_moments.webp | Bin 0 -> 3586 bytes .../res/drawable-xxhdpi/ic_share_weibo.webp | Bin 0 -> 4148 bytes .../layout/dialog_game_collection_share.xml | 211 ++++++++++++++++++ app/src/main/res/values/styles.xml | 27 +++ 19 files changed, 396 insertions(+), 21 deletions(-) rename app/src/main/java/com/gh/gamecenter/entity/{ForumShareEntity.kt => NormalShareEntity.kt} (93%) create mode 100644 app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionShareDialog.kt create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_copy.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_qq.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_qq_zone.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_sms.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_wechat.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_wechat_moments.webp create mode 100644 app/src/main/res/drawable-xxhdpi/ic_share_weibo.webp create mode 100644 app/src/main/res/layout/dialog_game_collection_share.xml diff --git a/app/src/main/java/com/gh/common/util/ShareUtils.java b/app/src/main/java/com/gh/common/util/ShareUtils.java index 875cee32c8..a8cd940a73 100644 --- a/app/src/main/java/com/gh/common/util/ShareUtils.java +++ b/app/src/main/java/com/gh/common/util/ShareUtils.java @@ -87,7 +87,8 @@ public class ShareUtils { web("web链接"), userHome("个人主页"), qaDetail("QA内容详情"), - inviteFriends("邀请好友"); + inviteFriends("邀请好友"), + gameCollection("游戏单"); private String name; diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumShareEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/NormalShareEntity.kt similarity index 93% rename from app/src/main/java/com/gh/gamecenter/entity/ForumShareEntity.kt rename to app/src/main/java/com/gh/gamecenter/entity/NormalShareEntity.kt index fda128ebcb..0f5f4c4b2b 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ForumShareEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/NormalShareEntity.kt @@ -5,7 +5,7 @@ import com.gh.common.util.ShareUtils import kotlinx.android.parcel.Parcelize @Parcelize -data class ForumShareEntity( +data class NormalShareEntity( var id: String = "", var shareUrl: String = "", var shareIcon: String = "", diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt index 12fcc89471..84eef46d1b 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -4,6 +4,8 @@ import android.graphics.Color import android.os.Bundle import android.view.View import androidx.annotation.ColorRes +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick import com.gh.common.history.HistoryHelper @@ -15,8 +17,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.databinding.FragmentGameCollectionDetailBinding import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding -import com.gh.gamecenter.entity.CommentEntity -import com.gh.gamecenter.entity.GamesCollectionDetailEntity +import com.gh.gamecenter.entity.* import com.gh.gamecenter.eventbus.EBUserFollow import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel @@ -394,7 +395,7 @@ class GameCollectionDetailFragment : @OnClick(R.id.backIv, R.id.imageDesIv, R.id.videoItemDesIv, R.id.toolbarLightUserContainer, R.id.toolbarUserContainer, R.id.videoItemUserContainer, R.id.toolbarFollowTv, R.id.videoItemFollowTv, R.id.squareIv, R.id.bottomStarIv, - R.id.bottomLikeIv) + R.id.bottomLikeIv, R.id.bottomShareIv) override fun onClick(v: View) { when (v.id) { R.id.backIv -> requireActivity().finish() @@ -450,9 +451,34 @@ class GameCollectionDetailFragment : } } } + + R.id.bottomShareIv -> { + showMoreItemDialog() + } } } + private fun showMoreItemDialog() { + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return + mEntity?.run { + GameCollectionShareDialog.showMoreDialog( + requireActivity() as AppCompatActivity, + getShareEntity(this) + ) + } + } + + private fun getShareEntity(entity: GamesCollectionDetailEntity): NormalShareEntity { + return NormalShareEntity( + id = entity.id, + shareUrl = "https://www.baidu.com", + shareIcon = entity.cover, + shareTitle = entity.title, + shareSummary = entity.intro, + shareEntrance = ShareUtils.ShareEntrance.gameCollection + ) + } + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { val data = mGameAdapter?.positionAndPackageMap ?: return for (gameAndPosition in data) { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionShareDialog.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionShareDialog.kt new file mode 100644 index 0000000000..6fd9087023 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionShareDialog.kt @@ -0,0 +1,111 @@ +package com.gh.gamecenter.gamecollection.detail + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import com.gh.common.dialog.BaseDraggableDialogFragment +import com.gh.common.util.* +import com.gh.gamecenter.databinding.DialogGameCollectionShareBinding +import com.gh.gamecenter.entity.NormalShareEntity + +class GameCollectionShareDialog : BaseDraggableDialogFragment() { + + private lateinit var mBinding: DialogGameCollectionShareBinding + private var mShareEntity: NormalShareEntity? = null + private var mShareUtils: ShareUtils? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mShareEntity = requireArguments().getParcelable(KEY_SHARE) + mShareUtils = getShareUtils() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return DialogGameCollectionShareBinding.inflate(inflater, container, false) + .apply { mBinding = this }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mBinding.run { + wechatItem.setOnClickListener { + mShareUtils?.wechatShare() + } + + wechatMomentsItem.setOnClickListener { + mShareUtils?.wechatMomentsShare() + } + + qqItem.setOnClickListener { + mShareUtils?.qqShare() + } + + qqZoneItem.setOnClickListener { + mShareUtils?.qZoneShare() + } + + weiboItem.setOnClickListener { + mShareUtils?.sinaWeiboShare() + } + + smsItem.setOnClickListener { + mShareUtils?.shortMessageShare() + } + + copyItem.setOnClickListener { + mShareUtils?.copyLink(mShareUtils?.shareUrl ?: "") + } + + cancelTv.setOnClickListener { + dismissAllowingStateLoss() + } + } + } + + private fun getShareUtils(): ShareUtils { + val shareUtils = ShareUtils.getInstance(requireContext()) + mShareEntity?.run { + shareUtils.shareParamsDetail( + requireActivity(), + shareUrl, + shareIcon, + shareTitle, + shareSummary, + shareEntrance, + id, + null + ) + } + return shareUtils + } + + override fun getRootView(): View = mBinding.root + override fun getDragCloseView(): View = mBinding.dragClose + + companion object { + const val KEY_SHARE = "share" + const val REQUEST_CODE = 1105 + + @JvmStatic + fun showMoreDialog( + activity: AppCompatActivity, + share: NormalShareEntity, + ) { + GameCollectionShareDialog().apply { + arguments = Bundle().apply { + putParcelable(KEY_SHARE, share) + } + }.show( + activity.supportFragmentManager, + GameCollectionShareDialog::class.java.name + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt index 4d0009d7a7..c95a6e392e 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt @@ -635,8 +635,8 @@ open class AnswerDetailFragment : NormalFragment() { } } - private fun getShareEntity(answer: AnswerDetailEntity): ForumShareEntity { - return ForumShareEntity( + private fun getShareEntity(answer: AnswerDetailEntity): NormalShareEntity { + return NormalShareEntity( id = mAnswerId, shareUrl = if (isPublishEnv()) { getString(R.string.share_answers_url, mAnswerId) diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt index 21a6b49f39..11aaa9b9ed 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt @@ -26,7 +26,6 @@ import com.gh.common.util.* import com.gh.gamecenter.ImageViewerActivity import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.baselist.LoadStatus import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.databinding.FragmentArticleDetailBinding import com.gh.gamecenter.entity.* @@ -575,8 +574,8 @@ class ArticleDetailFragment : BaseCommentFragment, title: String, - share: ForumShareEntity, + share: NormalShareEntity, status: String, parentTag: String ) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/detail/QuestionsDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/detail/QuestionsDetailFragment.kt index 8bf3d17a15..02c96cabab 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/detail/QuestionsDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/detail/QuestionsDetailFragment.kt @@ -33,7 +33,7 @@ import com.gh.gamecenter.SuggestionActivity import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.baselist.LoadType -import com.gh.gamecenter.entity.ForumShareEntity +import com.gh.gamecenter.entity.NormalShareEntity import com.gh.gamecenter.entity.MenuItemEntity import com.gh.gamecenter.entity.Permissions import com.gh.gamecenter.entity.SpecialColumn @@ -446,8 +446,8 @@ class QuestionsDetailFragment : } } - private fun getShareEntity(questionEntity: QuestionsDetailEntity): ForumShareEntity { - return ForumShareEntity( + private fun getShareEntity(questionEntity: QuestionsDetailEntity): NormalShareEntity { + return NormalShareEntity( id = questionEntity.id ?: "", shareUrl = if (isPublishEnv()) { getString(R.string.share_questions_url, questionEntity.id) diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt index e1dd264230..eb9792a79b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt @@ -23,7 +23,7 @@ import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.databinding.FragmentArticleDetailBinding import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.ForumShareEntity +import com.gh.gamecenter.entity.NormalShareEntity import com.gh.gamecenter.entity.MenuItemEntity import com.gh.gamecenter.entity.Permissions import com.gh.gamecenter.eventbus.EBDeleteDetail @@ -529,8 +529,8 @@ class NewQuestionDetailFragment : } } - private fun getShareEntity(questionEntity: QuestionsDetailEntity): ForumShareEntity { - return ForumShareEntity( + private fun getShareEntity(questionEntity: QuestionsDetailEntity): NormalShareEntity { + return NormalShareEntity( id = questionEntity.id ?: "", shareUrl = if (isPublishEnv()) { getString(R.string.share_questions_url, questionEntity.id) diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt index 16a1cb74d0..9c53a92203 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt @@ -505,8 +505,8 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { } } - private fun getShareEntity(): ForumShareEntity { - return ForumShareEntity( + private fun getShareEntity(): NormalShareEntity { + return NormalShareEntity( id = mForumVideoEntity?.id ?: "", shareUrl = if (isPublishEnv()) { "https://m.ghzs666.com/video/${mForumVideoEntity?.id}" diff --git a/app/src/main/res/drawable-xxhdpi/ic_share_copy.webp b/app/src/main/res/drawable-xxhdpi/ic_share_copy.webp new file mode 100644 index 0000000000000000000000000000000000000000..86256469ffae7775ef77a4049fb3a97c9ac7b50e GIT binary patch literal 3922 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_share_qq.webp b/app/src/main/res/drawable-xxhdpi/ic_share_qq.webp new file mode 100644 index 0000000000000000000000000000000000000000..6dee908c5fccbf0713140b8aec202415892d1e46 GIT binary patch literal 2442 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 A0ssI2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_share_sms.webp b/app/src/main/res/drawable-xxhdpi/ic_share_sms.webp new file mode 100644 index 0000000000000000000000000000000000000000..45d020ce5cdef8ba4ff07567e53ba3cd2bfa3471 GIT binary patch literal 2706 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 I5106sAoibJ9yp%bb!+#mx4 zu{VAzpomb)3o%g4{m=Y2um|+t@Spbk0(}5KW<6bhi=)>A^i9IkNrQ-|Bw$r z4hIgTvZPZa08gc+eWl_2a*$Nxg+@y)MKEWcmGhxAnf7FA#Wi=SY!4 z3?+Qqo~a~1-B38V$vo5(vE)I)kNk0Eh<`lF^y(eJ&S|VZ`ZQW({!b(PSwV}zB1RyY zNsbs^f}=TO+;ajCa`Csg^^}XEQ`!6D|I4=D4|$K&k7EZI;em)6UclX}lOSPahH9O6 z-K*54ft2&dve(N8=(JuK4gWD^6en7UK`H(ezIn5C&EcE|2J0H!<_0QU!+69q^5=n? zu;bnB4?Z&H+?*{dJaH}UOU+ZKYT?-8HsR$wR&zY&<8{jEyWm>sUy;vd_p<^;B1F3;+QB|ILX!>Y@7&hPZ*WR2Y9H96|Y}T|ET^6LNz$k5;fr zUSw8)96LGl^EUHV^QX6Fxe+&x4d3+%7Sz&6GLK4eOKuX=lMg^g29z-{i$_d6yA?M1 zr(F&AMOa5K1WP=$I&%*vtOs`weHVlShkW1fG@^>299w!3++gqn`wA2AbUV+OnL1M)f6LU-LJ$eP3# z#~(T`eR}hJ@}!r(5NDYt^%rO13U}hn#8nMuhgzZ9G4Au+65rNrPo?pzV>6viVp5)7 zx?Ayr=-R&xPD|UDfp7h}KCIrl8~SjY$39rY3Z+mbZqJ>ymf&_vs(7*UO?D&+HF0xL+aV8E_$G%Q0 z9`4X+UTg*iL;4@{5~Uz}yjR~8+_u2K!(mjLzH{L;8EyYNW>$$+d`2tY8(5?#1)c$_ zKM?ANx^!Ww+E!203!E$xYx7%|M?&IyJRD(>pG%Q$ZGZcYM_|Bu(1uE?8I5T@h3XFN z`-tBGGO>#~bLvi0&kC|# z{L;Pa6V&JJ-B2O%@VIz$&eEz75GJshoF(=_y*a?EDbur=&1GTSREO?a>BQAvF@~4qK|gMLQZitC6OG^rP z)9GAG`#|vuc<|grRg^zu`s2?VDFd395>EX1-%Nlj3RgpAzcoDZJLUG>wea>ELiE|{ zH%$0={vQ9|!_T(_osv&QlTVeM`2oOppawK_r(TnL&AjsJFrTY>76hy11h@iAM$PdHPlycn9229T~<&>=)lQt_V!~*-G+Era=aaG zdHH&k^SiX$kj9;PZz(vLvvq*|Ta{(blcN_?1*V{KmUu*W^hYf|i+6DFPwJC1?_U}v zhj6fs1FH%@kpS)Ks&CC8c;yLU#AR7>eC2?10(d-Kt+Xw3xSAjR?(}B{(tk+v?DSG0 zMVf@qoAzJCa9EVr`zqVaL4}(+?xahRU`pPxg2Na06+Jj~?Evj_hyBA&o?uwovk>c~ z)Cj{k=pnN`EP9p)IiR)Iy9s9_Ir^3;NXPV?CS{%!fV3@w=Pqz^tw%Ap`#&HEUqoCy zLl?xCnluTZ-Jzbk*Jo#ptko>!nUcRA7pVLvY=qdxBqiiA^Xio!SM-28!_~=&Yx#7#s z?d74(e&J2^lM&nzaAU#xXW5>hm#jov7wEBrCIB#C@IKm+g2vONs5HM1LSK+kd z(o>OL?e`(Nc61o0BYprR%ztjT%wF@nD06&kY@|dJ(VU=rSa!gBN3hm~-O$7se2Lef zJX#ywVfhz0MY4|Jn~T7fUj^7|r86fPT)2ZNv!l`6GxmxUs-G7~vp@NqFUtMz`ly28 zH!n#k0MGG`EP2`wMk8YlsvYCAMw8&2hzS?w*ZTOZas2aM<{zR69#L#w2iDSOcyONMlbzuRaAG?u6X4!CoBUA5vFfWr57h( zabwolPd+{NGbXk1Vb{A?t<{B%Y&$=M zyw6WchS|v=vL~v27A=*2XW)XWaiu^;zNA~P@=68)pFEG^j2NVm*fK)DSuZ1Ni&EIf zJ2ls3}om6~vX!0%E$@j43eG#}f zYF=P~y6wYgEZFY@bLg@!oaH45izZbq6Z9_tew$f+9Dl*#o@iy=L`LG$C95+(u|(|S zgi$CYD*_<@+>(q}<}d^JkBCG#MmD^S%z0PHO}M`;&;5Lu9@ouxNeKG&#z9s`;HHKO zwE2OVq>dDU!dqZcJp-*Y@}I{!vLw@|UPy_AX8++83{g{I{c>_M?`{0pE4IM!uJ^-y zZU8%rjMt0#nt%2*RK-8E@gMGxfJPL1c1$l`8vR%Zipv%2u$nnuJH{Oku2`+V~5vfwQUDg{28u$RNb8fkfn!Ty4#D;;XC#f z#_+>2aTX)Sn7|)Tg}Io}3}VVKdUP^&4zprvlx0XHxu$Km=*M2_x9*}}D>IPs?K}9J zj>u_@x)oGE( zS{uZhl1N7FIFoM?>bPNpD#S^o*_T;QgSVf#pZJFxpfs!&2<*z6Ba+;xV|BhYKtVMv z^q2z^bTMnTCj}mXx2Trywx-*C=B*zzLY|Fc6Uqdqr&qc>R$8Ljgw>YW0B1RGCC&@K zK!_V$D?R#0pfy}K&k8C!0K&FY3H=iUH?A~qowGBhcdt2FYeS;`R!l8A+o&%OOQIS?|NcAqqP`pjYEubbmABDWDT7-R)^_t5bbT?@^E+XTDA}AJ^%Q&SiFJ^M*f7BfQwiUt%56R@egafK`xiS zV^u*YVGt1v)?Z4IYE!Wnua;?Vg^f)FtDxh%P8K5}+kPd$o!ju!qpq+z(3vP|Ps;|1 zcDrw@?K<92X?mdI?4o+p^QLS-MjI`gZ|O;3Oj}Yuwb=I0zJx5^_lf}|22zxUWxK?2 z6&J?|VYp}`?!pglt@dGNW3?CU9~8J9_|mRF#Qt78TnKfa4$c&Nn3me;>CVSK+HyX< z9NOzi(BA`DQoXhKudY$L6k{Y*3z(R53RC#H=8jE=rKZ)ZcS&l4p5lg7`5^Zp!p*c} zuR)-amI?mI%;i&(-B7ul-KF19A2XnXyRkjZHI%Bi9q%UAW?CF8}F{Hc;kS{?R6$}M@CcmM#Ten%Dn literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/dialog_game_collection_share.xml b/app/src/main/res/layout/dialog_game_collection_share.xml new file mode 100644 index 0000000000..c086f1753b --- /dev/null +++ b/app/src/main/res/layout/dialog_game_collection_share.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3f95011d70..75e08d7d6a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -341,6 +341,33 @@ 11sp + + + + + + + +