diff --git a/.gitmodules b/.gitmodules index 39515fb249..b7106566b4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = libraries/LGLibrary url = git@git.ghzs.com:android/common-library.git branch = master +[submodule "assistant_flutter"] + path = assistant_flutter + url = git@git.ghzs.com:halo/android/flutter-module.git diff --git a/app/build.gradle b/app/build.gradle index 3e7835f218..3fa3b0338a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -224,6 +224,11 @@ android { buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\"" } } + + lintOptions { + // For flutter release build, see https://github.com/flutter/flutter/issues/58247 + checkReleaseBuilds false + } } repositories { @@ -348,8 +353,7 @@ dependencies { implementation "net.lingala.zip4j:zip4j:${zip4j}" - // plugin 需要字符串,故不能用值 - implementation "io.sentry:sentry-android:3.2.0" + implementation "io.sentry:sentry-android:${sentry}" implementation("com.github.piasy:BigImageViewer:${bigImageViewer}", { exclude group: 'com.squareup.okhttp3' @@ -373,6 +377,8 @@ dependencies { implementation "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl:${splitties}" + compileOnly "com.github.axen1314.lancet:lancet-base:$lancet_version" + implementation project(':libraries:LGLibrary') // implementation project(':libraries:MTA') implementation project(':libraries:QQShare') @@ -451,6 +457,8 @@ andResGuard { mergeDuplicatedRes = true whiteList = [ "R.drawable.icon", + "R.drawable.ic_bar_back", + "R.drawable.toolbar_search_icon", "R.drawable.bg_notification_answer_style_1", "R.drawable.bg_notification_answer_style_2", "R.drawable.bg_notification_article_style_1", @@ -509,6 +517,28 @@ andResGuard { "R.id.bottomShareTv", "R.id.recommendStarPref", "R.id.recommendStar", + "R.drawable.help_search_delete", + "R.drawable.suggest_type_normal", + "R.drawable.suggest_type_crash", + "R.drawable.suggest_type_game_question", + "R.drawable.suggest_type_game_collect", + "R.drawable.suggest_type_function_suggest", + "R.drawable.suggest_type_article_collect", + "R.drawable.suggest_type_copyright", + "R.drawable.help_result_empty", + "R.drawable.news_comment_detail_read", + "R.drawable.news_comment_detail_comment", + "R.drawable.news_comment_detail_share", + "R.drawable.ic_libao", + "R.drawable.ic_link", + "R.drawable.concern_message_icon", + "R.drawable.reuse_blank_hint", + "R.drawable.ic_concern", + "R.drawable.concern_down", + "R.drawable.concern_up", + "R.drawable.ic_libao_more", + "R.drawable.ic_libao_delete", + "R.drawable.ic_dialog_close" ] compressFilePattern = [ "*.png", diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index 9f86cbc953..b991dad8bf 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -140,4 +140,12 @@ ### 避免 WebChromeClient 被混淆 -keepclassmembers class * extends android.webkit.WebChromeClient{ public void openFileChooser(...); -} \ No newline at end of file +} + +# Flutter模块 +-keep class com.gh.common.util.DirectUtils { + public static void directToQa(...); + public static void directToQaCollection(...); + public static void directToFeedback(...); +} + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 970b3d2e58..df0356030a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,7 +69,7 @@ android:networkSecurityConfig="@xml/network_security_config" android:resizeableActivity="true" android:theme="@style/AppCompatTheme.APP" - tools:replace="android:allowBackup" + tools:replace="android:name,android:allowBackup" tools:targetApi="n"> > signatureSchemeApkContentDigests = + new HashMap<>(); + + // The SUPPORTED_APK_SIG_SCHEME_NAMES contains the mapping from version number to scheme + // name, but the verifiers use this parameter as the schemes supported by the target SDK + // range. Since the code below skips signature verification based on max SDK the mapping of + // supported schemes needs to be modified to ensure the verifiers do not report a stripped + // signature for an SDK range that does not support that signature version. For instance an + // APK with V1, V2, and V3 signatures and a max SDK of O would skip the V3 signature + // verification, but the SUPPORTED_APK_SIG_SCHEME_NAMES contains version 3, so when the V2 + // verification is performed it would see the stripping protection attribute, see that V3 + // is in the list of supported signatures, and report a stripped signature. + Map supportedSchemeNames = getSupportedSchemeNames(maxSdkVersion); + + // Android N and newer attempts to verify APKs using the APK Signing Block, which can + // include v2 and/or v3 signatures. If none is found, it falls back to JAR signature + // verification. If the signature is found but does not verify, the APK is rejected. + Set foundApkSigSchemeIds = new HashSet<>(2); + + int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections); + V1SchemeVerifier.Result v1Result = + V1SchemeVerifier.verify( + apk, + zipSections, + supportedSchemeNames, + foundApkSigSchemeIds, + minSdkVersion, + maxSdkVersion); + + result.mergeFrom(v1Result); + return result; + } + + /** * Verifies the APK's signatures and returns the result of verification. The APK can be * considered verified iff the result's {@link Result#isVerified()} returns {@code true}. diff --git a/app/src/main/java/com/gh/base/ToolBarActivity.java b/app/src/main/java/com/gh/base/ToolBarActivity.java index bd1199b84e..ee667c3283 100644 --- a/app/src/main/java/com/gh/base/ToolBarActivity.java +++ b/app/src/main/java/com/gh/base/ToolBarActivity.java @@ -189,21 +189,25 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon } } - if (mTitleContainer != null) { - ViewGroup.LayoutParams layoutParams = mTitleContainer.getLayoutParams(); - if (showToolbarAtLeft() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - mTitleTv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); - } else { - final int[] diff = {0}; - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) layoutParams; - mActionMenuView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { - if (diff[0] != mActionMenuView.getWidth() - mBackContainer.getWidth()) { - diff[0] = mActionMenuView.getWidth() - mBackContainer.getWidth(); - params.setMargins(diff[0], 0, 0, 0); - mTitleContainer.setLayoutParams(params); - } - }); - } + if (showToolbarAtLeft() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mTitleTv != null) { + mTitleTv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START); + } + } + + @Override + protected void onResume() { + super.onResume(); + setTitleCenter(); + } + + // 设置标题居中 + public void setTitleCenter() { + if (mActionMenuView != null && mTitleContainer != null && mBackContainer != null && !showToolbarAtLeft()) { + mActionMenuView.post(() -> { + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mTitleContainer.getLayoutParams(); + params.setMargins(mActionMenuView.getWidth() - mBackContainer.getWidth(), 0, 0, 0); + mTitleContainer.setLayoutParams(params); + }); } } diff --git a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt index f5816300a2..d0b5acb411 100644 --- a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt +++ b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt @@ -1,10 +1,12 @@ package com.gh.common +import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.text.TextUtils import android.util.Base64 +import com.gh.common.constant.Constants import com.gh.common.util.* import com.gh.common.util.DirectUtils.directToFeedback import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming @@ -14,13 +16,12 @@ import com.gh.common.util.DirectUtils.directToLegacyVideoDetail import com.gh.common.util.DirectUtils.directToLinkPage import com.gh.common.util.DirectUtils.directToQa import com.gh.common.util.GsonUtils.gson -import com.gh.gamecenter.LibaoDetailActivity -import com.gh.gamecenter.MainActivity -import com.gh.gamecenter.NewsDetailActivity +import com.gh.gamecenter.* import com.gh.gamecenter.entity.* import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.gh.gamecenter.subject.SubjectActivity +import com.gh.gamecenter.suggest.SuggestType import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel import com.lightgame.utils.Utils import java.nio.charset.Charset @@ -341,6 +342,40 @@ object DefaultUrlHandler { DirectUtils.directToHelpAndFeedback(context, position.toInt()) } + EntranceUtils.HOST_HELP_DETAIL -> { + var url = uri.getQueryParameter("url") + if (!url.isNullOrEmpty()) { + context.startActivity(WebActivity.getIntent(context, url, false)) + } else { + url = if ("internal" == BuildConfig.FLAVOR) { + Constants.HELP_ADDRESS_DEV + } else { + Constants.HELP_ADDRESS + } + val id = uri.getQueryParameter("id") + val name = uri.getQueryParameter("name") + val qaCollectionId = uri.getQueryParameter("collection_id") + context.startActivity(WebActivity.getIntent(context, "$url$id", name, true, !qaCollectionId.isNullOrEmpty())) + } + } + + EntranceUtils.HOST_HELP_SUGGESTION -> { + val suggestType = when(uri.getQueryParameter("type")) { + "normal" -> SuggestType.normal + "crash" -> SuggestType.crash + "gameQuestion" -> SuggestType.gameQuestion + "gameCollect" -> SuggestType.gameCollect + "functionSuggest" -> SuggestType.functionSuggest + "articleCollect" -> SuggestType.articleCollect + "copyright" -> SuggestType.copyright + else -> null + } + suggestType?.let { + (context as Activity).startActivityForResult(SuggestionActivity.getIntent(context, it), 11) + } + } + + EntranceUtils.HOST_GAME_COLLECTION_DETAIL -> { DirectUtils.directToGameCollectionDetail(context, id, entrance) } diff --git a/app/src/main/java/com/gh/common/tracker/TrackerLogger.kt b/app/src/main/java/com/gh/common/tracker/TrackerLogger.kt index dcd82cb877..9db61185f2 100644 --- a/app/src/main/java/com/gh/common/tracker/TrackerLogger.kt +++ b/app/src/main/java/com/gh/common/tracker/TrackerLogger.kt @@ -119,7 +119,7 @@ object TrackerLogger { fun logAppLaunch(context: Context) { val jsonObject = JSONObject() val payloadObject = JSONObject() - val signatureHash = PackageUtils.getApkSignatureByPackageName(context, context.packageName) + val signatureHash = PackageUtils.getApkSignatureByPackageName(context, context.packageName)[0] val sideLoadInfo = PackageUtils.getSideLoadedInfo() tryCatchInRelease { diff --git a/app/src/main/java/com/gh/common/util/ConcernUtils.kt b/app/src/main/java/com/gh/common/util/ConcernUtils.kt index a446021a3f..7ef61ce670 100644 --- a/app/src/main/java/com/gh/common/util/ConcernUtils.kt +++ b/app/src/main/java/com/gh/common/util/ConcernUtils.kt @@ -39,6 +39,7 @@ object ConcernUtils { super.onResponse(response) listener?.onSuccess() EventBus.getDefault().post(EBConcernChanged(gameId, true)) + dispatchFollowChangeEventToFlutter(gameId, true) } override fun onFailure(e: HttpException?) { @@ -58,6 +59,7 @@ object ConcernUtils { super.onResponse(response) listener?.onSuccess() EventBus.getDefault().post(EBConcernChanged(gameId, false)) + dispatchFollowChangeEventToFlutter(gameId, false) } override fun onFailure(e: HttpException?) { @@ -131,6 +133,17 @@ object ConcernUtils { }) } + /** + * 通知Flutter游戏关注状态发生变化 + * 请不要随意修改方法名 + * @param gameId 游戏ID + * @param follow 关注状态 + */ + @JvmStatic + fun dispatchFollowChangeEventToFlutter(gameId: String, follow: Boolean) { + + } + interface onConcernListener { fun onSuccess() diff --git a/app/src/main/java/com/gh/common/util/DataUtils.java b/app/src/main/java/com/gh/common/util/DataUtils.java index c7aa2b056a..15cffcfb54 100644 --- a/app/src/main/java/com/gh/common/util/DataUtils.java +++ b/app/src/main/java/com/gh/common/util/DataUtils.java @@ -133,7 +133,7 @@ public class DataUtils { options.setDebug(BuildConfig.DEBUG); options.setEnableSessionTracking(true); options.setEnvironment(BuildConfig.FLAVOR); - options.setDsn("https://6b1caf0d17c1408e8680f3f73ff80bd0@sentry.ghzs.com/22"); + options.setDsn("https://6b1caf0d17c1408e8680f3f73ff80bd0@sentry.shanqu.cc/22"); options.setBeforeSend((event, hint) -> { if (BuildConfig.DEBUG) { 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 c0b47b228e..491ac2ef90 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -377,7 +377,7 @@ object DirectUtils { } /** - * 跳转至QA + * 跳转至QA,请不要随意修改方法名 */ @JvmStatic fun directToQa(context: Context, text: String? = "", id: String) { @@ -390,7 +390,7 @@ object DirectUtils { } /** - * 跳转至QA合集 + * 跳转至QA合集,请不要随意修改方法名 */ @JvmStatic fun directToQaCollection(context: Context, text: String, id: String) { @@ -836,6 +836,15 @@ object DirectUtils { jumpActivity(context, bundle) } + /** + * 跳转到礼包中心,请不要随意修改方法名 + */ + @JvmStatic + fun directToGift(context: Context, entrance: String) { + val intent = LibaoActivity.getIntent(context, entrance) + context.startActivity(intent) + } + /** * 切换到社区页面(旧社区页面已经没有了,处理为跳转到论坛详情) */ @@ -1369,7 +1378,7 @@ object DirectUtils { } /** - * 跳转至使用帮助与反馈 + * 跳转至使用帮助与反馈,请不要随意修改方法名 * @param position 使用帮助:[HelpAndFeedbackActivity.HELP_ITEM],意见反馈:[HelpAndFeedbackActivity.SUGGESTION_ITEM] */ @JvmStatic @@ -1653,6 +1662,14 @@ object DirectUtils { jumpActivity(context, bundle) } + /** + * 跳转到游戏动态,请不要随意修改方法名 + */ + @JvmStatic + fun directToConcernInfo(context: Context) { + context.startActivity(ConcernInfoActivity.getIntent(context)); + } + /** * 跳转至游戏单广场 */ 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 c69011a99c..5eeb5c2385 100644 --- a/app/src/main/java/com/gh/common/util/EntranceUtils.java +++ b/app/src/main/java/com/gh/common/util/EntranceUtils.java @@ -94,6 +94,8 @@ public class EntranceUtils { public static final String HOST_GAME_RATING_DETAIL = "game_rating_detail"; public static final String HOST_HELP_AND_FEEDBACK = "help_and_feedback"; public static final String HOST_LAUNCH_SIMULATOR_GAME = "launch_simulator_game"; + public static final String HOST_HELP_DETAIL = "help_detail"; + public static final String HOST_HELP_SUGGESTION = "help_suggestion"; public static final String HOST_GAME_COLLECTION_DETAIL = "game_collection_detail"; public static final String KEY_DATA = "data"; public static final String KEY_MESSAGE = "message"; diff --git a/app/src/main/java/com/gh/common/util/Extensions.kt b/app/src/main/java/com/gh/common/util/Extensions.kt index 3bee858976..6f5e3dd630 100644 --- a/app/src/main/java/com/gh/common/util/Extensions.kt +++ b/app/src/main/java/com/gh/common/util/Extensions.kt @@ -737,6 +737,13 @@ fun List.safelyGetInRelease(index: Int): T? { } } +/** + * Array related + */ +fun Array.secondOrNull(): T? { + return if (isEmpty() || size < 2) null else this[1] +} + /** * 拦截 TextView 中的 Url Span,用应用内页面的形式打开链接 * @param shrankText 未展开时的文字 diff --git a/app/src/main/java/com/gh/common/util/LibaoUtils.java b/app/src/main/java/com/gh/common/util/LibaoUtils.java index a9241cf8ea..078c30e509 100644 --- a/app/src/main/java/com/gh/common/util/LibaoUtils.java +++ b/app/src/main/java/com/gh/common/util/LibaoUtils.java @@ -39,6 +39,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Objects; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -371,6 +372,71 @@ public class LibaoUtils { }); } + public static void libaoBtnClick(final Context context, final TextView libaoBtn, final LibaoEntity libaoEntity, + final boolean isInstallRequired, final LibaoDetailAdapter adapter, + final String entrance, final String loginEntrance, final OnLibaoStatusChangeListener listener) { + String status = libaoEntity.getStatus(); + // 领取限制 + CheckLoginUtils.checkLogin(context, loginEntrance, () -> {// "礼包详情-[" + btnStatus + "]" + if ("ling".equals(status) || "tao".equals(status)) { + if (isInstallRequired && !isAppInstalled(context, libaoEntity.getPackageName()) && adapter != null) { + String platform; + if (TextUtils.isEmpty(libaoEntity.getPlatform())) { + platform = ""; + } else { + platform = PlatformUtils.getInstance(context).getPlatformName(libaoEntity.getPlatform()); + } + + boolean isExistPlatform = false; + ArrayList apk = adapter.getGameEntity().getApk(); + for (ApkEntity apkEntity : apk) { + if (TextUtils.isEmpty(libaoEntity.getPlatform())) break; + if (libaoEntity.getPlatform().equals(apkEntity.getPlatform())) { + isExistPlatform = true; + break; + } + } + + String dialogContent = context.getString(R.string.ling_rules_dialog, libaoEntity.getGame().getName(), platform); + boolean finalIsExistPlatform = isExistPlatform; + DialogHelper.showWarningDialog(context, "条件不符", + Html.fromHtml(dialogContent), isExistPlatform ? "关闭" : "", + isExistPlatform ? "立即安装" : "关闭", + () -> { + if (finalIsExistPlatform) { + adapter.openDownload(libaoEntity.getPlatform()); + } + return null; + }, null); + return; + } + } + + switch (Objects.requireNonNull(status)) { + case "coming": + Utils.toast(context, "还没到开始领取时间"); + break; + case "check": + if (!TextUtils.isEmpty(libaoEntity.getDes())) { + DialogHelper.showDialog(context, "使用说明", Html.fromHtml(libaoEntity.getDes()), "关闭", "", () -> {}, () -> {}, false, "", ""); + } + break; + case "repeatLing": + ToastUtils.showToast("礼包每天0点刷新,明日0点后可再领一个"); + break; + case "repeatLinged": + case "ling": + libaoLing(context, libaoBtn, libaoEntity, adapter, isInstallRequired, null, entrance, listener); + break; + case "repeatTao": + case "repeatTaoed": + case "tao": + libaoTao(context, libaoBtn, libaoEntity, isInstallRequired, adapter, status, entrance, listener); + break; + } + }); + } + private static void libaoTao(Context context, TextView libaoBtn, LibaoEntity libaoEntity, boolean isInstallRequired, LibaoDetailAdapter adapter, String status, String entrance, final OnLibaoStatusChangeListener listener) { if ("repeatTao".equals(status)) { @@ -414,11 +480,11 @@ public class LibaoUtils { return; } libaoEntity.setStatus("taoed"); - initLibaoCode(libaoEntity, new UserDataLibaoEntity(libaoCode, "tao", Utils.getTime(context))); - if (adapter != null) { - adapter.initLibaoCode(new UserDataLibaoEntity(libaoCode, "tao", Utils.getTime(context))); - } + UserDataLibaoEntity me = new UserDataLibaoEntity(libaoCode, "ling", Utils.getTime(context)); + initLibaoCode(libaoEntity, me); + if (adapter != null) adapter.initLibaoCode(me); EventBus.getDefault().post(new EBReuse("libaoChanged")); + dispatchLiBaoChangeEventToFlutter(libaoEntity.getId(), "linged", GsonUtils.toJson(me)); if (listener != null) listener.onLibaoStatusChange(); uploadEvent(libaoEntity, true, entrance); String des; @@ -452,9 +518,11 @@ public class LibaoUtils { case "fetched": Utils.toast(context, "你今天已领过这个礼包了, 不能再淘号"); - libaoBtn.setText("已淘号"); - libaoBtn.setBackgroundResource(R.drawable.libao_taoed_style); - libaoBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.libao_taoed_selector)); + if (libaoBtn != null) { + libaoBtn.setText("已淘号"); + libaoBtn.setBackgroundResource(R.drawable.libao_taoed_style); + libaoBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.libao_taoed_selector)); + } libaoEntity.setStatus("taoed"); break; case "try tao": @@ -487,11 +555,16 @@ public class LibaoUtils { }); } - private static void libaoLing(final Context context, final TextView libaoBtn, final LibaoEntity libaoEntity, final LibaoDetailAdapter adapter, + private static void libaoLing(final Context context, TextView libaoBtn, final LibaoEntity libaoEntity, final LibaoDetailAdapter adapter, final boolean isInstallRequired, String captchaCode, final String entrance, final OnLibaoStatusChangeListener listener) { if (BuildConfig.DEBUG) { - Log.e("LIBAO", "context? " + context + libaoBtn.getContext()); + if (libaoBtn != null) { + Log.e("LIBAO", "context? " + context + libaoBtn.getContext()); + } else { + Log.e("LIBAO", "context? " + context); + } + } final Dialog loadingDialog = DialogUtils.showWaitDialog(context, "领取中..."); @@ -518,10 +591,11 @@ public class LibaoUtils { } libaoEntity.setAvailable(libaoEntity.getAvailable() - 1); libaoEntity.setStatus("linged"); - initLibaoCode(libaoEntity, new UserDataLibaoEntity(libaoCode, "ling", Utils.getTime(context))); + UserDataLibaoEntity me = new UserDataLibaoEntity(libaoCode, "ling", Utils.getTime(context)); + initLibaoCode(libaoEntity, me); if (listener != null) listener.onLibaoStatusChange(); EventBus.getDefault().post(new EBReuse("libaoChanged")); - + dispatchLiBaoChangeEventToFlutter(libaoEntity.getId(), "linged", GsonUtils.toJson(me)); uploadEvent(libaoEntity, false, entrance); if (adapter != null) { @@ -572,10 +646,11 @@ public class LibaoUtils { EventBus.getDefault().post(new EBUISwitch(REFRESH_LIBAO_TIME, countdown)); } - libaoBtn.setText(R.string.libao_linged); - libaoBtn.setBackgroundResource(R.drawable.libao_linged_style); - libaoBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.libao_linged_selector)); - + if (libaoBtn != null) { + libaoBtn.setText(R.string.libao_linged); + libaoBtn.setBackgroundResource(R.drawable.libao_linged_style); + libaoBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.libao_linged_selector)); + } libaoEntity.setStatus("linged"); break; case "try tao": @@ -583,7 +658,9 @@ public class LibaoUtils { DialogUtils.showHintDialog(context, "礼包已领光" , "手速不够快,礼包已经被抢光了,十分抱歉", "知道了"); libaoEntity.setStatus("used_up"); - initLibaoBtn(context, libaoBtn, libaoEntity, isInstallRequired, adapter, false, entrance, listener); + if (libaoBtn != null) { + initLibaoBtn(context, libaoBtn, libaoEntity, isInstallRequired, adapter, false, entrance, listener); + } break; case "maintaining": Utils.toast(context, "网络状态异常,请稍后再试"); @@ -611,6 +688,15 @@ public class LibaoUtils { }, captchaCode); } + /** + * 发送礼包领取状态变化事件给Flutter + * 请不要随意修改方法名 + * @param libaoId 礼包ID + * @param status 礼包状态 + * @param meJson 礼包领取信息 + */ + public static void dispatchLiBaoChangeEventToFlutter(String libaoId, String status, String meJson) {} + public static void initLibaoCode(LibaoEntity libaoEntity, UserDataLibaoEntity userDataLibaoEntity) { MeEntity userData = libaoEntity.getMe(); if (userData == null) { diff --git a/app/src/main/java/com/gh/common/util/PackageUtils.java b/app/src/main/java/com/gh/common/util/PackageUtils.java index 4ef51a72aa..c3cc763cf1 100644 --- a/app/src/main/java/com/gh/common/util/PackageUtils.java +++ b/app/src/main/java/com/gh/common/util/PackageUtils.java @@ -216,14 +216,17 @@ public class PackageUtils { * 判断是否是插件包 */ public static boolean isSignedByGh(Context context, String packageName) { - String signature = getApkSignatureByPackageName(context, packageName); + String signature = getApkSignatureByPackageName(context, packageName)[0]; return publicKey.equals(signature); } /* * 根据包名,获取apk的签名信息 + * String[0] 为系统风格的公钥字符串 + * String[1] 为接口风格的公钥字符串 + * 请自行根据需要取用 */ - public static String getApkSignatureByPackageName(Context context, String packageName) { + public static String[] getApkSignatureByPackageName(Context context, String packageName) { try { PackageInfo packageInfo = context.getApplicationContext().getPackageManager() .getPackageInfo(packageName, PackageManager.GET_SIGNATURES); @@ -231,14 +234,14 @@ public class PackageUtils { // 使用幸运破解器破解安卓签名认证可能会出现不用签名也能装的情况,这里有可能是空的 if (signatures[0] != null) { - return parseSignature(signatures[0].toByteArray())[0]; + return parseSignature(signatures[0].toByteArray()); } else { - return null; + return new String[]{null, null}; } } catch (NameNotFoundException e) { e.printStackTrace(); } - return null; + return new String[]{null, null}; } /** @@ -251,15 +254,22 @@ public class PackageUtils { */ public static boolean isInstalledAppAndApkFileShareTheSameSignature(Context context, String packageName, String apkFilePath) { try { - String installedPublicKey = getApkSignatureByPackageName(context, packageName); + String[] installedPublicKeys = getApkSignatureByPackageName(context, packageName); + + String installedPublicKey1 = installedPublicKeys[0]; + String installedPublicKey2 = installedPublicKeys[1]; String v1SignaturePublicKey = getV1SignatureFromFile(apkFilePath); if (!TextUtils.isEmpty(v1SignaturePublicKey)) { - return v1SignaturePublicKey.equals(getApkSignatureByPackageName(context, packageName)); + return v1SignaturePublicKey.equals(installedPublicKey1) || v1SignaturePublicKey.equals(installedPublicKey2); + } + String v1SignaturePublicKeyFromSystemApi = getV1SignatureFromFileBySystemApi(apkFilePath); + if (!TextUtils.isEmpty(v1SignaturePublicKeyFromSystemApi)) { + return v1SignaturePublicKeyFromSystemApi.equals(installedPublicKey1); } - return getV2SignatureFromFile(apkFilePath).equals(installedPublicKey); - } catch (Exception e) { + return getV2SignatureFromFile(apkFilePath).equals(installedPublicKey1); + } catch (Throwable e) { e.printStackTrace(); } return false; @@ -286,12 +296,23 @@ public class PackageUtils { return cert.getPublicKey().toString(); } } - return ""; - } catch (Exception e) { + + ApkVerifier verifier = new ApkVerifier.Builder(new File(apkFilePath)).build(); + ApkVerifier.Result result = verifier.retrieveV1Signature(); + + return result.getV1SchemeSigners().get(0).getCertificate().getPublicKey().toString(); + } catch (Throwable e) { return ""; } } + /** + * 用系统 API 获取 V1 签名 (最后一招了) (大文件可能会 ANR,但 ANR 一会可用的话比不能用好点!) + */ + private static String getV1SignatureFromFileBySystemApi(String apkFilePath) { + return parseSignature(HaloApp.getInstance().getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES).signatures[0].toByteArray())[0]; + } + /** * 从 APK 文件中获取 V2 签名公钥 * @@ -321,7 +342,7 @@ public class PackageUtils { new ByteArrayInputStream(signature)); ret = new String[]{cert.getPublicKey().toString(), cert.getSerialNumber().toString()}; if (ret[0].startsWith("DSA")) { - ret[0] = "DSAPublicKey{" + ApkSigningBlockUtilsLite.toHex(cert.getPublicKey().getEncoded()) + "}"; + ret[1] = "DSAPublicKey{" + ApkSigningBlockUtilsLite.toHex(cert.getPublicKey().getEncoded()) + "}"; } } catch (CertificateException e) { e.printStackTrace(); @@ -352,7 +373,7 @@ public class PackageUtils { } // 判断当前已安装应用是否为光环签名 - if (publicKey.equals(getApkSignatureByPackageName(context, packageName))) { + if (publicKey.equals(getApkSignatureByPackageName(context, packageName)[0])) { return true; } diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt index e03cb5806e..5631cdd6ec 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt @@ -96,10 +96,10 @@ class DownloadDialogAdapter(context: Context, mContext.startActivity(NewsDetailActivity.getIntentById(mContext, data.linkId, mEntrance)) } "qa" -> { - mContext.startActivity(QaActivity.getIntent(mContext, data.linkText, qaId = data.linkId)) + DirectUtils.directToQa(mContext, data.linkText, data.linkId) } "qa_collection" -> { - mContext.startActivity(QaActivity.getIntent(mContext, data.linkText, qaCollectionId = data.linkId)) + DirectUtils.directToQaCollection(mContext, data.linkText, data.linkId) } else -> { //Utils.toast(mContext, "暂不支持类型:" + data.linkType) diff --git a/app/src/main/java/com/gh/flutter/FlutterBoostRouteServiceImplAop.java b/app/src/main/java/com/gh/flutter/FlutterBoostRouteServiceImplAop.java new file mode 100644 index 0000000000..c2deb4ef7d --- /dev/null +++ b/app/src/main/java/com/gh/flutter/FlutterBoostRouteServiceImplAop.java @@ -0,0 +1,151 @@ +package com.gh.flutter; + +import static com.gh.gamecenter.info.ConcernFragment.NEWS_MESSAGE_ARTICLE_REQUEST; +import static com.gh.gamecenter.libao.LibaoNewFragment.LIBAO_NEW_REQUEST; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import com.gh.common.DefaultUrlHandler; +import com.gh.common.util.DataCollectionUtils; +import com.gh.common.util.EntranceUtils; +import com.gh.common.util.StringUtils; +import com.gh.gamecenter.GameDetailActivity; +import com.gh.gamecenter.LibaoDetailActivity; +import com.gh.gamecenter.MessageDetailActivity; +import com.gh.gamecenter.NewsDetailActivity; +import com.gh.gamecenter.ShareCardActivity; +import com.gh.gamecenter.ShareCardPicActivity; +import com.gh.gamecenter.WebActivity; +import com.gh.gamecenter.entity.ConcernEntity; +import com.gh.gamecenter.entity.GameEntity; +import com.gh.gamecenter.entity.LibaoEntity; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +import java.util.Map; + +import me.ele.lancet.base.annotations.Insert; +import me.ele.lancet.base.annotations.TargetClass; + +public class FlutterBoostRouteServiceImplAop { + private static final String TARGET_CLASS = "com.halo.assistant.flutter.messenger.service.FlutterBoostRouteServiceImpl"; + private static final String METHOD_HANDLE_HELP_DETAIL = "handleHelpDetail"; + private static final String METHOD_HANDLE_HELP_SUGGESTION = "handleHelpSuggestion"; + private static final String METHOD_HANDLE_QQ_GROUP = "handleQQGroup"; + private static final String METHOD_HANDLE_GIFT_DETAIL = "handleGiftDetail"; + private static final String METHOD_HANDLE_COMMENT_DETAIL = "handleGameMomentCommentDetail"; + private static final String METHOD_HANDLE_SHARE_CARD = "handleShareCard"; + private static final String METHOD_HANDLE_GAME_MOMENT_DETAIL = "handleGameMomentDetail"; + private static final String METHOD_HANDLE_GAME_DETAIL = "handleGameDetail"; + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_HELP_DETAIL) + public void handleHelpDetail(Context context, Map params) { + StringBuilder builder = new StringBuilder("ghzhushou://" + EntranceUtils.HOST_HELP_DETAIL); + String url = (String) params.get("url"); + if(url != null && !url.isEmpty()) { + builder.append("?url=").append(params.get("url")); + } else { + builder.append("?id=").append(params.get("id")); + builder.append("&name=").append(params.get("name")); + builder.append("&collection_id=").append(params.get("collection_id")); + } + DefaultUrlHandler.interceptUrl(context, builder.toString(), ""); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_HELP_SUGGESTION) + public void handleHelpSuggestion(Context context, Map params) { + String url = "ghzhushou://" + EntranceUtils.HOST_HELP_SUGGESTION + "?type=" + params.get("type"); + DefaultUrlHandler.interceptUrl(context, url, ""); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_QQ_GROUP) + public void handleQQGroup(Context context, Map params) { + String url = "ghzhushou://" + EntranceUtils.HOST_QQ_QUN + "?key=" + params.get("key"); + DefaultUrlHandler.interceptUrl(context, url, ""); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_GIFT_DETAIL) + public void handleGiftDetail(Context context, Map params) { + String entrance = (String) params.get("entrance"); + Map libaoStatus = (Map) params.get("libaoStatus"); + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(params.get("libao")); + LibaoEntity libaoEntity = new Gson().fromJson(element, LibaoEntity.class); + libaoEntity.setBeforeStatus((String) libaoStatus.get("status")); + libaoEntity.setAvailable((Integer) libaoStatus.get("available")); + libaoEntity.setTotal(Integer.parseInt((String) libaoStatus.get("total"))); + libaoEntity.setCode((String) libaoStatus.get("code")); + Intent intent = LibaoDetailActivity.getIntent(context, libaoEntity, false, entrance + "+(礼包中心:最新)"); + ((Activity)context).startActivityForResult(intent, LIBAO_NEW_REQUEST); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_COMMENT_DETAIL) + public void handleGameMomentCommentDetail(Context context, Map params) { + String entrance = (String) params.get("entrance"); + Integer views = (Integer) params.get("views"); + Integer commentNum = (Integer) params.get("commentnum"); + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(params.get("concern")); + ConcernEntity concern = new Gson().fromJson(element, ConcernEntity.class); + if (views != null) concern.setViews(views); + if (commentNum != null) concern.setCommentnum(commentNum); + Intent intent = MessageDetailActivity.getIntentByEntity(context, concern, entrance); + ((Activity)context).startActivityForResult(intent, NEWS_MESSAGE_ARTICLE_REQUEST); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_SHARE_CARD) + public void handleShareCard(Context context, Map params) { + String entrance = (String) params.get("entrance"); + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(params.get("concern")); + ConcernEntity concern = new Gson().fromJson(element, ConcernEntity.class); + if (concern.getImg() != null && concern.getImg().size() > 0) { + ShareCardPicActivity.startShareCardPicActivity(context, concern, entrance); + } else { + String shareContent; + if (concern.getBrief() != null) { + shareContent = concern.getBrief(); + } else { + shareContent = concern.getContent(); + } + context.startActivity(ShareCardActivity.getIntent(context, concern, shareContent)); + } + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_GAME_MOMENT_DETAIL) + public void handleGameMomentDetail(Context context, Map params) { + String entrance = (String) params.get("entrance"); + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(params.get("concern")); + ConcernEntity concern = new Gson().fromJson(element, ConcernEntity.class); + DataCollectionUtils.uploadClick(context, "列表", "资讯-关注", concern.getTitle()); + //统计阅读量 + if (concern.getLink() != null) { + Intent intent = WebActivity.getIntentByNews(context, concern, entrance); + ((Activity)context).startActivityForResult(intent, NEWS_MESSAGE_ARTICLE_REQUEST); + } else { + Intent intent = NewsDetailActivity.getIntentById(context, concern.getId(), entrance); + ((Activity)context).startActivityForResult(intent, NEWS_MESSAGE_ARTICLE_REQUEST); + } + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_HANDLE_GAME_DETAIL) + public void handleGameDetail(Context context, Map params) { + String entrance = (String) params.get("entrance"); + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(params.get("game")); + GameEntity game = new Gson().fromJson(element, GameEntity.class); + DataCollectionUtils.uploadClick(context, "列表", "我的关注", game.getName()); + GameDetailActivity.startGameDetailActivity(context, game, StringUtils.buildString(entrance, "+(我的关注-列表)")); + } +} diff --git a/app/src/main/java/com/gh/flutter/FlutterContextServiceImplAop.java b/app/src/main/java/com/gh/flutter/FlutterContextServiceImplAop.java new file mode 100644 index 0000000000..d598533aa5 --- /dev/null +++ b/app/src/main/java/com/gh/flutter/FlutterContextServiceImplAop.java @@ -0,0 +1,106 @@ +package com.gh.flutter; + +import android.text.TextUtils; + +import com.gh.common.constant.Config; +import com.gh.common.exposure.meta.MetaUtil; +import com.gh.common.util.TimestampUtils; +import com.gh.gamecenter.BuildConfig; +import com.gh.gamecenter.entity.SettingsEntity; +import com.gh.gamecenter.manager.UserManager; +import com.halo.assistant.HaloApp; + +import me.ele.lancet.base.annotations.Insert; +import me.ele.lancet.base.annotations.TargetClass; + + +public class FlutterContextServiceImplAop { + private static final String TARGET_CLASS = "com.halo.assistant.flutter.messenger.service.FlutterContextServiceImpl"; + private static final String METHOD_GET_TOKEN = "getToken"; + private static final String METHOD_GET_USER_AGENT = "getUserAgent"; + private static final String METHOD_GET_CHANNEL = "getChannel"; + private static final String METHOD_ADD_TIMESTAMP = "addTimeStamp"; + private static final String METHOD_GET_OAID = "getOAID"; + private static final String METHOD_GET_DEVICE_ID = "getDeviceId"; + private static final String METHOD_GET_HOST = "getHost"; + private static final String METHOD_GET_QQ_QUN = "getQQQun"; + private static final String METHOD_GET_QQ_QUN_KEY = "getQQQunKey"; + private static final String METHOD_GET_BASE64_ENCODED_IMEI = "getBase64EncodedIMEI"; + private static final String METHOD_GET_USER_ID = "getUserId"; + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_TOKEN) + public String getToken() { + return UserManager.getInstance().getToken(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_USER_AGENT) + public String getUserAgent() { + return HaloApp.getInstance().getUserAgent(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_CHANNEL) + public String getChannel() { + return HaloApp.getInstance().getChannel(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_ADD_TIMESTAMP) + public String addTimeStamp(String url) { + return TimestampUtils.addTimestamp(url); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_OAID) + public String getOAID() { + return HaloApp.getInstance().getOAID(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_DEVICE_ID) + public String getDeviceId() { + return UserManager.getInstance().getDeviceId(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_HOST) + public String getHost() { + return BuildConfig.API_HOST; + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_QQ_QUN) + public String getQQQun() { + SettingsEntity mSettings = Config.getSettings(); + if (mSettings != null && mSettings.getSupport() != null && + !TextUtils.isEmpty(mSettings.getSupport().getQQun())) { + return mSettings.getSupport().getQQun(); + } + return "367541038"; + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_QQ_QUN_KEY) + public String getQQQunKey() { + SettingsEntity mSettings = Config.getSettings(); + if (mSettings != null && mSettings.getSupport() != null && + !TextUtils.isEmpty(mSettings.getSupport().getQQunKey())) { + return mSettings.getSupport().getQQunKey(); + } + return "vd754P2_uNUJqDcgX4V-pyXEGZZVH0DE"; + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_BASE64_ENCODED_IMEI) + public String getBase64EncodedIMEI() { + return MetaUtil.getBase64EncodedIMEI(); + } + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_GET_USER_ID) + public String getUserId() { + return UserManager.getInstance().getUserId(); + } +} diff --git a/app/src/main/java/com/gh/flutter/FlutterLiBaoServiceImplAop.java b/app/src/main/java/com/gh/flutter/FlutterLiBaoServiceImplAop.java new file mode 100644 index 0000000000..1d0806fead --- /dev/null +++ b/app/src/main/java/com/gh/flutter/FlutterLiBaoServiceImplAop.java @@ -0,0 +1,38 @@ +package com.gh.flutter; + +import android.content.Context; + +import com.gh.common.util.LibaoUtils; +import com.gh.gamecenter.entity.LibaoEntity; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +import java.util.Map; + +import me.ele.lancet.base.annotations.Insert; +import me.ele.lancet.base.annotations.TargetClass; + +public class FlutterLiBaoServiceImplAop { + + private static final String TARGET_CLASS = + "com.halo.assistant.flutter.messenger.service.FlutterLiBaoServiceImpl"; + private static final String METHOD_LING = "ling"; + + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_LING) + public void ling(final Context context, + final String entrance, + final String libaoText, + final String beforeStatus, + final Map libao) { + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(libao); + LibaoEntity libaoEntity = new Gson().fromJson(element, LibaoEntity.class); + libaoEntity.setBeforeStatus(beforeStatus); + LibaoUtils.libaoBtnClick(context, null, + libaoEntity, false, null, + entrance + "+(礼包中心:最新)", "礼包详情-[" + libaoText + "]", + null); + } +} diff --git a/app/src/main/java/com/gh/flutter/FlutterLogHubServiceImplAop.kt b/app/src/main/java/com/gh/flutter/FlutterLogHubServiceImplAop.kt new file mode 100644 index 0000000000..9452c7564c --- /dev/null +++ b/app/src/main/java/com/gh/flutter/FlutterLogHubServiceImplAop.kt @@ -0,0 +1,38 @@ +package com.gh.flutter + +import com.gh.common.json.json +import com.gh.common.loghub.LoghubUtils +import com.gh.common.tracker.Tracker +import com.gh.common.util.LogUtils +import com.lightgame.utils.Utils +import me.ele.lancet.base.annotations.Insert +import me.ele.lancet.base.annotations.TargetClass +import org.json.JSONObject + +class FlutterLogHubServiceImplAop { + + companion object { + private const val TARGET_CLASS = "com.halo.assistant.flutter.messenger.service.FlutterLogHubServiceImpl" + private const val METHOD_LOG = "log" + fun build(event: String): JSONObject { + return json { + "event" to event + "meta" to LogUtils.getMetaObject() + "launch_id" to Tracker.launchId + "session_id" to Tracker.sessionId + "timestamp" to System.currentTimeMillis() / 1000 + } + } + } + + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_LOG) + fun log(event: String, store: String, extras: Map?) { + val json = build(event) + if (!extras.isNullOrEmpty()) + extras.forEach { json.put(it.key, it.value) } + Utils.log("NewLogUtils", json.toString(4)) + LoghubUtils.log(json, store, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/flutter/FlutterLoginServiceImplAop.java b/app/src/main/java/com/gh/flutter/FlutterLoginServiceImplAop.java new file mode 100644 index 0000000000..f7a0c93979 --- /dev/null +++ b/app/src/main/java/com/gh/flutter/FlutterLoginServiceImplAop.java @@ -0,0 +1,21 @@ +package com.gh.flutter; + +import android.content.Context; + +import com.gh.common.util.CheckLoginUtils; + +import me.ele.lancet.base.annotations.Insert; +import me.ele.lancet.base.annotations.TargetClass; + +public class FlutterLoginServiceImplAop { + private static final String TARGET_CLASS = + "com.halo.assistant.flutter.messenger.service.FlutterLoginServiceImpl"; + private static final String METHOD_LING = "login"; + + + @TargetClass(TARGET_CLASS) + @Insert(METHOD_LING) + public void login(Context context, String entrance) { + CheckLoginUtils.checkLogin(context, entrance, null); + } +} diff --git a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java b/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java index eff0c7e391..11d60ed592 100644 --- a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java +++ b/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java @@ -288,6 +288,7 @@ public class NewsDetailActivity extends ToolBarActivity implements OnClickListen adapter.getNewsDetail(); if (!mHideUselessInfo) { mNewsShare.setVisible(true); + setTitleCenter(); } } } else { @@ -460,6 +461,7 @@ public class NewsDetailActivity extends ToolBarActivity implements OnClickListen public void loadDone() { // 通知更新收藏按钮 if (!mHideUselessInfo) { mNewsCollection.setVisible(true); + setTitleCenter(); NewsDetailEntity newsDetailEntity = adapter.getNewsDetailEntity(); if (newsDetailEntity.getMe() != null && newsDetailEntity.getMe().isArticleFavorite()) { mNewsCollection.setIcon(R.drawable.community_content_detail_collect_select); @@ -522,6 +524,7 @@ public class NewsDetailActivity extends ToolBarActivity implements OnClickListen if (!mHideUselessInfo) { mNewsShare.setVisible(true); + setTitleCenter(); } HistoryHelper.insertNewsEntity(mNewsEntity); diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt index ec5971b472..ef70dd1035 100644 --- a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt @@ -164,11 +164,15 @@ class UpdatableGameViewModel( mismatchedVersionUpdateList.add(update) } } - } else if (samePackageNameUpdateList.find { it.signature == installedSignature || it.isPluggable } != null) { + } else if (samePackageNameUpdateList.find { + it.signature == installedSignature.firstOrNull() + || it.signature == installedSignature.secondOrNull() + || it.isPluggable } != null) { // 1. 存在同包名同签名的游戏,以同包名同签名的游戏作为我的版本 // 2. 若不存在同包名同签名的游戏,以插件化的游戏作为我的版本 for (update in samePackageNameUpdateList) { - if (update.isPluggable || (matchedVersionUpdate == null && installedSignature == update.signature)) { + if (update.isPluggable + || (matchedVersionUpdate == null && (installedSignature.firstOrNull() == update.signature || installedSignature.secondOrNull() == update.signature))) { matchedVersionUpdate = update } else { mismatchedVersionUpdateList.add(update) diff --git a/app/src/main/java/com/gh/gamecenter/entity/LibaoEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/LibaoEntity.kt index d9bf01e514..dae5e987e6 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/LibaoEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/LibaoEntity.kt @@ -17,7 +17,7 @@ data class LibaoEntity( var name: String? = null, var des: String? = null, //使用说明 var platform: String? = null, - @SerializedName("type") + @SerializedName("type", alternate = ["status"]) var status: String? = null, var beforeStatus: String? = null, var code: String? = null, diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt index a8492f62cc..adff595420 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt @@ -36,13 +36,13 @@ class WelfaresAdapter(context: Context, "礼包中心" -> { NewLogUtils.logForumPageEvent("click_forum_gift_center") - mContext.startActivity(LibaoActivity.getIntent(mContext, "(社区-论坛:礼包中心)")) + DirectUtils.directToGift(mContext, "(社区-论坛:礼包中心)") } "游戏动态" -> { NewLogUtils.logForumPageEvent("click_forum_game_general") CheckLoginUtils.checkLogin(mContext, "社区-论坛:游戏动态") { - mContext.startActivity(ConcernInfoActivity.getIntent(mContext)) + DirectUtils.directToConcernInfo(mContext) } } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiFragment.kt index 5f3ffd3df4..78949148ee 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiFragment.kt @@ -58,8 +58,6 @@ class FuLiFragment : LazyFragment(), IScrollable { } override fun onFragmentFirstVisible() { - super.onFragmentFirstVisible() - val gameEntity = arguments?.getParcelable(GameEntity.TAG) as? GameEntity val gameDetailFactory = GameDetailViewModel.Factory(HaloApp.getInstance().application, gameEntity?.id, gameEntity) shouldScroolToLibao = arguments?.getBoolean(EntranceUtils.KEY_SCROLL_TO_LIBAO) ?: false @@ -67,6 +65,8 @@ class FuLiFragment : LazyFragment(), IScrollable { mGameDetailViewModel = viewModelProviderFromParent(gameDetailFactory) mFuLiViewModel = viewModelProvider() + super.onFragmentFirstVisible() + if (gameEntity != null) { mFuLiViewModel?.updateGameEntity(gameEntity) } diff --git a/app/src/main/java/com/gh/gamecenter/help/HelpContentFragment.kt b/app/src/main/java/com/gh/gamecenter/help/HelpContentFragment.kt index 1fa21160e2..e3a75e60cb 100644 --- a/app/src/main/java/com/gh/gamecenter/help/HelpContentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/help/HelpContentFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DirectUtils import com.gh.common.util.EntranceUtils import com.gh.common.util.NewLogUtils import com.gh.common.util.UrlFilterUtils @@ -65,7 +66,7 @@ class HelpContentFragment : ListFragment { MtaHelper.onEvent("我的光环", "反馈") - context.startActivity(HelpAndFeedbackActivity.getIntent(context)) + DirectUtils.directToHelpAndFeedback(context) } "浏览记录" -> { context.startActivity(HistoryActivity.getHistoryIntent(context, "我的光环-浏览记录")) @@ -224,7 +224,7 @@ class PersonalFunctionAdapter(val context: Context, val groupName: String, var m "游戏动态" -> { if (UserManager.getInstance().isLoggedIn) { DataCollectionUtils.uploadClick(context, "游戏动态", "发现") - context.startActivity(ConcernInfoActivity.getIntent(context)) + DirectUtils.directToConcernInfo(context) } else { MtaHelper.onEvent("我的光环_新", "功能入口-跳转登录", "游戏动态") CheckLoginUtils.checkLogin(context, "我的光环-游戏动态") { } @@ -243,9 +243,7 @@ class PersonalFunctionAdapter(val context: Context, val groupName: String, var m } "礼包中心" -> { DataCollectionUtils.uploadClick(context, "礼包中心", "发现") - - val intent = LibaoActivity.getIntent(context, "(发现:礼包)") - context.startActivity(intent) + DirectUtils.directToGift(context, "(发现:礼包)") } "工具箱" -> { DataCollectionUtils.uploadClick(context, "工具箱", "发现") diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt index c1e7bd4c63..12b796d464 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt @@ -450,19 +450,24 @@ open class NewCommentFragment : ListFragment } } R.id.imageBtn -> { - if (mViewModel.pictureList.size >= 9) { - toast("至多上传9张") - return - } - val maxChooseCount = 9 - mViewModel.pictureList.size - val intent = LocalMediaActivity.getIntent( - requireContext(), - LocalMediaActivity.ChooseType.IMAGE, - maxChooseCount, - "评论列表" - ) - Util_System_Keyboard.hideSoftKeyboard(requireActivity()) - startActivityForResult(intent, REQUEST_CODE_IMAGE) + PermissionHelper.checkStoragePermissionBeforeAction( + requireActivity(), object : EmptyCallback { + override fun onCallback() { + if (mViewModel.pictureList.size >= 9) { + toast("至多上传9张") + return + } + val maxChooseCount = 9 - mViewModel.pictureList.size + val intent = LocalMediaActivity.getIntent( + requireContext(), + LocalMediaActivity.ChooseType.IMAGE, + maxChooseCount, + "评论列表" + ) + Util_System_Keyboard.hideSoftKeyboard(requireActivity()) + startActivityForResult(intent, REQUEST_CODE_IMAGE) + } + }) } } } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java b/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java index 10c25999b6..37c296fe3e 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java @@ -85,7 +85,7 @@ class OkHttpCacheInterceptor implements Interceptor { } String token = UserManager.getInstance().getToken(); - String deviceId = UserManager.getInstance().getDeviceId(); + String deviceId = HaloApp.getInstance().getGid(); if (!TextUtils.isEmpty(token)) { request = request.newBuilder() .addHeader("TOKEN", token) diff --git a/app/src/main/java/com/gh/gamecenter/user/UserRepository.java b/app/src/main/java/com/gh/gamecenter/user/UserRepository.java index bfcdae2605..791cbb36f5 100644 --- a/app/src/main/java/com/gh/gamecenter/user/UserRepository.java +++ b/app/src/main/java/com/gh/gamecenter/user/UserRepository.java @@ -19,9 +19,9 @@ import com.gh.common.repository.ReservationRepository; import com.gh.common.util.BiCallback; import com.gh.common.util.DataUtils; import com.gh.common.util.DeviceUtils; +import com.gh.common.util.EnergyTaskHelper; import com.gh.common.util.ErrorHelper; import com.gh.common.util.GameSubstituteRepositoryHelper; -import com.gh.common.util.EnergyTaskHelper; import com.gh.common.util.GsonUtils; import com.gh.common.util.LoginHelper; import com.gh.common.util.LoginUtils; @@ -314,6 +314,12 @@ public class UserRepository { }); } + /** + * 发送登录成功回调事件给Flutter + * 请不要随意修改方法名 + */ + public static void dispatchOnLoginToFlutter() {} + //更改用户信息 @SuppressLint("CheckResult") public void changeUserInfo(final String content, final String editType, final Boolean isForcedToCertificate) { @@ -528,6 +534,7 @@ public class UserRepository { EnergyTaskHelper.postEnergyTask("login"); } } + dispatchOnLoginToFlutter(); } @Override diff --git a/assistant_flutter b/assistant_flutter new file mode 160000 index 0000000000..b86b049973 --- /dev/null +++ b/assistant_flutter @@ -0,0 +1 @@ +Subproject commit b86b049973379af1ad6abd01c8f7fa56cb55641f diff --git a/build.gradle b/build.gradle index 14ebb16f50..acffa2c09f 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:4.1.0" + classpath "com.android.tools.build:gradle:4.1.3" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // 使用了 1.2.21 在蓝叠模拟器上无法进入首页? 但是不使用又会出现触发 V3 签名... classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.20' diff --git a/dependencies.gradle b/dependencies.gradle index 5084fad2c5..a5b3ff335a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -113,5 +113,7 @@ ext { shapeOfView = "1.4.7" splitties = "3.0.0" - sentry = "3.2.0" + sentry = "4.3.0" + + lancet_version = "v1.0.6" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9f56dcd109..89f7f9faf1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -82,5 +82,14 @@ android.enableJetifier=true kapt.use.worker.api = true kapt.incremental.apt = true +# 数据绑定增量注解处理 +android.databinding.incremental=true + # 部分 vivo 手机不去掉 testOnly 直接装不上 :( android.injected.testOnly = false + +# 启用后每个库的 R 类仅包含库本身声明的资源,而不包含其依赖项的资源 +# 声明该属性可加快Flutter模块编译速度(Experimental) +# android.nonTransitiveRClass=true + + diff --git a/init.flutter.gradle b/init.flutter.gradle new file mode 100644 index 0000000000..dc9edf6103 --- /dev/null +++ b/init.flutter.gradle @@ -0,0 +1,36 @@ +// Gradle初始化脚本(Flutter注入) +apply from: "init.gradle" +apply from: "dependencies.gradle" + +allprojects { project -> + buildscript { + ext.flutter_io = System.env.FLUTTER_STORAGE_BASE_URL + ?: "https://storage.googleapis.com" + repositories { + maven { url 'https://jitpack.io' } + } + dependencies { + classpath "com.github.axen1314.lancet:lancet-plugin:$lancet_version" + } + } + repositories { + maven { url "$flutter_io/download.flutter.io" }// Flutter + } + project.afterEvaluate { + if (project.name == "app") { + Project flutterProject = project.rootProject.findProject(":flutter") + if (flutterProject == null) { + project.logger.error("Flutter project not found!") + } else { + project.apply plugin: 'me.ele.lancet' + project.dependencies { + implementation (flutterProject, { + exclude group: 'androidx.swiperefreshlayout' + }) + compileOnly "com.github.axen1314.lancet:lancet-base:$lancet_version" + } + } + + } + } +} \ No newline at end of file diff --git a/scripts/jenkins_flutter_build.sh b/scripts/jenkins_flutter_build.sh new file mode 100755 index 0000000000..2cd7c74eec --- /dev/null +++ b/scripts/jenkins_flutter_build.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# @author juntao +# @2021.11.30 + +version=$(awk -v FS="versionName = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g") +versionCode=$(awk -v FS="versionCode = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g") +build_time=$(TZ=Asia/Shanghai date +'%Y-%m%d-%H%M') +build_time_without_divider=$(TZ=Asia/Shanghai date +'%Y%m%d%H%M')L + +git checkout app/build.gradle +git checkout gradle.properties + +if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' '10 a setBinding(new Binding([gradle: this]))' settings.gradle + sed -i '' '11 a evaluate(new File(settingsDir, "assistant_flutter/.android/include_flutter.groovy"))' settings.gradle +else + sed -i '10 a setBinding(new Binding([gradle: this]))' settings.gradle + sed -i '11 a evaluate(new File(settingsDir, "assistant_flutter/.android/include_flutter.groovy"))' settings.gradle +fi + +cd assistant_flutter + +flutter pub get + +cd .. + +./gradlew --stop +./gradlew clean + +sed -i 's/buildConfigField "long", "BUILD_TIME", "0"/buildConfigField "long", "BUILD_TIME", '"\"${build_time_without_divider}\""'/g' app/build.gradle + +./gradlew rIR -I init.flutter.gradle +if [ ! -f app/build/outputs/apk/internal/release/app-internal-release.apk ]; then + ./gradlew rIR -I init.gradle +fi +mv app/build/outputs/apk/internal/release/app-internal-release.apk app/build/tmp/${version}-${versionCode}-internal-${build_time}.apk + +./gradlew rPR -I init.flutter.gradle +if [ ! -f app/build/outputs/apk/publish/release/app-publish-release.apk ]; then + ./gradlew rPR -I init.gradle +fi +mv app/build/outputs/apk/publish/release/app-publish-release.apk app/build/tmp/${version}-${versionCode}-publish-${build_time}.apk + +git checkout app/build.gradle +git checkout gradle.properties +git checkout settings.gradle \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1b339eb9e9..9d1351d890 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,4 +6,6 @@ include ':libraries:QQShare' //include ':libraries:TalkingData' //include ':libraries:UmengPush' //include ':libraries:WechatShare' -include ':libraries:Matisse' \ No newline at end of file +include ':libraries:Matisse' +//setBinding(new Binding([gradle: this])) +//evaluate(new File(settingsDir, "assistant_flutter/.android/include_flutter.groovy"))