the second phase commit

This commit is contained in:
yuyl 2025-04-29 18:15:37 +08:00
parent 24dc576f4a
commit efbcd71dd7
382 changed files with 12374 additions and 2432 deletions

View File

@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.firebase.firebase-perf'
apply from: '../common.gradle'
android {
defaultConfig {
applicationId 'com.localee.mireo.app'
resConfigs 'zh'
applicationId 'com.localee.mireo.shortapp'
resConfigs 'xxhdpi'
proguardFiles 'proguard-sdk.pro', 'proguard-app.pro'
buildConfigField('boolean', 'LOG_ENABLE', '' + LOG_ENABLE + '')
@ -35,7 +35,7 @@ android {
debug {
applicationIdSuffix '.debug'
// applicationIdSuffix '.debug'
debuggable true
jniDebuggable true
zipAlignEnabled false
@ -77,9 +77,7 @@ android {
exclude 'META-INF/*******'
}
aspectjx {
include android.defaultConfig.applicationId
}
applicationVariants.all { variant ->
variant.outputs.all { output ->
@ -90,13 +88,18 @@ android {
outputFileName += '.apk'
}
}
sourceSets {
main {
res.srcDirs(
'src/main/res',
'src/main/res-sw',
)
}
}
}
dependencies {
implementation project(':library:base')
implementation project(':library:widget')
implementation 'com.github.getActivity:XXPermissions:12.3'
implementation 'com.github.getActivity:TitleBar:9.2'
@ -108,26 +111,16 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.8'
implementation 'com.github.getActivity:GsonFactory:5.2'
implementation 'com.github.getActivity:ShapeView:9.0'
implementation 'org.aspectj:aspectjrt:1.9.6'
implementation 'com.github.getActivity:ShapeView:9.6'
implementation 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
implementation 'com.github.Baseflow:PhotoView:2.3.0'
implementation 'com.airbnb.android:lottie:4.1.0'
implementation 'com.scwang.smart:refresh-layout-kernel:2.0.3'
implementation 'com.scwang.smart:refresh-header-material:2.0.3'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'me.relex:circleindicator:2.1.6'
implementation 'com.tencent:mmkv-static:1.2.10'
// banner
@ -135,8 +128,8 @@ dependencies {
implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.4"
implementation "com.afollestad.material-dialogs:core:3.1.1"
implementation "com.afollestad.material-dialogs:lifecycle:3.1.1"
implementation "com.afollestad.material-dialogs:core:3.3.0"
implementation "com.afollestad.material-dialogs:lifecycle:3.3.0"
implementation "androidx.media3:media3-ui:1.4.0"
implementation "androidx.media3:media3-exoplayer:1.4.0"
@ -148,5 +141,20 @@ dependencies {
implementation("com.blankj:utilcodex:1.31.1")
implementation("com.github.li-xiaojun:XPopup:2.10.0")
implementation("com.facebook.android:facebook-android-sdk:17.0.2")
implementation("com.adjust.sdk:adjust-android:5.2.0")
implementation("com.adjust.sdk:adjust-android-webbridge:5.2.0")
implementation("com.android.installreferrer:installreferrer:2.2")
implementation("com.android.billingclient:billing:7.0.0")
implementation(platform("com.google.firebase:firebase-bom:32.3.1"))
implementation("com.google.firebase:firebase-messaging-ktx:24.0.0")
implementation("androidx.work:work-runtime-ktx:2.9.1")
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-perf")
implementation("com.github.centerzx:ShapeBlurView:1.0.5")
}

View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "905575925094",
"project_id": "mireotv",
"storage_bucket": "mireotv.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:905575925094:android:01c39ac6692cb6bda974a7",
"android_client_info": {
"package_name": "com.localee.mireo.shortapp"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyBAC_RDCSdtba_yMQ0oXjKTstaMe6WzHmc"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -1,5 +1,175 @@
#-ignorewarning
-keep public class * extends androidx.appcompat.app.AppCompatActivity
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep class android.support.** {*;}
-keep interface android.support.** {*;}
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-dontwarn android.support.**
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-keep class com.google.android.material.** {*;}
-dontwarn androidx.**
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-keepclasseswithmembernames class * {
native <methods>;
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context,android.util.AttributeSet);
public <init>(android.content.Context,android.util.AttributeSet,int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keep public class * implements java.io.Serializable {*;}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class **.R$* {*;}
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * {
public <init>(org.json.JSONObject);
}
-keepattributes Signature
-keepattributes InnerClasses
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}
-dontwarn kotlin.**
-keep class kotlin.** { *; }
-keep interface kotlin.** { *; }
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclasseswithmembers @kotlin.Metadata class * { *; }
-keepclassmembers class **.WhenMappings {
<fields>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}
-keep class kotlinx.** { *; }
-keep interface kotlinx.** { *; }
-dontwarn kotlinx.**
-keep class org.jetbrains.** { *; }
-keep interface org.jetbrains.** { *; }
-dontwarn org.jetbrains.**
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
# ViewBinding
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
public static * inflate(android.view.LayoutInflater);
}
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}
-libraryjars libs/lib-decoder-ffmpeg-release.aar
-keep class com.localee.mireo.app.http.api.** {
@ -15,11 +185,6 @@
<fields>;
}
-keepclassmembernames class ** {
@com.localee.mireo.app.aop.Log <methods>;
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
@ -33,4 +198,65 @@
}
-keepclassmembers class * {
public void *(android.view.View);
}
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# 不要删除无用代码
-dontshrink
# 不混淆泛型
-keepattributes Signature
# 不混淆注解类
-keepattributes *Annotation*
# 不混淆本地方法
-keepclasseswithmembernames class * {
native <methods>;
}
# 不混淆 Activity XML 布局所设置的 onClick 属性值
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# 不混淆枚举类
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 不混淆 Parcelable 子类
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# 不混淆 Serializable 子类
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 不混淆 R 文件中的字段
-keepclassmembers class **.R$* {
public static <fields>;
}
# 不混淆 WebView 设置的 JS 接口的方法名
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}

View File

@ -14,17 +14,10 @@
# for DexGuard only
#-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
# Bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# AOP
-adaptclassstrings
-keepattributes InnerClasses, EnclosingMethod, Signature, *Annotation*
-keepnames @org.aspectj.lang.annotation.Aspect class * {
public <methods>;
}
# OkHttp3
-keepattributes Signature
@ -42,4 +35,184 @@
-keep class com.hjq.permissions.** {*;}
-keep class com.hjq.bar.** {*;}
-keep class com.hjq.toast.** {*;}
-keep class com.hjq.shape.** {*;}
-keep class com.hjq.shape.** {*;}
-keep class com.adjust.sdk.** { *; }
-keep class com.google.android.gms.common.ConnectionResult {
int SUCCESS;
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
java.lang.String getId();
boolean isLimitAdTrackingEnabled();
}
-keep public class com.android.installreferrer.** { *; }
-keep class com.wang.avi.** { *; }
-keep class com.wang.avi.indicators.** { *; }
-keep class com.bytedance.sdk.** { *; }
-keep public class com.google.android.gms.** { public protected *; }
-keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
-keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
-keep class android.support.v8.renderscript.** { *; }
-keep class androidx.renderscript.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.mbridge.** {*; }
-keep interface com.mbridge.** {*; }
-dontwarn com.mbridge.**
-keepclassmembers class **.R$* { public static final int mbridge*; }
-keep public class com.mbridge.* extends androidx.** { *; }
-keep public class androidx.viewpager.widget.PagerAdapter{*;}
-keep public class androidx.viewpager.widget.ViewPager.OnPageChangeListener{*;}
-keep interface androidx.annotation.IntDef{*;}
-keep interface androidx.annotation.Nullable{*;}
-keep interface androidx.annotation.CheckResult{*;}
-keep interface androidx.annotation.NonNull{*;}
-keep public class androidx.fragment.app.Fragment{*;}
-keep public class androidx.core.content.FileProvider{*;}
-keep public class androidx.core.app.NotificationCompat{*;}
-keep public class androidx.appcompat.widget.AppCompatImageView {*;}
-keep public class androidx.recyclerview.*{*;}
-keep class com.mbridge.msdk.foundation.tools.FastKV{*;}
-keep class com.mbridge.msdk.foundation.tools.FastKV$Builder{*;}
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#noinspection ShrinkerUnresolvedReference
#unity
-keep class com.google.android.gms.ads.** {public *;}
-keep class com.google.android.gms.appset.** { *; }
-keep class com.google.android.gms.tasks.** { *; }
#adapters
-keep class com.ironsource.adapters.** { *; }
#sdk
-dontwarn com.ironsource.**
-dontwarn com.ironsource.adapters.**
-keepclassmembers class com.ironsource.** { public *; }
-keep public class com.ironsource.**
-keep class com.ironsource.adapters.** { *;
}
#omid
-dontwarn com.iab.omid.**
-keep class com.iab.omid.** {*;}
#javascript
-keepattributes JavascriptInterface
-keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; }
#For AmazonAps integration
-keep class com.amazon.device.ads.DtbThreadService {
static *;
}
-keep public interface com.amazon.device.ads** {*; }
#For AppLovin integration
-keepclassmembers class com.applovin.sdk.AppLovinSdk {
static *;
}
-keep public interface com.applovin.sdk** {*; }
-keep public interface com.applovin.adview** {*; }
-keep public interface com.applovin.mediation** {*; }
-keep public interface com.applovin.communicator** {*; }
#For Bytedance integration
-keep public interface com.bytedance.sdk.openadsdk** {*; }
#For Facebook integration
-keepclassmembers class com.facebook.ads.internal.AdSdkVersion {
static *;
}
-keepclassmembers class com.facebook.ads.internal.settings.AdSdkVersion {
static *;
}
-keepclassmembers class com.facebook.ads.BuildConfig {
static *;
}
-keep public interface com.facebook.ads** {*; }
#For Fairbid
-keep public interface com.fyber.fairbid.ads.interstitial** {*; }
-keep public interface com.fyber.fairbid.ads.rewarded** {*; }
-keep class com.fyber.offerwall.*
#For Fivead
-keep public interface com.five_corp.ad** {*; }
#For Fyber(Inneractive) integration
-keep public interface com.fyber.inneractive.sdk.external** {*; }
-keep public interface com.fyber.inneractive.sdk.activities** {*; }
-keep public interface com.fyber.inneractive.sdk.ui** {*; }
#For HyprMX integration
-keepclassmembers class com.hyprmx.android.sdk.utility.HyprMXProperties {
static *;
}
-keepclassmembers class com.hyprmx.android.BuildConfig {
static *;
}
-keep public interface com.hyprmx.android.sdk.activity** {*; }
-keep public interface com.hyprmx.android.sdk.graphics** {*; }
# For Inmobi integration
-keep class com.inmobi.*
-keep public interface com.inmobi.ads.listeners** {*; }
-keep public interface com.inmobi.ads.InMobiInterstitial** {*; }
-keep public interface com.inmobi.ads.InMobiBanner** {*; }
# For ironSource integration
-keep public interface com.ironsource.mediationsdk.sdk** {*; }
-keep public interface com.ironsource.mediationsdk.impressionData.ImpressionDataListener {*; }
#For Maio integration
-keep public interface jp.maio.sdk.android.MaioAdsListenerInterface {*; }
# For Mintergral integration
-keep public interface com.mbridge.msdk.out** {*; }
-keep public interface com.mbridge.msdk.videocommon.listener** {*; }
-keep public interface com.mbridge.msdk.interstitialvideo.out** {*; }
-keep public interface com.mintegral.msdk.out** {*; }
-keep public interface com.mintegral.msdk.videocommon.listener** {*; }
-keep public interface com.mintegral.msdk.interstitialvideo.out** {*; }
#For MyTarget integration
-keep class com.my.target.** {*;}
#For Ogury integration
-keep public interface io.presage.interstitial** {*; }
-keep public interface io.presage.interstitial.PresageInterstitialCallback {*; }
#For Pubnative integration
-keep public interface net.pubnative.lite.sdk.interstitial.HyBidInterstitialAd** {*; }
-keep public interface net.pubnative.lite.sdk.rewarded.HyBidRewardedAd** {*; }
-keep public interface net.pubnative.lite.sdk.views.HyBidAdView** {*; }
#For Smaato integration
-keep public interface com.smaato.sdk.interstitial** {*; }
-keep public interface com.smaato.sdk.video.vast** {*; }
-keep public interface com.smaato.sdk.banner.widget** {*; }
-keep public interface com.smaato.sdk.core.util** {*; }
# For Tapjoy integration
-keep public interface com.tapjoy.** {*; }
# For Tencent integration
-keep public interface com.qq.e.ads.interstitial2** {*; }
-keep public interface com.qq.e.ads.interstitial3** {*; }
-keep public interface com.qq.e.ads.rewardvideo** {*; }
-keep public interface com.qq.e.ads.rewardvideo2** {*; }
-keep public interface com.qq.e.ads.banner2** {*; }
-keep public interface com.qq.e.comm.adevent** {*; }
#For Verizon integration
-keepclassmembers class com.verizon.ads.edition.BuildConfig {
static *;
}
-keep public interface com.verizon.ads.interstitialplacement** {*; }
-keep public interface com.verizon.ads.inlineplacement** {*; }
-keep public interface com.verizon.ads.vastcontroller** {*; }
-keep public interface com.verizon.ads.webcontroller** {*; }
#For Vungle integration
-keep public interface com.vungle.warren.PlayAdCallback {*; }
-keep public interface com.vungle.warren.ui.contract** {*; }
-keep public interface com.vungle.warren.ui.view** {*; }
#For AndroidX
-keep class androidx.localbroadcastmanager.content.LocalBroadcastManager { *;}
-keep class androidx.recyclerview.widget.RecyclerView { *;}
-keep class androidx.recyclerview.widget.RecyclerView$OnScrollListener { *;}
#For Android
-keep class * extends android.app.Activity

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.localee.mireo.app">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@ -14,15 +13,10 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".app.AppApplication"
android:name="com.localee.mireo.app.app.AppApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_app_logo"
android:label="${app_name}"
@ -31,15 +25,20 @@
android:resizeableActivity="true"
android:roundIcon="@mipmap/ic_app_logo"
android:supportsRtl="false"
android:theme="@style/AppTheme"
android:theme="@style/Theme.Example"
android:usesCleartextTraffic="true"
tools:ignore="AllowBackup,LockedOrientationActivity"
tools:replace="android:allowBackup,android:supportsRtl"
tools:targetApi="31">
tools:replace="android:allowBackup,android:supportsRtl">
<meta-data
android:name="ScopedStorage"
android:value="true" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
<meta-data
android:name="com.facebook.sdk.ClientToken"
android:value="@string/facebook_client_token" />
<provider
android:name="androidx.core.content.FileProvider"
@ -53,12 +52,9 @@
</provider>
<activity
android:name=".ui.activity.SplashActivity"
android:name="com.localee.mireo.app.ui.activity.SplashActivity"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.Splash"
>
android:theme="@style/Theme.Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -66,45 +62,109 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mireoapp" />
</intent-filter>
</activity>
<activity
android:name=".ui.activity.HomeActivity"
android:name="com.localee.mireo.app.ui.activity.HomeActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".ui.activity.SettingActivity"
android:name="com.localee.mireo.app.ui.activity.SettingActivity"
android:label="@string/setting_title"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.AboutActivity"
android:name="com.localee.mireo.app.ui.activity.AboutActivity"
android:label="@string/about_title"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.BrowserActivity"
android:name="com.localee.mireo.app.ui.activity.BrowserActivity"
android:label="@string/web_title"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.VideoPlayActivity"
android:launchMode="singleTop"
android:theme="@style/FullScreenTheme" />
android:name="com.localee.mireo.app.ui.activity.VideoPlayActivity"
android:launchMode="singleTask" />
<activity
android:name=".ui.activity.SearchActivity"
android:name="com.localee.mireo.app.ui.activity.SearchActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name="com.localee.mireo.app.ui.activity.FeedBackActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="com.localee.mireo.app.ui.activity.FeedBackListActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="com.localee.mireo.app.ui.activity.FeedBackDetailsActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize" />
<activity android:name="com.localee.mireo.app.ui.activity.LanguageSwitchActivity" />
<activity android:name="com.localee.mireo.app.ui.activity.MyWalletActivity" />
<activity android:name="com.localee.mireo.app.ui.activity.MyVipActivity" />
<activity android:name="com.localee.mireo.app.ui.activity.StoreActivity" />
<activity android:name="com.localee.mireo.app.ui.activity.AccountDeleteActivity" />
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_app_logo" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/black" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
<service
android:name="com.localee.mireo.app.other.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
<queries>

View File

@ -7,7 +7,9 @@ import androidx.annotation.DrawableRes
import androidx.annotation.RawRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.localee.mireo.app.R
import com.hjq.toast.ToastUtils
import com.localee.mireo.app.app.AppApplication
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.widget.StatusLayout
import com.localee.mireo.app.widget.StatusLayout.OnRetryListener
@ -39,27 +41,37 @@ interface StatusAction {
fun showEmpty() {
// showLayout(R.drawable.status_empty_ic, R.string.status_layout_no_data, null)
showLayout(R.mipmap.status_nothing, R.string.status_layout_no_data, null)
}
fun showError(listener: OnRetryListener?) {
getStatusLayout()?.let {
val manager: ConnectivityManager? = ContextCompat.getSystemService(it.context, ConnectivityManager::class.java)
val manager: ConnectivityManager? =
ContextCompat.getSystemService(it.context, ConnectivityManager::class.java)
if (manager != null) {
val info: NetworkInfo? = manager.activeNetworkInfo
if (info == null || !info.isConnected) {
// showLayout(R.drawable.status_network_ic, R.string.status_layout_error_network, listener)
ToastUtils.show(AppApplication.instance.getString(R.string.network_abnormality))
showLayout(R.mipmap.ic_network, R.string.status_layout_error_network, listener)
return
}
}
// showLayout(R.drawable.status_error_ic, R.string.status_layout_error_request, listener)
showLayout(R.mipmap.status_nothing, R.string.status_layout_no_data, null)
}
}
fun showLayout(@DrawableRes drawableId: Int, @StringRes stringId: Int, listener: OnRetryListener?) {
fun showLayout(
@DrawableRes drawableId: Int,
@StringRes stringId: Int,
listener: OnRetryListener?
) {
getStatusLayout()?.let {
showLayout(ContextCompat.getDrawable(it.context, drawableId), it.context.getString(stringId), listener)
showLayout(
ContextCompat.getDrawable(it.context, drawableId),
it.context.getString(stringId),
listener
)
}
}

View File

@ -1,7 +0,0 @@
package com.localee.mireo.app.aop
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER)
annotation class CheckNet

View File

@ -1,36 +0,0 @@
package com.localee.mireo.app.aop
import android.app.*
import android.net.ConnectivityManager
import android.net.NetworkInfo
import androidx.core.content.ContextCompat
import com.localee.mireo.app.R
import com.localee.mireo.app.manager.ActivityManager
import com.hjq.toast.ToastUtils
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
@Suppress("unused")
@Aspect
class CheckNetAspect {
@Pointcut("execution(@com.localee.mireo.app.aop.CheckNet * *(..))")
fun method() {}
@Around("method() && @annotation(checkNet)")
@Throws(Throwable::class)
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint, checkNet: CheckNet) {
val application: Application = ActivityManager.getInstance().getApplication()
val manager: ConnectivityManager? = ContextCompat.getSystemService(application, ConnectivityManager::class.java)
if (manager != null) {
val info: NetworkInfo? = manager.activeNetworkInfo
if (info == null || !info.isConnected) {
ToastUtils.show(R.string.common_network_hint)
return
}
}
joinPoint.proceed()
}
}

View File

@ -1,8 +0,0 @@
package com.localee.mireo.app.aop
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.CONSTRUCTOR)
annotation class Log constructor(val value: String = "AppLog")

View File

@ -1,96 +0,0 @@
package com.localee.mireo.app.aop
import android.os.Looper
import android.os.Trace
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.Signature
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.CodeSignature
import org.aspectj.lang.reflect.MethodSignature
import timber.log.Timber
import java.util.concurrent.TimeUnit
@Suppress("unused")
@Aspect
class LogAspect {
@Pointcut("execution(@com.localee.mireo.app.aop.Log *.new(..))")
fun constructor() {}
@Pointcut("execution(@com.localee.mireo.app.aop.Log * *(..))")
fun method() {}
@Around("(method() || constructor()) && @annotation(log)")
@Throws(Throwable::class)
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint, log: Log): Any? {
enterMethod(joinPoint, log)
val startNanos: Long = System.nanoTime()
val result: Any? = joinPoint.proceed()
val stopNanos: Long = System.nanoTime()
exitMethod(joinPoint, log, result, TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos))
return result
}
private fun enterMethod(joinPoint: ProceedingJoinPoint, log: Log) {
val codeSignature: CodeSignature = joinPoint.signature as CodeSignature
val className: String = codeSignature.declaringType.name
val methodName: String = codeSignature.name
val parameterNames: Array<String?> = codeSignature.parameterNames
val parameterValues: Array<Any?> = joinPoint.args
val builder: StringBuilder =
getMethodLogInfo(className, methodName, parameterNames, parameterValues)
log(log.value, builder.toString())
val section: String = builder.substring(2)
Trace.beginSection(section)
}
private fun getMethodLogInfo(className: String, methodName: String, parameterNames: Array<String?>, parameterValues: Array<Any?>): StringBuilder {
val builder: StringBuilder = StringBuilder("\u21E2 ")
builder.append(className)
.append(".")
.append(methodName)
.append('(')
for (i in parameterValues.indices) {
if (i > 0) {
builder.append(", ")
}
builder.append(parameterNames[i]).append('=')
builder.append(parameterValues[i].toString())
}
builder.append(')')
if (Looper.myLooper() != Looper.getMainLooper()) {
builder.append(" [Thread:\"").append(Thread.currentThread().name).append("\"]")
}
return builder
}
private fun exitMethod(joinPoint: ProceedingJoinPoint, log: Log, result: Any?, lengthMillis: Long) {
Trace.endSection()
val signature: Signature = joinPoint.signature
val className: String? = signature.declaringType.name
val methodName: String? = signature.name
val builder: StringBuilder = StringBuilder("\u21E0 ")
.append(className)
.append(".")
.append(methodName)
.append(" [")
.append(lengthMillis)
.append("ms]")
// 判断方法是否有返回值
if (signature is MethodSignature && signature.returnType != Void.TYPE) {
builder.append(" = ")
builder.append(result.toString())
}
log(log.value, builder.toString())
}
private fun log(tag: String?, msg: String?) {
Timber.tag(tag)
Timber.d(msg)
}
}

View File

@ -1,9 +0,0 @@
package com.localee.mireo.app.aop
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER)
annotation class Permissions constructor(
vararg val value: String
)

View File

@ -1,62 +0,0 @@
package com.localee.mireo.app.aop
import android.app.Activity
import com.hjq.permissions.XXPermissions
import com.localee.mireo.app.manager.ActivityManager
import com.localee.mireo.app.other.PermissionCallback
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import timber.log.Timber
@Suppress("unused")
@Aspect
class PermissionsAspect {
@Pointcut("execution(@com.localee.mireo.app.aop.Permissions * *(..))")
fun method() {
}
@Around("method() && @annotation(permissions)")
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint, permissions: Permissions) {
var activity: Activity? = null
val parameterValues: Array<Any?> = joinPoint.args
for (arg: Any? in parameterValues) {
if (arg !is Activity) {
continue
}
activity = arg
break
}
if ((activity == null) || activity.isFinishing || activity.isDestroyed) {
activity = ActivityManager.getInstance().getTopActivity()
}
if ((activity == null) || activity.isFinishing || activity.isDestroyed) {
Timber.e("The activity has been destroyed and permission requests cannot be made")
return
}
requestPermissions(joinPoint, activity, permissions.value)
}
private fun requestPermissions(
joinPoint: ProceedingJoinPoint,
activity: Activity,
permissions: Array<out String>
) {
XXPermissions.with(activity)
.permission(*permissions)
.request(object : PermissionCallback() {
override fun onGranted(permissions: MutableList<String?>?, all: Boolean) {
if (all) {
try {
// 获得权限,执行原方法
joinPoint.proceed()
} catch (e: Throwable) {
// CrashReport.postCatchedException(e)
}
}
}
})
}
}

View File

@ -1,9 +0,0 @@
package com.localee.mireo.app.aop
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER)
annotation class SingleClick constructor(
val value: Long = 1000
)

View File

@ -1,51 +0,0 @@
package com.localee.mireo.app.aop
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.CodeSignature
import timber.log.Timber
@Suppress("unused")
@Aspect
class SingleClickAspect {
private var lastTime: Long = 0
private var lastTag: String? = null
@Pointcut("execution(@com.localee.mireo.app.aop.SingleClick * *(..))")
fun method() {}
@Around("method() && @annotation(singleClick)")
@Throws(Throwable::class)
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint, singleClick: SingleClick) {
val codeSignature: CodeSignature = joinPoint.signature as CodeSignature
val className: String = codeSignature.declaringType.name
val methodName: String = codeSignature.name
val builder: StringBuilder = StringBuilder("$className.$methodName")
builder.append("(")
val parameterValues: Array<Any?> = joinPoint.args
for (i in parameterValues.indices) {
val arg: Any? = parameterValues[i]
if (i == 0) {
builder.append(arg)
} else {
builder.append(", ")
.append(arg)
}
}
builder.append(")")
val tag: String = builder.toString()
val currentTimeMillis: Long = System.currentTimeMillis()
if (currentTimeMillis - lastTime < singleClick.value && (tag == lastTag)) {
Timber.tag("SingleClick")
Timber.i("Within %s milliseconds, there was a rapid click%s", singleClick.value, tag)
return
}
lastTime = currentTimeMillis
lastTag = tag
joinPoint.proceed()
}
}

View File

@ -6,9 +6,9 @@ import android.view.View
import androidx.annotation.StringRes
import com.gyf.immersionbar.ImmersionBar
import com.hjq.bar.TitleBar
import com.hjq.base.BaseActivity
import com.hjq.base.BaseDialog
import com.localee.mireo.app.R
import com.localee.mireo.app.base.BaseActivity
import com.localee.mireo.app.base.BaseDialog
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.action.TitleBarAction
import com.localee.mireo.app.action.ToastAction
import com.localee.mireo.app.http.model.HttpData

View File

@ -4,7 +4,7 @@ import android.content.Context
import android.view.View
import androidx.annotation.IntRange
import androidx.annotation.LayoutRes
import com.hjq.base.BaseAdapter
import com.localee.mireo.app.base.BaseAdapter
import java.util.*
abstract class AppAdapter<T> constructor(context: Context) :

View File

@ -6,9 +6,22 @@ import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.adjust.sdk.Adjust
import com.adjust.sdk.AdjustConfig
import com.adjust.sdk.LogLevel
import com.adjust.sdk.OnEventTrackingFailedListener
import com.adjust.sdk.OnEventTrackingSucceededListener
import com.facebook.FacebookSdk
import com.facebook.FacebookSdk.fullyInitialize
import com.facebook.FacebookSdk.setAutoInitEnabled
import com.facebook.LoggingBehavior
import com.facebook.appevents.AppEventsLogger
import com.facebook.applinks.AppLinkData
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonToken
import com.hjq.bar.TitleBar
@ -19,49 +32,143 @@ import com.hjq.http.model.HttpHeaders
import com.hjq.http.model.HttpParams
import com.hjq.http.request.HttpRequest
import com.hjq.toast.ToastUtils
import com.localee.mireo.app.R
import com.localee.mireo.app.aop.Log
import com.localee.mireo.app.http.exception.HttpBodyInterceptor
import com.localee.mireo.app.http.model.RequestHandler
import com.localee.mireo.app.http.model.RequestServer
import com.localee.mireo.app.manager.ActivityManager
import com.localee.mireo.app.other.AppConfig
import com.localee.mireo.app.other.AppConfig.getPackageName
import com.localee.mireo.app.other.CrashHandler
import com.localee.mireo.app.other.DebugLoggerTree
import com.localee.mireo.app.other.MaterialHeader
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.other.SmartBallPulseFooter
import com.localee.mireo.app.other.TitleBarStyle
import com.localee.mireo.app.other.ToastStyle
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.MsSystemUtlis
import com.localee.mireo.shortapp.R
import com.lxj.xpopup.XPopup
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import timber.log.Timber
import org.greenrobot.eventbus.EventBus
class AppApplication : Application() {
private val LOG_TAG: String = "AppApplication"
@Log("启动耗时")
override fun onCreate() {
super.onCreate()
instance = this;
// Initialize Adjust SDK
initAdjust()
// Register lifecycle callbacks
registerActivityLifecycleCallbacks(AdjustLifecycleCallbacks())
initSdk(this)
GlobalScope.launch(Dispatchers.Main) {
// Initialize Facebook SDK
initFacebookSdk()
}
}
override fun onLowMemory() {
super.onLowMemory()
private fun initFacebookSdk() {
setAutoInitEnabled(true)
fullyInitialize()
if (AppConfig.isDebug()) {
FacebookSdk.setIsDebugEnabled(true)
FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS)
}
AppEventsLogger.activateApp(this)
AppLinkData.fetchDeferredAppLinkData(
this
) {
// Process app link data
if (null != it) {
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_DDL_Url, it.targetUri.toString())
Log.d(
"initFacebookSdk",
"fetchDeferredAppLinkData callback called!====${it.targetUri}"
)
}
Log.d("initFacebookSdk", "fetchDeferredAppLinkData callback called!")
}
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
private fun initAdjust() {
val appToken = "rs3mgqcl1wjk"
val environment = AdjustConfig.ENVIRONMENT_PRODUCTION
val config = AdjustConfig(instance, appToken, environment)
config.setLogLevel(LogLevel.VERBOSE)
config.onEventTrackingSucceededListener =
OnEventTrackingSucceededListener { adjustEventSuccess ->
Log.d(LOG_TAG, "Event recorded at " + adjustEventSuccess.timestamp)
}
config.onEventTrackingFailedListener =
OnEventTrackingFailedListener { adjustEventFailure ->
Log.v(
LOG_TAG,
"Event recording failed. Response: " + adjustEventFailure.message
)
}
config.setOnDeferredDeeplinkResponseListener { deeplink ->
Log.d(LOG_TAG, "Deferred deep link callback called!")
Log.d(LOG_TAG, "Deep link URL: $deeplink")
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_DDL_Url, deeplink.toString())
true
}
Adjust.initSdk(config)
}
inner class AdjustLifecycleCallbacks : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
countActivity++
if (countActivity == 1 && isAppInBackground) {
isAppInBackground = false
Log.d("Lifecycle", "onActivityStarted")
EventBus.getDefault().post(MsConstants.CONSTANTS_enterTheApp)
}
}
override fun onActivityResumed(activity: Activity) {
Adjust.onResume()
}
override fun onActivityPaused(activity: Activity) {
Adjust.onPause()
}
override fun onActivityStopped(activity: Activity) {
countActivity--
if (countActivity <= 0 && !isAppInBackground) {
isAppInBackground = true
Log.d("Lifecycle", "onActivityStopped")
EventBus.getDefault().post(MsConstants.CONSTANTS_leaveApp)
// EventBus.getDefault().post(ExampleAppConstants.Constants_interrupt_ad)
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
}
companion object {
lateinit var instance: Application;
var isCurrentPage: Boolean = true
var isAppInBackground = true
var countActivity = 0
fun initSdk(application: Application) {
TitleBar.setDefaultStyle(TitleBarStyle())
SmartRefreshLayout.setDefaultRefreshHeaderCreator { context: Context, layout: RefreshLayout ->
@ -89,6 +196,7 @@ class AppApplication : Application() {
CrashHandler.register(application)
ActivityManager.getInstance().init(application)
XPopup.setPrimaryColor(ContextCompat.getColor(instance, R.color.mireo_color_2b292a))
MMKV.initialize(application)
@ -122,11 +230,21 @@ class AppApplication : Application() {
)
headers.put("system-type", "android")
headers.put(
"app_version",
"app-version",
MsSystemUtlis.getVerNameInfo(instance)
)
// headers.put("security","true")
headers.put(
"app-name",
getPackageName()
)
headers.put(
"model",
Build.MODEL
)
headers.put(
"brand",
Build.BRAND
)
}
})
@ -136,10 +254,6 @@ class AppApplication : Application() {
GsonFactory.setJsonCallback { typeToken: TypeToken<*>, fieldName: String?, jsonToken: JsonToken ->
}
if (AppConfig.isLogEnable()) {
Timber.plant(DebugLoggerTree())
}
val connectivityManager: ConnectivityManager? =
ContextCompat.getSystemService(application, ConnectivityManager::class.java)
if (connectivityManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

View File

@ -1,11 +1,12 @@
package com.localee.mireo.app.app
import com.hjq.base.BaseFragment
import com.localee.mireo.app.base.BaseFragment
import com.localee.mireo.app.action.ToastAction
import com.localee.mireo.app.http.model.HttpData
import com.hjq.http.config.IRequestApi
import com.hjq.http.listener.OnHttpListener
import com.hjq.toast.ToastUtils
import com.localee.mireo.app.other.MsConstants
abstract class AppFragment<A : AppActivity> : BaseFragment<A>(),
ToastAction, OnHttpListener<Any> {
@ -37,7 +38,9 @@ abstract class AppFragment<A : AppActivity> : BaseFragment<A>(),
}
override fun onHttpFail(throwable: Throwable) {
ToastUtils.show(throwable.message)
if (!MsConstants.IsFirst){
ToastUtils.show(throwable.message)
}
}
override fun onHttpEnd(api: IRequestApi) {

View File

@ -4,7 +4,7 @@ import android.os.Bundle
import android.view.*
import com.gyf.immersionbar.ImmersionBar
import com.hjq.bar.TitleBar
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.action.TitleBarAction

View File

@ -1,4 +1,4 @@
package com.hjq.base
package com.localee.mireo.app.base
import android.app.Activity
import android.content.Context
@ -11,8 +11,12 @@ import android.view.Window
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import com.hjq.base.action.*
import java.util.*
import com.localee.mireo.app.base.action.ActivityAction
import com.localee.mireo.app.base.action.BundleAction
import com.localee.mireo.app.base.action.ClickAction
import com.localee.mireo.app.base.action.HandlerAction
import com.localee.mireo.app.base.action.KeyboardAction
import java.util.Random
import kotlin.math.pow
abstract class BaseActivity : AppCompatActivity(), ActivityAction,
@ -86,7 +90,7 @@ abstract class BaseActivity : AppCompatActivity(), ActivityAction,
return super<AppCompatActivity>.startActivity(intent)
}
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val fragments: MutableList<Fragment?> = supportFragmentManager.fragments
for (fragment: Fragment? in fragments) {
if (fragment !is BaseFragment<*> || fragment.lifecycle.currentState != Lifecycle.State.RESUMED) {
@ -117,7 +121,11 @@ abstract class BaseActivity : AppCompatActivity(), ActivityAction,
}
@Suppress("deprecation")
open fun startActivityForResult(intent: Intent, options: Bundle?, callback: OnActivityCallback?) {
open fun startActivityForResult(
intent: Intent,
options: Bundle?,
callback: OnActivityCallback?
) {
val requestCode: Int = Random().nextInt(2.0.pow(16.0).toInt())
activityCallbacks.put(requestCode, callback)
startActivityForResult(intent, requestCode, options)

View File

@ -1,4 +1,4 @@
package com.hjq.base
package com.localee.mireo.app.base
import android.content.Context
import android.util.SparseArray
@ -9,7 +9,7 @@ import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hjq.base.action.ResourcesAction
import com.localee.mireo.app.base.action.ResourcesAction
@Suppress("LeakingThis")
abstract class BaseAdapter<VH : BaseAdapter<VH>.BaseViewHolder>(private val context: Context) :

View File

@ -1,4 +1,4 @@
package com.hjq.base
package com.localee.mireo.app.base
import android.app.Activity
import android.app.Application.ActivityLifecycleCallbacks
@ -29,15 +29,14 @@ import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatDialog
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.hjq.base.action.ActivityAction
import com.hjq.base.action.AnimAction
import com.hjq.base.action.ClickAction
import com.hjq.base.action.HandlerAction
import com.hjq.base.action.KeyboardAction
import com.hjq.base.action.ResourcesAction
import com.localee.mireo.app.base.action.ActivityAction
import com.localee.mireo.app.base.action.AnimAction
import com.localee.mireo.app.base.action.ClickAction
import com.localee.mireo.app.base.action.HandlerAction
import com.localee.mireo.app.base.action.KeyboardAction
import com.localee.mireo.app.base.action.ResourcesAction
import com.localee.mireo.shortapp.R
import java.lang.ref.SoftReference
@Suppress("LeakingThis")
@ -49,8 +48,10 @@ open class BaseDialog constructor(
HandlerAction, ClickAction, AnimAction, KeyboardAction, DialogInterface.OnShowListener,
DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
private val listeners: ListenersWrapper<BaseDialog> = ListenersWrapper(this)
override val lifecycle: LifecycleRegistry = LifecycleRegistry(this)
// override val lifecycle: LifecycleRegistry = LifecycleRegistry(this)
private var showListeners: MutableList<OnShowListener?>? = null
private var cancelListeners: MutableList<OnCancelListener?>? = null
private var dismissListeners: MutableList<OnDismissListener?>? = null
@ -117,6 +118,7 @@ open class BaseDialog constructor(
window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
}
open fun setBackgroundDimAmount(@FloatRange(from = 0.0, to = 1.0) dimAmount: Float) {
window?.setDimAmount(dimAmount)
}
@ -226,7 +228,7 @@ open class BaseDialog constructor(
* [DialogInterface.OnShowListener]
*/
override fun onShow(dialog: DialogInterface?) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
showListeners?.let {
for (i in it.indices) {
it[i]?.onShow(this)
@ -249,7 +251,7 @@ open class BaseDialog constructor(
* [DialogInterface.OnDismissListener]
*/
override fun onDismiss(dialog: DialogInterface?) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
dismissListeners?.let {
for (i in it.indices) {
it[i]?.onDismiss(this)
@ -259,17 +261,17 @@ open class BaseDialog constructor(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
override fun onStart() {
super.onStart()
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
override fun onStop() {
super.onStop()
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
@Suppress("UNCHECKED_CAST")

View File

@ -1,4 +1,4 @@
package com.hjq.base
package com.localee.mireo.app.base
import android.app.Activity
import android.app.Application
@ -12,11 +12,11 @@ import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import com.hjq.base.BaseActivity.OnActivityCallback
import com.hjq.base.action.BundleAction
import com.hjq.base.action.ClickAction
import com.hjq.base.action.HandlerAction
import com.hjq.base.action.KeyboardAction
import com.localee.mireo.app.base.BaseActivity.OnActivityCallback
import com.localee.mireo.app.base.action.BundleAction
import com.localee.mireo.app.base.action.ClickAction
import com.localee.mireo.app.base.action.HandlerAction
import com.localee.mireo.app.base.action.KeyboardAction
abstract class BaseFragment<A : BaseActivity> : Fragment(),
HandlerAction, ClickAction, BundleAction, KeyboardAction {

View File

@ -1,4 +1,4 @@
package com.hjq.base
package com.localee.mireo.app.base
import android.view.ViewGroup
import androidx.fragment.app.Fragment

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.app.Activity
import android.content.Context

View File

@ -1,6 +1,7 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import com.localee.mireo.shortapp.R
import com.hjq.base.R
interface AnimAction {

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.os.Bundle
import android.os.Parcelable

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.view.View
import androidx.annotation.IdRes

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.os.Handler
import android.os.Looper

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.content.Context
import android.view.View

View File

@ -1,4 +1,4 @@
package com.hjq.base.action
package com.localee.mireo.app.base.action
import android.content.Context
import android.content.res.Resources

View File

@ -0,0 +1,14 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class ActionPushApi : IRequestApi {
override fun getApi(): String {
return "action/push"
}
var action: String? = null
}

View File

@ -0,0 +1,11 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class ActiveAfterWatchingVideoApi : IRequestApi {
override fun getApi(): String {
return "activeAfterWatchingVideo"
}
}

View File

@ -0,0 +1,15 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class CreateOrderApi : IRequestApi {
override fun getApi(): String {
return "createOrder"
}
data class Bean(
val order_code: String
)
}

View File

@ -0,0 +1,40 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class CustomerBuyRecordsApi : IRequestApi {
override fun getApi(): String {
return "getCustomerBuyRecords"
}
var current_page: Int? = null
var page_size: Int? = null
class Bean (
val list: List<Data>,
val pagination: Pagination
) {
data class Data(
val short_play_id: Int,
val coins: String,
var short_play_video_id: Int,
val coin_type: Int,
val created_at: String,
val episode: Int,
val image_url: String,
var name: String,
)
data class Pagination(
val current_page: Int,
val page_size: Int,
val page_total: Int,
val total_size: Int
)
}
}

View File

@ -0,0 +1,37 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class CustomerOrderApi : IRequestApi {
override fun getApi(): String {
return "getCustomerOrder"
}
var buy_type: String? = null
var current_page: Int? = null
var page_size: Int? = null
class Bean (
val list: List<Data>,
val pagination: Pagination
) {
data class Data(
val created_at: String,
var type: String,
var value: String
)
data class Pagination(
val current_page: Int,
val page_size: Int,
val page_total: Int,
val total_size: Int
)
}
}

View File

@ -0,0 +1,38 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
import java.io.Serializable
class DetailsRecommandApi : IRequestApi {
override fun getApi(): String {
return "getDetailsRecommand"
}
data class Bean(
val brief: String,
val description: String,
val list: List<Item>,
val tag: String,
val title: String
) {
data class Item(
val all_coins: Int,
val buy_type: Int,
val collect_total: Int,
val description: String,
val episode_total: Int,
val horizontally_img: String,
val video_url: String,
val id: Int,
val image_url: String,
val name: String,
val process: Int,
val short_id: Int,
val short_play_id: Int?,
val tag_type: String,
val watch_total: Int
)
}
}

View File

@ -0,0 +1,22 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class DoBuyVideoApi : IRequestApi {
override fun getApi(): String {
return "buy_video"
}
var short_play_id: Int? = null
var video_id: Int? = null
data class Bean(
val status: String,
val coin_left_total: Int,
val send_coin_left_total: Int
)
}

View File

@ -0,0 +1,24 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
import com.localee.mireo.app.http.bean.LoginBean
class DoLoginApi : IRequestApi {
override fun getApi(): String {
return "customer/login"
}
/** 登录密码 */
private var loginBean: LoginBean? = null
fun setLoginBean(loginBean: LoginBean?): DoLoginApi = apply {
this.loginBean = loginBean
}
data class Bean(
val customer_id: String,
val token: String
)
}

View File

@ -0,0 +1,16 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class DoLogoffApi : IRequestApi {
override fun getApi(): String {
return "customer/logoff"
}
data class Bean(
val customer_id: String,
val token: String
)
}

View File

@ -0,0 +1,16 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class DoLogoutApi : IRequestApi {
override fun getApi(): String {
return "customer/signout"
}
data class Bean(
val customer_id: String,
val token: String
)
}

View File

@ -0,0 +1,11 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class EnterTheAppApi : IRequestApi {
override fun getApi(): String {
return "customer/enterTheApp"
}
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class FirebaseTokenApi : IRequestApi {
override fun getApi(): String {
return "customer/firebaseToken"
}
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class GooglePaidApi : IRequestApi {
override fun getApi(): String {
return "googlePaid"
}
}

View File

@ -1,7 +1,7 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
import java.io.Serializable
import com.localee.mireo.app.http.bean.RecommendBean
class HomeDayMaxRechargeShortPlayRankApi : IRequestApi {

View File

@ -1,6 +1,7 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
import com.localee.mireo.app.http.bean.RecommendBean
import java.io.Serializable
@ -18,6 +19,7 @@ class HomeModuleApi : IRequestApi {
var newTopThree: List<RecommendBean>? = null
var highestPayment: List<RecommendBean>? = null
var hottestPist: List<RecommendBean>? = null
var nineSquare: RecommandDataBean? = null
}

View File

@ -1,7 +1,7 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
import java.io.Serializable
import com.localee.mireo.app.http.bean.RecommendBean
class HomeVideoListApi : IRequestApi {

View File

@ -0,0 +1,26 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class LanguageApi : IRequestApi {
override fun getApi(): String {
return "languges"
}
class Bean(
val list: List<Data>,
) {
data class Data(
val cn_name: String,
val show_name: String,
var id: Int,
val lang_key: String,
val description: String,
val is_default: Int
)
}
}

View File

@ -0,0 +1,14 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class LeaveAppApi : IRequestApi {
override fun getApi(): String {
return "customer/leaveApp"
}
var action: String? = null
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class MessageSendReportApi : IRequestApi {
override fun getApi(): String {
return "message/sendReport"
}
}

View File

@ -0,0 +1,16 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class NoticeNumApi : IRequestApi {
override fun getApi(): String {
return "noticeNum"
}
class Bean(
val feedback_notice_num: Int
)
}

View File

@ -0,0 +1,14 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class OnLineApi : IRequestApi {
override fun getApi(): String {
return "customer/onLine"
}
var action: String? = null
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class OpenNotifyApi : IRequestApi {
override fun getApi(): String {
return "openNotify"
}
}

View File

@ -0,0 +1,15 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class PaySettingsApi : IRequestApi {
override fun getApi(): String {
return "paySettingsV3"
}
var short_play_id: Int? = null
var short_play_video_id: Int? = null
}

View File

@ -0,0 +1,39 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class SendCoinListApi : IRequestApi {
override fun getApi(): String {
return "sendCoinList"
}
var current_page: Int? = null
var page_size: Int? = null
class Bean (
val list: List<Data>,
val pagination: Pagination
) {
data class Data(
val id: Int,
val created_at: String,
var type: String,
val left_coins: String,
val expired_time: String,
val is_effective: Int,
val coins: String,
var diff_datetime: String,
)
data class Pagination(
val current_page: Int,
val page_size: Int,
val page_total: Int,
val total_size: Int
)
}
}

View File

@ -0,0 +1,12 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class TranslatesLanguageApi : IRequestApi {
override fun getApi(): String {
return "translates"
}
var language_key : String? = null
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class UploadHistorySecondsApi : IRequestApi {
override fun getApi(): String {
return "uploadHistorySeconds"
}
}

View File

@ -19,7 +19,7 @@ class UserInfoRes (
val registered_days: Int = 0,
val send_coin_left_total: Int = 0,
val third_access_platform: String = "",
val vip_end_time: Int = 0,
val vip_end_time: String = "",
val vip_type: String = ""
) {
companion object {

View File

@ -3,7 +3,7 @@ package com.localee.mireo.app.http.api
import android.os.Parcel
import android.os.Parcelable
import com.hjq.http.config.IRequestApi
import java.io.Serializable
import java.util.ArrayList
class VideoDetailsApi : IRequestApi {
@ -16,6 +16,9 @@ class VideoDetailsApi : IRequestApi {
var video_id: Int? = null
var activity_id: Int? = null
data class Bean(
val episodeList: List<Episode>,
@ -100,7 +103,8 @@ class VideoDetailsApi : IRequestApi {
val name: String,
val process: Int,
val short_id: Int,
val watch_total: Int
val watch_total: Int,
val category: ArrayList<String>?
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
@ -114,7 +118,8 @@ class VideoDetailsApi : IRequestApi {
parcel.readString().toString(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt()
parcel.readInt(),
parcel.createStringArrayList()
) {
}
@ -131,6 +136,7 @@ class VideoDetailsApi : IRequestApi {
parcel.writeInt(process)
parcel.writeInt(short_id)
parcel.writeInt(watch_total)
parcel.writeStringList(category)
}
override fun describeContents(): Int {
@ -146,6 +152,7 @@ class VideoDetailsApi : IRequestApi {
return arrayOfNulls(size)
}
}
}
data class VideoInfo(

View File

@ -0,0 +1,12 @@
package com.localee.mireo.app.http.api
import com.hjq.http.config.IRequestApi
class W2aApi : IRequestApi {
override fun getApi(): String {
return "w2aSelfAttribution"
}
}

View File

@ -0,0 +1,8 @@
package com.localee.mireo.app.http.bean
class CreateOrderReqBean (
val pay_setting_id: String,
val payment_channel: String,
val short_play_id: Int,
val video_id: Int
)

View File

@ -1,4 +1,4 @@
package com.localee.mireo.app.http.api
package com.localee.mireo.app.http.bean
class HistoryBean (
val list: List<Data>,
@ -15,7 +15,7 @@ class HistoryBean (
val current_episode: String,
var is_collect: Boolean,
var is_check: Boolean,
val categoryList: List<categoryBean>
val category: List<String>
)
data class Pagination(

View File

@ -0,0 +1,8 @@
package com.localee.mireo.app.http.bean
class HomeDataHistoryBean (
val video_name: String,
val video_id: Int,
val video_last: String,
val video_img: String
)

View File

@ -0,0 +1,3 @@
package com.localee.mireo.app.http.bean
class IncidentBean(val incident: String?, val res: String?, val time: Long)

View File

@ -0,0 +1,9 @@
package com.localee.mireo.app.http.bean
class JsBean(
val token: String,
val time_zone: String,
val lang: String,
val type: String,
val theme: String
)

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.bean
class JsDetailsBean(
val token: String,
val time_zone: String,
val lang: String,
val type: String,
val id: String,
val theme: String
)

View File

@ -0,0 +1,14 @@
package com.localee.mireo.app.http.bean
class JsonInfoBean(
val data: Data?,
val is_complete: Boolean,
val is_show: Int,
val type: String
) {
data class Data(
val activity_id: Int,
val short_play_id: Int?
)
}

View File

@ -0,0 +1,12 @@
package com.localee.mireo.app.http.bean
class JsonInfoUrlBean(
val `data`: Data,
val is_complete: Int,
val is_show: Int,
val type: String
) {
data class Data(
val link: String
)
}

View File

@ -0,0 +1,10 @@
package com.localee.mireo.app.http.bean
class LoginBean (
val avator: String,
val email: String,
val family_name: String,
val giving_name: String,
val platform: String,
val third_id: String
)

View File

@ -0,0 +1,11 @@
package com.localee.mireo.app.http.bean
class PayBean (
val order_code: String,
val pay_setting_id: String,//id
val pkg_name: String,
val product_id: String,//template id
val purchases_token: String,
val transaction_id: String,
val show_money: String,
)

View File

@ -0,0 +1,8 @@
package com.localee.mireo.app.http.bean
class PayResBean (
val is_backhaul: Int,//1可以上传
val money: String,//回传价格
val order_code: String,
val status: String
)

View File

@ -0,0 +1,68 @@
package com.localee.mireo.app.http.bean
class PaySettingsBean(
val list_coins: List<Coins>,
val list_vip: List<Vip>,
val list_sub_vip: List<Vip>,
val list_retrieve: List<Coins>
) {
data class Coins(
val android_template_id: String,
val backhaul_price: String,
val brief: String,
val buy_type: String,
val coins: Int,
val created_at: String,
val currency: String,
var currency_goolge: String?,
val description: String,
val id: Int,
val ios_template_id: String,
val corner_marker: String,
val lang_id: Int,
val origin_price: String,
val platform: String,
val price: String,
var price_google: String?,
val send_coins: Int,
val sort: Int,
val status: String,
val title: String,
val size: String,
val translate_key: String,
val updated_at: String,
val vip_type: String
)
data class Vip(
val android_template_id: String,
val backhaul_price: String,
val brief: String,
val short_type: String,
val buy_type: String,
val coins: Int,
val created_at: String,
val currency: String,
var currency_goolge: String?,
val description: String,
val id: Int,
val ios_template_id: String,
val lang_id: Int,
val origin_price: String,
val platform: String,
val price: String,
var price_google: String?,
val send_coins: Int,
val send_coin_ttl: Int,
val sort: Int,
var show: Int,
val status: String,
val title: String,
val translate_key: String,
val updated_at: String,
val auto_sub: String,
var vip_type: String,
var vip_type_key: String,
)
}

View File

@ -1,4 +1,4 @@
package com.localee.mireo.app.http.api
package com.localee.mireo.app.http.bean
class RecommendBean(
var id: Int,

View File

@ -1,6 +1,6 @@
package com.localee.mireo.app.ui.videoPaly
package com.localee.mireo.app.http.bean
class TranslatesRes(
class TranslatesBean(
val languages: List<Language>,
val last_update_time: String,
val translates: Translates

View File

@ -0,0 +1,7 @@
package com.localee.mireo.app.http.bean
class UploadHistoryBean (
val play_seconds: Long?,
val short_play_id: Int,
val video_id: Int?
)

View File

@ -1,8 +1,12 @@
package com.localee.mireo.app.http.exception
import com.localee.mireo.app.other.MsConstants
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody
import org.greenrobot.eventbus.EventBus
import java.io.IOException
@ -12,7 +16,9 @@ class HttpBodyInterceptor : Interceptor {
@kotlin.jvm.Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val k_center = chain.proceed(chain.request())
if (k_center.code() != 200) {
return k_center
}
return if (k_center.body() != null && k_center.body()!!.contentType() != null) {
val actiity = k_center.body()!!.contentType()
val circle = k_center.body()!!.string()
@ -82,4 +88,35 @@ class HttpBodyInterceptor : Interceptor {
}
}
private var lastPostTime: Long = 0L
private val lock = Any()
private val lock1 = Any()
suspend fun handle401Response() = withContext(Dispatchers.Main) {
synchronized(lock1) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) {
EventBus.getDefault()
.post(MsConstants.CONSTANTS_auth_refresh)
lastPostTime = currentTime
}
}
}
suspend fun handle402Response() = withContext(Dispatchers.Main) {
synchronized(lock) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) {
EventBus.getDefault()
.post(MsConstants.CONSTANTS_out_login)
lastPostTime = currentTime
}
}
}
companion object {
private const val DELAY_TIME_MILLIS = 2000L // 延迟时间,单位毫秒
}
}

View File

@ -0,0 +1,15 @@
package com.localee.mireo.app.http.exception;
import com.hjq.http.exception.HttpException;
public final class Token402Exception extends HttpException {
public Token402Exception(String message) {
super(message);
}
public Token402Exception(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -24,7 +24,11 @@ open class HttpData<T> {
return code == 200
}
fun isTokenFailure(): Boolean {
return code == 1001
fun is401Failure(): Boolean {
return code == 401
}
fun is402Failure(): Boolean {
return code == 402
}
}

View File

@ -19,12 +19,20 @@ import com.hjq.http.exception.ServerException
import com.hjq.http.exception.TimeoutException
import com.hjq.http.request.HttpRequest
import com.localee.mireo.app.http.exception.ResultException
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.http.exception.Token402Exception
import com.localee.mireo.app.http.exception.TokenException
import com.localee.mireo.app.other.MsConstants
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Headers
import okhttp3.Response
import okhttp3.ResponseBody
import org.greenrobot.eventbus.EventBus
import java.io.IOException
import java.io.InputStream
import java.lang.reflect.GenericArrayType
@ -41,14 +49,23 @@ class RequestHandler constructor(private val mApplication: Application) : IReque
return response
}
if (!response.isSuccessful) {
throw ResponseException(
String.format(
mApplication.getString(R.string.http_response_error),
response.code(), response.message()
), response
)
if (response.code() == 401) {
throw TokenException(
mApplication.getString(R.string.http_token_error)
)
}else if (response.code() == 402) {
throw Token402Exception(
mApplication.getString(R.string.http_token_error)
)
}else {
throw ResponseException(
String.format(
mApplication.getString(R.string.http_response_error),
response.code(), response.message()
), response
)
}
}
if (Headers::class.java == type) {
return response.headers()
@ -113,20 +130,33 @@ class RequestHandler constructor(private val mApplication: Application) : IReque
return result
}
if (model.isTokenFailure()) {
if (model.is401Failure()) {
throw TokenException(
mApplication.getString(R.string.http_token_error)
)
}
if (model.is402Failure()) {
throw Token402Exception(
mApplication.getString(R.string.http_token_error)
)
}
throw ResultException(model.getMessage(), model)
}
return result
}
@OptIn(DelicateCoroutinesApi::class)
override fun requestFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
if (throwable is HttpException) {
if (throwable is TokenException) {
GlobalScope.launch(Dispatchers.Main) {
handle401Response()
}
}else if (throwable is Token402Exception) {
GlobalScope.launch(Dispatchers.Main) {
handle402Response()
}
}
return throwable
}
@ -234,5 +264,34 @@ class RequestHandler constructor(private val mApplication: Application) : IReque
HttpCacheManager.clearCache()
}
private var lastPostTime: Long = 0L
private val lock = Any()
private val lock1 = Any()
private suspend fun handle401Response() = withContext(Dispatchers.Main) {
synchronized(lock1) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) {
EventBus.getDefault()
.post(MsConstants.CONSTANTS_auth_refresh)
lastPostTime = currentTime
}
}
}
private suspend fun handle402Response() = withContext(Dispatchers.Main) {
synchronized(lock) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) {
EventBus.getDefault()
.post(MsConstants.CONSTANTS_out_login)
lastPostTime = currentTime
}
}
}
companion object {
private const val DELAY_TIME_MILLIS = 2000L // 延迟时间,单位毫秒
}
}

View File

@ -5,7 +5,6 @@ import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Bundle
import androidx.collection.ArrayMap
import timber.log.Timber
import java.util.*
class ActivityManager private constructor() : ActivityLifecycleCallbacks {
@ -58,6 +57,7 @@ class ActivityManager private constructor() : ActivityLifecycleCallbacks {
fun registerApplicationLifecycleCallback(callback: ApplicationLifecycleCallback) {
lifecycleCallbacks.add(callback)
}
fun unregisterApplicationLifecycleCallback(callback: ApplicationLifecycleCallback) {
lifecycleCallbacks.remove(callback)
}
@ -108,39 +108,32 @@ class ActivityManager private constructor() : ActivityLifecycleCallbacks {
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
Timber.i("%s - onCreate", activity.javaClass.simpleName)
if (activitySet.size == 0) {
for (callback: ApplicationLifecycleCallback? in lifecycleCallbacks) {
callback?.onApplicationCreate(activity)
}
Timber.i("%s - onApplicationCreate", activity.javaClass.simpleName)
}
activitySet[getObjectTag(activity)] = activity
topActivity = activity
}
override fun onActivityStarted(activity: Activity) {
Timber.i("%s - onStart", activity.javaClass.simpleName)
}
override fun onActivityResumed(activity: Activity) {
Timber.i("%s - onResume", activity.javaClass.simpleName)
if (topActivity === activity && resumedActivity == null) {
for (callback: ApplicationLifecycleCallback in lifecycleCallbacks) {
callback.onApplicationForeground(activity)
}
Timber.i("%s - onApplicationForeground", activity.javaClass.simpleName)
}
topActivity = activity
resumedActivity = activity
}
override fun onActivityPaused(activity: Activity) {
Timber.i("%s - onPause", activity.javaClass.simpleName)
}
override fun onActivityStopped(activity: Activity) {
Timber.i("%s - onStop", activity.javaClass.simpleName)
if (resumedActivity === activity) {
resumedActivity = null
}
@ -148,16 +141,13 @@ class ActivityManager private constructor() : ActivityLifecycleCallbacks {
for (callback: ApplicationLifecycleCallback in lifecycleCallbacks) {
callback.onApplicationBackground(activity)
}
Timber.i("%s - onApplicationBackground", activity.javaClass.simpleName)
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
Timber.i("%s - onSaveInstanceState", activity.javaClass.simpleName)
}
override fun onActivityDestroyed(activity: Activity) {
Timber.i("%s - onDestroy", activity.javaClass.simpleName)
activitySet.remove(getObjectTag(activity))
if (topActivity === activity) {
topActivity = null
@ -166,7 +156,6 @@ class ActivityManager private constructor() : ActivityLifecycleCallbacks {
for (callback: ApplicationLifecycleCallback in lifecycleCallbacks) {
callback.onApplicationDestroy(activity)
}
Timber.i("%s - onApplicationDestroy", activity.javaClass.simpleName)
}
}

View File

@ -1,6 +1,6 @@
package com.localee.mireo.app.other
import com.localee.mireo.app.BuildConfig
import com.localee.mireo.shortapp.BuildConfig
object AppConfig {

View File

@ -1,19 +0,0 @@
package com.localee.mireo.app.other
import android.os.Build
import timber.log.Timber.DebugTree
class DebugLoggerTree : DebugTree() {
companion object {
private const val MAX_TAG_LENGTH: Int = 23
}
override fun createStackElementTag(element: StackTraceElement): String {
val tag: String = "(" + element.fileName + ":" + element.lineNumber + ")"
if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return tag
}
return tag.substring(0, MAX_TAG_LENGTH)
}
}

View File

@ -0,0 +1,21 @@
package com.localee.mireo.app.other
import android.util.Log
object Logger {
private const val GLOBAL_TAG = "AppLogger"
fun d(message: String, tag: String = GLOBAL_TAG) {
if (AppConfig.isDebug()) Log.d(tag, buildLogMessage(message))
}
fun e(throwable: Throwable, message: String = "", tag: String = GLOBAL_TAG) {
if (AppConfig.isDebug()) {
Log.e(tag, buildLogMessage(message), throwable)
}
}
private fun buildLogMessage(message: String): String {
return "[${Thread.currentThread().name}] $message"
}
}

View File

@ -11,7 +11,7 @@ import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.scwang.smart.refresh.header.material.CircleImageView
import com.scwang.smart.refresh.header.material.MaterialProgressDrawable
import com.scwang.smart.refresh.layout.api.RefreshHeader

View File

@ -11,27 +11,16 @@ object MsConstants {
const val Constants_user_agreement = "https://www.mireotv.com/user_policy"
const val Constants_privacy_policy = "https://www.mireotv.com/private"
const val EVENT_VIDEO_CLOSE = "event_video_close"
const val EVENT_VIDEO_NEXT = "event_video_next"
const val EVENT_VIDEO_HOME_NEXT = "event_video_home_next"
const val EVENT_VIDEO_PLAY = "event_video_play"
const val EVENT_VIDEO_UPDATE = "event_video_update"
const val EVENT_SELECT_COLLECT = "event_select_collect"
const val EVENT_SELECT_HOME_COLLECT = "event_select_home_collect"
const val EVENT_CLICK_COLLECTION = "event_click_Collection"
const val EVENT_TO_LOGIN = "event_to_login"
const val EVENT_SHOW_AD = "event_show_ad"
const val EVENT_ADD_IN = "event_add_in"
const val EVENT_TO_HOME = "event_to_home"
const val EVENT_DELE_ACC = "event_dele_acc"
const val EVENT_UPDATE_MAIN = "event_update_main"
const val EVENT_UPDATE_USER = "event_update_user_info"
const val EVENT_MAIN_UPDATE_USER_INFO = "event_update_user_info"
const val CLOSE_VIDEO = "close_video"
const val EVENT_SHOW_ADS = "event_show_ads"
const val EVENT_UPDATE_COLLECTION = "event_update_Collection"
const val feedback_URL_res: String = "https://campaign.mireotv.com/pages/leave/index"
const val feedback_list_URL_res: String = "https://campaign.mireotv.com/pages/leave/list"
const val feedback_detail_URL_res: String = "https://campaign.mireotv.com/pages/leave/detail"
const val REWARD_URL_RES: String = "https://campaign.mireotv.com/"
const val CONSTANTS_lang_key = "lang-key"
const val CONSTANTS_Detail_id = "CONSTANTS_Detail_id"
var IsRegister: Boolean = true
var IsFirst: Boolean = true
var ExampleIsCurrentPage: Boolean = true
var Exampleplaying: Boolean = false
var ExampleDetailPlaying: Boolean = false
@ -40,6 +29,7 @@ object MsConstants {
var CanNotification: Boolean = false
var WebRefresh: Boolean = false
var isBannerScrolling: Boolean = true
var ExampleLock: Boolean = false
var seek = true
const val Constants_RecommendPlayerView_PLAYER_STATUS_FINISHExample =
"Constants_RecommendPlayerView_PLAYER_STATUS_FINISHExample"
@ -62,4 +52,41 @@ object MsConstants {
const val CONSTANTS_activity_id = "CONSTANTS_activity_id"
const val CONSTANTS_stop_play = "CONSTANTS_stop_play"
const val Constants_requestPermissions_photo = "Constants_requestPermissions_photo"
const val CONSTANTS_auth_refresh = "CONSTANTS_auth_refresh"
const val CONSTANTS_refresh_translate = "CONSTANTS_refresh_translate"
const val CONSTANTS_out_login = "CONSTANTS_out_login"
const val CONSTANTS_refresh_home = "CONSTANTS_refresh_home"
const val CONSTANTS_refresh_me = "CONSTANTS_refresh_me"
const val CONSTANTS_user_refresh = "CONSTANTS_user_refresh"
const val Constants_language_set = "Constants_language_set"
const val Constants_language_refresh = "Constants_language_refresh"
const val Constants_Main_Video_info = "Constants_Main_Video_info"
const val Constants_openFeedback = "Constants_openFeedback"
const val Constants_openFeedbackDetail = "Constants_openFeedbackDetail"
const val Constants_requestPermissions_photo_detail =
"Constants_requestPermissions_photo_detail"
const val CONSTANTS_rating = "CONSTANTS_rating"
const val CONSTANTS_web_refresh = "CONSTANTS_web_refresh"
const val CONSTANTS_Episode = "CONSTANTS_Episode"
const val CONSTANTS_Login = "CONSTANTS_Login"
const val CONSTANTS_enterTheApp = "CONSTANTS_enterTheApp"
const val CONSTANTS_onLine = "CONSTANTS_onLine"
const val CONSTANTS_leaveApp = "CONSTANTS_leaveApp"
const val Constants_DDL_Url = "Constants_DDL_Url"
const val Constants_Main_Video_status = "Constants_Main_Video_status"
const val CONSTANTS_examplePayReq = "CONSTANTS_examplePayReq"
const val CONSTANTS_pay_refresh = "CONSTANTS_pay_refresh"
const val Constants_onTokenRefresh = "Constants_onTokenRefresh"
const val CONSTANTS_PREF_LAST_POPUP_TIME_Notification =
"CONSTANTS_PREF_LAST_POPUP_TIME_Notification"
const val ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L
const val ONE_DAY_IN_MILLIS_VIP = 60 * 60 * 1000L
const val CONSTANTS_web_notification = "CONSTANTS_web_notification"
}

View File

@ -0,0 +1,231 @@
package com.localee.mireo.app.other
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.media.RingtoneManager
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.localee.mireo.app.ui.activity.HomeActivity
import com.localee.mireo.shortapp.R
import org.greenrobot.eventbus.EventBus
class MyFirebaseMessagingService : FirebaseMessagingService() {
private var notificationId: Int = 0
// [START receive_message]
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "From: ${remoteMessage.from}")
// Check if message contains a data payload.
// if (remoteMessage.data.isNotEmpty()) {
// Check if data needs to be processed by long running job
// if (needsToBeScheduled()) {
// // For long-running tasks (10 seconds or more) use WorkManager.
// scheduleJob()
// } else {
// // Handle message within 10 seconds
// handleNow()
// }
// }
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
// Check if message contains a notification payload.
// remoteMessage.notification?.let {
// Log.d(TAG, "Message Notification Body: ${it.body}")
//
// }
// if (ExampleApplication.isAppInBackground) {
sendNotification(remoteMessage.data)
// }
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
private fun needsToBeScheduled() = true
// [START on_new_token]
/**
* Called if the FCM registration token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the
* FCM registration token is initially generated so this is where you would retrieve the token.
*/
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// FCM registration token to your app server.
sendRegistrationToServer(token)
}
// [END on_new_token]
private fun scheduleJob() {
// [START dispatch_job]
val work = OneTimeWorkRequest.Builder(MyWorker::class.java)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
// [END dispatch_job]
}
private fun handleNow() {
Log.d(TAG, "Short lived task is done.")
}
private fun sendRegistrationToServer(token: String?) {
Log.d(TAG, "sendRegistrationTokenToServer($token)")
EventBus.getDefault()
.post(MsConstants.Constants_onTokenRefresh)
}
private fun sendNotification(
data: MutableMap<String, String>
) {
val intent = Intent(this, HomeActivity::class.java)
var title = ""
var messageBody = ""
var poster = ""
if (data.isNotEmpty()) {
if (data.containsKey("message_title")) {
title = data["message_title"].toString()
intent.putExtra("title", title)
}
if (data.containsKey("message_body")) {
messageBody = data["message_body"].toString()
}
if (data.containsKey("message_id")) {
val message_id = data["message_id"]
intent.putExtra("message_id", message_id)
}
if (data.containsKey("path")) {
when (data["path"]) {
"detail" -> {
if (data.containsKey("short_play_id")) {
val short_play_id = data["short_play_id"]
intent.putExtra("short_play_id", short_play_id)
}
intent.putExtra("path", "detail")
}
"promotion" -> {
intent.putExtra("path", "promotion")
}
"orderDetail" -> {
intent.putExtra("path", "orderDetail")
}
"feedback" -> {
intent.putExtra("path", "feedback")
}
}
}
if (data.containsKey("poster")) {
poster = data["poster"].toString()
Glide.with(this)
.asBitmap()
.load(poster)
.into(object : CustomTarget<Bitmap?>() {
override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap?>?
) {
setNotification(intent, title, messageBody, resource)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
} else {
setNotification(intent, title, messageBody, null)
}
}
}
private fun setNotification(
intent: Intent,
title: String,
messageBody: String,
resource: Bitmap?
) {
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
} else {
PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
val channelId = getString(R.string.default_notification_channel_id)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = if (resource != null) NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setLargeIcon(resource)
.setContentIntent(pendingIntent) else NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager =
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
val channel = NotificationChannel(
channelId,
getString(R.string.default_notification_channel_id),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
notificationId++
notificationManager.notify(notificationId, notificationBuilder.build())
}
companion object {
private const val TAG = "MyFirebaseMsgService"
}
internal class MyWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
return Result.success()
}
}
}

View File

@ -1,188 +0,0 @@
package com.localee.mireo.app.other
import android.app.Activity
import android.content.*
import android.os.*
import com.hjq.base.BaseDialog
import com.localee.mireo.app.manager.*
import com.localee.mireo.app.ui.dialog.MessageDialog
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import com.hjq.toast.ToastUtils
import com.localee.mireo.app.manager.ActivityManager
import java.util.*
import com.localee.mireo.app.R
abstract class PermissionCallback : OnPermissionCallback {
override fun onDenied(permissions: MutableList<String>, never: Boolean) {
if (never) {
showPermissionDialog(permissions)
return
}
if (permissions.size == 1 && (Permission.ACCESS_BACKGROUND_LOCATION == permissions[0])) {
ToastUtils.show(R.string.common_permission_fail_4)
return
}
ToastUtils.show(R.string.common_permission_fail_1)
}
protected fun showPermissionDialog(permissions: MutableList<String>) {
val activity: Activity? = ActivityManager.getInstance().getTopActivity()
if ((activity == null) || activity.isFinishing || activity.isDestroyed) {
return
}
MessageDialog.Builder(activity)
.setTitle(R.string.common_permission_alert)
.setMessage(getPermissionHint(activity, permissions))
.setConfirm(R.string.common_permission_goto)
.setCancel(null)
.setCancelable(false)
.setListener(object : MessageDialog.OnListener {
override fun onConfirm(dialog: BaseDialog?) {
XXPermissions.startPermissionActivity(activity, permissions)
}
})
.show()
}
protected fun getPermissionHint(context: Context, permissions: MutableList<String>): String {
if (permissions.isEmpty()) {
return context.getString(R.string.common_permission_fail_2)
}
val hints: MutableList<String> = ArrayList()
for (permission: String? in permissions) {
when (permission) {
Permission.READ_EXTERNAL_STORAGE,
Permission.WRITE_EXTERNAL_STORAGE,
Permission.MANAGE_EXTERNAL_STORAGE -> {
val hint: String = context.getString(R.string.common_permission_storage)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.CAMERA -> {
val hint: String = context.getString(R.string.common_permission_camera)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.RECORD_AUDIO -> {
val hint: String = context.getString(R.string.common_permission_microphone)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.ACCESS_FINE_LOCATION,
Permission.ACCESS_COARSE_LOCATION,
Permission.ACCESS_BACKGROUND_LOCATION -> {
val hint: String = if (!permissions.contains(Permission.ACCESS_FINE_LOCATION) &&
!permissions.contains(Permission.ACCESS_COARSE_LOCATION)) {
context.getString(R.string.common_permission_location_background)
} else {
context.getString(R.string.common_permission_location)
}
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.READ_PHONE_STATE,
Permission.CALL_PHONE,
Permission.ADD_VOICEMAIL,
Permission.USE_SIP,
Permission.READ_PHONE_NUMBERS,
Permission.ANSWER_PHONE_CALLS -> {
val hint: String = context.getString(R.string.common_permission_phone)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.GET_ACCOUNTS,
Permission.READ_CONTACTS,
Permission.WRITE_CONTACTS -> {
val hint: String = context.getString(R.string.common_permission_contacts)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.READ_CALENDAR,
Permission.WRITE_CALENDAR -> {
val hint: String = context.getString(R.string.common_permission_calendar)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.READ_CALL_LOG,
Permission.WRITE_CALL_LOG,
Permission.PROCESS_OUTGOING_CALLS -> {
val hint: String = context.getString(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) R.string.common_permission_call_log else R.string.common_permission_phone)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.BODY_SENSORS -> {
val hint: String = context.getString(R.string.common_permission_sensors)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.ACTIVITY_RECOGNITION -> {
val hint: String = context.getString(R.string.common_permission_activity_recognition)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.SEND_SMS,
Permission.RECEIVE_SMS,
Permission.READ_SMS,
Permission.RECEIVE_WAP_PUSH,
Permission.RECEIVE_MMS -> {
val hint: String = context.getString(R.string.common_permission_sms)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.REQUEST_INSTALL_PACKAGES -> {
val hint: String = context.getString(R.string.common_permission_install)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.NOTIFICATION_SERVICE -> {
val hint: String = context.getString(R.string.common_permission_notification)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.SYSTEM_ALERT_WINDOW -> {
val hint: String = context.getString(R.string.common_permission_window)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
Permission.WRITE_SETTINGS -> {
val hint: String = context.getString(R.string.common_permission_setting)
if (!hints.contains(hint)) {
hints.add(hint)
}
}
}
}
if (hints.isNotEmpty()) {
val builder: StringBuilder = StringBuilder()
for (text: String? in hints) {
if (builder.isEmpty()) {
builder.append(text)
} else {
builder.append("")
.append(text)
}
}
builder.append(" ")
return context.getString(R.string.common_permission_fail_3, builder.toString())
}
return context.getString(R.string.common_permission_fail_2)
}
}

View File

@ -9,7 +9,7 @@ import android.util.AttributeSet
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.scwang.smart.refresh.layout.api.RefreshFooter
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.constant.SpinnerStyle

View File

@ -7,8 +7,8 @@ import android.widget.TextView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import com.hjq.bar.style.LightBarStyle
import com.localee.mireo.app.R
import com.hjq.widget.view.PressAlphaTextView
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.widget.view.PressAlphaTextView
class TitleBarStyle : LightBarStyle() {

View File

@ -3,7 +3,7 @@ package com.localee.mireo.app.other
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.hjq.toast.style.BlackToastStyle
class ToastStyle : BlackToastStyle() {

View File

@ -3,10 +3,10 @@ package com.localee.mireo.app.ui.activity
import android.content.Intent
import android.net.Uri
import android.view.View
import com.localee.mireo.app.R
import com.localee.mireo.app.aop.SingleClick
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.utils.singleClick
class AboutActivity : AppActivity() {
@ -23,20 +23,22 @@ class AboutActivity : AppActivity() {
override fun initData() {
}
@SingleClick
override fun onClick(view: View) {
when (view.id) {
R.id.sb_about_web -> {
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse(MsConstants.Constants_web))
startActivity(webIntent)
}
singleClick {
when (view.id) {
R.id.sb_about_web -> {
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse(MsConstants.Constants_web))
startActivity(webIntent)
}
R.id.sb_about_privacy -> {
BrowserActivity.start(this, MsConstants.Constants_privacy_policy)
}
R.id.sb_about_privacy -> {
BrowserActivity.start(this, MsConstants.Constants_privacy_policy)
}
R.id.sb_about_agreement -> {
BrowserActivity.start(this, MsConstants.Constants_user_agreement)
}
R.id.sb_about_agreement -> {
BrowserActivity.start(this, MsConstants.Constants_user_agreement)
}
}

View File

@ -0,0 +1,132 @@
package com.localee.mireo.app.ui.activity
import android.graphics.Color
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatTextView
import com.facebook.login.LoginManager
import com.hjq.http.EasyHttp
import com.hjq.http.listener.HttpCallbackProxy
import com.hjq.shape.view.ShapeTextView
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.http.api.CustomerRegisterApi
import com.localee.mireo.app.http.api.DoLogoffApi
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.other.MsConstants.CONSTANTS_user_refresh
import com.localee.mireo.app.ui.videoPaly.ExampleUnFavoriteDialog
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.singleClick
import com.localee.mireo.shortapp.R
import org.greenrobot.eventbus.EventBus
class AccountDeleteActivity : AppActivity() {
private val ivSelect: ImageView? by lazy { findViewById(R.id.iv_select) }
private val tvEight: ShapeTextView? by lazy { findViewById(R.id.tv_eight) }
private var isSelect = false
override fun getLayoutId(): Int {
return R.layout.activity_account_delete
}
override fun initView() {
ivSelect?.setOnClickListener {
singleClick {
if (isSelect) {
isSelect = false
ivSelect?.setImageResource(R.mipmap.compound_normal_ic_1)
tvEight?.setTextColor(Color.parseColor("#8B8B8B"))
tvEight?.shapeDrawableBuilder?.setSolidGradientColors(
0xff272a30.toInt(), 0xff272a30.toInt()
)
?.intoBackground()
} else {
isSelect = true
ivSelect?.setImageResource(R.mipmap.checkbox_checked_ic)
tvEight?.setTextColor(getColor(R.color.white))
tvEight?.shapeDrawableBuilder?.setSolidGradientColors(
0XffF8726D.toInt(), 0XFFF24C92.toInt()
)
?.intoBackground()
}
}
}
tvEight?.setOnClickListener {
singleClick {
if (!isSelect) {
return@singleClick
}
val exampleUnFavoriteDialog = ExampleUnFavoriteDialog(this)
val tvThinkAgain =
exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_think_again)
val tvUnfavorite =
exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_unfavorite)
val tvTitle =
exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_title)
val tvContent =
exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_content)
tvThinkAgain.text = "Cancel"
tvUnfavorite.text = "Confirm"
tvTitle.text = "Tips"
tvContent.text = "Are you sure you want to cancel you account?"
tvThinkAgain.setOnClickListener { exampleUnFavoriteDialog.dismiss() }
tvUnfavorite.setOnClickListener {
doLogoff()
exampleUnFavoriteDialog.dismiss()
}
exampleUnFavoriteDialog.show()
}
}
}
override fun initData() {
}
fun doLogoff() {
EasyHttp.post(this)
.api(DoLogoffApi())
.request(object : HttpCallbackProxy<HttpData<DoLogoffApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<DoLogoffApi.Bean>) {
result.getData()?.let {
toast("Logout successfully")
LoginManager.getInstance().logOut()
getCustomerRegister()
} ?: run {
toast(getString(R.string.example_service_exception_please_try_again))
}
}
})
}
private fun getCustomerRegister() {
EasyHttp.post(this).api(CustomerRegisterApi())
.request(object : HttpCallbackProxy<HttpData<CustomerRegisterApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<CustomerRegisterApi.Bean>) {
result.getData()?.let {
MsMMKVUtils.getMMKV()
.putString(MsConstants.ACCESS_TOKEN, it.token)
EventBus.getDefault()
.post(MsConstants.CONSTANTS_leaveApp)
EventBus.getDefault()
.post(MsConstants.CONSTANTS_enterTheApp)
EventBus.getDefault().post(MsConstants.CONSTANTS_quality_refresh)
EventBus.getDefault()
.post(CONSTANTS_user_refresh)
MsConstants.WebRefresh = true
HomeActivity.start(this@AccountDeleteActivity)
finish()
} ?: run {
toast(getString(R.string.example_service_exception_please_try_again))
}
}
})
}
}

View File

@ -1,17 +1,17 @@
package com.localee.mireo.app.ui.activity
import android.app.Activity
import android.content.*
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.text.TextUtils
import android.view.*
import android.view.KeyEvent
import android.view.View
import android.webkit.WebView
import android.widget.ProgressBar
import com.localee.mireo.app.R
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.action.StatusAction
import com.localee.mireo.app.aop.CheckNet
import com.localee.mireo.app.aop.Log
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.widget.BrowserView
import com.localee.mireo.app.widget.BrowserView.BrowserChromeClient
@ -28,8 +28,6 @@ class BrowserActivity : AppActivity(), StatusAction, OnRefreshListener {
const val INTENT_KEY_IN_URL: String = "url"
@CheckNet
@Log
fun start(context: Context, url: String) {
if (TextUtils.isEmpty(url)) {
return
@ -85,7 +83,6 @@ class BrowserActivity : AppActivity(), StatusAction, OnRefreshListener {
return super.onKeyDown(keyCode, event)
}
@CheckNet
private fun reload() {
browserView?.reload()
}
@ -99,7 +96,12 @@ class BrowserActivity : AppActivity(), StatusAction, OnRefreshListener {
private inner class AppBrowserViewClient : BrowserViewClient() {
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
override fun onReceivedError(
view: WebView,
errorCode: Int,
description: String,
failingUrl: String
) {
post {
showError(object : OnRetryListener {
override fun onRetry(layout: StatusLayout) {
@ -120,7 +122,8 @@ class BrowserActivity : AppActivity(), StatusAction, OnRefreshListener {
}
}
private inner class AppBrowserChromeClient constructor(view: BrowserView) : BrowserChromeClient(view) {
private inner class AppBrowserChromeClient constructor(view: BrowserView) :
BrowserChromeClient(view) {
override fun onReceivedTitle(view: WebView, title: String?) {
if (title == null) {

View File

@ -0,0 +1,321 @@
package com.localee.mireo.app.ui.activity
import android.Manifest
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.util.Base64
import android.util.Log
import android.view.View
import android.webkit.JsPromptResult
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.PermissionUtils
import com.hjq.bar.TitleBar
import com.hjq.http.EasyHttp
import com.hjq.http.listener.HttpCallbackProxy
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.http.api.NoticeNumApi
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.utils.JsBridge
import com.localee.mireo.app.utils.TranslatesUtils
import com.localee.mireo.app.utils.singleClick
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.ByteArrayOutputStream
import java.io.InputStream
class FeedBackActivity : AppActivity() {
private val title: TitleBar? by lazy { findViewById(R.id.title) }
private val ivRight: ImageView? by lazy { findViewById(R.id.iv_right) }
private val tvBackNum: TextView? by lazy { findViewById(R.id.tv_back_num) }
private val webView: WebView? by lazy { findViewById(R.id.webView) }
override fun getLayoutId(): Int {
return R.layout.activity_feedback
}
override fun initView() {
EventBus.getDefault().register(this)
if (TranslatesUtils.translates() != null) {
title?.setTitle(TranslatesUtils.translates()?.feedback)
}
ivRight?.setOnClickListener {
singleClick {
startActivity(
Intent(
this,
FeedBackListActivity::class.java
)
)
}
}
}
override fun initData() {
setWebView()
showDialog()
loadPageUrl(MsConstants.feedback_URL_res)
}
override fun onResume() {
super.onResume()
getNoticeNum()
}
private fun getNoticeNum() {
EasyHttp.post(this)
.api(NoticeNumApi())
.request(object : HttpCallbackProxy<HttpData<NoticeNumApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<NoticeNumApi.Bean>) {
result.getData()?.let {
if (it.feedback_notice_num != 0) {
tvBackNum?.visibility = View.VISIBLE
tvBackNum?.text = it.feedback_notice_num.toString()
} else {
tvBackNum?.visibility = View.INVISIBLE
}
}
}
})
}
@SuppressLint("SetJavaScriptEnabled")
private fun setWebView() {
val webSettings: WebSettings = webView!!.settings
webSettings.javaScriptEnabled = true
// webView?.webChromeClient = WebChromeClient()
webView?.webChromeClient = object : WebChromeClient() {
override fun onJsAlert(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
result.confirm()
return true
}
override fun onJsConfirm(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
return true
}
override fun onJsPrompt(
view: WebView,
url: String,
message: String,
defaultValue: String,
result: JsPromptResult
): Boolean {
return true
}
}
webView?.setBackgroundColor(Color.TRANSPARENT)
webView?.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
hideDialog()
}
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
super.onReceivedError(view, request, error)
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
hideDialog()
}
}
webSettings.domStorageEnabled = true
webSettings.loadsImagesAutomatically = true
webSettings.useWideViewPort = true
webSettings.loadWithOverviewMode = true
webSettings.builtInZoomControls = true
webSettings.displayZoomControls = false
webView?.addJavascriptInterface(
JsBridge(this),
"AndroidInterface"
)
}
private fun loadPageUrl(url: String) {
webView?.loadUrl(url)
}
private val REQUEST_PICK_FILE: Int = 1002
private val REQUEST_PERMISSIONS = 1001
@RequiresApi(Build.VERSION_CODES.M)
private fun requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
),
REQUEST_PERMISSIONS
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
),
REQUEST_PERMISSIONS
)
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
),
REQUEST_PERMISSIONS
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
openFilePicker()
} else {
// toast(TranslatesUtils.translates()?.open_photo_tips.toString())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PermissionUtils.launchAppDetailsSettings()
}
}
}
}
private fun openFilePicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
}
startActivityForResult(intent, REQUEST_PICK_FILE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_PICK_FILE && resultCode == RESULT_OK) {
data?.data?.let { uri ->
compressAndConvertImage(uri)
}
}
}
private fun compressAndConvertImage(uri: Uri) {
lifecycleScope.launch {
val compressedImageBytes = withContext(Dispatchers.IO) {
compressImage(uri, contentResolver)
}
if (compressedImageBytes.isNotEmpty()) {
val base64String = Base64.encodeToString(compressedImageBytes, Base64.DEFAULT)
webView?.loadUrl(
"javascript:uploadConvertImage(" + "'" + base64String + "'" + ")"
)
}
}
}
private fun compressImage(uri: Uri, contentResolver: ContentResolver): ByteArray {
try {
val inputStream: InputStream? = contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
val outputStream = ByteArrayOutputStream()
var quality = 100
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
while (outputStream.toByteArray().size > 100 * 1024 && quality > 10) {
quality -= 10
outputStream.reset()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
}
val compressedImageBytes = outputStream.toByteArray()
Log.d(
"compressedImageBytes",
"Compressed image size: ${compressedImageBytes.size} bytes"
)
return compressedImageBytes
} catch (e: Exception) {
e.printStackTrace()
toast(
TranslatesUtils.translates()?.picture_error ?: "Please select the correct picture~"
)
}
return byteArrayOf()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: String) {
if (MsConstants.Constants_requestPermissions_photo == event) {
requestPermissions()
}
if (MsConstants.Constants_openFeedback == event) {
singleClick {
startActivity(
Intent(
this,
FeedBackListActivity::class.java
)
)
}
}
}
override fun onDestroy() {
super.onDestroy()
EventBus.getDefault().unregister(this)
}
}

View File

@ -0,0 +1,236 @@
package com.localee.mireo.app.ui.activity
import android.Manifest
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.util.Base64
import android.util.Log
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.PermissionUtils
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.app.BaseEventBus
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.utils.JsBridge
import com.localee.mireo.app.utils.JsBridgeDetail
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.TranslatesUtils
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.ByteArrayOutputStream
import java.io.InputStream
class FeedBackDetailsActivity : AppActivity() {
private val webView: WebView? by lazy { findViewById(R.id.webView) }
private val rlStatusRefresh: SmartRefreshLayout? by lazy { findViewById(R.id.rl_status_refresh) }
override fun getLayoutId(): Int {
return R.layout.activity_feedback_details
}
override fun initView() {
EventBus.getDefault().register(this)
showDialog()
rlStatusRefresh?.setOnRefreshListener {
loadPageUrl(MsConstants.feedback_detail_URL_res)
}
}
override fun initData() {
setWebView()
loadPageUrl(MsConstants.feedback_detail_URL_res)
}
@SuppressLint("SetJavaScriptEnabled")
private fun setWebView() {
val webSettings: WebSettings = webView!!.settings
webSettings.javaScriptEnabled = true
webView?.webChromeClient = WebChromeClient()
webView?.setBackgroundColor(Color.TRANSPARENT)
webView?.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
rlStatusRefresh?.finishRefresh()
hideDialog()
}
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
super.onReceivedError(view, request, error)
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
rlStatusRefresh?.finishRefresh()
hideDialog()
}
}
webSettings.domStorageEnabled = true
webSettings.loadsImagesAutomatically = true
webSettings.useWideViewPort = true
webSettings.loadWithOverviewMode = true
webSettings.builtInZoomControls = true
webSettings.displayZoomControls = false
webView?.addJavascriptInterface(
JsBridgeDetail(this),
"AndroidInterface"
)
}
private fun loadPageUrl(url: String) {
webView?.loadUrl(url)
}
private val REQUEST_PICK_FILE: Int = 1003
private val REQUEST_PERMISSIONS = 1004
@RequiresApi(Build.VERSION_CODES.M)
private fun requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
),
REQUEST_PERMISSIONS
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
),
REQUEST_PERMISSIONS
)
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
),
REQUEST_PERMISSIONS
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
openFilePicker()
} else {
toast(TranslatesUtils.translates()?.open_photo_tips.toString())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PermissionUtils.launchAppDetailsSettings()
}
}
}
}
private fun openFilePicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
}
startActivityForResult(intent, REQUEST_PICK_FILE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_PICK_FILE && resultCode == RESULT_OK) {
data?.data?.let { uri ->
compressAndConvertImage(uri)
}
}
}
private fun compressAndConvertImage(uri: Uri) {
lifecycleScope.launch {
val compressedImageBytes = withContext(Dispatchers.IO) {
compressImage(uri, contentResolver)
}
if (compressedImageBytes.isNotEmpty()) {
val base64String = Base64.encodeToString(compressedImageBytes, Base64.DEFAULT)
webView?.loadUrl(
"javascript:uploadConvertImage(" + "'" + base64String + "'" + ")"
)
}
}
}
private fun compressImage(uri: Uri, contentResolver: ContentResolver): ByteArray {
try {
val inputStream: InputStream? = contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
val outputStream = ByteArrayOutputStream()
var quality = 100
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
while (outputStream.toByteArray().size > 100 * 1024 && quality > 10) {
quality -= 10
outputStream.reset()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
}
val compressedImageBytes = outputStream.toByteArray()
Log.d(
"compressedImageBytes",
"Compressed image size: ${compressedImageBytes.size} bytes"
)
return compressedImageBytes
} catch (e: Exception) {
e.printStackTrace()
toast(
TranslatesUtils.translates()?.picture_error ?: "Please select the correct picture~"
)
}
return byteArrayOf()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: String) {
if (MsConstants.Constants_requestPermissions_photo_detail == event) {
requestPermissions()
}
}
override fun onDestroy() {
super.onDestroy()
EventBus.getDefault().unregister(this)
}
}

View File

@ -0,0 +1,108 @@
package com.localee.mireo.app.ui.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.app.BaseEventBus
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.utils.JsBridge
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.TranslatesUtils
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class FeedBackListActivity : AppActivity() {
private val webView: WebView? by lazy { findViewById(R.id.webView) }
private val rlStatusRefresh: SmartRefreshLayout? by lazy { findViewById(R.id.rl_status_refresh) }
override fun getLayoutId(): Int {
return R.layout.activity_feedback_list
}
override fun initView() {
EventBus.getDefault().register(this)
showDialog()
rlStatusRefresh?.setOnRefreshListener {
loadPageUrl(MsConstants.feedback_list_URL_res)
}
}
override fun initData() {
setWebView()
loadPageUrl(MsConstants.feedback_list_URL_res)
}
@SuppressLint("SetJavaScriptEnabled")
private fun setWebView() {
val webSettings: WebSettings = webView!!.settings
webSettings.javaScriptEnabled = true
webView?.webChromeClient = WebChromeClient()
webView?.setBackgroundColor(Color.TRANSPARENT)
webView?.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
rlStatusRefresh?.finishRefresh()
hideDialog()
}
override fun onReceivedError(
view: WebView?, request: WebResourceRequest?, error: WebResourceError?
) {
super.onReceivedError(view, request, error)
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
rlStatusRefresh?.finishRefresh()
hideDialog()
}
}
webSettings.domStorageEnabled = true
webSettings.loadsImagesAutomatically = true
webSettings.useWideViewPort = true
webSettings.loadWithOverviewMode = true
webSettings.builtInZoomControls = true
webSettings.displayZoomControls = false
webView?.addJavascriptInterface(
JsBridge(this), "AndroidInterface"
)
}
private fun loadPageUrl(url: String) {
webView?.loadUrl(url)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: BaseEventBus<String>) {
if (MsConstants.Constants_openFeedbackDetail == event.code) {
MsMMKVUtils.getMMKV()
.putString(MsConstants.CONSTANTS_Detail_id, event.data)
startActivity(
Intent(
this, FeedBackDetailsActivity::class.java
)
)
}
}
override fun onDestroy() {
super.onDestroy()
EventBus.getDefault().unregister(this)
}
}

View File

@ -1,32 +1,95 @@
package com.localee.mireo.app.ui.activity
import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.localee.mireo.app.utils.MsMMKVUtils
import com.blankj.utilcode.util.NetworkUtils
import com.bumptech.glide.Glide
import com.facebook.AccessToken
import com.facebook.CallbackManager
import com.facebook.CallbackManager.Factory.create
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.GraphRequest
import com.facebook.login.LoginManager
import com.facebook.login.LoginResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.gyf.immersionbar.ImmersionBar
import com.hjq.base.FragmentPagerAdapter
import com.hjq.http.EasyHttp
import com.hjq.http.config.IRequestApi
import com.hjq.http.listener.HttpCallbackProxy
import com.localee.mireo.app.R
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.app.AppFragment
import com.localee.mireo.app.http.api.UserInfoApi
import com.localee.mireo.app.http.api.UserInfoRes
import com.localee.mireo.app.app.BaseEventBus
import com.localee.mireo.app.base.FragmentPagerAdapter
import com.localee.mireo.app.http.api.ActionPushApi
import com.localee.mireo.app.http.api.CustomerRegisterApi
import com.localee.mireo.app.http.api.DoLoginApi
import com.localee.mireo.app.http.api.EnterTheAppApi
import com.localee.mireo.app.http.api.FirebaseTokenApi
import com.localee.mireo.app.http.api.LeaveAppApi
import com.localee.mireo.app.http.api.MessageSendReportApi
import com.localee.mireo.app.http.api.OnLineApi
import com.localee.mireo.app.http.api.OpenNotifyApi
import com.localee.mireo.app.http.api.W2aApi
import com.localee.mireo.app.http.bean.HomeDataHistoryBean
import com.localee.mireo.app.http.bean.IncidentBean
import com.localee.mireo.app.http.bean.LoginBean
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.other.Logger
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.other.MsConstants.CONSTANTS_user_refresh
import com.localee.mireo.app.ui.adapter.NavigationAdapter
import com.localee.mireo.app.ui.dialog.NotificationPermissionDialog
import com.localee.mireo.app.ui.fragment.ExploreFragment
import com.localee.mireo.app.ui.fragment.HomeFragment
import com.localee.mireo.app.ui.fragment.MessageFragment
import com.localee.mireo.app.ui.fragment.MineFragment
import com.localee.mireo.app.ui.fragment.RewardFragment
import com.localee.mireo.app.ui.fragment.SharedViewModel
import com.localee.mireo.app.ui.popup.CustomBottomLoginPopup
import com.localee.mireo.app.utils.DHStringUtils.getPublicRequest
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.NotificationUtils
import com.localee.mireo.app.utils.TranslatesUtils
import com.localee.mireo.app.utils.singleClick
import com.localee.mireo.shortapp.R
import com.lxj.xpopup.XPopup
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.json.JSONObject
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener,
CustomBottomLoginPopup.CustomPopupOnclick {
companion object {
@ -47,10 +110,29 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
}
}
private var notificationPermissionDialog: NotificationPermissionDialog? = null
private var callbackManager: CallbackManager? = null
private var scheduler: ScheduledExecutorService? = Executors.newSingleThreadScheduledExecutor()
private val dialogHistory: ViewGroup? by lazy { findViewById(R.id.dialog_history) }
private val ivCloseHistory: ImageView? by lazy { dialogHistory?.findViewById(R.id.iv_close_history) }
private val ivVideo: ImageView? by lazy { dialogHistory?.findViewById(R.id.iv_video) }
private val tvVideoName: TextView? by lazy { dialogHistory?.findViewById(R.id.tv_video_name) }
private val tvVideoLast: TextView? by lazy { dialogHistory?.findViewById(R.id.tv_video_last) }
private val llMain: ConstraintLayout? by lazy { findViewById(R.id.ll_main) }
private val viewPager: ViewPager? by lazy { findViewById(R.id.vp_home_pager) }
private val navigationView: RecyclerView? by lazy { findViewById(R.id.rv_home_navigation) }
private var navigationAdapter: NavigationAdapter? = null
private var pagerAdapter: FragmentPagerAdapter<AppFragment<*>>? = null
private var shortVideoId: Int = 0
private var videoId: Int = 0
private var needSave = false
private var path = ""
private var short_play_id = ""
private var message_id = ""
private var title = ""
private val viewModel: SharedViewModel by viewModels()
@ -59,6 +141,11 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
}
override fun initView() {
EventBus.getDefault().register(this)
path = intent?.getStringExtra("path").toString()
short_play_id = intent?.getStringExtra("short_play_id").toString()
message_id = intent?.getStringExtra("message_id").toString()
title = intent?.getStringExtra("title").toString()
navigationAdapter = NavigationAdapter(this).apply {
addItem(
@ -73,6 +160,12 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
ContextCompat.getDrawable(this@HomeActivity, R.drawable.home_explore_selector)
)
)
addItem(
NavigationAdapter.MenuItem(
getString(R.string.home_nav_rewards),
ContextCompat.getDrawable(this@HomeActivity, R.drawable.home_rewards_selector)
)
)
addItem(
NavigationAdapter.MenuItem(
getString(R.string.home_nav_my_list),
@ -88,14 +181,15 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
setOnNavigationListener(this@HomeActivity)
navigationView?.adapter = this
}
}
override fun initData() {
// getCustomerUser()
pagerAdapter = FragmentPagerAdapter<AppFragment<*>>(this).apply {
addFragment(HomeFragment.newInstance())
addFragment(ExploreFragment.newInstance())
addFragment(RewardFragment.newInstance())
addFragment(MessageFragment.newInstance())
addFragment(MineFragment.newInstance())
viewPager?.adapter = this
@ -103,12 +197,91 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
onNewIntent(intent)
viewModel.action.observe(this) { data ->
switchFragment(data);
switchFragment(data)
}
viewModel.loginAction.observe(this) {
setLoginDialog()
}
if (MsMMKVUtils.getMMKV()
.getString(MsConstants.ACCESS_TOKEN, "").toString()
.isNotEmpty()
) {
enterTheApp()
val intervalMillis = 10 * 60 * 1000
scheduler?.scheduleAtFixedRate({
try {
lifecycleScope.launch {
onLine()
}
} catch (e: Exception) {
e.printStackTrace()
}
}, 0, intervalMillis.toLong(), TimeUnit.MILLISECONDS)
}
callbackManager = create()
LoginManager.getInstance().registerCallback(callbackManager,
object : FacebookCallback<LoginResult> {
override fun onSuccess(loginResult: LoginResult) {
val enableButtons = AccessToken.getCurrentAccessToken() != null
if (enableButtons) {
val mGraphRequest = GraphRequest.newMeRequest(
loginResult.accessToken
) { jsonObject, response ->
if (response!!.error != null) {
toast("Facebook login exception.${response.error?.exception.toString()}")
} else {
val id = jsonObject?.optString("id")
val name = jsonObject?.optString("name")
val object_pic: JSONObject? = jsonObject!!.optJSONObject("picture")
val object_data = object_pic?.optJSONObject("data")
val photo = object_data?.optString("url")
leaveApp()
doLogin(
LoginBean(
photo.toString(),
"",
name.toString(),
"",
"facebook",
id.toString()
)
)
}
}
val parameters = Bundle()
parameters.putString("fields", "id,name,email,picture")
mGraphRequest.parameters = parameters
mGraphRequest.executeAsync()
}
}
override fun onCancel() {
toast("Facebook login Cancel")
}
override fun onError(exception: FacebookException) {
toast("Facebook login exception.$exception")
}
})
GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(this)
.addOnCompleteListener {
if (it.isSuccessful) {
askNotificationPermission()
}
}
llMain?.postDelayed({ notificationGo() }, 700)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
path = intent?.getStringExtra("path").toString()
short_play_id = intent?.getStringExtra("short_play_id").toString()
message_id = intent?.getStringExtra("message_id").toString()
title = intent?.getStringExtra("title").toString()
notificationGo()
pagerAdapter?.let {
switchFragment(it.getFragmentIndex(getSerializable(INTENT_KEY_IN_FRAGMENT_CLASS)))
}
@ -130,10 +303,24 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
if (fragmentIndex == -1) {
return
}
viewPager?.currentItem = fragmentIndex
navigationAdapter?.setSelectedPosition(fragmentIndex)
when (fragmentIndex) {
0, 1, 2, 3 -> {
viewPager?.currentItem = fragmentIndex
navigationAdapter?.setSelectedPosition(fragmentIndex)
0 -> {
dialogHistory?.postDelayed(
{
val string = MsMMKVUtils.getMMKV()
.getString(MsConstants.Constants_Main_Video_info, "")
if (string?.isNotEmpty() == true && NetworkUtils.isConnected()) {
val fromJson = Gson().fromJson(string, HomeDataHistoryBean::class.java)
showHistoryDialog(fromJson)
}
}, 500
)
}
1, 2, 3, 4 -> {
dialogHistory?.visibility = View.INVISIBLE
}
}
}
@ -143,14 +330,199 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
*/
override fun onNavigationItemSelected(position: Int): Boolean {
return when (position) {
0, 1, 2, 3 -> {
0 -> {
dialogHistory?.postDelayed(
{
val string = MsMMKVUtils.getMMKV()
.getString(MsConstants.Constants_Main_Video_info, "")
if (string?.isNotEmpty() == true && NetworkUtils.isConnected()) {
val fromJson = Gson().fromJson(string, HomeDataHistoryBean::class.java)
showHistoryDialog(fromJson)
}
}, 500
)
viewPager?.currentItem = position
true
}
1, 2, 3, 4 -> {
dialogHistory?.visibility = View.INVISIBLE
viewPager?.currentItem = position
true
}
else -> false
}
}
fun getCustomerRegister() {
EasyHttp.post(this)
.api(CustomerRegisterApi().apply {
})
.request(object : HttpCallbackProxy<HttpData<CustomerRegisterApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<CustomerRegisterApi.Bean>) {
result.getData()?.token?.let {
MsConstants.IsFirst = false
MsMMKVUtils.saveToken(it)
EventBus.getDefault().post(MsConstants.CONSTANTS_refresh_me)
EventBus.getDefault().post(MsConstants.CONSTANTS_refresh_home)
}
}
})
}
fun getActionPush() {
val sMap: MutableMap<String, String?> = LinkedHashMap()
// StringMap = getSortMap(StringMap);
sMap.put("action", String::class.simpleName)
EasyHttp.post(this)
.api(ActionPushApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<CustomerRegisterApi.Bean>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<CustomerRegisterApi.Bean>) {
result.getData()?.token?.let {
}
}
})
}
fun enterTheApp() {
EasyHttp.post(this)
.api(EnterTheAppApi())
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
})
}
fun onLine() {
EasyHttp.post(this)
.api(OnLineApi())
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
})
}
fun leaveApp() {
EasyHttp.post(this)
.api(LeaveAppApi())
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
})
}
fun doLogin(data: LoginBean) {
val sMap: MutableMap<String, String?> = LinkedHashMap()
// StringMap = getSortMap(StringMap);
sMap.put("avator", data.avator)
sMap.put("email", data.email)
sMap.put("family_name", data.family_name)
sMap.put("giving_name", data.giving_name)
sMap.put("platform", data.platform)
sMap.put("third_id", data.third_id)
EasyHttp.post(this)
.api(DoLoginApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<DoLoginApi.Bean>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<DoLoginApi.Bean>) {
result.getData()?.let {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.success.toString())
}
Logger.d(
"CompleteRegistration",
"login_".plus(it.customer_id)
)
MsMMKVUtils.getMMKV()
.putString(MsConstants.ACCESS_TOKEN, it.token)
EventBus.getDefault()
.post(MsConstants.CONSTANTS_enterTheApp)
EventBus.getDefault()
.post(MsConstants.CONSTANTS_onLine)
EventBus.getDefault()
.post(CONSTANTS_user_refresh)
// if (activityId.isNotEmpty()) {
// EventBus.getDefault()
// .post(CONSTANTS_web_refresh)
// } else {
// WebRefresh = true
// }
} ?: run {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
}
}
})
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: String) {
if (MsConstants.CONSTANTS_auth_refresh == event) {
getCustomerRegister()
}
if (MsConstants.CONSTANTS_out_login == event) {
getCustomerRegister()
if (TranslatesUtils.translates() != null) {
toast(
TranslatesUtils.translates()?.kick_out_login
?: getString(R.string.your_account_is_already_logged_in_on_another_device)
)
}
}
if (MsConstants.CONSTANTS_refresh_translate == event) {
// exampleHomeViewModel.gettranslates(
// MsMMKVUtils.getMMKV().getString(MsConstants.CONSTANTS_lang_key, "en")
// .toString()
// )
}
if (MsConstants.Constants_language_refresh == event) {
HomeActivity.start(this)
}
if (MsConstants.CONSTANTS_Login == event) {
setLoginDialog()
}
if (MsConstants.CONSTANTS_enterTheApp == event) {
enterTheApp()
}
if (MsConstants.CONSTANTS_leaveApp == event) {
leaveApp()
}
if (MsConstants.CONSTANTS_onLine == event) {
onLine()
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: BaseEventBus<IncidentBean>) {
if (MsConstants.Constants_onTokenRefresh == event.code) {
firebase()
}
}
override fun createStatusBarConfig(): ImmersionBar {
return super.createStatusBarConfig()
@ -161,11 +533,458 @@ class HomeActivity : AppActivity(), NavigationAdapter.OnNavigationListener {
moveTaskToBack(false)
}
override fun onDestroy() {
super.onDestroy()
viewPager?.adapter = null
navigationView?.adapter = null
navigationAdapter?.setOnNavigationListener(null)
}
fun setLoginDialog() {
XPopup.Builder(this)
.hasShadowBg(true)
.isTouchThrough(false)
.isLightStatusBar(false)
.asCustom(CustomBottomLoginPopup(this, this))
.show()
}
override fun onFacebook() {
singleClick {
LoginManager.getInstance()
.logInWithReadPermissions(this, arrayListOf("public_profile"))
}
}
override fun onGoogle() {
toast("google")
}
override fun onTikTok() {
toast("tiktok")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == NotificationUtils.NOTIFICATION_SETTINGS_REQUEST_CODE) {
MsConstants.CanNotification = NotificationUtils.isNotificationEnabled(this)
if (MsConstants.CanNotification) {
firebase()
openNotify()
notificationPermissionDialog?.dismiss()
}
// exampleHomeViewModel.uploadNoticeStatus(
// FbNotificationReq(
// if (MsConstants.CanNotification) "1" else "0"
// )
// )
} else {
callbackManager?.onActivityResult(
requestCode,
resultCode, data
)
}
}
private fun adjustToDetail() {
llMain?.postDelayed({
startActivity(Intent(
this, VideoPlayActivity::class.java
).apply {
putExtra(
MsConstants.CONSTANTS_short_play_id, shortVideoId
)
})
clearClipboardContent(this)
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_DDL_Url, "")
}, 1000)
needSave = false
}
private fun getClipContent(): String {
val manager: ClipboardManager = getSystemService(
CLIPBOARD_SERVICE
) as ClipboardManager
val primaryClip = manager.primaryClip
val itemCount = primaryClip?.itemCount
if (itemCount != null) {
if (manager.hasPrimaryClip() && itemCount > 0) {
val itemAt = manager.primaryClip?.getItemAt(0)
val addedText: CharSequence = itemAt?.text.toString()
val addedTextString = addedText.toString()
if (!TextUtils.isEmpty(addedTextString)) {
return addedTextString
}
}
}
return ""
}
override fun onResume() {
super.onResume()
this.window.decorView.post {
val clipContent = getClipContent()
if (clipContent.isNotEmpty()) {
if (clipContent.startsWith("[QJ]")) {
val urlString = clipContent.removePrefix("[QJ]").trim()
val extractVideoInfo = parseVideoAndShortPlayIds(urlString)
shortVideoId = extractVideoInfo.second?.toInt() ?: 0
videoId = extractVideoInfo.first?.toInt() ?: 0
if (shortVideoId != 0) {
MsMMKVUtils.getMMKV().putString(
MsConstants.CONSTANTS_short_play_id, extractVideoInfo.second
)
needSave = true
w2aSelfAttribution(clipContent)
}
}
}
}
llMain?.postDelayed({
val ddl =
MsMMKVUtils.getMMKV().getString(MsConstants.Constants_DDL_Url, "")
if (ddl?.isNotEmpty() == true) {
w2aSelfAttribution(ddl)
// 定义正则表达式
val regex = """short_play_id=(\d+).*""".toRegex()
// 匹配 URL
val matchResult = regex.find(ddl)
if (matchResult != null) {
// 获取匹配的组
val shortPlayId = matchResult.groupValues[1]
val toInt = shortPlayId.toInt()
if (toInt != 0) {
llMain?.postDelayed({
startActivity(Intent(
this, VideoPlayActivity::class.java
).apply {
putExtra(
MsConstants.CONSTANTS_short_play_id, toInt
)
})
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_DDL_Url, "")
}, 200)
}
} else {
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_DDL_Url, "")
}
}
}, 1500)
if (viewPager?.currentItem == 0) {
dialogHistory?.postDelayed(
{
val string = MsMMKVUtils.getMMKV()
.getString(MsConstants.Constants_Main_Video_info, "")
if (string?.isNotEmpty() == true && NetworkUtils.isConnected()) {
val fromJson = Gson().fromJson(string, HomeDataHistoryBean::class.java)
showHistoryDialog(fromJson)
}
}, 500
)
}
}
private fun clearClipboardContent(context: Context) {
// 获取ClipboardManager的实例
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
// 创建一个空的ClipData对象
val emptyClip = ClipData.newPlainText("", "")
// 将空的ClipData对象设置到剪切板上从而清除之前的内容
clipboardManager.setPrimaryClip(emptyClip)
}
private fun parseVideoAndShortPlayIds(clipboardContent: String): Pair<String?, String?> {
// 提取查询字符串
val queryStartIndex = clipboardContent.indexOf('?')
val queryString =
if (queryStartIndex != -1) clipboardContent.substring(queryStartIndex + 1) else ""
// 使用正则表达式匹配 video_id 和 short_play_id
val videoIdRegex = Regex("video_id=(\\d+)")
val shortPlayIdRegex = Regex("short_play_id=(\\d+)")
// 匹配 video_id 和 short_play_id
val videoIdMatch = videoIdRegex.find(queryString)?.groupValues?.get(1)
val shortPlayIdMatch = shortPlayIdRegex.find(queryString)?.groupValues?.get(1)
return Pair(videoIdMatch, shortPlayIdMatch)
}
private fun w2aSelfAttribution(data: String?) {
if (data?.contains("follow") == true) {
val regex = """facebook_id=(\d+).*""".toRegex()
// 匹配 URL
val matchResult = regex.find(data)
if (matchResult != null) {
val facebook_id = matchResult.groupValues[1]
// exampleHomeViewModel.deepFb(FbDeepReq(facebook_id))
}
}
data?.let { w2aSelfAttributionLoad(it) }
}
fun w2aSelfAttributionLoad(data: String?) {
val sMap: MutableMap<String, String?> = LinkedHashMap()
// StringMap = getSortMap(StringMap);
sMap.put("data", data)
EasyHttp.post(this)
.api(W2aApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
if (needSave) {
adjustToDetail()
}
}
})
}
private fun showHistoryDialog(data: HomeDataHistoryBean) {
ivCloseHistory?.setOnClickListener {
MsMMKVUtils.getMMKV()
.putBoolean(MsConstants.Constants_Main_Video_status, false)
dialogHistory?.visibility = View.INVISIBLE
}
if (MsMMKVUtils.getMMKV()
.getBoolean(MsConstants.Constants_Main_Video_status, false)
) {
ivVideo?.let {
if (!isFinishing && !isDestroyed) {
Glide.with(this).load(
data.video_img
).placeholder(R.mipmap.ic_loading_h).into(it)
}
}
dialogHistory?.setOnClickListener {
singleClick {
startActivity(Intent(
this, VideoPlayActivity::class.java
).apply {
putExtra(
MsConstants.CONSTANTS_short_play_id, data.video_id
)
})
}
}
dialogHistory?.post {
tvVideoName?.text = data.video_name
tvVideoLast?.text = "Episode " + data.video_last
}
dialogHistory?.visibility = View.VISIBLE
}
}
private fun firebase() {
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.d(
"LOG_TAG", "Fetching FCM registration token failed", task.exception
)
return@OnCompleteListener
}
// Get new FCM registration token
val token = task.result
// Log and toast
Log.d("LOG_TAG", token)
firebaseToken(token)
})
}
fun firebaseToken(data: String?) {
val sMap: MutableMap<String, String?> = LinkedHashMap()
sMap.put("fcm_token", data)
EasyHttp.post(this)
.api(FirebaseTokenApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
})
}
private fun notificationGo() {
if (message_id.isNotBlank() && !message_id.contentEquals("null")) {
if ("0" != message_id) {
messageSendReport(message_id, title)
}
}
when (path) {
"detail" -> {
if (short_play_id.isNotEmpty() && "null" != short_play_id) {
try {
val toInt = short_play_id.toInt()
llMain?.postDelayed({
startActivity(Intent(
this, VideoPlayActivity::class.java
).apply {
putExtra(
MsConstants.CONSTANTS_short_play_id, toInt
)
})
}, 700)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
"promotion" -> {
switchFragment(2)
}
"orderDetail" -> {
llMain?.postDelayed({
startActivity(
Intent(
this, StoreActivity::class.java
)
)
}, 700)
}
"feedback" -> {
llMain?.postDelayed({
if (message_id.isNotBlank() && message_id != "null") {
MsMMKVUtils.getMMKV()
.putString(MsConstants.CONSTANTS_Detail_id, message_id)
startActivity(
Intent(
this, FeedBackDetailsActivity::class.java
)
)
} else {
startActivity(
Intent(
this, FeedBackDetailsActivity::class.java
)
)
}
}, 700)
}
}
}
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
MsConstants.CanNotification = isGranted
if (isGranted) {
firebase()
openNotify()
} else {
NotificationUtils.openNotificationSettings(this)
}
notificationPermissionDialog?.dismiss()
}
private fun askNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
firebase()
openNotify()
} else {
// if (shouldShowNotification()) {
openNotification()
// }
}
} else {
if (NotificationUtils.isNotificationEnabled(this)) {
firebase()
openNotify()
} else {
// if (shouldShowNotification()) {
openNotification()
// }
}
}
}
private fun openNotification() {
notificationPermissionDialog = NotificationPermissionDialog(this)
val example_tv_content =
notificationPermissionDialog?.findViewById<AppCompatTextView>(R.id.example_tv_content)
val example_tv_content_info =
notificationPermissionDialog?.findViewById<AppCompatTextView>(R.id.example_tv_content_info)
val example_tv_later =
notificationPermissionDialog?.findViewById<AppCompatTextView>(R.id.example_tv_later)
val iv_close_notification =
notificationPermissionDialog?.findViewById<AppCompatImageView>(R.id.iv_close_notification)
val example_open =
notificationPermissionDialog?.findViewById<AppCompatTextView>(R.id.example_open)
example_tv_content?.text =
TranslatesUtils.translates()?.open_notification ?: "Enable Notifications"
example_tv_content_info?.text = TranslatesUtils.translates()?.open_notification_info
?: "Stay informed with popular recommendations and latest updates!"
example_tv_later?.text = TranslatesUtils.translates()?.open_notification_later ?: "Later"
example_open?.text = TranslatesUtils.translates()?.open_notification_open ?: "Open"
notificationPermissionDialog?.setOnDismissListener {
MsMMKVUtils.getMMKV().putLong(
MsConstants.CONSTANTS_PREF_LAST_POPUP_TIME_Notification,
System.currentTimeMillis()
)
}
example_tv_later?.setOnClickListener { notificationPermissionDialog?.dismiss() }
iv_close_notification?.setOnClickListener { notificationPermissionDialog?.dismiss() }
example_open?.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
notificationPermissionDialog?.dismiss()
NotificationUtils.openNotificationSettings(this)
}
}
notificationPermissionDialog?.show()
}
fun openNotify() {
EasyHttp.post(this)
.api(OpenNotifyApi())
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
override fun onHttpFail(throwable: Throwable) {
}
})
}
fun messageSendReport(message_id: String, title: String) {
val sMap: MutableMap<String, String?> = LinkedHashMap()
sMap.put("message_id", message_id)
sMap.put("title", title)
EasyHttp.post(this)
.api(MessageSendReportApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<Any>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<Any>) {
}
})
}
}

View File

@ -0,0 +1,214 @@
package com.localee.mireo.app.ui.activity
import android.graphics.Rect
import android.view.View
import android.widget.CheckBox
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hjq.bar.TitleBar
import com.hjq.http.EasyHttp
import com.hjq.http.listener.HttpCallbackProxy
import com.hjq.shape.layout.ShapeConstraintLayout
import com.localee.mireo.shortapp.R
import com.localee.mireo.app.action.StatusAction
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.http.api.LanguageApi
import com.localee.mireo.app.http.api.TranslatesLanguageApi
import com.localee.mireo.app.http.bean.TranslatesBean
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.other.MsConstants
import com.localee.mireo.app.ui.adapter.LanguageSwitchAdapter
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.TranslatesUtils
import com.localee.mireo.app.utils.singleClick
import com.localee.mireo.app.widget.StatusLayout
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.util.Locale
class LanguageSwitchActivity : AppActivity(), StatusAction {
private val title: TitleBar? by lazy { findViewById(R.id.title) }
private val hintLayout: StatusLayout? by lazy { findViewById(R.id.hl_status_hint) }
private val clSystem: ShapeConstraintLayout? by lazy { findViewById(R.id.cl_system) }
private val tvSystem: TextView? by lazy { findViewById(R.id.tv_system) }
private val tvDescription: TextView? by lazy { findViewById(R.id.tv_description) }
private val cbSystem: CheckBox? by lazy { findViewById(R.id.cb_system) }
private val recyclerView: RecyclerView? by lazy { findViewById(R.id.recyclerView) }
private val tvOk: TextView? by lazy { findViewById(R.id.tv_ok) }
private var mAdapter: LanguageSwitchAdapter? = null
private var langKey = ""
private var isSystem = false
override fun getLayoutId(): Int {
return R.layout.activity_language_switch
}
override fun initView() {
val layoutManager = GridLayoutManager(this, 2)
// 设置给 RecyclerView
recyclerView!!.layoutManager = layoutManager
mAdapter = LanguageSwitchAdapter()
recyclerView?.adapter = mAdapter
recyclerView?.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
outRect.right = 8
outRect.left = 8
outRect.top = 8
outRect.bottom = 8
}
})
mAdapter?.setOnItemClickListener { adapter, view, position ->
isSystem = false
updateUIForSystemLanguage(false)
val language = mAdapter?.getItem(position) as LanguageApi.Bean.Data
langKey = language.lang_key
mAdapter?.currentPosition = position
mAdapter?.notifyDataSetChanged()
}
clSystem?.setOnClickListener {
isSystem = true
updateUIForSystemLanguage(true)
mAdapter?.currentPosition = -1
mAdapter?.notifyDataSetChanged()
}
tvOk?.setOnClickListener {
singleClick {
if (isSystem) {
val locale = when (Locale.getDefault().language) {
"zh" -> when (Locale.getDefault().country) {
"HK", "TW" -> "zh_hk"
"CN" -> "zh"
else -> Locale.getDefault().language
}
else -> Locale.getDefault().language
}
langKey = locale
getTranslatesLanguageApi(langKey)
} else {
if (langKey.isEmpty()) return@singleClick
getTranslatesLanguageApi(langKey)
}
}
}
}
override fun initData() {
TranslatesUtils.translates()?.let { translates ->
title?.setTitle(translates.change_language)
tvDescription?.text = translates.lang_option_notice
tvSystem?.text = "${translates.system}${translates.language}"
tvOk?.text = translates.ok
}
getLanguage()
}
fun getLanguage() {
EasyHttp.get(this)
.api(LanguageApi())
.request(object : HttpCallbackProxy<HttpData<LanguageApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<LanguageApi.Bean>) {
result.getData()?.let {
if (it.list.isEmpty()) {
hintLayout?.show()
} else {
hintLayout?.hide()
}
mAdapter?.submitList(it.list)
if (MsMMKVUtils.getMMKV()
.getString(MsConstants.Constants_language_set, "") == "system"
) {
updateUIForSystemLanguage(true)
isSystem = true
} else {
updateUIForSystemLanguage(false)
isSystem = false
val string = MsMMKVUtils.getMMKV()
.getString(MsConstants.CONSTANTS_lang_key, "en")
it.list.find { item -> item.lang_key == string }?.let { foundItem ->
mAdapter?.currentPosition = it.list.indexOf(foundItem)
}
langKey = string.toString()
mAdapter?.notifyDataSetChanged()
}
}
}
})
}
fun getTranslatesLanguageApi(languageKey: String) {
EasyHttp.get(this)
.api(TranslatesLanguageApi().apply {
language_key = languageKey
})
.request(object : HttpCallbackProxy<HttpData<TranslatesBean>>(this) {
override fun onHttpSuccess(result: HttpData<TranslatesBean>) {
result.getData()?.let {
MsMMKVUtils.getMMKV()
.putString(MsConstants.Constants_Main_Video_info, "")
lifecycleScope.launch {
withContext(Dispatchers.IO) {
TranslatesUtils.saveTranslates(it.translates)
}
}
MsMMKVUtils.getMMKV().putString(
MsConstants.Constants_language_set,
if (isSystem) "system" else "custom"
)
if (langKey.isNotEmpty()) {
MsMMKVUtils.getMMKV()
.putString(MsConstants.CONSTANTS_lang_key, langKey)
EventBus.getDefault().post(MsConstants.Constants_language_refresh)
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(langKey))
}
finish()
} ?: run {
toast(
TranslatesUtils.translates()?.network_error?.toString()
?: getString(R.string.example_service_exception_please_try_again)
)
}
}
})
}
private fun updateUIForSystemLanguage(isSystem: Boolean) {
if (isSystem) {
cbSystem?.isChecked = true
clSystem?.shapeDrawableBuilder?.setSolidColor(0x33ff0049)?.setStrokeSize(1)
?.intoBackground()
} else {
cbSystem?.isChecked = false
clSystem?.shapeDrawableBuilder?.setSolidColor(0xff272a30.toInt())?.setStrokeSize(0)
?.intoBackground()
}
}
override fun getStatusLayout(): StatusLayout? {
return hintLayout
}
}

View File

@ -0,0 +1,462 @@
package com.localee.mireo.app.ui.activity
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.billingclient.api.AcknowledgePurchaseParams
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.ProductDetailsResponseListener
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryProductDetailsParams
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.request.RequestOptions
import com.google.common.reflect.TypeToken
import com.google.gson.Gson
import com.hjq.bar.TitleBar
import com.hjq.http.EasyHttp
import com.hjq.http.config.IRequestApi
import com.hjq.http.listener.HttpCallbackProxy
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.http.api.CreateOrderApi
import com.localee.mireo.app.http.api.GooglePaidApi
import com.localee.mireo.app.http.api.PaySettingsApi
import com.localee.mireo.app.http.api.UserInfoApi
import com.localee.mireo.app.http.api.UserInfoRes
import com.localee.mireo.app.http.bean.CreateOrderReqBean
import com.localee.mireo.app.http.bean.PayBean
import com.localee.mireo.app.http.bean.PayResBean
import com.localee.mireo.app.http.bean.PaySettingsBean
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.other.AppConfig
import com.localee.mireo.app.other.Logger
import com.localee.mireo.app.ui.adapter.MyVipBuyAdapter
import com.localee.mireo.app.utils.DHStringUtils.getPublicRequest
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.TranslatesUtils
import com.localee.mireo.app.utils.singleClick
import com.localee.mireo.app.utils.transToString
import com.localee.mireo.shortapp.R
import kotlinx.coroutines.launch
class MyVipActivity : AppActivity() {
private val title: TitleBar? by lazy { findViewById(R.id.title) }
private val tvName: TextView? by lazy { findViewById(R.id.tv_name) }
private val ivPortrait: ImageView? by lazy { findViewById(R.id.iv_portrait) }
private val tvVipExpires: TextView? by lazy { findViewById(R.id.tv_vip_expires) }
private val tvVipLeave: TextView? by lazy { findViewById(R.id.tv_vip_leave) }
private val progressVip: ProgressBar? by lazy { findViewById(R.id.progress_vip) }
private val tvNextVip: TextView? by lazy { findViewById(R.id.tv_next_vip) }
private val recyclerView: RecyclerView? by lazy { findViewById(R.id.recyclerView) }
private val tvBuy: TextView? by lazy { findViewById(R.id.tv_buy) }
private val ivVip: ImageView? by lazy { findViewById(R.id.iv_vip) }
private var billingClient: BillingClient? = null
private var isConnect = false
private var connectNum = 0
private var order_code = ""
private var vipData: PaySettingsBean.Vip? = null
private var payReq: PayBean? = null
private var mAdapter: MyVipBuyAdapter? = null
override fun getLayoutId(): Int {
return R.layout.activity_my_vip
}
override fun initView() {
recyclerView?.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
mAdapter = MyVipBuyAdapter()
recyclerView?.adapter = mAdapter
mAdapter?.setOnItemClickListener { adapter, view, position ->
mAdapter?.currentPosition = position
mAdapter?.notifyDataSetChanged()
}
setUI()
initPay()
tvBuy?.setOnClickListener {
if (mAdapter?.currentPosition == -1) {
toast(getString(R.string.select_a_payment_item))
return@setOnClickListener
}
singleClick {
if (!isConnect) {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.g_pay_error.toString())
} else {
toast(getString(R.string.google_pay_error))
}
return@singleClick
}
showDialog()
vipData = mAdapter!!.getItem(mAdapter!!.currentPosition) as PaySettingsBean.Vip
getCreateOrder(
CreateOrderReqBean(
vipData?.id.toString(),
"google",
0,
0
)
)
}
}
}
private fun setUI() {
if (MsMMKVUtils.isTourist()) {
tvName?.text = getString(R.string.example_visitor)
} else {
tvName?.text =
MsMMKVUtils.getUserInfo()?.family_name.plus(MsMMKVUtils.getUserInfo()?.giving_name)
ivPortrait?.let {
Glide.with(this).load(MsMMKVUtils.getUserInfo()?.avator).skipMemoryCache(true)
.diskCacheStrategy(
DiskCacheStrategy.NONE
)
.apply(RequestOptions.bitmapTransform(CircleCrop()))
.placeholder(R.mipmap.iv_login_icon)
.error(R.mipmap.iv_login_icon).into(it)
}
}
if (MsMMKVUtils.isVip()) {
ivVip?.visibility = View.VISIBLE
tvVipExpires?.text =
"Vip Expires".plus(MsMMKVUtils.getUserInfo()?.vip_end_time?.let {
transToString(
it.toLong()
)
})
} else {
ivVip?.visibility = View.GONE
tvVipExpires?.text = "Enjoy all dramas for free"
}
}
override fun initData() {
getPaySettings(null, null)
}
private fun getPaySettings(shortPlayId: Int?, videoId: Int?) {
EasyHttp.get(this)
.api(PaySettingsApi().apply {
short_play_id = shortPlayId
short_play_video_id = videoId
})
.request(object : HttpCallbackProxy<HttpData<PaySettingsBean>>(this) {
override fun onHttpSuccess(result: HttpData<PaySettingsBean>) {
result.getData()?.let {
mAdapter?.submitList(it.list_sub_vip)
it.list_sub_vip?.let { it1 -> querySubProductDetails(it1) }
}
}
})
}
private fun initPay() {
val purchasesUpdatedListener =
PurchasesUpdatedListener { billingResult, purchases ->
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
for (purchase in purchases!!) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
consumePurchaseSub(purchase)
}
}
}
BillingClient.BillingResponseCode.USER_CANCELED -> {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.g_pay_cancel.toString())
} else {
toast(getString(R.string.google_pay_canceled))
}
Logger.d(
"", order_code
)
hideDialog()
}
else -> {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.g_pay_error.toString())
} else {
toast(getString(R.string.google_pay_error))
}
hideDialog()
}
}
}
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
val stateListener: BillingClientStateListener = object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
isConnect = true
}
}
override fun onBillingServiceDisconnected() {
if (connectNum < 5) {
connectNum++
isConnect = false
billingClient?.startConnection(this)
}
}
}
billingClient?.startConnection(stateListener)
}
private fun consumePurchaseSub(
purchase: Purchase
) {
if (billingClient?.isReady == true) {
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient?.acknowledgePurchase(
acknowledgePurchaseParams
) {
val examplePayReq = PayBean(
order_code,
vipData?.id.toString(),
AppConfig.getPackageName(),
vipData?.android_template_id.toString(),
purchase.purchaseToken,
purchase.orderId.toString(),
vipData?.price.toString()
)
if (it.responseCode == BillingClient.BillingResponseCode.OK) {
lifecycleScope.launch {
googlePaid(examplePayReq)
payReq = examplePayReq
MsMMKVUtils.saveOrder(examplePayReq)
}
} else {
MsMMKVUtils.saveOrder(examplePayReq)
lifecycleScope.launch {
toast(it.toString())
hideDialog()
}
}
}
}
}
}
private fun querySubProductDetails(listSubVip: List<PaySettingsBean.Vip>) {
val productDetailsResponseListener =
ProductDetailsResponseListener { billingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val priceInfo = productDetailsList.mapNotNull { productDetails ->
productDetails.subscriptionOfferDetails?.get(0)?.pricingPhases?.pricingPhaseList?.get(
0
)?.let {
productDetails.productId to (it.formattedPrice to it.priceCurrencyCode)
}
}.toMap()
// 更新VIP列表的价格和货币代码
val updatedVipList = listSubVip.map { vip ->
priceInfo[vip.android_template_id]?.let { (price, currency) ->
vip.copy(price_google = price, currency_goolge = currency)
} ?: vip
}
mAdapter?.recyclerView?.postDelayed({
mAdapter?.submitList(updatedVipList)
hideDialog()
}, 500)
} else {
hideDialog()
}
}
val productType: String = BillingClient.ProductType.SUBS
val inAppProductInfo = listSubVip.map {
QueryProductDetailsParams.Product.newBuilder()
.setProductId(it.android_template_id)
.setProductType(productType)
.build()
}
if (inAppProductInfo.isNotEmpty()) {
val productDetailsParams = QueryProductDetailsParams.newBuilder()
.setProductList(inAppProductInfo)
.build()
billingClient?.queryProductDetailsAsync(
productDetailsParams,
productDetailsResponseListener
)
} else {
hideDialog()
}
}
private fun getProduct(productId: String) {
val productDetailsResponseListener =
ProductDetailsResponseListener { billingResult, productDetailsList ->
if (productDetailsList.isNotEmpty()) {
pay(productDetailsList[0])
} else {
lifecycleScope.launch {
toast(billingResult.toString())
hideDialog()
}
}
}
val inAppProductInfo = ArrayList<QueryProductDetailsParams.Product>()
inAppProductInfo.add(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(productId)
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
val productDetailsParams = QueryProductDetailsParams.newBuilder()
.setProductList(inAppProductInfo)
.build()
billingClient?.queryProductDetailsAsync(
productDetailsParams,
productDetailsResponseListener
)
}
private fun pay(productDetailInfo: ProductDetails) {
if (productDetailInfo.subscriptionOfferDetails?.isNotEmpty() == true) {
val params = ArrayList<BillingFlowParams.ProductDetailsParams>()
productDetailInfo.subscriptionOfferDetails?.get(0)?.offerToken?.let {
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetailInfo)
.setOfferToken(it)
.build()
}?.let {
params.add(
it
)
}
val billingFlowParams = BillingFlowParams.newBuilder()
.setObfuscatedProfileId(order_code)
.setObfuscatedAccountId(MsMMKVUtils.getCustomId())
.setProductDetailsParamsList(params)
.build()
billingClient?.launchBillingFlow(this, billingFlowParams)
}
}
fun Any.toMapViaGson(): MutableMap<String, String?> {
val json = Gson().toJson(this)
return Gson().fromJson(json, object : TypeToken<MutableMap<String, String?>>() {}.type)
}
fun getCreateOrder(data: CreateOrderReqBean) {
EasyHttp.post(this)
.api(CreateOrderApi())
.body(getPublicRequest(data.toMapViaGson()))
.request(object : HttpCallbackProxy<HttpData<CreateOrderApi.Bean>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<CreateOrderApi.Bean>) {
result.getData()?.let {
order_code = it.order_code.toString()
vipData?.android_template_id?.let { it1 -> getProduct(it1) }
} ?: run {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
}
}
override fun onHttpFail(throwable: Throwable) {
super.onHttpFail(throwable)
}
})
}
fun googlePaid(data: PayBean) {
EasyHttp.post(this)
.api(GooglePaidApi())
.body(getPublicRequest(data.toMapViaGson()))
.request(object : HttpCallbackProxy<HttpData<PayResBean>>(this) {
override fun onHttpStart(api: IRequestApi) {
}
override fun onHttpSuccess(result: HttpData<PayResBean>) {
result.getData()?.let {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.g_pay_success.toString())
} else {
toast(getString(R.string.google_pay_success))
}
if (null != payReq) {
MsMMKVUtils.removeOrder(payReq)
}
getCustomerUser()
} ?: run {
if (TranslatesUtils.translates() != null) {
toast(TranslatesUtils.translates()?.network_error.toString())
} else {
toast(getString(R.string.example_service_exception_please_try_again))
}
}
}
})
}
private fun getCustomerUser() {
EasyHttp.get(this)
.api(UserInfoApi())
.request(object : HttpCallbackProxy<HttpData<UserInfoRes>>(this) {
override fun onHttpSuccess(result: HttpData<UserInfoRes>) {
result.getData()?.let {
MsMMKVUtils.saveUserInfo(it)
setUI()
}
}
})
}
override fun onDestroy() {
super.onDestroy()
payReq = null
billingClient?.endConnection()
billingClient = null
System.gc()
}
}

View File

@ -0,0 +1,321 @@
package com.localee.mireo.app.ui.activity
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.request.RequestOptions
import com.hjq.bar.TitleBar
import com.hjq.demo.ui.dialog.MenuDialog
import com.hjq.http.EasyHttp
import com.hjq.http.listener.HttpCallbackProxy
import com.localee.mireo.app.action.StatusAction
import com.localee.mireo.app.app.AppActivity
import com.localee.mireo.app.base.BaseDialog
import com.localee.mireo.app.http.api.CustomerBuyRecordsApi
import com.localee.mireo.app.http.api.CustomerOrderApi
import com.localee.mireo.app.http.api.SendCoinListApi
import com.localee.mireo.app.http.model.HttpData
import com.localee.mireo.app.ui.adapter.CustomerBuyRecordAdapter
import com.localee.mireo.app.ui.adapter.CustomerOrderRecordAdapter
import com.localee.mireo.app.ui.adapter.SendCoinRecordAdapter
import com.localee.mireo.app.utils.DHStringUtils.getPublicRequest
import com.localee.mireo.app.utils.MsMMKVUtils
import com.localee.mireo.app.utils.singleClick
import com.localee.mireo.app.utils.transToString
import com.localee.mireo.app.widget.StatusLayout
import com.localee.mireo.app.widget.StatusLayout.OnRetryListener
import com.localee.mireo.app.widget.layout.SettingBar
import com.localee.mireo.shortapp.R
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
class MyWalletActivity : AppActivity(), OnRefreshLoadMoreListener,
StatusAction {
private val title: TitleBar? by lazy { findViewById(R.id.title) }
private val tvName: TextView? by lazy { findViewById(R.id.tv_name) }
private val ivPortrait: ImageView? by lazy { findViewById(R.id.iv_portrait) }
private val tvVipExpires: TextView? by lazy { findViewById(R.id.tv_vip_expires) }
private val ivVip: ImageView? by lazy { findViewById(R.id.iv_vip) }
private val tvTopUp: TextView? by lazy { findViewById(R.id.tv_top_up) }
private val tvConinsText: TextView? by lazy { findViewById(R.id.tv_conins_text) }
private val tvConins: TextView? by lazy { findViewById(R.id.tv_conins) }
private val tvDonateText: TextView? by lazy { findViewById(R.id.tv_donate_text) }
private val tvDonate: TextView? by lazy { findViewById(R.id.tv_donate) }
private val sbDetailTitle: SettingBar? by lazy { findViewById(R.id.sb_detail_title) }
private val hintLayout: StatusLayout? by lazy { findViewById(R.id.hl_status_hint) }
private val recyclerView: RecyclerView? by lazy { findViewById(R.id.recyclerView) }
private val tvFeedback: TextView? by lazy { findViewById(R.id.tv_feedback) }
private val rlStatusRefresh: SmartRefreshLayout? by lazy { findViewById(R.id.rl_status_refresh) }
private var selectPosition = 0
private var mBuyRecordAdapter: CustomerBuyRecordAdapter? = null
private var mSendCoinRecordAdapter: SendCoinRecordAdapter? = null
private var mCustomerOrderRecordAdapter: CustomerOrderRecordAdapter? = null
private var pageIndex = 1
private var pageTotal = 1
private var pageSize = 10
override fun getLayoutId(): Int {
return R.layout.activity_my_wallet
}
override fun initView() {
recyclerView?.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
mBuyRecordAdapter = CustomerBuyRecordAdapter()
mSendCoinRecordAdapter = SendCoinRecordAdapter()
mCustomerOrderRecordAdapter = CustomerOrderRecordAdapter()
rlStatusRefresh?.setOnRefreshLoadMoreListener(this)
rlStatusRefresh?.setEnableRefresh(false)
setUI()
}
private fun setUI() {
if (MsMMKVUtils.isTourist()) {
tvName?.text = getString(R.string.example_visitor)
} else {
tvName?.text =
MsMMKVUtils.getUserInfo()?.family_name.plus(MsMMKVUtils.getUserInfo()?.giving_name)
ivPortrait?.let {
Glide.with(this).load(MsMMKVUtils.getUserInfo()?.avator).skipMemoryCache(true)
.diskCacheStrategy(
DiskCacheStrategy.NONE
)
.apply(RequestOptions.bitmapTransform(CircleCrop()))
.placeholder(R.mipmap.iv_login_icon)
.error(R.mipmap.iv_login_icon).into(it)
}
}
if (MsMMKVUtils.isVip()) {
ivVip?.visibility = View.VISIBLE
tvVipExpires?.text =
"Vip Expires".plus(MsMMKVUtils.getUserInfo()?.vip_end_time?.let {
transToString(
it.toLong()
)
})
} else {
ivVip?.visibility = View.GONE
tvVipExpires?.text = "Enjoy all dramas for free"
}
tvConins?.text =
MsMMKVUtils.getUserInfo()?.coin_left_total.toString()
tvDonate?.text =
MsMMKVUtils.getUserInfo()?.send_coin_left_total.toString()
}
override fun initData() {
sbDetailTitle?.setOnClickListener {
val data = ArrayList<String>()
data.add("Consumption Records")
data.add("Recharge Coins")
data.add("Purchase VIP")
data.add("Reward Coins")
MenuDialog.Builder(this)
.setList(data)
.setSelectPosition(selectPosition)
.setListener(object : MenuDialog.OnListener<String> {
override fun onSelected(dialog: BaseDialog?, position: Int, data: String) {
if (selectPosition != position) {
pageIndex = 1
selectPosition = position
sbDetailTitle?.setRightText(data)
loadData()
}
}
override fun onCancel(dialog: BaseDialog?) {
}
})
.show()
}
tvTopUp?.setOnClickListener {
startActivity(StoreActivity::class.java)
}
loadData()
}
fun loadData() {
when (selectPosition) {
0 -> {
recyclerView?.adapter = mBuyRecordAdapter
rlStatusRefresh?.setEnableLoadMore(false)
setCustomerBuyRecords()
}
1 -> {
recyclerView?.adapter = mCustomerOrderRecordAdapter
rlStatusRefresh?.setEnableLoadMore(true)
getCustomerOrder("coins")
}
2 -> {
recyclerView?.adapter = mCustomerOrderRecordAdapter
rlStatusRefresh?.setEnableLoadMore(true)
getCustomerOrder("vip")
}
3 -> {
recyclerView?.adapter = mSendCoinRecordAdapter
rlStatusRefresh?.setEnableLoadMore(true)
setSendCoinList()
}
}
}
fun setSendCoinList() {
val sMap: MutableMap<String, String?> = LinkedHashMap()
sMap["current_page"] = pageIndex.toString()
sMap["page_size"] = pageSize.toString()
EasyHttp.post(this)
.api(SendCoinListApi())
.body(getPublicRequest(sMap))
.request(object : HttpCallbackProxy<HttpData<SendCoinListApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<SendCoinListApi.Bean>) {
result.getData()?.let {
if (pageIndex == 1) {
if (it.list.isEmpty()) {
showEmpty()
} else {
showComplete()
}
mSendCoinRecordAdapter?.submitList(it.list)
it.pagination?.let { it1 ->
pageTotal = it1.page_total
}
} else {
mSendCoinRecordAdapter?.addAll(it.list)
}
} ?: run {
showEmpty()
}
}
override fun onHttpFail(throwable: Throwable) {
super.onHttpFail(throwable)
if (pageIndex == 1) {
showError(object : OnRetryListener {
override fun onRetry(layout: StatusLayout) {
singleClick {
loadData()
}
}
})
}
}
})
}
fun setCustomerBuyRecords() {
EasyHttp.get(this)
.api(CustomerBuyRecordsApi())
.request(object : HttpCallbackProxy<HttpData<CustomerBuyRecordsApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<CustomerBuyRecordsApi.Bean>) {
result.getData()?.let {
if (it.list.isEmpty()) {
showEmpty()
} else {
showComplete()
}
mBuyRecordAdapter?.submitList(it.list)
} ?: run {
showEmpty()
}
}
override fun onHttpFail(throwable: Throwable) {
super.onHttpFail(throwable)
if (pageIndex == 1) {
showError(object : OnRetryListener {
override fun onRetry(layout: StatusLayout) {
singleClick {
loadData()
}
}
})
}
}
})
}
fun getCustomerOrder(buyType: String) {
EasyHttp.get(this)
.api(CustomerOrderApi().apply {
buy_type = buyType
current_page = pageIndex
page_size = pageSize
})
.request(object : HttpCallbackProxy<HttpData<CustomerOrderApi.Bean>>(this) {
override fun onHttpSuccess(result: HttpData<CustomerOrderApi.Bean>) {
result.getData()?.let {
if (pageIndex == 1) {
if (it.list.isEmpty()) {
showEmpty()
} else {
showComplete()
}
mCustomerOrderRecordAdapter?.submitList(it.list)
it.pagination?.let { it1 ->
pageTotal = it1.page_total
}
} else {
mCustomerOrderRecordAdapter?.addAll(it.list)
}
} ?: run {
showEmpty()
}
}
override fun onHttpFail(throwable: Throwable) {
super.onHttpFail(throwable)
if (pageIndex == 1) {
showError(object : OnRetryListener {
override fun onRetry(layout: StatusLayout) {
singleClick {
loadData()
}
}
})
}
}
})
}
override fun onRefresh(refreshLayout: RefreshLayout) {
}
override fun onLoadMore(refreshLayout: RefreshLayout) {
if (pageIndex == pageTotal) {
rlStatusRefresh?.finishLoadMore(1000)
} else {
pageIndex++
loadData()
}
}
override fun getStatusLayout(): StatusLayout? {
return hintLayout
}
}

Some files were not shown because too many files have changed in this diff Show More