commit 058e46bae8a5a041771c8e9a665156aa56147b67 Author: raoqian <506411586@qq.com> Date: Wed Sep 17 18:35:26 2025 +0800 初次代码上传 diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..da6ad36 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,109 @@ +import java.text.SimpleDateFormat +import java.util.Date + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +// id("com.google.gms.google-services") +// id("com.google.firebase.firebase-perf") +} + +android { + namespace = "com.jia.er.nebuluxe.app" + compileSdk = 35 + + defaultConfig { + applicationId = "com.jia.er.nebuluxe.app" + minSdk = 24 + targetSdk = 35 + versionCode = 1 + versionName = "1.0.0" + } + + buildTypes { + debug { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) +// signingConfig = signingConfigs.getByName("debug") + } + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + buildConfig = true + viewBinding = true + } + android.applicationVariants.all { + val buildType = this.buildType.name + val date = SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(Date()) + outputs.all { + if (this is com.android.build.gradle + .internal.api.ApkVariantOutputImpl + ) { + this.outputFileName = "Nebuluxe" + + "_${android.defaultConfig.versionName}_${android.defaultConfig.versionCode}_${date}_${buildType}.apk" + } + } + } + +} + +dependencies { + implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar")))) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.material) + implementation(libs.com.github.wuao.flycotablayout2) + implementation(libs.io.github.youth5201314.banner) + implementation(libs.io.github.cymchad.baserecyclerviewadapterhelper4) + implementation(libs.com.github.bumptech.glide.glide2) + implementation(libs.org.greenrobot.eventbus3) + implementation(libs.androidx.media3.media3.exoplayer2) + implementation(libs.androidx.media3.media3.exoplayer.dash2) + implementation(libs.androidx.media3.media3.ui2) + implementation(libs.androidx.media3.media3.exoplayer.hls2) + implementation(libs.androidx.media3.media3.exoplayer.rtsp2) + implementation(libs.squareup.okhttp) + implementation(libs.squareup.logging.interceptor) + implementation(libs.squareup.retrofit) + implementation(libs.squareup.converter.gson) + implementation(libs.com.tencent.mmkv) + implementation(libs.androidx.lifecycle.lifecycle.livedata.ktx3) + implementation(libs.androidx.lifecycle.lifecycle.extensions4) + implementation(libs.scwang90.refresh.layout.kernel) + implementation(libs.scwang90.refresh.header.material) + implementation(libs.scwang90.refresh.footer.classics) + implementation(libs.com.getkeepsafe.relinker.relinker) + implementation(libs.utilcodex) + implementation(libs.shapeblurview) + implementation(libs.com.google.android.flexbox.flexbox) + implementation(libs.play.services.ads.identifier) + implementation("com.github.getActivity:ShapeView:9.8") +// implementation(libs.android.billing) +// implementation(platform(libs.google.firebase.bom)) +// implementation(libs.google.firebase.messaging.ktx) +// implementation(libs.google.firebase.analytics.ktx) +// implementation(libs.firebase.perf) +// implementation(libs.adjust.android) +// implementation(libs.adjust.android.webbridge) +// implementation(libs.com.android.installreferrer.installreferrer) +// implementation("com.facebook.android:facebook-android-sdk:17.0.2") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7ebc1bb --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseActivity.kt new file mode 100644 index 0000000..aad71ec --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseActivity.kt @@ -0,0 +1,141 @@ +package com.jia.er.nebuluxe.app.basics + +import android.app.Dialog +import android.content.Context +import android.content.pm.ActivityInfo +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Build +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import android.widget.FrameLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.viewbinding.ViewBinding +import com.blankj.utilcode.util.BarUtils +import com.jia.er.nebuluxe.app.R + +abstract class BaseActivity : AppCompatActivity() { + lateinit var binding: BV + private var networkErrorView: View? = null + private var noDramaView: View? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = getViewBinding() + val container = FrameLayout(this).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + addView( + binding.root, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + setContentView(container) + networkErrorView = + layoutInflater.inflate(R.layout.layout_network_error, container, false).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + visibility = View.INVISIBLE + findViewById(R.id.tv_example_retry)?.setOnClickListener { + onRetry() + } + } + container.addView(networkErrorView) + noDramaView = + layoutInflater.inflate(R.layout.layout_no_drama, container, false).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + visibility = View.INVISIBLE + findViewById(R.id.tv_example_retry)?.setOnClickListener { + onReturn() + } + } + container.addView(noDramaView) + BarUtils.transparentStatusBar(this) + BarUtils.setStatusBarLightMode(this, false) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + top() + center() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + ViewCompat.setOnApplyWindowInsetsListener(container) { v, insets -> + val systemInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) + v.setPadding(0, 0, 0, systemInsets.bottom) + window.navigationBarColor = ContextCompat.getColor(this, R.color.black) + insets + } + } + } + + protected abstract fun top() + protected abstract fun center() + protected abstract fun getViewBinding(): BV + + protected open fun onRetry() { + } + + protected open fun onReturn() { + } + + fun showNetError() { + networkErrorView?.visibility = View.VISIBLE + } + + fun hideNetError() { + networkErrorView?.visibility = View.INVISIBLE + } + + fun showNoDrama(){ + noDramaView?.visibility = View.VISIBLE + } + + private var loadingDialog: Dialog? = null + + protected open fun showLoading( + message: String = "Loading..." + ) { + if (loadingDialog?.isShowing == true) return + + loadingDialog = Dialog(this, R.style.FullScreenLoadingDialog).apply { + setContentView(R.layout.dialog_loading) + window?.apply { + setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + decorView.setBackgroundColor(Color.argb(150, 0, 0, 0)) + } + setCancelable(true) + findViewById(R.id.tv_loading_text)?.text = message + } + loadingDialog?.show() + } + + protected open fun hideLoading() { + loadingDialog?.takeIf { it.isShowing }?.dismiss() + loadingDialog = null + } + + fun showKeyBord(mEditText: EditText, mContext: Context) { + val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN) + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY) + } + + fun hideKeyBord(mEditText: EditText, mContext: Context) { + val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(mEditText.windowToken, 0) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseFragment.kt new file mode 100644 index 0000000..c687b90 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/basics/BaseFragment.kt @@ -0,0 +1,99 @@ +package com.jia.er.nebuluxe.app.basics + +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding +import com.jia.er.nebuluxe.app.R + +abstract class BaseFragment : Fragment() { + lateinit var binding: VB + var networkErrorView: View? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = getViewBinding() + val layout = FrameLayout(requireContext()).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + addView( + binding.root, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + networkErrorView = + layoutInflater.inflate(R.layout.layout_network_error, layout, false).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + visibility = View.INVISIBLE + findViewById(R.id.tv_example_retry)?.setOnClickListener { + onRetry() + } + } + layout.addView(networkErrorView) + return layout + } + + protected open fun onRetry() { + } + + fun showNetError() { + networkErrorView?.visibility = View.VISIBLE + } + + fun hideNetError() { + networkErrorView?.visibility = View.INVISIBLE + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + top() + center() + } + + protected abstract fun top() + protected abstract fun center() + protected abstract fun getViewBinding(): VB + + private var loadingDialog: Dialog? = null + + protected open fun showLoading( + message: String = "Loading..." + ) { + if (loadingDialog?.isShowing == true) return + + loadingDialog = Dialog(requireContext(), R.style.FullScreenLoadingDialog).apply { + setContentView(R.layout.dialog_loading) + window?.apply { + setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + decorView.setBackgroundColor(Color.argb(150, 0, 0, 0)) + } + setCancelable(true) + + findViewById(R.id.tv_loading_text)?.text = message + } + loadingDialog?.show() + } + + protected open fun hideLoading() { + loadingDialog?.takeIf { it.isShowing }?.dismiss() + loadingDialog = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/basics/Constants.kt b/app/src/main/java/com/jia/er/nebuluxe/app/basics/Constants.kt new file mode 100644 index 0000000..1e1425b --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/basics/Constants.kt @@ -0,0 +1,60 @@ +package com.jia.er.nebuluxe.app.basics + + +object Constants { + var isUat = false + const val Constants_BASE_URL_RES = "https://api-nebuluxetv.nebuluxetv.com/xe/" + const val CONSTANTS_AuthorizationExample = "Authorization" + const val CONSTANTS_device_id = "device-id" + const val CONSTANTS_device_gaid = "device-gaid" + const val CONSTANTS_app_name = "app-name" + const val CONSTANTS_app_version = "app-version" + const val CONSTANTS_system_type = "system-type" + const val CONSTANTS_lang_key = "lang-key" + const val CONSTANTS_time_zone = "time-zone" + const val CONSTANTS_model = "model" + const val CONSTANTS_brand = "brand" + const val CONSTANTS_edit = "CONSTANTS_edit" + const val CONSTANTS_system_version = "system-version" + const val CONSTANTS_auth_refresh = "CONSTANTS_auth_refresh" + const val CONSTANTS_User_STRING = "CONSTANTS_User_STRING" + const val CONSTANTS_SEARCH_STRINGExample = "searchString" + const val CONSTANTS_main_stop = "CONSTANTS_main_stop" + const val CONSTANTS_short_play_id = "CONSTANTS_short_play_id" + const val Constants_Episodes_Series_Data_currentPositionExample = + "Constants_Episodes_Series_Data_currentPositionExample" + const val Constants_Episodes_Series_Data_ListExample = + "Constants_Episodes_Series_Data_ListExample" + const val Constants_Episodes_Series_DataExample = "Constants_Episodes_Series_DataExample" + const val CONSTANTS_activity_id = "activity_id" + const val CONSTANTS_refresh_me = "CONSTANTS_refresh_me" + const val Constants_DDL_Url = "Constants_DDL_Url" + const val CONSTANTS_leaveApp = "CONSTANTS_leaveApp" + const val CONSTANTS_enterTheApp = "CONSTANTS_enterTheApp" + const val CONSTANTS_onLine = "CONSTANTS_onLine" + const val CONSTANTS_examplePayReq = "CONSTANTS_examplePayReq" + const val CONSTANTS_Episode = "CONSTANTS_Episode" + const val CONSTANTS_pay_refresh = "CONSTANTS_pay_refresh" + const val CONSTANTS_collect_refresh = "CONSTANTS_collect_refresh" + const val Constants_onTokenRefresh = "onTokenRefresh" + const val Constants_getClip = "Constants_getClip" + const val CONSTANTS_web_Login = "CONSTANTS_web_Login" + const val CONSTANTS_PREF_LAST_POPUP_TIME_Notification = + "CONSTANTS_PREF_LAST_POPUP_TIME_Notification" + const val ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L + var CanNotification: Boolean = false + const val CONSTANTS_Detail_id = "CONSTANTS_Detail_id" + const val Constants_requestPermissions_photo = "Constants_requestPermissions_photo" + const val Constants_requestPermissions_photo_detail = + "Constants_requestPermissions_photo_detail" + const val Constants_openFeedbackDetail = "Constants_openFeedbackDetail" + const val Constants_openFeedback = "Constants_openFeedback" + const val CONSTANTS_web_notification = "CONSTANTS_web_notification" + const val feedback_URL_res: String = "https://campaign.nebuluxetv.com/pages/leave/index" + const val feedback_list_URL_res: String = "https://campaign.nebuluxetv.com/pages/leave/list" + const val feedback_detail_URL_res: String = "https://campaign.nebuluxetv.com/pages/leave/detail" + const val Constants_Main_Video_status = "Constants_Main_Video_status" + const val Constants_Main_Video_info = "Constants_Main_Video_info" + var WebRefresh: Boolean = false + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyApplication.kt b/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyApplication.kt new file mode 100644 index 0000000..d32cc1d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyApplication.kt @@ -0,0 +1,136 @@ +package com.jia.er.nebuluxe.app.basics + +import android.app.Activity +import android.app.Application +import android.content.Context +import android.os.Bundle +import android.util.Log +import com.jia.er.nebuluxe.app.BuildConfig.DEBUG +//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.LoggingBehavior +//import com.facebook.appevents.AppEventsLogger +//import com.facebook.applinks.AppLinkData +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.greenrobot.eventbus.EventBus + + +class MyApplication : Application() { + private val LOG_TAG: String = "MyApplication" + + companion object { + lateinit var context: Context + var isAppInBackground = true + var countActivity = 0 + } + + override fun onCreate() { + super.onCreate() + context = this +// initAdjust() + if (DEBUG) { + registerActivityLifecycleCallbacks(AdjustLifecycleCallbacks()) + } +// registerActivityLifecycleCallbacks({ +// fun onActivityResumed(activity: Activity) { +// Log.d("onResume", activity.javaClass.simpleName) +// } +// +// } as ActivityLifecycleCallbacks?) +// GlobalScope.launch(Dispatchers.Main) { +// initFacebookSdk() +// } + + } + + // private fun initAdjust() { +// val appToken = "dmq04yk7ftvk" +// val environment = AdjustConfig.ENVIRONMENT_PRODUCTION +// val config = AdjustConfig(context, 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") +// Memory.getMMKV() +// .putString(Constants.Constants_DDL_Url, deeplink.toString()) +// EventBus.getDefault().post(Constants.Constants_DDL_Url) +// 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(Constants.CONSTANTS_enterTheApp) +// } + } + + override fun onActivityResumed(activity: Activity) { +// Adjust.onResume() + Log.d("onResume", "=========" + activity.javaClass.simpleName + "=========") + } + + 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(Constants.CONSTANTS_leaveApp) +// } + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityDestroyed(activity: Activity) {} + } +// +// private fun initFacebookSdk() { +// FacebookSdk.setAutoInitEnabled(true) +// FacebookSdk.fullyInitialize() +// if (Constants.isUat) { +// FacebookSdk.setIsDebugEnabled(true) +// FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS) +// } +// AppEventsLogger.activateApp(this) +// AppLinkData.fetchDeferredAppLinkData( +// this +// ) { +// // Process app link data +// if (null != it) { +// Memory.getMMKV() +// .putString(Constants.Constants_DDL_Url, it.targetUri.toString()) +// Log.d( +// "initFacebookSdk", +// "fetchDeferredAppLinkData callback called!====${it.targetUri}" +// ) +// EventBus.getDefault().post(Constants.Constants_DDL_Url) +// } +// Log.d("initFacebookSdk", "fetchDeferredAppLinkData callback called!") +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyContentProvider.kt b/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyContentProvider.kt new file mode 100644 index 0000000..86b4d54 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/basics/MyContentProvider.kt @@ -0,0 +1,65 @@ +package com.jia.er.nebuluxe.app.basics + +import android.content.ContentProvider +import android.content.ContentValues +import android.database.Cursor +import android.net.Uri +import com.getkeepsafe.relinker.ReLinker +import com.tencent.mmkv.MMKV + +class MyContentProvider : ContentProvider() { + override fun onCreate(): Boolean { + initMMKV() + return true + } + + private fun initMMKV() { + try { + context?.let { MMKV.initialize(it) } + } catch (e: Exception) { + e.printStackTrace() + try { + val dir: String = context?.filesDir?.absolutePath + "/mmkv" + MMKV.initialize(context, dir) { libName -> ReLinker.loadLibrary( + context, libName) } + } catch (e2: Exception) { + e2.printStackTrace() + } + } + } + + + + + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor? { + return null + } + + override fun getType(uri: Uri): String? { + return null + } + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + return null + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + return 0 + } + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int { + return 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridge.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridge.kt new file mode 100644 index 0000000..e085da7 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridge.kt @@ -0,0 +1,130 @@ +package com.jia.er.nebuluxe.app.data + +import android.Manifest +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.webkit.JavascriptInterface +import androidx.core.content.ContextCompat +import com.jia.er.nebuluxe.app.net.Retrofit.getCurrentTimeZone +import com.jia.er.nebuluxe.app.utils.Memory +import com.jia.er.nebuluxe.app.utils.singleClick +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity +import org.greenrobot.eventbus.EventBus + +class AndroidBridge(private val context: Context) { + @JavascriptInterface + fun getUserInfo(): String { + val jsInfo = ReelCrushJsInfo( + Memory.getMMKV() + .getString(Constants.CONSTANTS_AuthorizationExample, "") + .toString(), + getCurrentTimeZone(), + Memory.getMMKV().getString(Constants.CONSTANTS_lang_key, "en") + .toString(), + "android", + Memory.getMMKV().getString(Constants.CONSTANTS_Detail_id, "") + .toString(),"theme_1" + ) + return Gson().toJson(jsInfo) + } + + @JavascriptInterface + fun js2app(string: String) { + val parser = JsonParser() + val rootJson = parser.parse(string) as JsonObject + when (rootJson.get("type").asString) { + + "open_notify" -> { + singleClick { + EventBus.getDefault().post(Constants.CONSTANTS_web_notification) + } + } + + "watch_video" -> { + singleClick { + val fromJson = Gson().fromJson(string, JsonInfo::class.java) + context.startActivity( + Intent( + context, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + fromJson.data?.short_play_id + ) + putExtra( + Constants.CONSTANTS_activity_id, + fromJson.data?.activity_id + ) + }) + } + } + + } + } + + @JavascriptInterface + fun openFeedbackDetail(id: String) { + EventBus.getDefault() + .post(BaseEventBus(Constants.Constants_openFeedbackDetail, id)) + } + + @JavascriptInterface + fun openFeedbackList() { + EventBus.getDefault() + .post(Constants.Constants_openFeedback) + } + + @JavascriptInterface + fun openPhotoPicker() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_MEDIA_IMAGES + ) == PackageManager.PERMISSION_GRANTED + ) { + openFilePicker() + } else { + EventBus.getDefault().post(Constants.Constants_requestPermissions_photo) + } + } else { + if ((ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission( + context, + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED) + ) { + openFilePicker() + } else { + EventBus.getDefault().post(Constants.Constants_requestPermissions_photo) + } + } + + } + + + private val REQUEST_PICK_FILE: Int = 1002 + + private fun openFilePicker() { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "image/*" + } + (context as? Activity)?.startActivityForResult(intent, REQUEST_PICK_FILE) + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridgeDetail.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridgeDetail.kt new file mode 100644 index 0000000..a18c80c --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/AndroidBridgeDetail.kt @@ -0,0 +1,79 @@ +package com.jia.er.nebuluxe.app.data + +import android.Manifest +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.webkit.JavascriptInterface +import androidx.core.content.ContextCompat +import com.jia.er.nebuluxe.app.utils.Memory +import com.google.gson.Gson +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.net.Retrofit.getCurrentTimeZone +import org.greenrobot.eventbus.EventBus + +class AndroidBridgeDetail(private val context: Context) { + @JavascriptInterface + fun getUserInfo(): String { + val moviaJsInfo = ReelCrushJsInfo( + Memory.getMMKV() + .getString(Constants.CONSTANTS_AuthorizationExample, "") + .toString(), + getCurrentTimeZone(), + Memory.getMMKV().getString(Constants.CONSTANTS_lang_key, "en") + .toString(), + "android", + Memory.getMMKV().getString(Constants.CONSTANTS_Detail_id, "") + .toString(),"theme_1" + ) + return Gson().toJson(moviaJsInfo) + } + + + @JavascriptInterface + fun openPhotoPicker() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_MEDIA_IMAGES + ) == PackageManager.PERMISSION_GRANTED + ) { + openFilePicker() + } else { + EventBus.getDefault().post(Constants.Constants_requestPermissions_photo_detail) + } + } else { + if ((ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission( + context, + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED) + ) { + openFilePicker() + } else { + EventBus.getDefault().post(Constants.Constants_requestPermissions_photo_detail) + } + } + + } + + + private val REQUEST_PICK_FILE: Int = 1003 + + private fun openFilePicker() { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "image/*" + } + (context as? Activity)?.startActivityForResult(intent, REQUEST_PICK_FILE) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseEventBus.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseEventBus.kt new file mode 100644 index 0000000..e64593d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseEventBus.kt @@ -0,0 +1,3 @@ +package com.jia.er.nebuluxe.app.data + +data class BaseEventBus(val code: String, val data: T) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseRes.kt new file mode 100644 index 0000000..b6133d1 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/BaseRes.kt @@ -0,0 +1,6 @@ +package com.jia.er.nebuluxe.app.data + +data class BaseRes( + val code: Int, + val msg: String, + val data: T?) \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/BuyVideoRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/BuyVideoRes.kt new file mode 100644 index 0000000..258177c --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/BuyVideoRes.kt @@ -0,0 +1,7 @@ +package com.jia.er.nebuluxe.app.data + +data class BuyVideoRes( + val status: String, + val coin_left_total: Int, + val send_coin_left_total: Int +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CategoriesDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CategoriesDataRes.kt new file mode 100644 index 0000000..c17f9a6 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CategoriesDataRes.kt @@ -0,0 +1,11 @@ +package com.jia.er.nebuluxe.app.data + +class CategoriesDataRes( + val list: ArrayList +) { + + data class Item8( + val id: Int, + val name: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CollectionRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CollectionRes.kt new file mode 100644 index 0000000..07eaf6d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CollectionRes.kt @@ -0,0 +1,24 @@ +package com.jia.er.nebuluxe.app.data + +data class CollectionRes( + val list: List, + val pagination: Pagination +) { + + data class CollectionData( + val description: String, + val episode_total: Int, + val image_url: String, + val name: String, + val short_play_id: Int, + val current_episode: Int, + val short_play_video_id: Int + ) + + data class Pagination( + val current_page: Int, + val page_size: Int, + val page_total: Int, + val total_size: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderReq.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderReq.kt new file mode 100644 index 0000000..d9a4043 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderReq.kt @@ -0,0 +1,8 @@ +package com.jia.er.nebuluxe.app.data + +data class CreateOrderReq( + val pay_setting_id: String, + val payment_channel: String, + val short_play_id: Int, + val video_id: Int +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderRes.kt new file mode 100644 index 0000000..b01373b --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CreateOrderRes.kt @@ -0,0 +1,5 @@ +package com.jia.er.nebuluxe.app.data + +data class CreateOrderRes( + val order_code: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerBuyRecordsRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerBuyRecordsRes.kt new file mode 100644 index 0000000..ceb3463 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerBuyRecordsRes.kt @@ -0,0 +1,18 @@ +package com.jia.er.nebuluxe.app.data + +class CustomerBuyRecordsRes( + val list: List +) { + + data class Data( + val coin_type: Int, + val coins: Int, + val created_at: String, + val episode: Int, + val image_url: String, + val is_grounding: Int, + val name: String, + val short_play_id: Int, + val short_play_video_id: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerOrderRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerOrderRes.kt new file mode 100644 index 0000000..c732cc3 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/CustomerOrderRes.kt @@ -0,0 +1,20 @@ +package com.jia.er.nebuluxe.app.data + +data class CustomerOrderRes( + val list: List, + val pagination: Pagination +) { + + data class Item0( + val created_at: String, + val type: String, + val value: String + ) + + data class Pagination( + val current_page: Int, + val page_size: Int, + val page_total: Int, + val total_size: Int + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/DetailsRecommendRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/DetailsRecommendRes.kt new file mode 100644 index 0000000..f0e28b4 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/DetailsRecommendRes.kt @@ -0,0 +1,28 @@ +package com.jia.er.nebuluxe.app.data + +data class DetailsRecommendRes( + val brief: String, + val description: String, + val list: List, + 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 + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/ExampleKeywordDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/ExampleKeywordDataRes.kt new file mode 100644 index 0000000..28100e2 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/ExampleKeywordDataRes.kt @@ -0,0 +1,30 @@ +package com.jia.er.nebuluxe.app.data + +class ExampleKeywordDataRes( + val list: List, +) { + + data class KeywordData( + val all_coins: Int, + val buy_type: Int, + val categoryList: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: 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: Long, + val search_click_total: Int, + ) + + data class Category( + val id: Int, + val name: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/FbNotificationReq.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/FbNotificationReq.kt new file mode 100644 index 0000000..ea9a8ba --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/FbNotificationReq.kt @@ -0,0 +1,3 @@ +package com.jia.er.nebuluxe.app.data + +data class FbNotificationReq(val is_open_notice: String) \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/FreeSeriesMoreRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/FreeSeriesMoreRes.kt new file mode 100644 index 0000000..8c22d69 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/FreeSeriesMoreRes.kt @@ -0,0 +1,24 @@ +package com.jia.er.nebuluxe.app.data + +data class FreeSeriesMoreRes( + val list: List +) { + + data class Item9( + val all_coins: Int, + val buy_type: Int, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HistoryDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HistoryDataRes.kt new file mode 100644 index 0000000..32d1aab --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HistoryDataRes.kt @@ -0,0 +1,26 @@ +package com.jia.er.nebuluxe.app.data + +class HistoryDataRes( + val list: List, + val pagination: Pagination +) { + + data class Data( + val description: String, + val episode_total: Int, + val category: List, + val image_url: String, + val name: String, + val short_play_id: Int, + var is_collect: Int, + val current_episode: Int, + val short_play_video_id: Int + ) + + data class Pagination( + val current_page: Int, + val page_size: Int, + val page_total: Int, + val total_size: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerAndNineSquarepRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerAndNineSquarepRes.kt new file mode 100644 index 0000000..29cd9bd --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerAndNineSquarepRes.kt @@ -0,0 +1,30 @@ +package com.jia.er.nebuluxe.app.data + +data class HomeBannerAndNineSquarepRes( + val arrangement: String, + val list: List, + val title: String +) { + + data class Item0( + val all_coins: Int, + val buy_type: Int, + val category: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val ffmpeg_id: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val jump_ffmpeg_id: Int, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val video_url: String, + val watch_total: Int + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerBean.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerBean.kt new file mode 100644 index 0000000..98ea271 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBannerBean.kt @@ -0,0 +1,21 @@ +package com.jia.er.nebuluxe.app.data + +data class HomeBannerBean( + val all_coins: Any, + val buy_type: Int, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val video_url: String, + val watch_total: Int, + val category: List +) \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBestSellersData.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBestSellersData.kt new file mode 100644 index 0000000..f68d8e9 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeBestSellersData.kt @@ -0,0 +1,28 @@ +package com.jia.er.nebuluxe.app.data + + +class HomeBestSellersData : ArrayList() { + data class HomeBestSellersDataItem( + val all_coins: String, + val buy_type: Int, + val categoryList: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int + ) + + data class Category( + val id: Int, + val name: String + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeModuleBean.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeModuleBean.kt new file mode 100644 index 0000000..be9c517 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeModuleBean.kt @@ -0,0 +1,13 @@ +package com.jia.er.nebuluxe.app.data + +import com.google.gson.JsonElement + + +class HomeModuleBean( + val list: List, +) { + data class RecommandDataBean( + val module_key :String, + val data: JsonElement + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayNoPaginateRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayNoPaginateRes.kt new file mode 100644 index 0000000..ac60f5d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayNoPaginateRes.kt @@ -0,0 +1,25 @@ +package com.jia.er.nebuluxe.app.data + +class HomeNewShortPlayNoPaginateRes( + val list: List +) { + + data class Item8( + val all_coins: Int, + val buy_type: Int, + val category: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayRes.kt new file mode 100644 index 0000000..d8c093d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeNewShortPlayRes.kt @@ -0,0 +1,61 @@ +package com.jia.er.nebuluxe.app.data + +class HomeNewShortPlayRes( + val category_list: List, + val pagination: Pagination, + val ranking_list: List, + val short_play_list: MutableList +) { + + data class Category( + val category_id: Int, + val category_name: String + ) + + data class Pagination( + val current_page: Int, + val page_size: Int, + val page_total: Int, + val total_size: Int + ) + + data class Ranking( + val all_coins: Int, + val buy_type: Int, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int + ) + + data class ShortPlay( + val all_coins: Int, + val buy_type: Int, + val category: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + var view_type: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int, + var ranking_list: List, + var category_list: List + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeRankingRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeRankingRes.kt new file mode 100644 index 0000000..359d273 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeRankingRes.kt @@ -0,0 +1,25 @@ +package com.jia.er.nebuluxe.app.data + +class HomeRankingRes( + val list: List +) { + + data class Item7( + val all_coins: Int, + val buy_type: Int, + val category: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Long + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopRes.kt new file mode 100644 index 0000000..d50cc23 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopRes.kt @@ -0,0 +1,30 @@ +package com.jia.er.nebuluxe.app.data + +data class HomeTopRes( + val category: List, + val hotData: List +) { + + data class Category( + val category_id: Int, + val category_name: String + ) + + data class HotData( + val all_coins: Int, + val buy_type: Int, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val watch_total: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopWeekRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopWeekRes.kt new file mode 100644 index 0000000..e4b1c32 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/HomeTopWeekRes.kt @@ -0,0 +1,25 @@ +package com.jia.er.nebuluxe.app.data + +data class HomeTopWeekRes( + val list: List +){ + +data class Item8( + val all_coins: String, + val buy_type: Int, + val category: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: String, + val id: Int, + val image_url: String, + val name: String, + val process: Int, + val search_click_total: Int, + val short_id: Int, + val short_play_id: Int, + val tag_type: String, + val video_url: String, + val watch_total: Int +)} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/JsonInfo.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/JsonInfo.kt new file mode 100644 index 0000000..d2188bc --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/JsonInfo.kt @@ -0,0 +1,14 @@ +package com.jia.er.nebuluxe.app.data + +class JsonInfo( + 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? + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginReq.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginReq.kt new file mode 100644 index 0000000..e3f84b5 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginReq.kt @@ -0,0 +1,10 @@ +package com.jia.er.nebuluxe.app.data + +data class LoginReq( + val avator: String, + val email: String, + val family_name: String, + val giving_name: String, + val platform: String, + val third_id: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginRes.kt new file mode 100644 index 0000000..8b70619 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/LoginRes.kt @@ -0,0 +1,6 @@ +package com.jia.er.nebuluxe.app.data + +data class LoginRes( + val customer_id: String, + val token: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/MainDataHis.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/MainDataHis.kt new file mode 100644 index 0000000..1d9dbf4 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/MainDataHis.kt @@ -0,0 +1,8 @@ +package com.jia.er.nebuluxe.app.data + +data class MainDataHis( + val episode_total: String, + val video_id: Int, + val video_last: String, + val video_img: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/NoticeNumRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/NoticeNumRes.kt new file mode 100644 index 0000000..cb8983d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/NoticeNumRes.kt @@ -0,0 +1,5 @@ +package com.jia.er.nebuluxe.app.data + +data class NoticeNumRes( + val feedback_notice_num: Int +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/PayReq.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/PayReq.kt new file mode 100644 index 0000000..cfd9d8e --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/PayReq.kt @@ -0,0 +1,11 @@ +package com.jia.er.nebuluxe.app.data + +data class PayReq( + 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, +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/PayRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/PayRes.kt new file mode 100644 index 0000000..3e97d50 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/PayRes.kt @@ -0,0 +1,8 @@ +package com.jia.er.nebuluxe.app.data + +data class PayRes( + val is_backhaul: Int, + val money: String, + val order_code: String, + val status: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/PaySettingRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/PaySettingRes.kt new file mode 100644 index 0000000..c1f64c6 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/PaySettingRes.kt @@ -0,0 +1,68 @@ +package com.jia.er.nebuluxe.app.data + +class PaySettingRes( + val list_coins: List, + val sort: List, + val list_sub_vip: List, +) { + + 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 corner_marker: String, + 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, + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/PlayerDetailDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/PlayerDetailDataRes.kt new file mode 100644 index 0000000..4cc98fd --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/PlayerDetailDataRes.kt @@ -0,0 +1,160 @@ +package com.jia.er.nebuluxe.app.data + +import android.os.Parcel +import android.os.Parcelable + +data class PlayerDetailDataRes( + val episodeList: List, + val is_collect: Boolean, + val show_share_coin: Boolean, + val share_coin: Int, + val install_coins: Int, + val unlock_video_ad_count: Int, + val revolution: Int, + val discount: Int, + var jump_type: Int, + var jump_short_play_id: Int, + var business_model: String, + val shortPlayInfo: ShortPlayInfo, + val video_info: VideoInfo +) { + + data class Episode( + val coins: Int, + val episode: Int, + val id: Int, + var is_lock: Boolean, + val is_vip: Int, + val short_play_id: Int, + val short_play_video_id: Int, + val video_url: String, + val vip_coins: Int, + var play_seconds: String?, + var shortPlayInfo: ShortPlayInfo?, + var promise_view_ad: Int, + ): Parcelable { + constructor(parcel: Parcel) : this( + parcel.readInt(), + parcel.readInt(), + parcel.readInt(), + parcel.readByte() != 0.toByte(), + parcel.readInt(), + parcel.readInt(), + parcel.readInt(), + parcel.readString().toString(), + parcel.readInt(), + parcel.readString(), + parcel.readParcelable(ShortPlayInfo::class.java.classLoader), + parcel.readInt() + ) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(coins) + parcel.writeInt(episode) + parcel.writeInt(id) + parcel.writeByte(if (is_lock) 1 else 0) + parcel.writeInt(is_vip) + parcel.writeInt(short_play_id) + parcel.writeInt(short_play_video_id) + parcel.writeString(video_url) + parcel.writeInt(vip_coins) + parcel.writeString(play_seconds) + parcel.writeParcelable(shortPlayInfo, flags) + parcel.writeInt(promise_view_ad) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Episode { + return Episode(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + + } + + data class ShortPlayInfo( + val all_coins: Int, + val buy_type: Int, + var collect_total: Int, + val description: String, + val category: ArrayList?, + val episode_total: Int, + val id: Int, + val image_url: String, + var is_collect: Boolean, + val name: String, + val process: Int, + val short_id: Int, + val watch_total: Int + ): Parcelable { + constructor(parcel: Parcel) : this( + parcel.readInt(), + parcel.readInt(), + parcel.readInt(), + parcel.readString().toString(), + parcel.createStringArrayList(), + parcel.readInt(), + parcel.readInt(), + parcel.readString().toString(), + parcel.readByte() != 0.toByte(), + parcel.readString().toString(), + parcel.readInt(), + parcel.readInt(), + parcel.readInt() + ) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(all_coins) + parcel.writeInt(buy_type) + parcel.writeInt(collect_total) + parcel.writeString(description) + parcel.writeStringList(category) + parcel.writeInt(episode_total) + parcel.writeInt(id) + parcel.writeString(image_url) + parcel.writeByte(if (is_collect) 1 else 0) + parcel.writeString(name) + parcel.writeInt(process) + parcel.writeInt(short_id) + parcel.writeInt(watch_total) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): ShortPlayInfo { + return ShortPlayInfo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + } + + data class VideoInfo( + val coins: Int, + val episode: Int, + val id: Int, + val is_vip: Int, + val short_id: Int, + val short_play_id: Int, + val short_play_video_id: Int, + val promise_view_ad: Int, + val video_url: String, + val vip_coins: Int + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/RecommendDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/RecommendDataRes.kt new file mode 100644 index 0000000..8462415 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/RecommendDataRes.kt @@ -0,0 +1,39 @@ +package com.jia.er.nebuluxe.app.data + +data class RecommendDataRes( + val list: List, + val pagination: Pagination +) { + + data class Data( + val all_price_total: Int, + val buy_type: Int, + var collect_total: Int?, + val description: String, + val episode_total: Int, + val id: Int, + val image_url: String, + var is_collect: Boolean, + val name: String, + val process: Int, + val video_info: VideoInfo?, + val watch_total: Int + ) + + data class Pagination( + val current_page: Int, + val page_size: Int, + val page_total: Int, + val total_size: Int + ) + + data class VideoInfo( + val episode: Int, + val id: Int, + val is_vip: Int, + val short_play_id: Int, + val short_play_video_id: Int, + val video_url: String, + val image_url: String + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/ReelCrushJsInfo.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/ReelCrushJsInfo.kt new file mode 100644 index 0000000..a065c7b --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/ReelCrushJsInfo.kt @@ -0,0 +1,10 @@ +package com.jia.er.nebuluxe.app.data + +data class ReelCrushJsInfo( + val token: String, + val time_zone: String, + val lang: String, + val type: String, + val id: String, + val theme: String +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/RewardCoinsRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/RewardCoinsRes.kt new file mode 100644 index 0000000..0252e1e --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/RewardCoinsRes.kt @@ -0,0 +1,14 @@ +package com.jia.er.nebuluxe.app.data + +class RewardCoinsRes(val list: List) { + data class ExampleRewardCoinsResItem( + val coins: Int, + val created_at: String, + val diff_datetime: String, + val expired_time: Int, + val id: Int, + val is_effective: Int, + val left_coins: String, + val type: String + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/ShortListCategoryDataInfo.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/ShortListCategoryDataInfo.kt new file mode 100644 index 0000000..fa843b4 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/ShortListCategoryDataInfo.kt @@ -0,0 +1,32 @@ +package com.jia.er.nebuluxe.app.data + + +class ShortListCategoryDataInfo( + val list: List, +) { + + data class CategoryListData( + + val id: Int, + val category_name: String, + val short_play_list: List + + ) + + data class ShortPlayListData( + val all_coins: Int, + val buy_type: Int, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: 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: Long + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/TabEntity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/TabEntity.kt new file mode 100644 index 0000000..8e3a05d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/TabEntity.kt @@ -0,0 +1,20 @@ +package com.jia.er.nebuluxe.app.data + +import com.flyco.tablayout.listener.CustomTabEntity + +class TabEntity(var s1: String, var i1: Int, var i2: Int) : + CustomTabEntity { + + override fun getTabTitle(): String { + return s1 + } + + override fun getTabSelectedIcon(): Int { + return i1 + } + + override fun getTabUnselectedIcon(): Int { + return i2 + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/UploadHistoryReq.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/UploadHistoryReq.kt new file mode 100644 index 0000000..9950b0e --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/UploadHistoryReq.kt @@ -0,0 +1,7 @@ +package com.jia.er.nebuluxe.app.data + +data class UploadHistoryReq( + val play_seconds: Long?, + val short_play_id: Int, + val video_id: Int? +) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/UserInfoRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/UserInfoRes.kt new file mode 100644 index 0000000..0f2e83c --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/UserInfoRes.kt @@ -0,0 +1,30 @@ +package com.jia.er.nebuluxe.app.data + +data class UserInfoRes( + val avator: String = "", + val coin_left_total: Int = 0, + val country: String = "", + val country_code: String = "", + val customer_id: String = "", + val email: String = "", + val family_name: String = "", + val fn: String = "", + val giving_name: String = "", + val id: String = "", + val ip_address: String = "0.0.0.0", + val is_guide_vip: Boolean = false, + val is_tourist: Boolean = true, + val is_vip: Boolean = false, + val ln: String = "", + 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_type: String = "" +) { + companion object { + fun createWithDefaults(): UserInfoRes { + return UserInfoRes() + } + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/UserRegisterRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/UserRegisterRes.kt new file mode 100644 index 0000000..53e90b3 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/UserRegisterRes.kt @@ -0,0 +1,4 @@ +package com.jia.er.nebuluxe.app.data + +data class UserRegisterRes(val customer_id: String, + val token: String) diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/data/VideoListDataRes.kt b/app/src/main/java/com/jia/er/nebuluxe/app/data/VideoListDataRes.kt new file mode 100644 index 0000000..8fc6f92 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/data/VideoListDataRes.kt @@ -0,0 +1,29 @@ +package com.jia.er.nebuluxe.app.data + +class VideoListDataRes( + val list: List, +) { + + data class VideoListData( + val all_coins: Int, + val buy_type: Int, + val categoryList: List, + val collect_total: Int, + val description: String, + val episode_total: Int, + val horizontally_img: 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: Long + ) + + data class Category( + val id: Int, + val name: String + ) +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshActivity.kt new file mode 100644 index 0000000..85de714 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshActivity.kt @@ -0,0 +1,84 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.blankj.utilcode.util.NetworkUtils +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.databinding.ActivityFreshBinding +import com.jia.er.nebuluxe.app.databinding.ActivityRankingsBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity + + +class FreshActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + private var type = "new_releases" + + override fun top() { + binding?.exampleIvBack?.setOnClickListener { finish() } + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.homeRanking(type) + } else { + showNetError() + } + binding.srRank.setOnRefreshListener { + mViewModel.homeRanking(type) + } + } + + override fun center() { + mViewModel.homeRankingData.observe(this) { + if (it != null) { + if (it.data?.list?.isNotEmpty() == true) { + val homeRankTrendingAdapter = FreshAdapter() + val manager1 = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding?.rvRanks?.layoutManager = manager1 + binding?.rvRanks?.adapter = homeRankTrendingAdapter + homeRankTrendingAdapter?.submitList(it.data.list) + homeRankTrendingAdapter?.isStateViewEnable = true + homeRankTrendingAdapter?.setStateViewLayout(this, R.layout.layout_emptyview) + homeRankTrendingAdapter?.setOnItemClickListener { adapter, view, position -> + val data = adapter.getItem(position) as HomeRankingRes.Item7 + startActivity(Intent( + this, PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, data.short_play_id + ) + }) + } + } + binding.srRank.finishRefresh() + hideNetError() + hideLoading() + } else { + showNetError() + toast(getString(R.string.network_error)) + hideLoading() + } + } + } + + override fun getViewBinding(): ActivityFreshBinding { + return ActivityFreshBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.homeRanking(type) + } else { + toast(getString(R.string.example_no_network)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshAdapter.kt new file mode 100644 index 0000000..7dff62a --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/FreshAdapter.kt @@ -0,0 +1,57 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatImageView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.fullspan.FullSpanAdapterType +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.ui.CustomRoundedCorners +import com.jia.er.nebuluxe.app.utils.dpToPxByFloat + + +class FreshAdapter : + BaseQuickAdapter(), + FullSpanAdapterType { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: HomeRankingRes.Item7? + ) { + Glide.with(context).load(item?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(holder.getView(R.id.iv_cover)) + holder.setText(R.id.tv_rank_name, item?.name) + if (item?.category?.isNotEmpty() == true) { + val string = item.category[0] + holder.setText(R.id.tv_rank_tag, string) + holder.setVisible(R.id.tv_rank_tag, true) + } else { + holder.setVisible(R.id.tv_rank_tag, false) + } + val view = holder.getView(R.id.example_iv_icon_ceo) + Glide.with(context) + .load(item?.image_url).placeholder(R.drawable.iv_placeholder_v) + .transform( + CenterCrop(), + CustomRoundedCorners( + dpToPxByFloat(20, context), + 0f, + dpToPxByFloat(20, context), + dpToPxByFloat(20, context) + ) + ) + .into(view) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_fresh, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresActivity.kt new file mode 100644 index 0000000..98fa4bb --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresActivity.kt @@ -0,0 +1,101 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import android.graphics.Rect +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.blankj.utilcode.util.NetworkUtils +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo +import com.jia.er.nebuluxe.app.databinding.ActivityFreshBinding +import com.jia.er.nebuluxe.app.databinding.ActivityRankingsBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity + + +class GenresActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + override fun top() { + binding?.exampleIvBack?.setOnClickListener { finish() } + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.userCenterRecommend() + } else { + showNetError() + } + binding.srRank.setOnRefreshListener { + mViewModel.userCenterRecommend() + } + } + + override fun center() { + mViewModel.userCenterRecommendData.observe(this) { + if (it != null) { + if (it.data?.list?.isNotEmpty() == true) { + val homeRankTrendingAdapter = GenresAdapter() + val manager1 = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding?.rvRanks?.layoutManager = manager1 + binding?.rvRanks?.adapter = homeRankTrendingAdapter + homeRankTrendingAdapter?.submitList(it.data.list) + binding?.rvRanks?.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + if (parent.getChildPosition(view) != (it.data.list.size - 1)) { + outRect.bottom = -100 + } + } + }) + homeRankTrendingAdapter?.isStateViewEnable = true + homeRankTrendingAdapter?.setStateViewLayout(this, R.layout.layout_emptyview) + homeRankTrendingAdapter?.setOnItemClickListener { adapter, view, position -> + val data = + adapter.getItem(position) as ShortListCategoryDataInfo.CategoryListData +// startActivity(Intent( +// this, PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, data.short_play_id +// ) +// }) + } + } + binding.srRank.finishRefresh() + hideNetError() + hideLoading() + } else { + showNetError() + toast(getString(R.string.network_error)) + hideLoading() + } + } + } + + override fun getViewBinding(): ActivityFreshBinding { + return ActivityFreshBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.userCenterRecommend() + } else { + toast(getString(R.string.example_no_network)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresAdapter.kt new file mode 100644 index 0000000..4d4bba3 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresAdapter.kt @@ -0,0 +1,67 @@ +package com.jia.er.nebuluxe.app.home + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.graphics.Rect +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.fullspan.FullSpanAdapterType +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo + + +class GenresAdapter : + BaseQuickAdapter(), + FullSpanAdapterType { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: ShortListCategoryDataInfo.CategoryListData? + ) { + when (position) { + 0 -> { + holder.setBackgroundResource(R.id.rl, R.drawable.iv_genres_1) + holder.setBackgroundResource(R.id.line, R.color.E3D4FF) + } + + 1 -> { + holder.setBackgroundResource(R.id.rl, R.drawable.iv_genres_2) + holder.setBackgroundResource(R.id.line, R.color.BDF5E2) + } + + 2 -> { + holder.setBackgroundResource(R.id.rl, R.drawable.iv_genres_3) + holder.setBackgroundResource(R.id.line, R.color.FFFA80) + } + + else -> { + holder.setBackgroundResource(R.id.rl, R.drawable.iv_genres_4) + holder.setBackgroundResource(R.id.line, R.color.F0C2E1) + } + } + val view = holder.getView(R.id.rv_genres_item) + holder.setText(R.id.tv_type, item?.category_name) + holder.setText( + R.id.tv_num, + item?.short_play_list?.size.toString().plus("\n").plus("Dramas") + ) + val homeRankTrendingAdapter = GenresItemAdapter() + val manager1 = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + view?.layoutManager = manager1 + view?.adapter = homeRankTrendingAdapter + homeRankTrendingAdapter?.submitList(item?.short_play_list) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_genres, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresItemAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresItemAdapter.kt new file mode 100644 index 0000000..7f68113 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/GenresItemAdapter.kt @@ -0,0 +1,32 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.view.ViewGroup +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.fullspan.FullSpanAdapterType +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo + + +class GenresItemAdapter : + BaseQuickAdapter(), + FullSpanAdapterType { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: ShortListCategoryDataInfo.ShortPlayListData? + ) { + Glide.with(context).load(item?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(holder.getView(R.id.iv_cover)) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_genres_img, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerAdapter.kt new file mode 100644 index 0000000..f0eb733 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerAdapter.kt @@ -0,0 +1,57 @@ +package com.jia.er.nebuluxe.app.home + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatTextView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.HomeBannerAndNineSquarepRes +import com.youth.banner.adapter.BannerAdapter + +class HomeBannerAdapter(items: List?) : + BannerAdapter( + items + ) { + override fun onCreateHolder(parent: ViewGroup, viewType: Int): BannerViewHolder { + val view: View = LayoutInflater.from(parent.context) + .inflate(R.layout.custom_banner_view, parent, false) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + return BannerViewHolder(view) + } + + + override fun onBindView( + holder: BannerViewHolder?, + data: HomeBannerAndNineSquarepRes.Item0?, + position: Int, + size: Int + ) { + val findViewById = + holder?.view?.findViewById(R.id.iv_icon_banner) + val tv_name = holder?.view?.findViewById(R.id.tv_name) + val tv_tag = holder?.view?.findViewById(R.id.tv_tag) + if (findViewById != null) { + Glide.with(holder.itemView.context) + .load(data?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(findViewById) + } + tv_name?.text = data?.name + if (data?.category?.isNotEmpty() == true) { + tv_tag?.visibility = View.VISIBLE + tv_tag?.text = data?.category[0] + } else { + tv_tag?.visibility = View.INVISIBLE + } + } + + inner class BannerViewHolder(var view: View) : RecyclerView.ViewHolder( + view + ) + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerBottomAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerBottomAdapter.kt new file mode 100644 index 0000000..fb0ab61 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeBannerBottomAdapter.kt @@ -0,0 +1,60 @@ +package com.jia.er.nebuluxe.app.home + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatTextView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.HomeBannerAndNineSquarepRes +import com.jia.er.nebuluxe.app.data.HomeTopWeekRes +import com.youth.banner.adapter.BannerAdapter + +class HomeBannerBottomAdapter(items: List?) : + BannerAdapter( + items + ) { + override fun onCreateHolder(parent: ViewGroup, viewType: Int): BannerViewHolder { + val view: View = LayoutInflater.from(parent.context) + .inflate(R.layout.custom_banner_view_bottom, parent, false) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + return BannerViewHolder(view) + } + + + override fun onBindView( + holder: BannerViewHolder?, + data: HomeTopWeekRes.Item8?, + position: Int, + size: Int + ) { + val findViewById = + holder?.view?.findViewById(R.id.iv_icon_banner) + val tv_name = holder?.view?.findViewById(R.id.tv_name) + val tv_tag = holder?.view?.findViewById(R.id.tv_tag) + val tv_des = holder?.view?.findViewById(R.id.tv_des) + if (findViewById != null) { + Glide.with(holder.itemView.context) + .load(data?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(findViewById) + } + tv_name?.text = data?.name + tv_des?.text = data?.description + if (data?.category?.isNotEmpty() == true) { + tv_tag?.visibility = View.VISIBLE + tv_tag?.text = data?.category[0] + } else { + tv_tag?.visibility = View.INVISIBLE + } + } + + inner class BannerViewHolder(var view: View) : RecyclerView.ViewHolder( + view + ) + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeForYouAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeForYouAdapter.kt new file mode 100644 index 0000000..4652915 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeForYouAdapter.kt @@ -0,0 +1,29 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.view.ViewGroup +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.RecommendDataRes + +class HomeForYouAdapter : + BaseQuickAdapter() { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: RecommendDataRes.Data? + ) { + Glide.with(context).load(item?.image_url) + .into(holder.getView(R.id.iv_icon)) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_home_for_you, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeFragment.kt new file mode 100644 index 0000000..bb2b6ed --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeFragment.kt @@ -0,0 +1,265 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.blankj.utilcode.util.NetworkUtils +import com.bumptech.glide.Glide +import com.google.gson.Gson +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseFragment +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.HomeBannerAndNineSquarepRes +import com.jia.er.nebuluxe.app.data.HomeBannerBean +import com.jia.er.nebuluxe.app.data.HomeTopWeekRes +import com.jia.er.nebuluxe.app.databinding.FragmentHomeBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.ui.RotateDownPageTransformer +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity +import org.greenrobot.eventbus.EventBus + + +class HomeFragment : BaseFragment() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + private var page: Int = 1 + private val gson = Gson() + override fun top() { + if (NetworkUtils.isConnected()) { + mViewModel.allModules() + } else { + EventBus.getDefault().post(Constants.CONSTANTS_main_stop) + showNetError() + } + binding?.srHome?.setOnRefreshListener { + page = 1 + mViewModel.allModules() + } + binding.tvHot.setOnClickListener { + singleClick { + startActivity( + Intent( + context, + HotActivity::class.java + )) + } + } + binding.tvTop.setOnClickListener { + singleClick { + startActivity( + Intent( + context, + RankActivity::class.java + )) + } + } + binding.tvFresh.setOnClickListener { + singleClick { + startActivity( + Intent( + context, + FreshActivity::class.java + )) + } + } + binding.tvFresh.setOnClickListener { + singleClick { + startActivity( + Intent( + context, + GenresActivity::class.java + )) + } + } + } + + private var dataList: MutableList = mutableListOf() + + override fun center() { + mViewModel.allModulesData.observe(this) { + if (it != null) { + if (it?.data?.list?.isNotEmpty() == true) { + for (item in it.data.list) { + when (item.module_key) { + "marquee" -> { + binding.root.postDelayed({ + dataList.clear() + val marqueeList = + gson.fromJson(item.data, Array::class.java) + .toList() + marqueeList?.forEach { it1 -> + dataList.add(it1.name) + } + if (dataList.isNotEmpty()) { + binding?.tvText?.setData(dataList) + } + }, 1000) + } + + "home_banner" -> { + val bannerList = + gson.fromJson(item.data, Array::class.java) + .toList() + if (bannerList?.isNotEmpty() == true) { + binding?.clFs?.visibility = View.VISIBLE + val exampleDominantCeoAdapter = HomeMostAdapter() + val layoutManager = + LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) + binding?.rvFs?.layoutManager = layoutManager + binding?.rvFs?.adapter = exampleDominantCeoAdapter + binding?.rvFs?.isNestedScrollingEnabled = false + exampleDominantCeoAdapter.submitList(bannerList) + val video = bannerList[0] + Glide.with(requireContext()).load(video?.horizontally_img) + .placeholder(R.drawable.iv_placeholder_h) + .into(binding.ivFs) + binding.tvName.text = video.name + if (video.category.isNotEmpty()) { + binding.tvTag.visibility = View.VISIBLE + binding.tvTag.text = video.category[0] + } else { + binding.tvTag.visibility = View.INVISIBLE + } + binding.ivFs.setOnClickListener { + startActivity( + Intent( + context, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + video.short_play_id + ) + }) + } + exampleDominantCeoAdapter.setOnItemClickListener { adapter, view, position -> + exampleDominantCeoAdapter.currentPosition = position + val video = + adapter.getItem(position) as HomeBannerBean + Glide.with(requireContext()).load(video?.horizontally_img) + .placeholder(R.drawable.iv_placeholder_h) + .into(binding.ivFs) + binding.tvName.text = video.name + if (video.category.isNotEmpty()) { + binding.tvTag.visibility = View.VISIBLE + binding.tvTag.text = video.category[0] + } else { + binding.tvTag.visibility = View.INVISIBLE + } + binding.ivFs.setOnClickListener { + startActivity( + Intent( + context, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + video.short_play_id + ) + }) + } + exampleDominantCeoAdapter.notifyDataSetChanged() + } + + } else { + binding?.clFs?.visibility = View.GONE + } + } + + "home_v3_recommand" -> { + val homeBannerAndNineSquarepRes = + gson.fromJson( + item.data, + HomeBannerAndNineSquarepRes::class.java + ) + if (homeBannerAndNineSquarepRes?.list?.isNotEmpty() == true) { + binding?.clBanner?.visibility = View.VISIBLE + val exampleHomeBannerAdapter = + HomeBannerAdapter(homeBannerAndNineSquarepRes?.list) + binding?.bannerHome?.apply { + setAdapter(exampleHomeBannerAdapter) + setBannerGalleryEffect(65, 10, 1f) + setPageTransformer(RotateDownPageTransformer(10.5f)) + } + exampleHomeBannerAdapter.setOnBannerListener { data, position -> + startActivity( + Intent( + context, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + data?.short_play_id + ) + }) + } + } else { + binding?.clBanner?.visibility = View.GONE + } + } + + "new_recommand" -> { + val cagetoryBean = + gson.fromJson(item.data, HomeTopWeekRes::class.java) + if (cagetoryBean.list.isNotEmpty()) { + binding?.clBannerB?.visibility = View.VISIBLE + val exampleHomeBannerAdapter = + HomeBannerBottomAdapter(cagetoryBean.list) + binding?.bannerHomeB?.setBannerGalleryEffect(24, 10, 1f) + binding?.bannerHomeB?.setAdapter( + exampleHomeBannerAdapter + )?.addBannerLifecycleObserver(this) + ?.setIndicator(binding.indicatorHome, false) + exampleHomeBannerAdapter.setOnBannerListener { data, position -> + startActivity( + Intent( + context, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + data?.short_play_id + ) + }) + } + } else { + binding?.clBannerB?.visibility = View.GONE + } + } + } + } + hideLoading() + hideNetError() + binding?.srHome?.finishRefresh() + EventBus.getDefault().post(Constants.CONSTANTS_main_stop) + } + } else { + toast(getString(R.string.network_error)) + hideLoading() + binding?.srHome?.finishRefresh() + EventBus.getDefault().post(Constants.CONSTANTS_main_stop) + } + } + } + + override fun getViewBinding(): FragmentHomeBinding { + return FragmentHomeBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.allModules() + } else { + toast(getString(R.string.example_no_network)) + } + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeMostAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeMostAdapter.kt new file mode 100644 index 0000000..a3ea2fb --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HomeMostAdapter.kt @@ -0,0 +1,58 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.Outline +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.google.android.material.imageview.ShapeableImageView +import com.google.android.material.shape.RelativeCornerSize +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.HomeBannerBean + +class HomeMostAdapter : + BaseQuickAdapter() { + var currentPosition = 0 + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: HomeBannerBean? + ) { + val view = holder.getView(R.id.example_iv_icon_ceo) + Glide.with(context).load(item?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(view) + view.scaleType = ImageView.ScaleType.CENTER_CROP + view.shapeAppearanceModel = view.shapeAppearanceModel + .toBuilder() + .setAllCornerSizes(RelativeCornerSize(0.5f)) + .build() + view.elevation = 0f + view.clipToOutline = true + view.outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + val size = minOf(view.width, view.height) + outline.setOval(0, 0, size, size) + } + } + + if (currentPosition == position) { + holder.setVisible(R.id.view, false) + } else { + holder.setVisible(R.id.view, true) + } + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_dominant_ceo, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HotActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HotActivity.kt new file mode 100644 index 0000000..482371d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HotActivity.kt @@ -0,0 +1,91 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.StaggeredGridLayoutManager +import com.blankj.utilcode.util.NetworkUtils +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.VideoListDataRes +import com.jia.er.nebuluxe.app.databinding.FragmentHotBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity + +class HotActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + private var hotAdapter: HotAdapter? = null + private var isLoad = false + override fun top() { + binding?.exampleIvBack?.setOnClickListener { finish() } + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.homeHot(1, 10) + } else { + showNetError() + } + binding?.srFree?.setOnRefreshListener { + mViewModel.homeHot(1, 10) + } + } + + + override fun center() { + mViewModel.homeHotData.observe(this) { + if (it != null) { + hotAdapter = HotAdapter() + val layoutManager = + StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) + binding?.rvDataFree?.layoutManager = layoutManager + binding?.rvDataFree?.adapter = hotAdapter + binding?.rvDataFree?.isNestedScrollingEnabled = false + hotAdapter?.submitList(it.data?.list) + hotAdapter?.isStateViewEnable = true + hotAdapter?.setStateViewLayout( + this, + R.layout.layout_emptyview + ) + hotAdapter?.setOnItemClickListener { adapter, _, position -> + val videoListData = + adapter.getItem(position) as VideoListDataRes.VideoListData + startActivity( + Intent( + this, + PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, + videoListData.id + ) + }) + } + } else { + toast(getString(R.string.network_error)) + } + binding?.srFree?.finishRefresh() + hideLoading() + hideNetError() + isLoad = true + } + } + + override fun getViewBinding(): FragmentHotBinding { + return FragmentHotBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.freeMoreVideo() + } else { + toast(getString(R.string.example_no_network)) + } + } + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/HotAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/HotAdapter.kt new file mode 100644 index 0000000..8df86d9 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/HotAdapter.kt @@ -0,0 +1,62 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.VideoListDataRes +import com.jia.er.nebuluxe.app.ui.PosterStyleImageView + +class HotAdapter : + BaseQuickAdapter() { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: VideoListDataRes.VideoListData? + ) { + val posterView = holder.getView(R.id.iv_hot) + posterView.loadImage( + url = item?.image_url.toString(), + placeholderRes = R.drawable.iv_placeholder_v, + errorRes = R.drawable.iv_placeholder_v + ) + if (0 == position) { + holder.setGone(R.id.iv_top, false) + posterView.apply { + // 顶部倾斜程度(左侧更低) + setTopSlantOffsetDp(25f) + + // 左上小圆角,其他正常 + setTopLeftShortStyle(topLeftDp = 12f, otherDp = 18f) + + // 如果还需更贴合 + setCornerRadiiDp(12f, 18f, 18f, 18f) + } + } else { + holder.setGone(R.id.iv_top, true) + posterView.apply { + // 顶部倾斜程度(左侧更低) + setTopSlantOffsetDp(0f) + + // 左上小圆角,其他正常 + setTopLeftShortStyle(topLeftDp = 18f, otherDp = 18f) + // 如果还需更贴合 + setCornerRadiiDp(18f, 18f, 18f, 18f) + } + } + + + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_hot, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/RankActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/RankActivity.kt new file mode 100644 index 0000000..3437f24 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/RankActivity.kt @@ -0,0 +1,83 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.blankj.utilcode.util.NetworkUtils +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.databinding.ActivityRankingsBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.video.PlayerDetailActivity + + +class RankActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + private var type = "most_trending" + + override fun top() { + binding?.exampleIvBack?.setOnClickListener { finish() } + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.homeRanking(type) + } else { + showNetError() + } + binding.srRank.setOnRefreshListener { + mViewModel.homeRanking(type) + } + } + + override fun center() { + mViewModel.homeRankingData.observe(this) { + if (it != null) { + if (it.data?.list?.isNotEmpty() == true) { + val homeRankTrendingAdapter = RankAdapter() + val manager1 = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding?.rvRanks?.layoutManager = manager1 + binding?.rvRanks?.adapter = homeRankTrendingAdapter + homeRankTrendingAdapter?.submitList(it.data.list) + homeRankTrendingAdapter?.isStateViewEnable = true + homeRankTrendingAdapter?.setStateViewLayout(this, R.layout.layout_emptyview) + homeRankTrendingAdapter?.setOnItemClickListener { adapter, view, position -> + val data = adapter.getItem(position) as HomeRankingRes.Item7 + startActivity(Intent( + this, PlayerDetailActivity::class.java + ).apply { + putExtra( + Constants.CONSTANTS_short_play_id, data.short_play_id + ) + }) + } + } + binding.srRank.finishRefresh() + hideNetError() + hideLoading() + } else { + showNetError() + toast(getString(R.string.network_error)) + hideLoading() + } + } + } + + override fun getViewBinding(): ActivityRankingsBinding { + return ActivityRankingsBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.homeRanking(type) + } else { + toast(getString(R.string.example_no_network)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/RankAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/RankAdapter.kt new file mode 100644 index 0000000..23549a0 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/RankAdapter.kt @@ -0,0 +1,95 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Context +import android.graphics.Color +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.fullspan.FullSpanAdapterType +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.utils.formatNumberByLong + +class RankAdapter : + BaseQuickAdapter(), + FullSpanAdapterType { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: HomeRankingRes.Item7? + ) { + Glide.with(context).load(item?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(holder.getView(R.id.iv_cover)) + holder.setText(R.id.tv_rank_name, item?.name) + holder.setText(R.id.tv_des, item?.description) + if (item?.category?.isNotEmpty() == true) { + val string = item.category[0] + holder.setText(R.id.tv_rank_tag, string) + holder.setVisible(R.id.tv_rank_tag, true) + } else { + holder.setVisible(R.id.tv_rank_tag, false) + } + val view = holder.getView(R.id.tv_rank_watch) + holder.setText(R.id.tv_num, (position + 1).toString()) + when (position) { + 0 -> { + holder.setBackgroundResource(R.id.tv_num, R.drawable.bg_rank_1) + holder.setTextColor(R.id.tv_num,Color.parseColor("#4F2500")) + holder.setTextColor(R.id.tv_rank_tag,Color.parseColor("#FFE8C4")) + view?.setCompoundDrawablesWithIntrinsicBounds( + context?.getDrawable(R.drawable.iv_item_rank_heat), + null, + null, + null + ) + } + + 1 -> { + holder.setBackgroundResource(R.id.tv_num, R.drawable.bg_rank_2) + holder.setTextColor(R.id.tv_num,Color.parseColor("#424242")) + holder.setTextColor(R.id.tv_rank_tag,Color.parseColor("#FFE8C4")) + view?.setCompoundDrawablesWithIntrinsicBounds( + context?.getDrawable(R.drawable.iv_item_rank_heat), + null, + null, + null + ) + } + + 2 -> { + holder.setBackgroundResource(R.id.tv_num, R.drawable.bg_rank_3) + holder.setTextColor(R.id.tv_num,Color.parseColor("#79471B")) + holder.setTextColor(R.id.tv_rank_tag,Color.parseColor("#FFE8C4")) + view?.setCompoundDrawablesWithIntrinsicBounds( + context?.getDrawable(R.drawable.iv_item_rank_heat), + null, + null, + null + ) + } + + else -> { + holder.setBackgroundResource(R.id.tv_num, R.drawable.bg_rank_other) + holder.setTextColor(R.id.tv_num,Color.parseColor("#0F0F0F")) + holder.setTextColor(R.id.tv_rank_tag,Color.parseColor("#F0C2E1")) + view?.setCompoundDrawablesWithIntrinsicBounds( + context?.getDrawable(R.drawable.iv_item_rank_heat_1), + null, + null, + null + ) + } + } + holder.setText(R.id.tv_rank_watch, item?.watch_total?.let { formatNumberByLong(it) }) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_ranks, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/ReelsFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/ReelsFragment.kt new file mode 100644 index 0000000..8837a21 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/ReelsFragment.kt @@ -0,0 +1,464 @@ +package com.jia.er.nebuluxe.app.home + +import android.annotation.SuppressLint +import android.graphics.Color +import android.net.Uri +import android.text.SpannableStringBuilder +import android.view.View +import android.view.WindowManager +import android.widget.FrameLayout +import android.widget.SeekBar +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatSeekBar +import androidx.appcompat.widget.AppCompatTextView +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DefaultDataSourceFactory +import androidx.media3.exoplayer.DefaultRenderersFactory +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.hls.HlsMediaSource +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource +import androidx.media3.ui.PlayerView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper +import androidx.recyclerview.widget.RecyclerView +import com.jia.er.nebuluxe.app.data.RecommendDataRes +import com.jia.er.nebuluxe.app.ui.FfmpegRenderersFactory +import com.jia.er.nebuluxe.app.ui.LoadingLine +import com.jia.er.nebuluxe.app.ui.OnSnapHelperCurrentListener +import com.jia.er.nebuluxe.app.ui.RecyclerViewScrollerDetection +import com.blankj.utilcode.util.NetworkUtils +import com.blankj.utilcode.util.ViewUtils +import com.bumptech.glide.Glide +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseFragment +import com.jia.er.nebuluxe.app.databinding.FragmentReelsBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.appendWithStyle +import com.jia.er.nebuluxe.app.utils.formatTimestamp +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.jia.er.nebuluxe.app.utils.DialogUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +@SuppressLint("UnsafeOptInUsageError") +class ReelsFragment : BaseFragment(), + OnSnapHelperCurrentListener { + private var builder: ExoPlayer.Builder? = null + private var player: ExoPlayer? = null + private val pagerSnapHelper = PagerSnapHelper() + private val recyclerViewScrollerDetection = RecyclerViewScrollerDetection() + private var homeForYouAdapter: HomeForYouAdapter? = null + private var playerView: PlayerView? = null + private var currentView: View? = null + private var loadingLine: LoadingLine? = null + private var tvName: AppCompatTextView? = null + private var tvTime: AppCompatTextView? = null + private var play: AppCompatImageView? = null + private var collection: AppCompatImageView? = null + private var ivIconPlayer: AppCompatImageView? = null + private var ivCover: AppCompatImageView? = null + private var exampleSeekbarPlayerController: AppCompatSeekBar? = null + private var exampleProgressJob: Job? = null + private var isDragging = false + private var isPlaying = false + private var currentPage = 1 + private val currentSize = 10 + private var dataRes: RecommendDataRes.Data? = null + private var currentPosition = 0 + private var tvEpTotal: AppCompatTextView? = null + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + private var isCanPlay = false + + override fun top() { + activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + builder = ExoPlayer.Builder(requireContext(), FfmpegRenderersFactory(requireContext())) + .setRenderersFactory( + DefaultRenderersFactory(requireContext()).setEnableDecoderFallback( + true + ) + ) + player = builder?.build() + playerView = ViewUtils.layoutId2View(R.layout.include_player_view) as PlayerView + playerView?.player = player + loadingLine = playerView?.findViewById(R.id.load_line) + tvName = playerView?.findViewById(R.id.example_tv_title_player_controller) + tvTime = playerView?.findViewById(R.id.tv_player_seek_time) + tvEpTotal = playerView?.findViewById(R.id.tv_ep_total) + play = playerView?.findViewById(R.id.example_iv_play_player_controller) + collection = playerView?.findViewById(R.id.example_iv_collection_controller) + ivIconPlayer = playerView?.findViewById(R.id.iv_icon_player) + exampleSeekbarPlayerController = + playerView?.findViewById(R.id.example_seekBar_player_controller) + play?.setOnClickListener { + singleClick { + if (isPlaying) { + pause() + } else { + play() + } + } + } + player?.addListener(object : Player.Listener { + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + when (playbackState) { + Player.STATE_BUFFERING -> { + if (!isHidden) { + ivIconPlayer?.visibility = View.VISIBLE + loadingLine?.visibility = View.VISIBLE + loadingLine?.postDelayed({ loadingLine?.startAnimation() }, 200) + } + } + + Player.STATE_READY -> { + if (isCanPlay) { + canPlay() + } + } + + Player.STATE_ENDED -> { + val plus = currentPosition.plus(1) + binding.rvRecommend.smoothScrollToPosition(plus) + } + + Player.STATE_IDLE -> { + } + } + } + + override fun onPlayerError(error: PlaybackException) { + super.onPlayerError(error) + + } + }) + collection?.setOnClickListener { + singleClick { + if (dataRes?.is_collect == true) { + dataRes?.video_info?.short_play_id?.let { it1 -> + dataRes?.video_info?.short_play_video_id?.let { it2 -> + DialogUtils.unFavoriteDialog( + requireContext(), + it1, it2, mViewModel + ) + } + } + } else { + dataRes?.video_info?.short_play_id?.let { + dataRes?.video_info?.short_play_video_id.let { it1 -> + if (it1 != null) { + mViewModel.doCollect( + it, it1 + ) + } + } + } + } + } + } + binding.srFy.apply { + setOnRefreshListener { + recyclerViewScrollerDetection.isFirstAttached = false + player?.stop() + mViewModel.getRecommands(currentPage, currentSize, "") + } + } + binding.rvRecommend.addOnScrollListener(object : RecyclerView.OnScrollListener() { + + var isSlidingUp = false + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + isSlidingUp = dy > 0 + } + + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + val totalItemCount = layoutManager.itemCount + + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + if (isSlidingUp && + lastVisibleItemPosition == totalItemCount - 1 + ) { + currentPage++ + mViewModel.getRecommands(currentPage, currentSize, "") + } + } + } + }) + exampleSeekbarPlayerController?.setOnSeekBarChangeListener(object : + SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged( + seekBar: SeekBar?, + progress: Int, + fromUser: Boolean + ) { + if (fromUser) { + seekTo(progress) + } + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) { + tvTime?.visibility = FrameLayout.VISIBLE + isDragging = true + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + isDragging = false + tvTime?.visibility = FrameLayout.INVISIBLE + } + }) + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.getRecommands(currentPage, currentSize, "") + } else { + showNetError() + } + } + + private fun setProgress() { + exampleProgressJob?.cancel() + val duration = player!!.duration + exampleSeekbarPlayerController?.max = duration.toInt() + exampleSeekbarPlayerController?.progress = player!!.currentPosition.toInt() + exampleProgressJob = lifecycleScope.launch { + while (isActive) { + if (!isDragging) { + withContext(Dispatchers.Main) { + exampleSeekbarPlayerController?.progress = + player!!.currentPosition.toInt() + } + } + delay(1000) + } + } + } + + fun play() { + player?.play() + play?.setImageResource(R.drawable.iv_example_stop) + isPlaying = true + isDragging = false + } + + fun pause() { + player?.pause() + play?.setImageResource(R.drawable.iv_example_play) + isPlaying = false + isDragging = true + } + + private fun seekTo(progress: Int) { + player?.seekTo(progress.toLong()) + seekTime() + } + + private fun seekTime() { + val currentPosition = player!!.currentPosition + val currentTime = formatTimestamp(currentPosition / 1000) + val totalDuration = player!!.duration + val totalTime = formatTimestamp(totalDuration / 1000) + tvTime?.text = "$currentTime/$totalTime" + } + + override fun center() { + mViewModel.recommendData.observe(this) { + if (it != null) { + if (it.data?.list?.isNotEmpty() == true) { + if (currentPage == 1) { + homeForYouAdapter = HomeForYouAdapter() + val layoutManager = + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + binding?.rvRecommend?.layoutManager = layoutManager + binding?.rvRecommend?.adapter = homeForYouAdapter + binding?.rvRecommend?.isNestedScrollingEnabled = false + pagerSnapHelper.attachToRecyclerView(binding?.rvRecommend) + recyclerViewScrollerDetection.setCurrentListener(this) + recyclerViewScrollerDetection.attachToSnapHelper(pagerSnapHelper) + binding?.rvRecommend?.let { it1 -> + recyclerViewScrollerDetection.addOnScrollListener( + it1 + ) + } + it.data?.list?.forEach { item -> + Glide.with(requireContext()).load(item.image_url).preload() + } + homeForYouAdapter?.submitList(it.data.list) + } else { + homeForYouAdapter?.addAll(it.data.list) + } + hideLoading() + } else { + toast(getString(R.string.no_more_data)) + } + hideNetError() + } else { + toast(getString(R.string.network_error)) + hideLoading() + } + binding.srFy.finishRefresh() + } + + mViewModel.collectData.observe(this) { + if (it != null) { + collection?.setImageResource(R.drawable.iv_example_collection_h) + dataRes?.collect_total = dataRes?.collect_total?.plus(1) + homeForYouAdapter?.getItem(currentPosition)?.is_collect = true + homeForYouAdapter?.getItem(currentPosition)?.collect_total = + dataRes?.collect_total + toast(getString(R.string.success)) + } else { + toast(getString(R.string.network_error)) + } + } + mViewModel.cancelCollectData.observe(this) { + if (it != null) { + collection?.setImageResource(R.drawable.iv_example_collection_n) + dataRes?.collect_total = dataRes?.collect_total?.minus(1) + homeForYouAdapter?.getItem(currentPosition)?.is_collect = false + homeForYouAdapter?.getItem(currentPosition)?.collect_total = + dataRes?.collect_total + toast(getString(R.string.success)) + } else { + toast(getString(R.string.network_error)) + } + } + } + + private fun buildMediaSource(videoPath: String): MediaSource { + val dataSourceFactory: DataSource.Factory = + DefaultDataSourceFactory(requireContext(), "loopdrama") + + return if (videoPath.endsWith(".m3u8")) { + HlsMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(videoPath))) + } else { + ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(videoPath))) + } + } + + override fun getViewBinding(): FragmentReelsBinding { + return FragmentReelsBinding.inflate(layoutInflater) + } + + override fun onPause() { + super.onPause() + isCanPlay = false + binding.root.postDelayed({ pause() }, 300) + } + + + override fun onResume() { + super.onResume() + if (isVisible) { + isCanPlay = true + canPlay() + } + } + + @SuppressLint("UnsafeOptInUsageError") + override fun setActive( + currentView: View?, position: Int, previousView: View?, previousPosition: Int + ) { + isCanPlay = true + currentPosition = position + this.currentView = currentView + val frameLayout = currentView?.findViewById(R.id.video) + (playerView?.parent as FrameLayout?)?.removeView(playerView) + frameLayout?.removeAllViews() + frameLayout?.addView(playerView) + playerView?.showController() + dataRes = homeForYouAdapter?.getItem(position) + dataRes?.video_info?.video_url?.let { buildMediaSource(it) } + ?.let { player?.setMediaSource(it) } + player?.prepare() + setUI() + } + + private fun setUI() { + ivIconPlayer?.let { it1 -> + Glide.with(this).load(dataRes?.image_url).into(it1) + } + tvName?.text = dataRes?.name + val builder = SpannableStringBuilder() + builder.appendWithStyle( + "Ep.".plus(dataRes?.video_info?.episode), + Color.parseColor("#50FFFFFF"), + isBold = true + ) + .appendWithStyle( + "/Ep.".plus(dataRes?.episode_total), + Color.parseColor("#50FFFFFF"), + isBold = false + ) + tvEpTotal?.text = builder + collection?.setImageResource(if (dataRes?.is_collect == true) R.drawable.iv_example_collection_h else R.drawable.iv_example_collection_n) + } + + override fun disActive(detachedView: View?, detachedPosition: Int) { + if (null == detachedView) return + ivCover = detachedView.findViewById(R.id.iv_icon) + ivCover?.visibility = View.VISIBLE + if (player?.isPlaying == true) { + pause() + } + } + + override fun onHiddenChanged(hidden: Boolean) { + super.onHiddenChanged(hidden) + if (hidden) { + isCanPlay = false + binding.root.postDelayed({ pause() }, 200) + activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + isCanPlay = true + canPlay() + activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + + private fun canPlay() { + loadingLine?.endAnimation() + loadingLine?.visibility = View.INVISIBLE + ivCover = currentView?.findViewById(R.id.iv_icon) + ivCover?.visibility = View.INVISIBLE + ivIconPlayer?.visibility = View.INVISIBLE + play() + setProgress() + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + mViewModel.getRecommands(currentPage, currentSize, "") + } else { + toast(getString(R.string.example_no_network)) + } + } + } + + override fun onDestroy() { + super.onDestroy() + player?.stop() + player?.release() + recyclerViewScrollerDetection.detachToSnapHelper() + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/home/SavedFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/home/SavedFragment.kt new file mode 100644 index 0000000..df6caf7 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/home/SavedFragment.kt @@ -0,0 +1,142 @@ +package com.jia.er.nebuluxe.app.home + +import android.content.Intent +import androidx.lifecycle.ViewModelProvider +import com.jia.er.nebuluxe.app.data.CollectionRes +import com.blankj.utilcode.util.NetworkUtils +import com.chad.library.adapter4.layoutmanager.QuickGridLayoutManager +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseFragment +import com.jia.er.nebuluxe.app.databinding.FragmentSavedBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast + +class SavedFragment : BaseFragment() { + private var page: Int = 1 +// private var myListAdapter: MyListAdapter? = null + private var edit = false + private var current: Int = 0 + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + override fun top() { +// if (NetworkUtils.isConnected()) { +// showLoading() +// mViewModel.getCollections(page) +// } else { +// showNetError() +// } +// binding?.srMyList?.setOnRefreshListener { +// page = 1 +// mViewModel.getCollections(page) +// } +// binding?.srMyList?.setOnLoadMoreListener { +// page++ +// mViewModel.getCollections(page) +// } +// binding?.exampleIvEditMyFavorites?.setOnClickListener { +// if (!edit) { +// edit = true +// binding?.exampleIvEditMyFavorites?.setImageResource(R.drawable.iv_edit_favorites_complete) +// myListAdapter?.eidt = true +// binding?.tvTitle?.text = "Delete" +// binding.tvTitle.background = context?.getDrawable(R.drawable.iv_list_title_bg_h) +// myListAdapter?.notifyDataSetChanged() +// } else { +// edit = false +// binding?.exampleIvEditMyFavorites?.setImageResource(R.drawable.iv_edit_favorites) +// binding.tvTitle.background = context?.getDrawable(R.drawable.iv_list_title_bg) +// myListAdapter?.eidt = false +// binding?.tvTitle?.text = "Favorites" +// myListAdapter?.notifyDataSetChanged() +// } +// } +// binding.ivHis.setOnClickListener { +// startActivity( +// Intent( +// requireContext(), +// HistoryActivity::class.java +// )) +// } + } + + override fun center() { +// mViewModel.collectionsData.observe(this) { +// if (it != null) { +// if (page == 1) { +// myListAdapter = MyListAdapter() +// val manager = QuickGridLayoutManager(requireContext(), 3) +// binding?.rvMyList?.layoutManager = manager +// binding?.rvMyList?.adapter = myListAdapter +// myListAdapter?.submitList(it.data?.list) +// myListAdapter?.isStateViewEnable = true +// myListAdapter?.setStateViewLayout(requireContext(), R.layout.layout_emptyview) +// myListAdapter?.addOnItemChildClickListener(R.id.iv_favorites_delete) { adapter, view, position -> +// current = position +// val collectionData = +// adapter.getItem(position) as CollectionRes.CollectionData +// DialogUtils.unFavoriteDialog( +// requireContext(), +// collectionData.short_play_id, +// collectionData.short_play_video_id, +// mViewModel +// ) +// } +// myListAdapter?.setOnItemClickListener { adapter, view, position -> +// val data = adapter.getItem(position) as CollectionRes.CollectionData +// startActivity( +// Intent( +// requireContext(), +// PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, +// data.short_play_id +// ) +// }) +// } +// } else { +// if (it.data?.list?.isNotEmpty() == true) { +// myListAdapter?.addAll(it.data.list) +// } else { +// toast(getString(R.string.no_more_data)) +// } +// } +// hideLoading() +// hideNetError() +// } else { +// hideLoading() +// toast(getString(R.string.network_error)) +// } +// binding?.srMyList?.finishRefresh() +// binding?.srMyList?.finishLoadMore() +// } +// +// mViewModel.cancelCollectData.observe(this) { +// if (it != null) { +// toast(getString(R.string.success)) +// myListAdapter?.removeAt(current) +// myListAdapter?.notifyDataSetChanged() +// } else { +// toast(getString(R.string.network_error)) +// } +// } + } + + override fun getViewBinding(): FragmentSavedBinding { + return FragmentSavedBinding.inflate(layoutInflater) + } + + override fun onRetry() { + super.onRetry() + singleClick { + if (NetworkUtils.isConnected()) { + showLoading() + page = 1 + mViewModel.getCollections(page) + } else { + toast(getString(R.string.example_no_network)) + } + } + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/main/MainActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/main/MainActivity.kt new file mode 100644 index 0000000..ac505b8 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/main/MainActivity.kt @@ -0,0 +1,705 @@ +package com.jia.er.nebuluxe.app.main + +import android.view.KeyEvent +import androidx.fragment.app.FragmentTransaction +import androidx.lifecycle.ViewModelProvider +import com.jia.er.nebuluxe.app.home.HomeFragment +import com.jia.er.nebuluxe.app.home.ReelsFragment +import com.jia.er.nebuluxe.app.home.SavedFragment +import com.flyco.tablayout.listener.CustomTabEntity +import com.flyco.tablayout.listener.OnTabSelectListener +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.TabEntity +import com.jia.er.nebuluxe.app.databinding.ActivityMainBinding +import com.jia.er.nebuluxe.app.me.MeFragment +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.Memory +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + + +class MainActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + private var titles = arrayOf( + "", + "", + "", + "" + ) + private val iconUnSelectIds by lazy { + intArrayOf( + R.drawable.iv_home_n, + R.drawable.iv_reels_n, + R.drawable.iv_saved_n, + R.drawable.iv_me_n + ) + } + private val iconSelectIds by lazy { + intArrayOf( + R.drawable.iv_home_h, + R.drawable.iv_reels_h, + R.drawable.iv_saved_h, + R.drawable.iv_me_h + ) + } + private val tabEntities = ArrayList() + private var homeFragment: HomeFragment? = null + private var reelsFragment: ReelsFragment? = null + private var myListFragment: SavedFragment? = null + private var meFragment: MeFragment? = null + private var index = 0 + private var shortVideoId: Int = 0 +// private var notificationDialog: NotificationDialog? = null +// private var path = "" +// private var short_play_id = "" +// private var message_id = "" +// private var title = "" +// private var scheduler: ScheduledExecutorService? = Executors.newSingleThreadScheduledExecutor() +// private val scope = CoroutineScope(Dispatchers.Main) + +// override fun onNewIntent(intent: Intent?) { +// super.onNewIntent(intent) +// val webpageURL = intent?.data +// uploadDDL(webpageURL) +// 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() +// } + + override fun top() { +// val webpageURL = intent.data +// uploadDDL(webpageURL) +// 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() + showLoading() + EventBus.getDefault().register(this) + (titles.indices).mapTo(tabEntities) { + TabEntity( + titles[it], iconSelectIds[it], iconUnSelectIds[it] + ) + } + binding.tabLayout.setTabData(tabEntities) + binding?.tabLayout?.setOnTabSelectListener(object : OnTabSelectListener { + override fun onTabSelect(position: Int) { + switchFragment(position) +// when (position) { +// 0 -> { +// binding?.dialogHistory?.root?.postDelayed( +// { +// val string = Memory.getMMKV() +// .getString(Constants.Constants_Main_Video_info, "") +// if (string?.isNotEmpty() == true && NetworkUtils.isConnected()) { +// val fromJson = Gson().fromJson(string, MainDataHis::class.java) +// showHistoryDialog(fromJson) +// } +// }, 500 +// ) +// } +// +// else -> { +// binding?.dialogHistory?.root?.visibility = View.INVISIBLE +// } +// } + } + + override fun onTabReselect(position: Int) { + + } + }) +// Constants.CanNotification = NotificationUtils.isNotificationEnabled(this) +// mViewModel.uploadNoticeStatus( +// FbNotificationReq( +// if (Constants.CanNotification) "1" else "0" +// ) +// ) + mViewModel.getInfo() + switchFragment(index) +// GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(this) +// .addOnCompleteListener { +// if (it.isSuccessful) { +// askNotificationPermission() +// } +// } +// binding?.root?.postDelayed({ notificationGo() }, 700) +// mViewModel.enterTheApp() +// val intervalMillis = 10 * 60 * 1000 +// scheduler?.scheduleWithFixedDelay({ +// try { +// lifecycleScope.launch { +// mViewModel.onLine() +// } +// } catch (e: Exception) { +// e.printStackTrace() +// } +// }, 0, intervalMillis.toLong(), TimeUnit.MILLISECONDS) +// binding?.root?.postDelayed({ +// if (Memory.getOrder().isNotEmpty()) { +// val string = Memory.getOrder() +// scope.launch { +// flow { +// for (item in string) { +// emit(item) +// } +// }.onEach { item -> +// upError( +// "pay restore", +// mViewModel, +// "restore", +// "pay_restore", +// "auto", GsonUtils.toJson(item), 0, 0 +// ) +// mViewModel.restorePaid(item) +// }.debounce(1000).collect {} +// } +// } +// }, 3000) + } + + override fun center() { +// mViewModel.restorePaidData.observe(this) { +// if (it != null) { +// it.data?.order_code?.let { it1 -> Memory.removeOrderString(it1) } +// } +// } + mViewModel.infoData.observe(this) { + if (it != null) { + it.data?.let { it1 -> + Memory.saveUserInfo(it1) + } + } + } + mViewModel.userRegisterData.observe(this) { + if (it != null) { + Memory.getMMKV() + .putString(Constants.CONSTANTS_AuthorizationExample, it.data?.token) + EventBus.getDefault().post(Constants.CONSTANTS_refresh_me) + } + } +// mViewModel.loginData.observe(this) { +// if (it != null) { +// toast(getString(R.string.success)) +// Memory.getMMKV() +// .putString(Constants.CONSTANTS_AuthorizationExample, it.data?.token) +// EventBus.getDefault() +// .post(Constants.CONSTANTS_enterTheApp) +// EventBus.getDefault() +// .post(Constants.CONSTANTS_onLine) +// mViewModel.getInfo() +// EventBus.getDefault() +// .post(Constants.CONSTANTS_refresh_me) +// hideLoading() +// loginDialog?.dismiss() +// } else { +// hideLoading() +// toast(getString(R.string.network_error)) +// } +// } + } + +// override fun onResume() { +// super.onResume() +// if (index == 0) { +// binding?.dialogHistory?.root?.postDelayed( +// { +// val string = Memory.getMMKV() +// .getString(Constants.Constants_Main_Video_info, "") +// if (string?.isNotEmpty() == true && NetworkUtils.isConnected()) { +// val fromJson = Gson().fromJson(string, MainDataHis::class.java) +// showHistoryDialog(fromJson) +// } +// }, 500 +// ) +// } +// } + +// private fun showHistoryDialog(data: MainDataHis) { +// binding?.dialogHistory?.ivCloseHistory?.setOnClickListener { +// Memory.getMMKV() +// .putBoolean(Constants.Constants_Main_Video_status, false) +// binding?.dialogHistory?.root?.visibility = View.INVISIBLE +// } +// if (Memory.getMMKV() +// .getBoolean(Constants.Constants_Main_Video_status, false) +// ) { +// binding?.dialogHistory?.ivVideo?.let { +// if (!isFinishing && !isDestroyed) { +// Glide.with(this).load( +// data.video_img +// ).placeholder(R.drawable.iv_placeholder_v).into(it) +// } +// } +// binding?.dialogHistory?.cl?.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// this, PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, data.video_id +// ) +// }) +// } +// } +// binding?.dialogHistory?.root?.post { +// binding?.dialogHistory?.tvEp?.text = +// "Ep.".plus(data.video_last).plus("/").plus("Ep.").plus(data.episode_total) +// } +// binding?.dialogHistory?.root?.visibility = View.VISIBLE +// } +// } +// +// private fun askNotificationPermission() { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// if (ContextCompat.checkSelfPermission( +// this, Manifest.permission.POST_NOTIFICATIONS +// ) == PackageManager.PERMISSION_GRANTED +// ) { +// NotificationUtils.firebase(mViewModel) +// } else { +// if (Constants.isUat) { +// openNotification() +// } else { +// if (shouldShowNotification()) { +// openNotification() +// } +// } +// } +// } else { +// if (NotificationUtils.isNotificationEnabled(this)) { +// NotificationUtils.firebase(mViewModel) +// } else { +// if (Constants.isUat) { +// openNotification() +// } else { +// if (shouldShowNotification()) { +// openNotification() +// } +// } +// } +// } +// } +// +// private fun openNotification() { +// notificationDialog = NotificationDialog(this) +// val example_tv_later = +// notificationDialog?.findViewById(R.id.example_tv_unfavorite) +// val iv_close_notification = +// notificationDialog?.findViewById(R.id.iv_close_notification) +// val example_open = +// notificationDialog?.findViewById(R.id.example_tv_think_again) +// notificationDialog?.setOnDismissListener { +// Memory.getMMKV().putLong( +// Constants.CONSTANTS_PREF_LAST_POPUP_TIME_Notification, +// System.currentTimeMillis() +// ) +// } +// example_tv_later?.setOnClickListener { notificationDialog?.dismiss() } +// iv_close_notification?.setOnClickListener { notificationDialog?.dismiss() } +// example_open?.setOnClickListener { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) +// notificationDialog?.dismiss() +// } else { +// NotificationUtils.openNotificationSettings(this) +// notificationDialog?.dismiss() +// } +// } +// notificationDialog?.show() +// } +// +// private val requestPermissionLauncher = registerForActivityResult( +// ActivityResultContracts.RequestPermission(), +// ) { isGranted: Boolean -> +// Constants.CanNotification = isGranted +// if (isGranted) { +// NotificationUtils.firebase(mViewModel) +// mViewModel.uploadNoticeStatus( +// FbNotificationReq( +// if (Constants.CanNotification) "1" else "0" +// ) +// ) +// notificationDialog?.dismiss() +// } else { +// NotificationUtils.openNotificationSettings(this) +// } +// } +// +// override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { +// super.onActivityResult(requestCode, resultCode, data) +// if (index == 0) { +// if (requestCode == NotificationUtils.NOTIFICATION_SETTINGS_REQUEST_CODE) { +// Constants.CanNotification = +// NotificationUtils.isNotificationEnabled(this) +// if (Constants.CanNotification) { +// NotificationUtils.firebase(mViewModel) +// } +// mViewModel.uploadNoticeStatus( +// FbNotificationReq( +// if (Constants.CanNotification) "1" else "0" +// ) +// ) +// } +// } else { +// val fragments: List = supportFragmentManager.fragments +// if (fragments.isNotEmpty()) { +// for (fragment in fragments) { +// if (fragment.isAdded && !fragment.isDetached) { +// fragment.onActivityResult(requestCode, resultCode, data) +// } +// } +// } +// } +// } +// +// private fun notificationGo() { +// if (message_id.isNotBlank() && !message_id.contentEquals("null")) { +// if ("0" != message_id) { +// mViewModel.sendReport(message_id, title) +// } +// } +// when (path) { +// "detail" -> { +// if (short_play_id.isNotEmpty() && "null" != short_play_id) { +// try { +// val toInt = short_play_id.toInt() +// binding?.root?.postDelayed({ +// startActivity(Intent( +// this, PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, toInt +// ) +// }) +// }, 700) +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } +// } +// +// "orderDetail" -> { +// binding?.root?.postDelayed({ +// startActivity( +// Intent( +// this, StoreActivity::class.java +// ) +// ) +// }, 500) +// } +// +// "feedback" -> { +// binding?.root?.postDelayed({ +// if (message_id.isNotBlank() && message_id != "null") { +// Memory.getMMKV() +// .putString(Constants.CONSTANTS_Detail_id, message_id) +// startActivity( +// Intent( +// this, FeedBackDetailActivity::class.java +// ) +// ) +// } else { +// startActivity( +// Intent( +// this, FeedBackListActivity::class.java +// ) +// ) +// } +// }, 500) +// } +// } +// } +// +// +// private fun uploadDDL(webpageURL: Uri?) { +// val ddl = webpageURL.toString() +// if (ddl.isNotEmpty() && !ddl.contentEquals("null")) { +// w2aSelfAttribution(ddl) +// val regex = """short_play_id=(\d+).*""".toRegex() +// val matchResult = regex.find(ddl) +// if (matchResult != null) { +// val shortPlayId = matchResult.groupValues[1] +// shortVideoId = shortPlayId.toInt() +// if (shortVideoId != 0) { +// binding.root.postDelayed({ +// startActivity( +// Intent( +// this, PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, shortVideoId +// ) +// }) +// }, 1000) +// } +// Memory.getMMKV() +// .putString(Constants.Constants_DDL_Url, "") +// clearClipboardContent(this) +// } +// } +// } +// +// fun getClip() { +// this.window.decorView.post { +// val clipContent = getClipContent() +// if (clipContent.isNotEmpty()) { +// if (clipContent.startsWith("[QJ]")) { +// val urlString = clipContent.removePrefix("[QJ]").trim() +// val extractVideoInfo = parseVideoAndShortPlayIds(urlString) +// if (urlString.contains("reelcrush")) { +// shortVideoId = extractVideoInfo.second?.toInt() ?: 0 +// w2aSelfAttribution(clipContent) +// if (shortVideoId != 0) { +// binding.root.postDelayed({ +// startActivity( +// Intent( +// this, PlayerDetailActivity::class.java +// ).apply { +// putExtra( +// Constants.CONSTANTS_short_play_id, shortVideoId +// ) +// }) +// }, 3000) +// } +// Memory.getMMKV() +// .putString(Constants.Constants_DDL_Url, "") +// clearClipboardContent(this) +// } +// } +// } +// } +// } + + private fun switchFragment(position: Int) { + val transaction = supportFragmentManager.beginTransaction() + hideFragments(transaction) + when (position) { + 0 -> homeFragment?.let { + transaction.show(it) + } ?: HomeFragment().let { + homeFragment = it + transaction.add(R.id.container, it, "HomeFragment") + } + + 1 -> reelsFragment?.let { + transaction.show(it) + } ?: ReelsFragment().let { + reelsFragment = it + transaction.add( + R.id.container, it, "ReelsFragment" + ) + } + + 2 -> myListFragment?.let { + transaction.show(it) + } ?: SavedFragment().let { + myListFragment = it + transaction.add(R.id.container, it, "SavedFragment") + } + + + 3 -> meFragment?.let { + transaction.show(it) + } ?: MeFragment().let { + meFragment = it + transaction.add(R.id.container, it, "MeFragment") + } + + else -> { + + } + } + + index = position + binding?.tabLayout?.currentTab = index + transaction.commitAllowingStateLoss() + } + + + private fun hideFragments(transaction: FragmentTransaction) { + homeFragment?.let { transaction.hide(it) } + reelsFragment?.let { transaction.hide(it) } + myListFragment?.let { transaction.hide(it) } + meFragment?.let { transaction.hide(it) } + } + +// 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 "" +// } +// +// private fun w2aSelfAttribution(data: String?) { +// data?.let { mViewModel.w2aSelfAttribution(it) } +// } +// +// private fun clearClipboardContent(context: Context) { +// val clipboardManager = +// context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager +// +// val emptyClip = ClipData.newPlainText("", "") +// +// clipboardManager.setPrimaryClip(emptyClip) +// } +// +// private fun parseVideoAndShortPlayIds(clipboardContent: String): Pair { +// +// val queryStartIndex = clipboardContent.indexOf('?') +// val queryString = +// if (queryStartIndex != -1) clipboardContent.substring(queryStartIndex + 1) else "" +// +// val videoIdRegex = Regex("video_id=(\\d+)") +// val shortPlayIdRegex = Regex("short_play_id=(\\d+)") +// +// val videoIdMatch = videoIdRegex.find(queryString)?.groupValues?.get(1) +// val shortPlayIdMatch = shortPlayIdRegex.find(queryString)?.groupValues?.get(1) +// +// return Pair(videoIdMatch, shortPlayIdMatch) +// } + + override fun getViewBinding(): ActivityMainBinding { + return ActivityMainBinding.inflate(layoutInflater) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEvent(event: String) { + if (Constants.CONSTANTS_auth_refresh == event) { + mViewModel.createUserAccount() + } + if (Constants.CONSTANTS_main_stop == event) { + binding?.root?.postDelayed({ hideLoading() }, 300) + } +// if (Constants.CONSTANTS_enterTheApp == event) { +// mViewModel.enterTheApp() +// } +// if (Constants.CONSTANTS_leaveApp == event) { +// mViewModel.leaveApp() +// } +// if (Constants.CONSTANTS_onLine == event) { +// mViewModel.onLine() +// } +// if (Constants.Constants_onTokenRefresh == event) { +// NotificationUtils.firebase(mViewModel) +// } +// if (Constants.Constants_getClip == event) { +// getClip() +// } +// if (Constants.CONSTANTS_web_Login == event) { +// if (!NetworkUtils.isConnected()) { +// toast(getString(R.string.example_no_network)) +// return +// } +// showLogin() +// } + } + +// private var callbackManager: CallbackManager? = null +// private var loginDialog: LoginDialog? = null +// +// private fun showLogin() { +// callbackManager = CallbackManager.Factory.create() +// LoginManager.getInstance().registerCallback(callbackManager, +// object : FacebookCallback { +// 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()}") +// loginDialog?.dismiss() +// } 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") +// showLoading() +// EventBus.getDefault() +// .post(Constants.CONSTANTS_leaveApp) +// mViewModel.doLogin( +// LoginReq( +// 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") +// loginDialog?.dismiss() +// } +// +// override fun onError(exception: FacebookException) { +// toast("Facebook login exception.$exception") +// loginDialog?.dismiss() +// } +// }) +// loginDialog = LoginDialog(this) +// val tv_facebook_login = +// loginDialog?.findViewById(R.id.tv_facebook_login) +// tv_facebook_login?.setOnClickListener { +// singleClick { +// callbackManager?.let { it1 -> +// LoginManager.getInstance() +// .logInWithReadPermissions(this, it1, arrayListOf("public_profile", "email")) +// } +// } +// } +// loginDialog?.show() +// loginDialog?.setOnDismissListener { +// LoginManager.getInstance().unregisterCallback(callbackManager) +// callbackManager = null +// loginDialog = null +// } +// } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + moveTaskToBack(true) + return true + } + return super.onKeyDown(keyCode, event) + } + + override fun onDestroy() { +// scheduler?.shutdown() +// scheduler = null + super.onDestroy() + EventBus.getDefault().unregister(this) + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/main/SplashActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/main/SplashActivity.kt new file mode 100644 index 0000000..cd09b2d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/main/SplashActivity.kt @@ -0,0 +1,54 @@ +package com.jia.er.nebuluxe.app.main + +import android.content.Intent +import androidx.lifecycle.ViewModelProvider +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.databinding.ActivitySplashBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.Memory + +class SplashActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + override fun top() { + + val toString = Memory.getMMKV() + .getString(Constants.CONSTANTS_AuthorizationExample, "") + .toString() + if (toString.isEmpty()) { + mViewModel.createUserAccount() + } else { + goMain() + } + } + + + + override fun center() { + mViewModel.userRegisterData.observe(this) { + if (it != null) { + Memory.getMMKV() + .putString(Constants.CONSTANTS_AuthorizationExample, it.data?.token) + goMain() + } else { + goMain() + } + } + } + + private fun goMain() { + binding?.root?.postDelayed({ + startActivity( + Intent( + this, + MainActivity::class.java + ) + ) + finish() + }, 1000) + } + + override fun getViewBinding(): ActivitySplashBinding { + return ActivitySplashBinding.inflate(layoutInflater) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/me/AboutActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/me/AboutActivity.kt new file mode 100644 index 0000000..c76f7c8 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/me/AboutActivity.kt @@ -0,0 +1,42 @@ +package com.jia.er.nebuluxe.app.me + +import android.content.Intent +import android.net.Uri +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.databinding.ActivityAboutBinding +import com.jia.er.nebuluxe.app.utils.PackageUtils +import com.jia.er.nebuluxe.app.utils.singleClick + +class AboutActivity : BaseActivity() { + override fun top() { + binding.exampleIvBack.setOnClickListener { finish() } + } + + override fun center() { + binding.tvVersion.text = "V".plus(PackageUtils.getPackageVersion(this)) + binding.tvPrivacyPolicy.setOnClickListener { + singleClick { + val webIntent = + Intent(Intent.ACTION_VIEW, Uri.parse("https://www.nebuluxetv.com/private")) + startActivity(webIntent) + } + } + binding.tvUserAgreement.setOnClickListener { + singleClick { + val webIntent = + Intent(Intent.ACTION_VIEW, Uri.parse("https://www.nebuluxetv.com/user_policy")) + startActivity(webIntent) + } + } + binding.tvVisitWebsite.setOnClickListener { + singleClick { + val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.nebuluxetv.com")) + startActivity(webIntent) + } + } + } + + override fun getViewBinding(): ActivityAboutBinding { + return ActivityAboutBinding.inflate(layoutInflater) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/me/MeFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/me/MeFragment.kt new file mode 100644 index 0000000..26d1928 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/me/MeFragment.kt @@ -0,0 +1,247 @@ +package com.jia.er.nebuluxe.app.me + +import android.annotation.SuppressLint +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.text.Spannable +import android.text.SpannableString +import android.text.style.UnderlineSpan +import android.view.View +import androidx.lifecycle.ViewModelProvider +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.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseFragment +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.databinding.FragmentMeBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.Memory +import com.jia.er.nebuluxe.app.utils.singleClick +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class MeFragment : BaseFragment() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + override fun top() { + EventBus.getDefault().register(this) + mViewModel.noticeNum() + binding.srMe.setOnRefreshListener { + mViewModel.getInfo() + mViewModel.noticeNum() + } +// binding.llFeedback.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// requireActivity(), +// FeedBackActivity::class.java +// ) +// ) +// } +// } +// binding.clTopUp.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// requireActivity(), +// StoreActivity::class.java +// ) +// ) +// } +// } +// binding.clVip.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// requireActivity(), +// StoreActivity::class.java +// ) +// ) +// } +// } +// binding.tvExampleAbout.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// requireActivity(), +// AboutActivity::class.java +// ) +// ) +// } +// } +// binding.tvExampleSettings.setOnClickListener { +// singleClick { +// startActivity( +// Intent( +// requireActivity(), +// SettingActivity::class.java +// ) +// ) +// } +// } +// binding.tvId.setOnClickListener { +// val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager +// clipboard.setPrimaryClip( +// ClipData.newPlainText( +// "label", +// Memory.getCustomId() +// ) +// ) +// toast("Copied") +// } +// binding.wallet.setOnClickListener { +// startActivity( +// Intent( +// requireActivity(), +// MyWalletActivity::class.java +// ) +// ) +// } +// setUserUI() +// binding.tvVipStatus.applyTextSkew(-10f) + } + + override fun onResume() { + super.onResume() + setUserUI() + } + + override fun center() { + mViewModel.infoData.observe(this) { + if (it != null) { + it.data?.let { it1 -> + Memory.saveUserInfo(it1) + setUserUI() + } + } + binding.srMe.finishRefresh() + } +// mViewModel.noticeNumData.observe(this) { +// if (it != null) { +// if (it.data?.feedback_notice_num != 0) { +// binding?.tvBackNum?.visibility = View.VISIBLE +// binding?.tvBackNum?.text = it.data?.feedback_notice_num.toString() +// } else { +// binding?.tvBackNum?.visibility = View.INVISIBLE +// } +// } else { +// binding?.tvBackNum?.visibility = View.INVISIBLE +// } +// } + } + + + @SuppressLint("UseCompatLoadingForDrawables") + private fun setUserUI() { + if (Memory.isTourist()) { + binding?.ivAvatar?.let { + Glide.with(this).load(Memory.getUserInfo()?.avator).skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .apply(RequestOptions.bitmapTransform(CircleCrop())) + .placeholder(R.drawable.iv_avatar) + .error(R.drawable.iv_avatar).into(it) + } + } else { + binding?.tvUserName?.text = + Memory.getUserInfo()?.family_name.plus(Memory.getUserInfo()?.giving_name) + binding?.ivAvatar?.let { + Glide.with(this).load(Memory.getUserInfo()?.avator).skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .apply(RequestOptions.bitmapTransform(CircleCrop())) + .placeholder(R.drawable.iv_avatar) + .error(R.drawable.iv_avatar).into(it) + } + } +// if (Memory.isVip()) { +// binding.clVip.background = context?.getDrawable(R.drawable.iv_me_vip_h) +// binding.tvVipStatus.text = Memory.getUserInfo()?.vip_type +// val content = binding.tvVipStatus.text +// val spannableString = SpannableString(content) +// spannableString.setSpan( +// UnderlineSpan(), +// 0, +// content.length, +// Spannable.SPAN_EXCLUSIVE_EXCLUSIVE +// ) +// binding.tvVipStatus.text = spannableString +// binding?.tvInfoVip?.text = +// "Expiration Time:".plus(Memory.getUserInfo()?.vip_end_time?.toLong() +// ?.let { transToString(it) }) +// binding?.tvInfoVip?.setCompoundDrawablesWithIntrinsicBounds( +// context?.getDrawable(R.drawable.iv_time), +// null, +// null, +// null +// ) +// binding?.tvVipStatus?.setCompoundDrawablesWithIntrinsicBounds( +// context?.getDrawable(R.drawable.iv_vip_h), +// null, +// context?.getDrawable(R.drawable.iv_vip_right), +// null +// ) +// binding.tvGo.text = "Renew" +// binding.tvGo.background = context?.getDrawable(R.drawable.bg_vip_go_h) +// } else { +// binding?.tvInfoVip?.setCompoundDrawablesWithIntrinsicBounds( +// null, +// null, +// null, +// null +// ) +// val spannableString = SpannableString("VIP Membership") +// spannableString.setSpan( +// UnderlineSpan(), +// 0, +// spannableString.length, +// Spannable.SPAN_EXCLUSIVE_EXCLUSIVE +// ) +// binding.tvVipStatus.text = spannableString +// binding?.tvVipStatus?.setCompoundDrawablesWithIntrinsicBounds( +// context?.getDrawable(R.drawable.iv_vip), +// null, +// context?.getDrawable(R.drawable.iv_vip_right), +// null +// ) +// binding.tvGo.text = "Go" +// binding.tvGo.background = context?.getDrawable(R.drawable.bg_vip_go) +// binding?.tvInfoVip?.text = "Unlock VIP Now" +// binding.clVip.background = context?.getDrawable(R.drawable.iv_me_vip_n) +// +// } + binding?.tvId?.text = "ID:".plus(Memory.getUserInfo()?.customer_id) +// binding?.tvCoinNum?.text = Memory.getUserInfo()?.coin_left_total.toString() +// binding?.tvDonateNum?.text = Memory.getUserInfo()?.send_coin_left_total.toString() + } + + + override fun onHiddenChanged(hidden: Boolean) { + super.onHiddenChanged(hidden) + if (!hidden) { + mViewModel.getInfo() + mViewModel.noticeNum() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEvent(event: String) { + if (Constants.CONSTANTS_refresh_me == event) { + mViewModel.getInfo() + mViewModel.noticeNum() + } + } + + override fun getViewBinding(): FragmentMeBinding { + return FragmentMeBinding.inflate(layoutInflater) + } + + override fun onDestroy() { + super.onDestroy() + EventBus.getDefault().unregister(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/me/SettingActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/me/SettingActivity.kt new file mode 100644 index 0000000..7fe71a5 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/me/SettingActivity.kt @@ -0,0 +1,131 @@ +package com.jia.er.nebuluxe.app.me + +import android.content.Context +import android.content.Intent +import androidx.appcompat.widget.AppCompatTextView +import androidx.lifecycle.ViewModelProvider +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.databinding.ActivitySettingBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.utils.toast +import org.greenrobot.eventbus.EventBus +import java.io.File + +class SettingActivity : BaseActivity() { + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + override fun top() { + binding.exampleIvBack.setOnClickListener { finish() } + binding.tvClearCache.setOnClickListener { + clearCache(this) + toast(getString(R.string.success)) + } + binding.exampleIvBack.setOnClickListener { finish() } +// binding.tvExampleLogOut.setOnClickListener { +// singleClick { +// if (!Memory.isTourist()) { +// startActivity( +// Intent( +// this, +// AccountDeleteActivity::class.java +// ) +// ) +// } else { +// toast(getString(R.string.log_first)) +// } +// } +// } +// binding?.tvExampleQuit?.setOnClickListener { +// singleClick { +// if (!Memory.isTourist()) { +// val exampleUnFavoriteDialog = NotificationDialog(this) +// val tvThinkAgain = +// exampleUnFavoriteDialog.findViewById(R.id.example_tv_think_again) +// val tvUnfavorite = +// exampleUnFavoriteDialog.findViewById(R.id.example_tv_unfavorite) +// val tvTitle = +// exampleUnFavoriteDialog.findViewById(R.id.example_tv_title) +// val tvContent = +// exampleUnFavoriteDialog.findViewById(R.id.example_tv_content) +// tvThinkAgain.text = "Cancel" +// tvUnfavorite.text = "Confirm" +// tvTitle.text = "Tips" +// tvContent.text = "Are you sure you want to log out?" +// tvThinkAgain.setOnClickListener { exampleUnFavoriteDialog.dismiss() } +// tvUnfavorite.setOnClickListener { +// singleClick { +// showLoading() +// EventBus.getDefault() +// .post(Constants.CONSTANTS_leaveApp) +// mViewModel.doSignout() +// exampleUnFavoriteDialog.dismiss() +// } +// } +// exampleUnFavoriteDialog.show() +// } else { +// toast(getString(R.string.log_first)) +// } +// } +// } + } + +// override fun onResume() { +// super.onResume() +// if (Constants.WebRefresh){ +// finish() +// } +// } + + + override fun center() { +// mViewModel.signoutData.observe(this) { +// if (it != null) { +// toast(getString(R.string.success)) +// LoginManager.getInstance().logOut() +// Memory.getMMKV() +// .putString(Constants.CONSTANTS_AuthorizationExample, it.data?.token) +// EventBus.getDefault() +// .post(Constants.CONSTANTS_enterTheApp) +// EventBus.getDefault() +// .post(Constants.CONSTANTS_onLine) +// EventBus.getDefault() +// .post(Constants.CONSTANTS_refresh_me) +// Constants.WebRefresh = true +// finish() +// } else { +// toast(getString(R.string.network_error)) +// } +// hideLoading() +// } + } + + override fun getViewBinding(): ActivitySettingBinding { + return ActivitySettingBinding.inflate(layoutInflater) + } + + + private fun clearCache(context: Context) { + deleteFilesInDirectory(context.cacheDir) + + context.externalCacheDir?.let { + deleteFilesInDirectory(it) + } + } + + private fun deleteFilesInDirectory(directory: File) { + if (directory.isDirectory) { + val files = directory.listFiles() + if (files != null) { + for (file in files) { + if (file.isDirectory) { + deleteFilesInDirectory(file) + } else { + file.delete() + } + } + } + } + directory.delete() + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/AppService.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/AppService.kt new file mode 100644 index 0000000..38de78f --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/AppService.kt @@ -0,0 +1,258 @@ +package com.jia.er.nebuluxe.app.net + +import com.jia.er.nebuluxe.app.data.BaseRes +import com.jia.er.nebuluxe.app.data.BuyVideoRes +import com.jia.er.nebuluxe.app.data.CategoriesDataRes +import com.jia.er.nebuluxe.app.data.CollectionRes +import com.jia.er.nebuluxe.app.data.CreateOrderReq +import com.jia.er.nebuluxe.app.data.CreateOrderRes +import com.jia.er.nebuluxe.app.data.CustomerBuyRecordsRes +import com.jia.er.nebuluxe.app.data.CustomerOrderRes +import com.jia.er.nebuluxe.app.data.DetailsRecommendRes +import com.jia.er.nebuluxe.app.data.ExampleKeywordDataRes +import com.jia.er.nebuluxe.app.data.FbNotificationReq +import com.jia.er.nebuluxe.app.data.FreeSeriesMoreRes +import com.jia.er.nebuluxe.app.data.HistoryDataRes +import com.jia.er.nebuluxe.app.data.HomeModuleBean +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayNoPaginateRes +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayRes +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.data.HomeTopRes +import com.jia.er.nebuluxe.app.data.LoginReq +import com.jia.er.nebuluxe.app.data.LoginRes +import com.jia.er.nebuluxe.app.data.NoticeNumRes +import com.jia.er.nebuluxe.app.data.PayReq +import com.jia.er.nebuluxe.app.data.PayRes +import com.jia.er.nebuluxe.app.data.PaySettingRes +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes +import com.jia.er.nebuluxe.app.data.RecommendDataRes +import com.jia.er.nebuluxe.app.data.RewardCoinsRes +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo +import com.jia.er.nebuluxe.app.data.UploadHistoryReq +import com.jia.er.nebuluxe.app.data.VideoListDataRes +import com.jia.er.nebuluxe.app.data.UserInfoRes +import com.jia.er.nebuluxe.app.data.UserRegisterRes +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query + +interface AppService { + + @POST("customer/register") + fun register(): Call> + + @GET("customer/info") + fun getInfo(): Call> + + + @POST("homeTop") + fun homeTop(): Call> + + @POST("newShortPlay") + fun newShortPlay( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @GET("myCollections") + fun getCollections( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @GET("getVideoDetails") + fun getVideoDetails( + @Query("short_play_id") short_play_id: Int, + @Query("video_id") video_id: Int, + @Query("activity_id") activity_id: Int, + @Query("revolution") revolution: String, + @Query("no_ads") no_ads: Boolean?, + ): Call> + + @POST("newShortPlayNoPaginate") + fun newShortPlayNoPaginate(): Call> + + @FormUrlEncoded + @POST("createHistory") + fun createHistory( + @Field("short_play_id") short_play_id: Int, + @Field("video_id") video_id: Int, + ): Call> + + @GET("getRecommands") + fun getRecommands( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int, + @Query("revolution") revolution: String, + ): Call> + + @FormUrlEncoded + @POST("collect") + fun collect( + @Field("short_play_id") short_play_id: Int, + @Field("video_id") video_id: Int + ): Call> + + @FormUrlEncoded + @POST("cancelCollect") + fun cancelCollect( + @Field("short_play_id") short_play_id: Int, + @Field("video_id") video_id: Int + ): Call> + + + @GET("myHistorys") + fun myHistorys( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @GET("search") + fun getVideoList(@Query("search") search: String): Call> + + @GET("videoList") + fun getVideoList( + @Query("current_page") current_page: Int, + @Query("category_id") category_id: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @POST("homeRanking") + fun homeRanking(@Query("type") type: String): Call> + + @POST("uploadHistorySeconds") + fun uploadHistorySeconds( + @Body uploadHistoryReq: UploadHistoryReq + ): Call> + + @GET("freeShorPlayListNoPaginate") + fun freeMoreVideo(): Call> + + @GET("getDetailsRecommand") + fun getDetailsRecommand( + ): Call> + + @GET("search/hots") + fun hots(): Call> + + @POST("search/click") + fun click( + @Query("short_play_id") short_play_id: Int, + ): Call> + + + @GET("search") + fun keyword(@Query("search") search: String): Call> + + @GET("home/all-modules") + fun allModules(): Call> + + @FormUrlEncoded + @POST("buy_video") + fun buyVideo( + @Field("short_play_id") short_play_id: Int, + @Field("video_id") video_id: Int, + ): Call> + + @GET("getCategories") + fun getCategories(): Call> + + @POST("customer/onLine") + fun onLine( + ): Call> + + @POST("customer/enterTheApp") + fun enterTheApp(): Call> + + @POST("customer/leaveApp") + fun leaveApp(): Call> + + @FormUrlEncoded + @POST("customer/firebaseToken") + fun firebaseToken(@Field("fcm_token") fcm_token: String): Call> + + @FormUrlEncoded + @POST("w2aSelfAttribution") + fun w2aSelfAttribution( + @Field("data") data: String + ): Call> + + + @POST("openNotify") + fun openNotify( + ): Call> + + @POST("customer/uploadNoticeStatus") + fun uploadNoticeStatus( + @Body fbNotificationReq: FbNotificationReq + ): Call> + + @FormUrlEncoded + @POST("message/sendReport") + fun sendReport( + @Field("message_id") message_id: String, @Field("title") title: String + ): Call> + + @POST("noticeNum") + fun noticeNum(): Call> + + @GET("getCustomerOrder") + fun getCustomerOrder( + @Query("current_page") current_page: Int, + @Query("buy_type") buy_type: String, + @Query("page_size") page_size: Int = 10 + ): Call> + + @GET("getCustomerBuyRecords") + fun getCustomerBuyRecords( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @POST("sendCoinList") + fun sendCoinList( + @Query("current_page") current_page: Int, + @Query("page_size") page_size: Int = 10 + ): Call> + + @GET("paySettingsV3") + fun getPaySetting( + @Query("short_play_id") short_play_id: Int?, + @Query("short_play_video_id") short_play_video_id: Int? + ): Call> + + + @POST("createOrder") + fun createOrder(@Body createOrderReq: CreateOrderReq): Call> + + @POST("googlePaid") + fun googlePaid(@Body examplePayReq: PayReq?): Call> + + @POST("event/add") + suspend fun upError(@Body body: Map): Call> + + @POST("customer/login") + fun login(@Body exampleLoginReq: LoginReq): Call> + + @POST("customer/logoff") + fun logoff(): Call> + + @POST("customer/signout") + fun signout(): Call> + + @GET("highestPaymentAndHottestVideo") + fun homeHot( + @Query("buy_count_num") buycount: Int, + @Query("hottest_num") hottestnum: Int, + ): Call> + + @GET("categoryListAppendShortPlay") + fun userCenterRecommend( + @Query("short_play_num") shortplaynum: Int, + ): Call> + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/BodyInterceptor.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/BodyInterceptor.kt new file mode 100644 index 0000000..04e43f4 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/BodyInterceptor.kt @@ -0,0 +1,30 @@ +package com.jia.er.nebuluxe.app.net + +import com.jia.er.nebuluxe.app.net.Decryption.EN_STR_TAG +import okhttp3.Interceptor +import okhttp3.Response +import okhttp3.ResponseBody +import java.io.IOException + + +class BodyInterceptor : Interceptor { + + @kotlin.jvm.Throws(IOException::class) + override fun intercept(chain: Interceptor.Chain): Response { + val k_center = chain.proceed(chain.request()) + return if (k_center.body != null && k_center.body!!.contentType() != null) { + val actiity = k_center.body!!.contentType() + val circle = k_center.body!!.string() + val str: String = if (!circle.startsWith(EN_STR_TAG)) { + circle + } else { + Decryption.deStr(circle) + } + val current = ResponseBody.create(actiity, str) + k_center.newBuilder().body(current).build() + } else { + k_center + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/Decryption.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/Decryption.kt new file mode 100644 index 0000000..b5edb57 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/Decryption.kt @@ -0,0 +1,66 @@ +package com.jia.er.nebuluxe.app.net + + +object Decryption { + const val BF_SIZE = 2048 + const val EN_STR_TAG = '$' + + fun deStr(data: String): String { + return String(deStrBytes(data), Charsets.UTF_8) + } + + // Decrypt bytes from hex string + fun deStrBytes(data: String): ByteArray { + if (!data.startsWith(EN_STR_TAG)) { + throw IllegalArgumentException("Invalid encoded string") + } + val hexData = data.substring(1) + val bytes = hexData.chunked(2).map { it.toInt(16).toByte() }.toByteArray() + return desadasd(bytes) + } + + // Decrypt data + fun desadasd(data: ByteArray): ByteArray { + if (data.isEmpty()) { + return data + } + val saltLen = data[0].toInt() + val salt = data.slice(1 until 1 + saltLen).toByteArray() + return deWithSalt(data.slice(1 + saltLen until data.size).toByteArray(), salt) + } + + // Decrypt data with salt + fun deWithSalt(data: ByteArray, salt: ByteArray): ByteArray { + val decryptedData = cxEd(data) + return removeSalt(decryptedData, salt) + } + + // Encrypt/Decrypt data by flipping bits + fun cxEd(data: ByteArray): ByteArray { + return data.map { (it.toInt() xor 0xFF).toByte() }.toByteArray() + } + + // Remove salt from data + fun removeSalt(data: ByteArray, salt: ByteArray): ByteArray { + if (salt.isEmpty()) return data + val ret = mutableListOf() + var idx = 0 + val sl = salt.size + data.forEach { + val s = salt[idx % sl] + ret.add(ssadcalRemoveSalt(it, s)) + idx++ + } + return ret.toByteArray() + } + + fun ssadcalRemoveSalt(v: Byte, s: Byte): Byte { + return if (v >= s) { + (v - s).toByte() + } else { + (0xFF - (s - v) + 1).toByte() + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/MainRequest.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/MainRequest.kt new file mode 100644 index 0000000..eb4291e --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/MainRequest.kt @@ -0,0 +1,316 @@ +package com.jia.er.nebuluxe.app.net + +import androidx.lifecycle.LiveData +import com.jia.er.nebuluxe.app.data.BaseRes +import com.jia.er.nebuluxe.app.data.BuyVideoRes +import com.jia.er.nebuluxe.app.data.CollectionRes +import com.jia.er.nebuluxe.app.data.CreateOrderReq +import com.jia.er.nebuluxe.app.data.CreateOrderRes +import com.jia.er.nebuluxe.app.data.CustomerBuyRecordsRes +import com.jia.er.nebuluxe.app.data.CustomerOrderRes +import com.jia.er.nebuluxe.app.data.DetailsRecommendRes +import com.jia.er.nebuluxe.app.data.ExampleKeywordDataRes +import com.jia.er.nebuluxe.app.data.FbNotificationReq +import com.jia.er.nebuluxe.app.data.FreeSeriesMoreRes +import com.jia.er.nebuluxe.app.data.HistoryDataRes +import com.jia.er.nebuluxe.app.data.HomeModuleBean +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayNoPaginateRes +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayRes +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.data.HomeTopRes +import com.jia.er.nebuluxe.app.data.NoticeNumRes +import com.jia.er.nebuluxe.app.data.PayReq +import com.jia.er.nebuluxe.app.data.PayRes +import com.jia.er.nebuluxe.app.data.PaySettingRes +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes +import com.jia.er.nebuluxe.app.data.RecommendDataRes +import com.jia.er.nebuluxe.app.data.RewardCoinsRes +import com.jia.er.nebuluxe.app.data.UploadHistoryReq +import com.jia.er.nebuluxe.app.data.VideoListDataRes +import com.jia.er.nebuluxe.app.data.UserInfoRes +import com.jia.er.nebuluxe.app.data.UserRegisterRes +import com.jia.er.nebuluxe.app.net.Retrofit.handleData +import com.jia.er.nebuluxe.app.net.Retrofit.response +import com.jia.er.nebuluxe.app.data.CategoriesDataRes +import com.jia.er.nebuluxe.app.data.LoginReq +import com.jia.er.nebuluxe.app.data.LoginRes +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo + +object MainRequest { + private val appService = Retrofit.build(AppService::class.java) + + + fun userRegister(): LiveData>> = handleData { + appService.register().response() + } + + fun getInfo(): LiveData>> = handleData { + appService.getInfo().response() + } + + + fun homeTop(): LiveData>> = handleData { + appService.homeTop() + .response() + } + + + fun newShortPlay(current_page: Int): LiveData>> = + handleData { + appService.newShortPlay(current_page) + .response() + } + + + fun getCollections(current_page: Int): LiveData>> = + handleData { + appService.getCollections(current_page).response() + } + + fun getVideoDetails( + short_play_id: Int, video_id: Int, activity_id: Int, revolution: String, no_ads: Boolean? + ): LiveData>> = handleData { + appService.getVideoDetails( + short_play_id, + video_id, + activity_id, + revolution, + no_ads + ) + .response() + } + + + fun newShortPlayNoPaginate(): LiveData>> = + handleData { + appService.newShortPlayNoPaginate() + .response() + } + + + fun doCreateHistory( + short_play_id: Int, video_id: Int + ): LiveData>> = handleData { + appService.createHistory(short_play_id, video_id).response() + } + + + fun getRecommands( + current_page: Int, page_size: Int, revolution: String + ): LiveData>> = handleData { + appService.getRecommands(current_page, page_size, revolution).response() + } + + + private suspend fun collect(short_play_id: Int, video_id: Int) = + appService.collect(short_play_id, video_id).response() + + fun doCollect( + short_play_id: Int, video_id: Int + ): LiveData>> = handleData { + collect(short_play_id, video_id) + } + + private suspend fun cancelCollect(short_play_id: Int, video_id: Int) = + appService.cancelCollect(short_play_id, video_id).response() + + fun doCancelCollect( + short_play_id: Int, video_id: Int + ): LiveData>> = handleData { + cancelCollect(short_play_id, video_id) + } + + + fun myHistorys( + current_page: Int + ): LiveData>> = handleData { + appService.myHistorys(current_page).response() + } + + + fun getVideoList(search: String): LiveData>> = + handleData { + appService.getVideoList(search).response() + } + + fun getVideoList( + current_page: Int, + category_id: Int + ): LiveData>> = + handleData { + appService.getVideoList(current_page, category_id).response() + } + + + fun homeRanking(type: String): LiveData>> = handleData { + appService.homeRanking(type) + .response() + } + + fun freeMoreVideo( + ): LiveData>> = + handleData { + appService.freeMoreVideo().response() + } + + + fun getDetailsRecommand( + ): LiveData>> = + handleData { + appService.getDetailsRecommand().response() + } + + fun uploadHistorySeconds( + uploadHistoryReq: UploadHistoryReq + ): LiveData>> = + handleData { + appService.uploadHistorySeconds(uploadHistoryReq).response() + } + + + fun hots(): LiveData>> = + handleData { + appService.hots().response() + } + + fun click(short_play_id: Int): LiveData>> = + handleData { + appService.click(short_play_id).response() + } + + fun keyword(search: String): LiveData>> = + handleData { + appService.keyword(search).response() + } + + + fun allModules(): LiveData>> = handleData { + appService.allModules().response() + } + + fun doBuyVideo( + short_play_id: Int, video_id: Int + ): LiveData>> = handleData { + appService.buyVideo(short_play_id, video_id).response() + } + + fun getCategories(): LiveData>> = + handleData { + appService.getCategories().response() + } + + fun enterTheApp(): LiveData>> = handleData { + appService.enterTheApp().response() + } + + fun leaveApp(): LiveData>> = handleData { + appService.leaveApp().response() + } + + fun onLine( + ): LiveData>> = + handleData { + appService.onLine().response() + } + + fun firebaseToken(fcm_token: String): LiveData>> = handleData { + appService.firebaseToken(fcm_token).response() + } + + fun w2aSelfAttribution( + data: String + ): LiveData>> = + handleData { + appService.w2aSelfAttribution(data).response() + } + + fun openNotify( + ): LiveData>> = + handleData { + appService.openNotify().response() + } + + fun uploadNoticeStatus(fbNotificationReq: FbNotificationReq): LiveData>> = + handleData { + appService.uploadNoticeStatus(fbNotificationReq) + .response() + } + + fun sendReport( + message_id: String, title: String + ): LiveData>> = + handleData { + appService.sendReport(message_id, title).response() + } + + fun noticeNum(): LiveData>> = + handleData { + appService.noticeNum().response() + } + + fun getCustomerOrder( + current_page: Int, + buy_type: String + ): LiveData>> = handleData { + appService.getCustomerOrder(current_page, buy_type).response() + } + + fun getCustomerBuyRecords(current_page: Int): LiveData>> = + handleData { + appService.getCustomerBuyRecords(current_page).response() + } + + + fun sendCoinList(current_page: Int): LiveData>> = + handleData { + appService.sendCoinList(current_page).response() + } + + fun getPaySetting( + short_play_id: Int?, + video_id: Int? + ): LiveData>> = handleData { + appService.getPaySetting(short_play_id, video_id).response() + } + + fun doGooglePaid(examplePayReq: PayReq?): LiveData>> = + handleData { + appService.googlePaid(examplePayReq).response() + } + + fun doCreateOrder(createOrderReq: CreateOrderReq): LiveData>> = + handleData { + appService.createOrder(createOrderReq).response() + } + + + fun upError(body: Map): LiveData>> = + handleData { + appService.upError(body).response() + } + + fun doLogin(exampleLoginReq: LoginReq): LiveData>> = + handleData { + appService.login(exampleLoginReq).response() + } + + fun doLogoff(): LiveData>> = handleData { + appService.logoff().response() + } + + fun doSignout(): LiveData>> = handleData { + appService.signout().response() + } + + fun homeHot(buycount: Int, hottestnum: Int): LiveData>> = + handleData { + appService.homeHot(buycount, hottestnum) + .response() + } + + fun userCenterRecommend(): LiveData>> = + handleData { + appService.userCenterRecommend(5) + .response() + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/MainViewModel.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/MainViewModel.kt new file mode 100644 index 0000000..dee5ef1 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/MainViewModel.kt @@ -0,0 +1,403 @@ +package com.jia.er.nebuluxe.app.net + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.jia.er.nebuluxe.app.data.LoginRes +import com.jia.er.nebuluxe.app.data.BaseRes +import com.jia.er.nebuluxe.app.data.BuyVideoRes +import com.jia.er.nebuluxe.app.data.CategoriesDataRes +import com.jia.er.nebuluxe.app.data.CollectionRes +import com.jia.er.nebuluxe.app.data.CreateOrderReq +import com.jia.er.nebuluxe.app.data.CreateOrderRes +import com.jia.er.nebuluxe.app.data.CustomerBuyRecordsRes +import com.jia.er.nebuluxe.app.data.CustomerOrderRes +import com.jia.er.nebuluxe.app.data.DetailsRecommendRes +import com.jia.er.nebuluxe.app.data.ExampleKeywordDataRes +import com.jia.er.nebuluxe.app.data.FbNotificationReq +import com.jia.er.nebuluxe.app.data.FreeSeriesMoreRes +import com.jia.er.nebuluxe.app.data.HistoryDataRes +import com.jia.er.nebuluxe.app.data.HomeModuleBean +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayNoPaginateRes +import com.jia.er.nebuluxe.app.data.HomeNewShortPlayRes +import com.jia.er.nebuluxe.app.data.HomeRankingRes +import com.jia.er.nebuluxe.app.data.LoginReq +import com.jia.er.nebuluxe.app.data.NoticeNumRes +import com.jia.er.nebuluxe.app.data.PayReq +import com.jia.er.nebuluxe.app.data.PayRes +import com.jia.er.nebuluxe.app.data.PaySettingRes +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes +import com.jia.er.nebuluxe.app.data.RecommendDataRes +import com.jia.er.nebuluxe.app.data.RewardCoinsRes +import com.jia.er.nebuluxe.app.data.ShortListCategoryDataInfo +import com.jia.er.nebuluxe.app.data.UploadHistoryReq +import com.jia.er.nebuluxe.app.data.UserInfoRes +import com.jia.er.nebuluxe.app.data.UserRegisterRes +import com.jia.er.nebuluxe.app.data.VideoListDataRes +import com.jia.er.nebuluxe.app.net.MainRequest + +class MainViewModel : ViewModel() { + private val userRegisterLiveData = MutableLiveData>() + val userRegisterData: MutableLiveData> get() = userRegisterLiveData + fun createUserAccount( + ) { + MainRequest.userRegister().observeForever { result -> + userRegisterLiveData.value = result.getOrNull() + } + } + + private val infoLiveData = MutableLiveData>() + val infoData: MutableLiveData> get() = infoLiveData + fun getInfo() { + MainRequest.getInfo().observeForever { result -> + infoLiveData.value = result.getOrNull() + } + } + + + private val newShortPlayLiveData = MutableLiveData>() + val newShortPlayData: MutableLiveData> get() = newShortPlayLiveData + fun newShortPlay(current_page: Int) { + MainRequest.newShortPlay(current_page) + .observeForever { + newShortPlayLiveData.value = it.getOrNull() + } + } + + private val videoDetailsLiveData = MutableLiveData>() + val videoDetailsData: MutableLiveData> get() = videoDetailsLiveData + fun getVideoDetails( + short_play_id: Int, + video_id: Int, + activity_id: Int, + revolution: String, + no_ads: Boolean? + ) { + MainRequest.getVideoDetails( + short_play_id, + video_id, + activity_id, + revolution, + no_ads + ) + .observeForever { result -> + videoDetailsLiveData.value = result.getOrNull() + } + } + + private val homeNewLiveData = MutableLiveData>() + val homeNewData: MutableLiveData> get() = homeNewLiveData + fun newShortPlayNoPaginate() { + MainRequest.newShortPlayNoPaginate() + .observeForever { + homeNewLiveData.value = it.getOrNull() + } + } + + + fun doCreateHistory(short_play_id: Int, video_id: Int) { + MainRequest.doCreateHistory(short_play_id, video_id).observeForever {} + } + + private val searchLiveData = MutableLiveData>() + val searchData: MutableLiveData> get() = searchLiveData + fun getVideoList(search: String) { + MainRequest.getVideoList(search).observeForever { result -> + searchLiveData.value = result.getOrNull() + } + } + + private val videoListLiveData = MutableLiveData>() + val videoListData: MutableLiveData> get() = videoListLiveData + fun getVideoList(current_page: Int, category_id: Int) { + MainRequest.getVideoList(current_page, category_id).observeForever { result -> + videoListLiveData.value = result.getOrNull() + } + } + + private val homeRankingLiveData = MutableLiveData>() + val homeRankingData: MutableLiveData> get() = homeRankingLiveData + fun homeRanking(type: String) { + MainRequest.homeRanking(type) + .observeForever { + homeRankingLiveData.value = it.getOrNull() + } + } + + + private val freeMoreLiveData = MutableLiveData>() + val freeMoreData: MutableLiveData> get() = freeMoreLiveData + + fun freeMoreVideo() { + MainRequest.freeMoreVideo().observeForever { result -> + freeMoreLiveData.value = result.getOrNull() + } + } + + private val recommendLiveData = MutableLiveData>() + val recommendData: MutableLiveData> get() = recommendLiveData + fun getRecommands(current_page: Int, page_size: Int, revolution: String) { + MainRequest.getRecommands(current_page, page_size, revolution) + .observeForever { result -> + recommendLiveData.value = result.getOrNull() + } + } + + + private val collectLiveData = MutableLiveData>() + val collectData: MutableLiveData> get() = collectLiveData + fun doCollect(short_play_id: Int, video_id: Int) { + MainRequest.doCollect(short_play_id, video_id).observeForever { result -> + collectLiveData.value = result.getOrNull() + } + } + + private val cancelCollectLiveData = MutableLiveData>() + val cancelCollectData: MutableLiveData> get() = cancelCollectLiveData + fun doCancelCollect(short_play_id: Int, video_id: Int) { + MainRequest.doCancelCollect(short_play_id, video_id).observeForever { result -> + cancelCollectLiveData.value = result.getOrNull() + } + } + + private val collectionsLiveData = MutableLiveData>() + val collectionsData: MutableLiveData> get() = collectionsLiveData + fun getCollections(current_page: Int) { + MainRequest.getCollections(current_page).observeForever { result -> + collectionsLiveData.value = result.getOrNull() + } + } + + private val historysLiveData = MutableLiveData>() + val historysData: MutableLiveData> get() = historysLiveData + fun myHistorys( + current_page: Int + ) { + MainRequest.myHistorys(current_page).observeForever { result -> + historysLiveData.value = result.getOrNull() + } + } + + private val getDetailsRecommandLiveData = MutableLiveData>() + val getDetailsRecommandData: MutableLiveData> get() = getDetailsRecommandLiveData + + fun getDetailsRecommand() { + MainRequest.getDetailsRecommand().observeForever { result -> + getDetailsRecommandLiveData.value = result.getOrNull() + } + } + + fun uploadHistorySeconds(uploadHistoryReq: UploadHistoryReq) { + MainRequest.uploadHistorySeconds(uploadHistoryReq).observeForever {} + } + + + private val hotsLiveData = MutableLiveData>() + val hotsData: MutableLiveData> get() = hotsLiveData + fun hots() { + MainRequest.hots().observeForever { result -> + hotsLiveData.value = result.getOrNull() + } + } + + fun click(short_play_id: Int) { + MainRequest.click(short_play_id).observeForever {} + } + + private val keywordLiveData = MutableLiveData>() + val keywordData: MutableLiveData> get() = keywordLiveData + fun keyword(search: String) { + MainRequest.keyword(search).observeForever { result -> + keywordLiveData.value = result.getOrNull() + } + } + + private val allModulesLiveData = MutableLiveData>() + val allModulesData: MutableLiveData> get() = allModulesLiveData + fun allModules() { + MainRequest.allModules().observeForever { result -> + allModulesLiveData.value = result.getOrNull() + } + } + + private val buy_videoLiveData = MutableLiveData>() + val buy_videoData: MutableLiveData> get() = buy_videoLiveData + fun doBuyVideo(short_play_id: Int, video_id: Int) { + MainRequest.doBuyVideo(short_play_id, video_id).observeForever { result -> + buy_videoLiveData.value = result.getOrNull() + } + } + + private val categoriesLiveData = MutableLiveData>() + val categoriesData: MutableLiveData> get() = categoriesLiveData + fun getCategories() { + MainRequest.getCategories().observeForever { result -> + categoriesLiveData.value = result.getOrNull() + } + } + + fun enterTheApp() { + MainRequest.enterTheApp().observeForever {} + } + + fun leaveApp() { + MainRequest.leaveApp().observeForever {} + } + + fun onLine() { + MainRequest.onLine().observeForever {} + } + + fun firebaseToken( + fcm_token: String + ) { + MainRequest.firebaseToken(fcm_token).observeForever {} + } + + private val w2aSelfAttributionLiveData = + MutableLiveData>() + + fun w2aSelfAttribution(data: String) { + MainRequest.w2aSelfAttribution(data).observeForever { result -> + w2aSelfAttributionLiveData.value = result.getOrNull() + } + } + + private val openNotifyLiveData = MutableLiveData>() + val openNotifyData: MutableLiveData> get() = openNotifyLiveData + + fun openNotify() { + MainRequest.openNotify().observeForever { result -> + openNotifyLiveData.value = result.getOrNull() + } + } + + private val notificationLiveData = MutableLiveData>() + fun uploadNoticeStatus(fbNotificationReq: FbNotificationReq) { + MainRequest.uploadNoticeStatus(fbNotificationReq) + .observeForever { + notificationLiveData.value = it.getOrNull() + } + } + + fun sendReport(message_id: String, title: String) { + MainRequest.sendReport(message_id, title).observeForever {} + } + + private val noticeNumLiveData = + MutableLiveData>() + val noticeNumData: MutableLiveData> get() = noticeNumLiveData + fun noticeNum() { + MainRequest.noticeNum().observeForever { result -> + noticeNumLiveData.value = result.getOrNull() + } + } + + private val getPaySettingLiveData = MutableLiveData>() + val getPaySettingData: MutableLiveData> get() = getPaySettingLiveData + fun getPaySetting( + short_play_id: Int?, + video_id: Int? + ) { + MainRequest.getPaySetting(short_play_id, video_id).observeForever { result -> + getPaySettingLiveData.value = result.getOrNull() + } + } + + private val restorePaidLiveData = MutableLiveData>() + val restorePaidData: MutableLiveData> get() = restorePaidLiveData + fun restorePaid(examplePayReq: PayReq?) { + MainRequest.doGooglePaid(examplePayReq).observeForever { result -> + restorePaidLiveData.value = result.getOrNull() + } + } + + private val createOrderLiveData = MutableLiveData>() + val createOrderData: MutableLiveData> get() = createOrderLiveData + fun createOrder(createOrderReq: CreateOrderReq) { + MainRequest.doCreateOrder(createOrderReq).observeForever { result -> + createOrderLiveData.value = result.getOrNull() + } + } + + private val googlePaidLiveData = MutableLiveData>() + val googlePaidData: MutableLiveData> get() = googlePaidLiveData + fun googlePaid(examplePayReq: PayReq?) { + MainRequest.doGooglePaid(examplePayReq).observeForever { result -> + googlePaidLiveData.value = result.getOrNull() + } + } + + private val customerOrderLiveData = MutableLiveData>() + val customerOrderData: MutableLiveData> get() = customerOrderLiveData + fun getCustomerOrder(current_page: Int, buy_type: String) { + MainRequest.getCustomerOrder(current_page, buy_type).observeForever { result -> + customerOrderLiveData.value = result.getOrNull() + } + } + + private val customerBuyRecordsLiveData = + MutableLiveData>() + val customerBuyRecordsData: MutableLiveData> get() = customerBuyRecordsLiveData + fun getCustomerBuyRecords(current_page: Int) { + MainRequest.getCustomerBuyRecords(current_page).observeForever { result -> + customerBuyRecordsLiveData.value = result.getOrNull() + } + } + + + private val sendCoinListLiveData = + MutableLiveData>() + val sendCoinListData: MutableLiveData> get() = sendCoinListLiveData + fun sendCoinList(current_page: Int) { + MainRequest.sendCoinList(current_page).observeForever { result -> + sendCoinListLiveData.value = result.getOrNull() + } + } + + fun upError(body: Map) { + MainRequest.upError(body).observeForever {} + } + + private val loginLiveData = MutableLiveData>() + val loginData: MutableLiveData> get() = loginLiveData + fun doLogin(exampleLoginReq: LoginReq) { + MainRequest.doLogin(exampleLoginReq).observeForever { result -> + loginLiveData.value = result.getOrNull() + } + } + + private val logoffLiveData = MutableLiveData>() + val logoffData: MutableLiveData> get() = logoffLiveData + fun doLogoff() { + MainRequest.doLogoff().observeForever { result -> + logoffLiveData.value = result.getOrNull() + } + } + + private val signoutLiveData = MutableLiveData>() + val signoutData: MutableLiveData> get() = signoutLiveData + fun doSignout() { + MainRequest.doSignout().observeForever { result -> + signoutLiveData.value = result.getOrNull() + } + } + + private val homeHotLiveData = MutableLiveData?>() + val homeHotData: MutableLiveData?> get() = homeHotLiveData + fun homeHot(buycount: Int, hottestnum: Int) { + MainRequest.homeHot(buycount, hottestnum) + .observeForever { + homeHotLiveData.value = it.getOrNull() + } + } + + private val userCenterRecommendLiveData = MutableLiveData?>() + val userCenterRecommendData: MutableLiveData?> get() = userCenterRecommendLiveData + fun userCenterRecommend(){ + MainRequest.userCenterRecommend() + .observeForever { + userCenterRecommendLiveData.value = it.getOrNull() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/net/Retrofit.kt b/app/src/main/java/com/jia/er/nebuluxe/app/net/Retrofit.kt new file mode 100644 index 0000000..ffeedbb --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/net/Retrofit.kt @@ -0,0 +1,221 @@ +package com.jia.er.nebuluxe.app.net + +import android.os.Build +import android.provider.Settings +import androidx.lifecycle.LiveData +import androidx.lifecycle.liveData +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.basics.MyApplication.Companion.context +import com.jia.er.nebuluxe.app.data.BaseRes +import com.jia.er.nebuluxe.app.utils.Memory +import com.jia.er.nebuluxe.app.utils.PackageUtils +import com.jia.er.nebuluxe.app.utils.getAdvertisingIdInfo +import com.jia.er.nebuluxe.app.utils.getUserAgent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.greenrobot.eventbus.EventBus +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.TimeZone +import java.util.concurrent.TimeUnit +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + + +object Retrofit { + private val retrofit: Retrofit + fun

build(serviceClass: Class

): P = retrofit.create(serviceClass) + private val httpLoggingInterceptor = HttpLoggingInterceptor() + private const val DELAY_TIME_MILLIS = 2000L + private var lastPostTime: Long = 0L + private val lock = Any() + private val lock1 = Any() + + init { + httpLoggingInterceptor.level = + if (Constants.isUat) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE + val okHttpClient = OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS) + .addInterceptor(httpLoggingInterceptor) + .addInterceptor(BodyInterceptor()) + .addInterceptor { chain -> + val request = + chain.request().newBuilder().header("Content-Type", "application/json") + .addHeader( + Constants.CONSTANTS_AuthorizationExample, + Memory.getMMKV() + .getString(Constants.CONSTANTS_AuthorizationExample, "") + .toString() + ) + .addHeader( + Constants.CONSTANTS_device_id, + Settings.Secure.getString( + context.contentResolver, + Settings.Secure.ANDROID_ID + ) + ).addHeader("User-Agent", getUserAgent()) + .addHeader( + Constants.CONSTANTS_device_gaid, + getAdvertisingIdInfo(context) + ) + .addHeader( + Constants.CONSTANTS_app_name, + context.getString(R.string.app_name) + ).addHeader( + Constants.CONSTANTS_app_version, + PackageUtils.getPackageVersion(context) + ) + .addHeader( + Constants.CONSTANTS_system_type, + "android" + ) + .addHeader( + Constants.CONSTANTS_lang_key, + Memory.getMMKV() + .getString(Constants.CONSTANTS_lang_key, "en").toString() + ) + .addHeader( + Constants.CONSTANTS_time_zone, + getCurrentTimeZone() + ) + .addHeader( + Constants.CONSTANTS_model, + Build.MODEL + ) + .addHeader( + Constants.CONSTANTS_brand, + Build.BRAND + ) + .addHeader( + Constants.CONSTANTS_system_version, + Build.VERSION.RELEASE + ) + .build() + if (Constants.isUat) { + for (headerName in request.headers.names()) { + println(headerName + ": " + request.header(headerName)) + } + } + chain.proceed(request) + } + .build() + retrofit = + Retrofit.Builder() + .baseUrl(Constants.Constants_BASE_URL_RES) + .addConverterFactory( + GsonConverterFactory.create() + ) + .client(okHttpClient) + .build() + } + + suspend fun Call.response(): T { + return suspendCoroutine { continuation -> + enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + continuation.resumeWithException(t) + } + + override fun onResponse(call: Call, response: Response) { + val body = response.body() + if (response.raw().code == 401) { + GlobalScope.launch(Dispatchers.Main) { + handle401Response() + } + } + if (response.raw().code == 402) { + GlobalScope.launch(Dispatchers.Main) { + handle402Response() + } + } + if (body != null) continuation.resume(body) + else continuation.resumeWithException(RuntimeException("response body is null")) + } + }) + } + } + + + private suspend fun handle402Response() = withContext(Dispatchers.Main) { + synchronized(lock) { + val currentTime = System.currentTimeMillis() + if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) { + EventBus.getDefault() + .post(Constants.CONSTANTS_auth_refresh) + lastPostTime = currentTime + } + } + } + + private suspend fun handle401Response() = withContext(Dispatchers.Main) { + synchronized(lock1) { + val currentTime = System.currentTimeMillis() + if (currentTime - lastPostTime >= DELAY_TIME_MILLIS) { + EventBus.getDefault() + .post(Constants.CONSTANTS_auth_refresh) + lastPostTime = currentTime + } + } + } + + + fun handleData(apiCall: suspend () -> BaseRes): LiveData>> { + return liveData(Dispatchers.IO) { + val result = try { + val response = apiCall.invoke() + if (response.code == 200) { + Result.success(response) + } else { + Memory.getMMKV().putString("errorMsg", response.msg) + Result.failure(RuntimeException("Result failure")) + } + } catch (e: Exception) { + Result.failure(e) + } + emit(result) + } + } + + fun getCurrentTimeZone(): String { + return createGmtOffsetString(true, true, TimeZone.getDefault().rawOffset) + } + + private fun createGmtOffsetString( + includeGmt: Boolean, includeMinuteSeparator: Boolean, offsetMillis: Int + ): String { + var offsetMinutes = offsetMillis / 60000 + var sign = '+' + if (offsetMinutes < 0) { + sign = '-' + offsetMinutes = -offsetMinutes + } + val builder: StringBuilder = StringBuilder(9) + if (includeGmt) { + builder.append("GMT") + } + builder.append(sign) + appendNumber(builder, 2, offsetMinutes / 60) + if (includeMinuteSeparator) { + builder.append(':') + } + appendNumber(builder, 2, offsetMinutes % 60) + return builder.toString() + } + + private fun appendNumber(builder: StringBuilder, count: Int, value: Int) { + val string = value.toString() + for (i in 0 until count - string.length) { + builder.append('0') + } + builder.append(string); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/CustomRoundedCorners.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/CustomRoundedCorners.kt new file mode 100644 index 0000000..75e206b --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/CustomRoundedCorners.kt @@ -0,0 +1,56 @@ +package com.jia.er.nebuluxe.app.ui + +import android.graphics.* +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation +import java.security.MessageDigest + +class CustomRoundedCorners( + private val topLeft: Float = 0f, + private val topRight: Float = 0f, + private val bottomRight: Float = 0f, + private val bottomLeft: Float = 0f +) : BitmapTransformation() { + + override fun updateDiskCacheKey(messageDigest: MessageDigest) { + messageDigest.update("$topLeft-$topRight-$bottomRight-$bottomLeft".toByteArray()) + } + + override fun transform( + pool: BitmapPool, + toTransform: Bitmap, + outWidth: Int, + outHeight: Int + ): Bitmap { + return roundCrop(pool, toTransform) + } + + private fun roundCrop(pool: BitmapPool, source: Bitmap?): Bitmap { + if (source == null) return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + + val result = pool.get(source.width, source.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(result) + + val paint = Paint().apply { + isAntiAlias = true + shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) + } + + val path = Path() + val radii = floatArrayOf( + topLeft, topLeft, + topRight, topRight, + bottomRight, bottomRight, + bottomLeft, bottomLeft + ) + path.addRoundRect( + RectF(0f, 0f, source.width.toFloat(), source.height.toFloat()), + radii, + Path.Direction.CW + ) + + canvas.drawPath(path, paint) + return result + } +} + diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/FfmpegRenderersFactory.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/FfmpegRenderersFactory.kt new file mode 100644 index 0000000..9c8daeb --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/FfmpegRenderersFactory.kt @@ -0,0 +1,44 @@ +package com.jia.er.nebuluxe.app.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Handler +import androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer +import androidx.media3.exoplayer.DefaultRenderersFactory +import androidx.media3.exoplayer.Renderer +import androidx.media3.exoplayer.audio.AudioRendererEventListener +import androidx.media3.exoplayer.audio.AudioSink +import androidx.media3.exoplayer.mediacodec.MediaCodecSelector + + +@SuppressLint("UnsafeOptInUsageError") +internal class FfmpegRenderersFactory(context: Context?) : DefaultRenderersFactory( + context!! +) { + init { + setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) + } + + override fun buildAudioRenderers( + context: Context, + extensionRendererMode: Int, + mediaCodecSelector: MediaCodecSelector, + enableDecoderFallback: Boolean, + audioSink: AudioSink, + eventHandler: Handler, + eventListener: AudioRendererEventListener, + out: ArrayList + ) { + out.add(FfmpegAudioRenderer()) + super.buildAudioRenderers( + context, + extensionRendererMode, + mediaCodecSelector, + enableDecoderFallback, + audioSink, + eventHandler, + eventListener, + out + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/ListItemsVisibilityCalculator.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/ListItemsVisibilityCalculator.kt new file mode 100644 index 0000000..78851af --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/ListItemsVisibilityCalculator.kt @@ -0,0 +1,9 @@ +package com.jia.er.nebuluxe.app.ui + +import androidx.recyclerview.widget.RecyclerView + +interface ListItemsVisibilityCalculator { + fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) + fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) + fun detachToSnapHelper() +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/LoadingLine.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/LoadingLine.kt new file mode 100644 index 0000000..3506255 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/LoadingLine.kt @@ -0,0 +1,59 @@ +package com.jia.er.nebuluxe.app.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.animation.AlphaAnimation +import android.view.animation.Animation +import android.view.animation.AnimationSet +import android.view.animation.ScaleAnimation +import android.widget.FrameLayout +import com.jia.er.nebuluxe.app.R + + +class LoadingLine : FrameLayout { + private var loadView: View? = null + + constructor(context: Context) : super(context) { + initView(context) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + initView(context) + } + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) { + initView(context) + } + + private fun initView(mContext: Context) { + val view: View = LayoutInflater.from(mContext).inflate(R.layout.line_layout, null) + loadView = view.findViewById(R.id.loadingView) + this.addView(view) + } + + fun startAnimation() { + val scale = ScaleAnimation( + 0.3f, 1f, 1f, 1f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + val alpha = AlphaAnimation(1f, 0.2f) + scale.repeatCount = -1 + alpha.repeatCount = -1 + val set = AnimationSet(true) + set.addAnimation(scale) + set.addAnimation(alpha) + set.duration = 500 + loadView!!.startAnimation(set) + } + + fun endAnimation(){ + loadView!!.clearAnimation() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/OnSnapHelperCurrentListener.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/OnSnapHelperCurrentListener.kt new file mode 100644 index 0000000..2346ad8 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/OnSnapHelperCurrentListener.kt @@ -0,0 +1,10 @@ +package com.jia.er.nebuluxe.app.ui + +import android.view.View + +interface OnSnapHelperCurrentListener { + + fun setActive(currentView: View?, position: Int, previousView: View?, previousPosition: Int) + + fun disActive(detachedView: View?, detachedPosition: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/PosterStyleImageView.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/PosterStyleImageView.kt new file mode 100644 index 0000000..dbf1537 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/PosterStyleImageView.kt @@ -0,0 +1,244 @@ +package com.jia.er.nebuluxe.app.ui + +import android.content.Context +import android.graphics.* +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.util.TypedValue +import android.view.View +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toBitmap +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition + +/** + * 海报风格图片视图 - 简化版 + * 使用顶部略倾斜的大圆角路径,贴合上传海报的形状 + */ +class PosterStyleImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + + // 画笔 + private val imagePaint = Paint(Paint.ANTI_ALIAS_FLAG) + + // 路径 + private val imagePath = Path() + + // 图片相关 + private var imageBitmap: Bitmap? = null + private var imageMatrix = Matrix() + private var imageShader: BitmapShader? = null + + // 尺寸和位置 + private var imageRect = RectF() + + // 圆角(像素)——默认左上更短,其余正常 + private var cornerRadiusTopLeft = dpToPx(12f) + private var cornerRadiusTopRight = dpToPx(18f) + private var cornerRadiusBottomRight = dpToPx(18f) + private var cornerRadiusBottomLeft = dpToPx(18f) + + // 顶边倾斜偏移(像素):左上相对右上向下偏移,正值代表左侧更低 + private var topSlantOffset = dpToPx(10f) + + init { + setupPaint() + } + + private fun setupPaint() { + imagePaint.style = Paint.Style.FILL + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + + imageRect.set( + paddingLeft.toFloat(), + paddingTop.toFloat(), + (w - paddingRight).toFloat(), + (h - paddingBottom).toFloat() + ) + + createImagePath() + } + + private fun createImagePath() { + imagePath.reset() + + // 四个角的半径,限定不要超过边长的一半 + val rTL = cornerRadiusTopLeft.coerceAtMost(minOf(imageRect.width(), imageRect.height()) / 2f) + val rTR = cornerRadiusTopRight.coerceAtMost(minOf(imageRect.width(), imageRect.height()) / 2f) + val rBR = cornerRadiusBottomRight.coerceAtMost(minOf(imageRect.width(), imageRect.height()) / 2f) + val rBL = cornerRadiusBottomLeft.coerceAtMost(minOf(imageRect.width(), imageRect.height()) / 2f) + + // 顶部两点,形成倾斜 + val left = imageRect.left + val right = imageRect.right + val topLeftY = imageRect.top + topSlantOffset + val topRightY = imageRect.top + val bottom = imageRect.bottom + + // 起点:左上边内切圆起始点(顺时针) + imagePath.moveTo(left + rTL, topLeftY) + + // 顶边(倾斜)到右上角前 + imagePath.lineTo(right - rTR, topRightY) + // 右上角圆弧(用二阶贝塞尔逼近) + imagePath.quadTo(right, topRightY, right, topRightY + rTR) + + // 右边到右下角前 + imagePath.lineTo(right, bottom - rBR) + // 右下角 + imagePath.quadTo(right, bottom, right - rBR, bottom) + + // 底边到左下角前 + imagePath.lineTo(left + rBL, bottom) + // 左下角 + imagePath.quadTo(left, bottom, left, bottom - rBL) + + // 左边到左上角前(注意顶边是倾斜的,终点是 topLeftY) + imagePath.lineTo(left, topLeftY + rTL) + // 左上角(小圆角) + imagePath.quadTo(left, topLeftY, left + rTL, topLeftY) + + imagePath.close() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + drawImage(canvas) + } + + private fun drawImage(canvas: Canvas) { + imageBitmap?.let { bitmap -> + if (imageShader == null) { + imageShader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) + } + + val bitmapWidth = bitmap.width.toFloat() + val bitmapHeight = bitmap.height.toFloat() + val scaleX = imageRect.width() / bitmapWidth + val scaleY = imageRect.height() / bitmapHeight + val scale = maxOf(scaleX, scaleY) + + imageMatrix.reset() + imageMatrix.setScale(scale, scale) + imageMatrix.postTranslate( + imageRect.left + (imageRect.width() - bitmapWidth * scale) / 2, + imageRect.top + (imageRect.height() - bitmapHeight * scale) / 2 + ) + + imageShader?.setLocalMatrix(imageMatrix) + imagePaint.shader = imageShader + canvas.drawPath(imagePath, imagePaint) + } + } + + fun setImage(bitmap: Bitmap) { + imageBitmap = bitmap + invalidate() + } + + fun setImageResource(resId: Int) { + ContextCompat.getDrawable(context, resId)?.let { drawable -> + setImage(drawable.toBitmap()) + } + } + + /** + * 使用 Glide 从网络加载图片 + */ + fun loadImage(url: String, placeholderRes: Int? = null, errorRes: Int? = null) { + val requestOptions = RequestOptions() + placeholderRes?.let { requestOptions.placeholder(it) } + errorRes?.let { requestOptions.error(it) } + + Glide.with(this) + .asBitmap() + .load(url) + .apply(requestOptions) + .into(object : CustomTarget() { + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + setImage(resource) + } + override fun onLoadCleared(placeholder: Drawable?) { + placeholder?.toBitmap()?.let { setImage(it) } + } + override fun onLoadFailed(errorDrawable: Drawable?) { + super.onLoadFailed(errorDrawable) + errorDrawable?.toBitmap()?.let { setImage(it) } + } + }) + } + + // 统一设置四角圆角(像素) + fun setCornerRadius(radiusPx: Float) { + cornerRadiusTopLeft = radiusPx + cornerRadiusTopRight = radiusPx + cornerRadiusBottomRight = radiusPx + cornerRadiusBottomLeft = radiusPx + createImagePath() + invalidate() + } + + // 分别设置四角圆角(像素) + fun setCornerRadii( + topLeftPx: Float, + topRightPx: Float, + bottomRightPx: Float, + bottomLeftPx: Float + ) { + cornerRadiusTopLeft = topLeftPx + cornerRadiusTopRight = topRightPx + cornerRadiusBottomRight = bottomRightPx + cornerRadiusBottomLeft = bottomLeftPx + createImagePath() + invalidate() + } + + // 统一设置四角圆角(dp) + fun setCornerRadiusDp(radiusDp: Float) { + setCornerRadius(dpToPx(radiusDp)) + } + + // 分别设置四角圆角(dp) + fun setCornerRadiiDp( + topLeftDp: Float, + topRightDp: Float, + bottomRightDp: Float, + bottomLeftDp: Float + ) { + setCornerRadii( + dpToPx(topLeftDp), + dpToPx(topRightDp), + dpToPx(bottomRightDp), + dpToPx(bottomLeftDp) + ) + } + + // 便捷方法:左上短、其他正常(传入dp) + fun setTopLeftShortStyle(topLeftDp: Float = 12f, otherDp: Float = 18f) { + setCornerRadiiDp(topLeftDp, otherDp, otherDp, otherDp) + } + + // 设置顶边倾斜偏移 + fun setTopSlantOffsetPx(offsetPx: Float) { + topSlantOffset = offsetPx + createImagePath() + invalidate() + } + fun setTopSlantOffsetDp(offsetDp: Float) { + setTopSlantOffsetPx(dpToPx(offsetDp)) + } + + fun getCornerRadius(): Float = cornerRadiusTopLeft + + private fun dpToPx(dp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/RecyclerViewScrollerDetection.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/RecyclerViewScrollerDetection.kt new file mode 100644 index 0000000..67dc2a1 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/RecyclerViewScrollerDetection.kt @@ -0,0 +1,120 @@ +package com.jia.er.nebuluxe.app.ui + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener +import androidx.recyclerview.widget.SnapHelper + +class RecyclerViewScrollerDetection : ListItemsVisibilityCalculator { + + private var mSnapHelper: SnapHelper? = null + private var mCurrentListener: OnSnapHelperCurrentListener? = null + var position = RecyclerView.NO_POSITION + private var previousPosition = RecyclerView.NO_POSITION + var isFirstAttached = false + private var recyclerView: RecyclerView? = null + val mSnapScrollListener = SnapScrollListener() + var mSnapChildAttachStateListener: SnapChildAttachStateChangeListener? = null + + var isFirstActive = true + private var currentItemView: View? = null + private var previousView: View? = null + + + fun attachToSnapHelper(snapHelper: SnapHelper) { + mSnapHelper = snapHelper + } + + fun addOnScrollListener(recyclerView: RecyclerView) { + this.recyclerView = recyclerView + recyclerView.addOnScrollListener(mSnapScrollListener) + mSnapChildAttachStateListener = SnapChildAttachStateChangeListener(recyclerView) + mSnapChildAttachStateListener?.run { + recyclerView.addOnChildAttachStateChangeListener(this) + } + } + + inner class SnapScrollListener : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + this@RecyclerViewScrollerDetection.onScrollStateChanged(recyclerView, newState) + } + } + + inner class SnapChildAttachStateChangeListener(var recyclerView: RecyclerView) : + OnChildAttachStateChangeListener { + override fun onChildViewAttachedToWindow(view: View) { + if (!isFirstActive || isFirstAttached) return + position = recyclerView.getChildAdapterPosition(view) + currentItemView = view + mCurrentListener?.setActive(view, position, null, previousPosition) + previousPosition = position + previousView = view + isFirstAttached = true + } + + override fun onChildViewDetachedFromWindow(view: View) { + val previousPosition = recyclerView.getChildAdapterPosition(view) + currentItemView?.run { + val position = recyclerView.getChildAdapterPosition( + this + ) + mCurrentListener?.let { + if (position == previousPosition) { + it.disActive(view, previousPosition) + } + } + } + } + + } + + fun setCurrentListener(mCurrentListener: OnSnapHelperCurrentListener?) { + this.mCurrentListener = mCurrentListener + } + + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + try { + when (newState) { + RecyclerView.SCROLL_STATE_IDLE -> { + val layoutManager = recyclerView.layoutManager + currentItemView = mSnapHelper?.findSnapView(layoutManager) + if (null == currentItemView) return + val currentPosition = recyclerView.getChildAdapterPosition(currentItemView!!) + if (previousPosition == currentPosition || null == mCurrentListener) return + position = recyclerView.getChildAdapterPosition(currentItemView!!) + val mPreviousView = previousView + val mPreviousPosition = previousPosition + mCurrentListener?.setActive( + currentItemView, + position, + mPreviousView, + mPreviousPosition + ) + previousPosition = currentPosition + previousView = currentItemView + } + + RecyclerView.SCROLL_STATE_DRAGGING -> { + } + + RecyclerView.SCROLL_STATE_SETTLING -> { + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + override fun detachToSnapHelper() { + recyclerView?.removeOnScrollListener(mSnapScrollListener) + mSnapChildAttachStateListener?.run { + recyclerView?.removeOnChildAttachStateChangeListener( + this + ) + } + } + + override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {} + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/RotateDownPageTransformer.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/RotateDownPageTransformer.kt new file mode 100644 index 0000000..5dfc840 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/RotateDownPageTransformer.kt @@ -0,0 +1,67 @@ + +package com.jia.er.nebuluxe.app.ui + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 + + +class RotateDownPageTransformer( + private val maxRotate: Float = DEFAULT_MAX_ROTATE, + private val sideMargin: Float = 0f, // Positive value creates visible gap + private val scaleFactor: Float = 0.95f +) : ViewPager2.PageTransformer { + + companion object { + private const val DEFAULT_MAX_ROTATE = 15.0f + private const val DEFAULT_CENTER = 0.5f + } + + override fun transformPage(view: View, position: Float) { + val scale = if (abs(position) <= 1) { + (1 - abs(position)) * (1 - scaleFactor) + scaleFactor + } else { + scaleFactor + } + + when { + position < -1 -> { + // [-Infinity,-1) + view.rotation = maxRotate * -1 + view.pivotX = view.width.toFloat() + view.pivotY = view.height.toFloat() + view.scaleX = scaleFactor + view.scaleY = scaleFactor + view.translationX = -sideMargin * 2 + } + position <= 1 -> { // [-1,1] + // Calculate direction (left or right) + val direction = if (position < 0) -1f else 1f + + view.pivotX = if (position < 0) { + view.width * (DEFAULT_CENTER + DEFAULT_CENTER * -position) + } else { + view.width * DEFAULT_CENTER * (1 - position) + } + + view.pivotY = view.height.toFloat() + view.rotation = maxRotate * position + view.scaleX = scale + view.scaleY = scale + view.translationX = (sideMargin * position) + (sideMargin * direction) + } + else -> { + // (1,+Infinity] + view.rotation = maxRotate + view.pivotX = 0f + view.pivotY = view.height.toFloat() + view.scaleX = scaleFactor + view.scaleY = scaleFactor + view.translationX = sideMargin * 2 + } + } + } + + private fun abs(value: Float): Float { + return if (value < 0) -value else value + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/ScrollTextView.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/ScrollTextView.kt new file mode 100644 index 0000000..79f0939 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/ScrollTextView.kt @@ -0,0 +1,61 @@ +package com.jia.er.nebuluxe.app.ui + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView + + +class ScrollTextView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : AppCompatTextView(context, attrs, defStyleAttr) { + + private var animator: ObjectAnimator? = null + private var currentIndex = 0 + private var dataList: MutableList = mutableListOf() + + fun setData(data: MutableList) { + dataList.clear() + dataList = data + currentIndex = 0 + animator?.cancel() + animator?.removeAllListeners() + if (dataList.isNotEmpty()) { + animateText(dataList[currentIndex]) + startSwitchAnimation() + } + } + + private fun startSwitchAnimation() { + animator = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f) + animator?.duration = 500 + animator?.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + if (dataList.size != 0) { + currentIndex = (currentIndex + 1) % dataList.size + text = dataList[currentIndex] + animateText(dataList[currentIndex]) + animate().alpha(1f).start() + startSwitchAnimation() + } + } + }) + animator?.startDelay = 4500 + animator?.start() + } + + private fun animateText(text: String) { + animate().cancel() + alpha = 0f + this.text = text + animate().alpha(1f).setDuration(500).start() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + animator?.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/ui/UnFavoriteDialog.kt b/app/src/main/java/com/jia/er/nebuluxe/app/ui/UnFavoriteDialog.kt new file mode 100644 index 0000000..9ad5d8a --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/ui/UnFavoriteDialog.kt @@ -0,0 +1,31 @@ +package com.jia.er.nebuluxe.app.ui + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.Gravity +import android.view.Window +import android.view.WindowManager +import com.jia.er.nebuluxe.app.R + +class UnFavoriteDialog(context: Context) : Dialog(context) { + + init { + init() + } + + private fun init() { + requestWindowFeature(Window.FEATURE_NO_TITLE) + setContentView(R.layout.dialog_un_collection) + + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + window?.setLayout( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT + ) + window?.setGravity(Gravity.CENTER) + setCancelable(true) + } + +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/utils/DialogUtils.kt b/app/src/main/java/com/jia/er/nebuluxe/app/utils/DialogUtils.kt new file mode 100644 index 0000000..db86578 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/utils/DialogUtils.kt @@ -0,0 +1,35 @@ +package com.jia.er.nebuluxe.app.utils + +import android.content.Context +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatTextView +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.ui.UnFavoriteDialog +import com.jia.er.nebuluxe.app.utils.singleClick + + +object DialogUtils { + + + fun unFavoriteDialog( + context: Context, + short_play_id: Int, + video_id: Int, + genresViewModel: MainViewModel + ) { + val exampleUnFavoriteDialog = UnFavoriteDialog(context) + val tvThinkAgain = + exampleUnFavoriteDialog.findViewById(R.id.example_tv_think_again) + val iv_close_notification = + exampleUnFavoriteDialog.findViewById(R.id.iv_close_notification) + iv_close_notification.setOnClickListener { exampleUnFavoriteDialog.dismiss() } + tvThinkAgain.setOnClickListener { + singleClick { + genresViewModel.doCancelCollect(short_play_id, video_id) + } + exampleUnFavoriteDialog.dismiss() + } + exampleUnFavoriteDialog.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/utils/Memory.kt b/app/src/main/java/com/jia/er/nebuluxe/app/utils/Memory.kt new file mode 100644 index 0000000..d647cc9 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/utils/Memory.kt @@ -0,0 +1,104 @@ +package com.jia.er.nebuluxe.app.utils + +import com.jia.er.nebuluxe.app.data.PayReq +import com.jia.er.nebuluxe.app.data.UserInfoRes +import com.google.gson.Gson +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.basics.Constants.CONSTANTS_SEARCH_STRINGExample +import com.tencent.mmkv.MMKV + + +object Memory { + + private var mmkv: MMKV? = null + + + fun getMMKV(): MMKV { + if (mmkv == null) { + mmkv = MMKV.mmkvWithID("nebuluxe") + } + return mmkv!! + } + + fun saveUserInfo(infoRes: UserInfoRes?) { + val toJson = Gson().toJson(infoRes) + getMMKV() + .putString(Constants.CONSTANTS_User_STRING, toJson) + } + + fun getUserInfo(): UserInfoRes? { + val string = getMMKV().getString(Constants.CONSTANTS_User_STRING, "{}") + if ("{}" == string) { + return UserInfoRes.createWithDefaults() + } + return Gson().fromJson(string, UserInfoRes::class.java) + } + + fun saveSearchString(search: String) { + val strings = getSearch() + if (!strings.contains(search)) { + strings.add(0, search) + } + val toJson = Gson().toJson(strings) + getMMKV().putString(CONSTANTS_SEARCH_STRINGExample, toJson) + } + + fun getSearch(): MutableList { + val string = getMMKV().getString(CONSTANTS_SEARCH_STRINGExample, "[]") + return Gson().fromJson(string, Array::class.java).toMutableList() + } + + fun isTourist(): Boolean { + return getUserInfo()?.is_tourist == true + } + + fun getCustomId(): String { + return getUserInfo()?.customer_id.toString() + } + + fun getAllCoin(): Int { + if (getUserInfo() == null) { + return 0 + } + return getUserInfo()?.coin_left_total!! + getUserInfo()?.send_coin_left_total!! + } + + fun isVip(): Boolean { + return getUserInfo()?.is_vip == true + } + + fun saveOrder(payReq: PayReq) { + val list = getOrder() + if (!list.contains(payReq)) { + list.add(payReq) + } + val toJson = Gson().toJson(list) + getMMKV().putString(Constants.CONSTANTS_examplePayReq, toJson) + } + + fun removeOrderString(order: String) { + val updatedList = getOrder().filterNot { it.order_code == order } + getMMKV().putString( + Constants.CONSTANTS_examplePayReq, + Gson().toJson(updatedList) + ) + } + + fun getOrder(): MutableList { + try { + val string = getMMKV().getString(Constants.CONSTANTS_examplePayReq, "[]") + if (null != string && "[]" != string) { + return Gson().fromJson(string, Array::class.java).toMutableList() + } + } catch (e: Exception) { + e.printStackTrace() + getMMKV().putString( + Constants.CONSTANTS_examplePayReq, + "[]" + ) + } + return mutableListOf() + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/utils/PackageUtils.kt b/app/src/main/java/com/jia/er/nebuluxe/app/utils/PackageUtils.kt new file mode 100644 index 0000000..612bd4e --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/utils/PackageUtils.kt @@ -0,0 +1,40 @@ +package com.jia.er.nebuluxe.app.utils + +import android.content.Context +import android.content.pm.PackageManager +import com.jia.er.nebuluxe.app.basics.MyApplication + +object PackageUtils { + + fun getPackageName(): String { + var packageName = "" + try { + packageName = MyApplication.context.packageName + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return packageName + } + + fun getPackageVersion(context: Context): String { + var packageVersion = "" + try { + val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) + packageVersion = packageInfo.versionName.toString() + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return packageVersion + } + + fun getPackageVersionCode(context: Context): Int { + var versionCode = 0 + try { + val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) + versionCode = packageInfo.versionCode + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return versionCode + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/utils/Toast.kt b/app/src/main/java/com/jia/er/nebuluxe/app/utils/Toast.kt new file mode 100644 index 0000000..217561c --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/utils/Toast.kt @@ -0,0 +1,183 @@ +package com.jia.er.nebuluxe.app.utils + + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.graphics.LinearGradient +import android.graphics.Shader +import android.graphics.Typeface +import android.os.Handler +import android.os.Looper +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.style.ForegroundColorSpan +import android.text.style.StyleSpan +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.widget.AppCompatTextView +import com.google.android.gms.ads.identifier.AdvertisingIdClient +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.basics.Constants.ONE_DAY_IN_MILLIS +import com.jia.er.nebuluxe.app.basics.MyApplication +import com.jia.er.nebuluxe.app.net.MainViewModel +import java.text.SimpleDateFormat +import java.util.Date +import kotlin.math.tan + +private val handler = Handler(Looper.getMainLooper()) +fun toast(text: String) { + handler.post { + Toast.makeText(MyApplication.context, text, Toast.LENGTH_SHORT).show() + } +} + + +fun formatNumber(num: Int): String { + return when { + num >= 1000000 -> "${(num / 1000000.0).format(1)}M" + num >= 1000 -> "${(num / 1000.0).format(1)}K" + else -> num.toString() + } +} + +fun formatNumberByLong(num: Long): String { + return when { + num >= 1000000 -> "${(num / 1000000.0).format(1)}M" + num >= 1000 -> "${(num / 1000.0).format(1)}K" + else -> num.toString() + } +} + +private fun Double.format(digits: Int) = "%.${digits}f".format(this) + + +var lastClickTime = 0L +fun singleClick(during: Long = 500L, callBack: () -> Unit) { + val now = Date().time + if (now - lastClickTime > during) { + callBack() + } + lastClickTime = now +} + +@SuppressLint("SimpleDateFormat") +fun transToString(time: Long): String { + val date = Date(time * 1000) + return SimpleDateFormat("yyyy-MM-dd").format(date) +} + +fun getUserAgent(): String { + return System.getProperty("http.agent") ?: "" +} + +fun formatTimestamp(timestampInSeconds: Long): String { + val minutes = timestampInSeconds / 60 + val seconds = timestampInSeconds % 60 + return String.format("%02d:%02d", minutes, seconds) +} + + +fun SpannableStringBuilder.appendWithStyle( + text: String, + color: Int = Color.BLACK, + isBold: Boolean = false +): SpannableStringBuilder { + val start = this.length + append(text) + val end = this.length + setSpan(ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan( + StyleSpan(if (isBold) Typeface.BOLD else Typeface.NORMAL), + start, + end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + return this +} + +fun shouldShowNotification(): Boolean { + val lastPopupTime = + Memory.getMMKV().getLong(Constants.CONSTANTS_PREF_LAST_POPUP_TIME_Notification, 0) + val currentTime = System.currentTimeMillis() + + return currentTime - lastPopupTime > ONE_DAY_IN_MILLIS +} + +fun getAdvertisingIdInfo(context: Context): String { + try { + return AdvertisingIdClient.getAdvertisingIdInfo(context).id.toString() + } catch (e: Exception) { + e.printStackTrace() + } + return "" +} + +fun AppCompatTextView.applyTextSkew(angle: Float) { + val skewX = tan(Math.toRadians(angle.toDouble())).toFloat() + paint.textSkewX = skewX + post { + val extraWidth = (height * skewX).toInt() + layoutParams.width = measuredWidth + extraWidth + paddingEnd + requestLayout() + } +} + +fun dpToPx(dp: Int, context: Context): Int { + return (dp * context.resources.displayMetrics.density).toInt() +} +fun dpToPxByFloat(dp: Int, context: Context): Float { + return (dp * context.resources.displayMetrics.density) +} + +fun upError( + event_name: String, + viewModel: MainViewModel, + error_msg: String, + event_key: String, + type: String? = "", + pay_data: String? = "", + short_play_id: Int, + short_play_video_id: Int +) { + val map = HashMap() + map.put("error_msg", error_msg) + map.put("userId", Memory.getCustomId()) + map.put("event_name", event_name) + map.put("event_key", event_key) + if (pay_data?.isNotEmpty() == true) { + map.put("pay_data", pay_data) + } + if (type?.isNotEmpty() == true) { + map.put("type", type) + } + map.put("short_play_id", short_play_id) + map.put("short_play_video_id", short_play_video_id) + viewModel.upError(map) +} + +fun setLeftRightGradient(textView: TextView, colors: IntArray) { + textView.paint.shader = LinearGradient( + 0f, 0f, + textView.textSize * textView.text.length, 0f, + colors, + null, + Shader.TileMode.CLAMP + ) + textView.invalidate() +} + +fun setUpDownGradient(textView: TextView, colors: IntArray) { + textView.paint.shader = LinearGradient( + 0f, 0f, + 0f, textView.textSize, + colors, + null, + Shader.TileMode.CLAMP + ) + textView.invalidate() +} + + + + diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/video/NewRecommendBannerAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/video/NewRecommendBannerAdapter.kt new file mode 100644 index 0000000..deb9172 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/video/NewRecommendBannerAdapter.kt @@ -0,0 +1,158 @@ +package com.jia.er.nebuluxe.app.video + +import android.annotation.SuppressLint +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatImageView +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.exoplayer.DefaultRenderersFactory +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.LoadControl +import androidx.media3.ui.PlayerView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.DetailsRecommendRes +import com.youth.banner.adapter.BannerAdapter + + +@SuppressLint("UnsafeOptInUsageError") +class NewRecommendBannerAdapter(items: List?, context: Context) : + BannerAdapter(items) { + var currentPlayingPosition = -1 + private var context: Context? = null + val playerMap = mutableMapOf() + val imageMap = mutableMapOf() + + init { + this.context = context + } + + override fun onCreateHolder(parent: ViewGroup, viewType: Int): BannerViewHolder { + val view: View = LayoutInflater.from(parent.context) + .inflate(R.layout.new_detail_player_banner_view, parent, false) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + return BannerViewHolder(view) + } + + + override fun onBindView( + holder: BannerViewHolder?, + data: DetailsRecommendRes.Item?, + position: Int, + size: Int + ) { + val playerView = holder?.view?.findViewById(R.id.player_view) + val imageView = holder?.view?.findViewById(R.id.iv_cover) + if (null != imageView) { + Glide.with(context!!) + .load(data?.image_url) + .placeholder(R.drawable.iv_placeholder_v) + .into(imageView) + } + imageView?.visibility = View.VISIBLE + imageView?.let { imageMap[position] = it } + var exoPlayer = playerMap[position] + if (exoPlayer == null) { + val mediaItem = MediaItem.fromUri(Uri.parse(data?.video_url)) + exoPlayer = ExoPlayer.Builder(context!!) + .setRenderersFactory( + DefaultRenderersFactory(context!!).setEnableDecoderFallback( + true + ) + ) + .setLoadControl(getLoadControl()) + .build().apply { + setMediaItem(mediaItem) + prepare() + } + playerMap[position] = exoPlayer + } + playerView?.player = exoPlayer + exoPlayer.let { player -> + + player.addListener(object : Player.Listener { + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + when (playbackState) { + Player.STATE_READY -> { + if (position == currentPlayingPosition) { + player.play() + imageView?.visibility = View.INVISIBLE + } else { + player.playWhenReady = false + player.pause() + } + } + + Player.STATE_ENDED -> { + player.seekTo(0) + player.play() + } + } + } + + override fun onPlayerError(error: PlaybackException) { + super.onPlayerError(error) + Handler(Looper.getMainLooper()).post { + imageView?.visibility = View.VISIBLE + } + } + }) + + } + } + + private fun getLoadControl(): LoadControl { + return androidx.media3.exoplayer.DefaultLoadControl.Builder() + .setBufferDurationsMs( + 2500, 15000, 1500, 2000 + ) + .setTargetBufferBytes(50 * 1024 * 1024) + .setPrioritizeTimeOverSizeThresholds(false) + .build() + } + + + inner class BannerViewHolder(var view: View) : RecyclerView.ViewHolder( + view + ) + + + fun resumeCurrentPlayer() { + playerMap[currentPlayingPosition]?.let { player -> + if (player.playbackState != Player.STATE_ENDED) { + player.play() + imageMap[currentPlayingPosition]?.visibility = View.INVISIBLE + } + } + } + + fun pauseAllPlayers() { + playerMap.values.forEach { player -> + if (player.isPlaying) { + player.pause() + } + } + imageMap[currentPlayingPosition]?.visibility = View.VISIBLE + } + + fun releaseAllPlayers() { + playerMap.values.forEach { player -> + player.stop() + player.release() + } + playerMap.clear() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailActivity.kt b/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailActivity.kt new file mode 100644 index 0000000..69b461c --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailActivity.kt @@ -0,0 +1,820 @@ +package com.jia.er.nebuluxe.app.video + +import android.annotation.SuppressLint +import android.graphics.Color +import android.net.Uri +import android.os.Bundle +import android.text.SpannableStringBuilder +import android.view.View +import android.view.WindowManager +import android.widget.FrameLayout +import android.widget.SeekBar +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatSeekBar +import androidx.appcompat.widget.AppCompatTextView +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DefaultDataSourceFactory +import androidx.media3.exoplayer.DefaultRenderersFactory +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.hls.HlsMediaSource +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource +import androidx.media3.ui.PlayerView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper +import com.blankj.utilcode.util.ViewUtils +import com.bumptech.glide.Glide +import com.google.gson.Gson +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.BaseActivity +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.data.DetailsRecommendRes +import com.jia.er.nebuluxe.app.data.MainDataHis +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes +import com.jia.er.nebuluxe.app.data.UploadHistoryReq +import com.jia.er.nebuluxe.app.databinding.ActivityPlayDetailBinding +import com.jia.er.nebuluxe.app.net.MainViewModel +import com.jia.er.nebuluxe.app.ui.FfmpegRenderersFactory +import com.jia.er.nebuluxe.app.ui.LoadingLine +import com.jia.er.nebuluxe.app.ui.OnSnapHelperCurrentListener +import com.jia.er.nebuluxe.app.ui.RecyclerViewScrollerDetection +import com.jia.er.nebuluxe.app.utils.DialogUtils +import com.jia.er.nebuluxe.app.utils.Memory +import com.jia.er.nebuluxe.app.utils.appendWithStyle +import com.jia.er.nebuluxe.app.utils.formatTimestamp +import com.jia.er.nebuluxe.app.utils.singleClick +import com.jia.er.nebuluxe.app.utils.toast +import com.youth.banner.listener.OnPageChangeListener +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +@SuppressLint("UnsafeOptInUsageError") +class PlayerDetailActivity : BaseActivity(), + OnSnapHelperCurrentListener, SeriesDialogFragment.SeriesCallBack { + private var builder: ExoPlayer.Builder? = null + private var player: ExoPlayer? = null + private val pagerSnapHelper = PagerSnapHelper() + private val recyclerViewScrollerDetection = RecyclerViewScrollerDetection() + private var playerDetailAdapter: PlayerDetailAdapter? = null + private var playerView: PlayerView? = null + private var currentView: View? = null + private var loadingLine: LoadingLine? = null + private var tvName: AppCompatTextView? = null + private var tvTime: AppCompatTextView? = null + private var tvEpTotal: AppCompatTextView? = null + private var tvCollectionNum: AppCompatTextView? = null + private var play: AppCompatImageView? = null + private var collection: AppCompatImageView? = null + private var ivIconPlayer: AppCompatImageView? = null + private var ivCover: AppCompatImageView? = null + private var ivBackController: AppCompatImageView? = null + private var exampleSeekbarPlayerController: AppCompatSeekBar? = null + private var exampleProgressJob: Job? = null + private var isDragging = false + private var isPlaying = false + private var shortVideoId: Int = 0 + private var dataRes: PlayerDetailDataRes.Episode? = null + private var currentPosition = 0 + private val mViewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + private var activityId: Int = 0 + private var progress: Long = 0L + private var seek = true + private var needRestart: Boolean = false + private var startTime: Long = 0 + private var recommendBannerAdapter: NewRecommendBannerAdapter? = null + private var revolution = "" + private var needRefresh: Boolean = false + private var payPosition = 0 + private var isCanPlay = false +// private var isLock = false + private var exampleSeriesDialogFragment: SeriesDialogFragment? = null + + @SuppressLint("UnsafeOptInUsageError") + override fun top() { + EventBus.getDefault().register(this) + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + showLoading() + startTime = System.currentTimeMillis() + shortVideoId = intent.getIntExtra(Constants.CONSTANTS_short_play_id, 0) + activityId = intent.getIntExtra(Constants.CONSTANTS_activity_id, 0) + builder = ExoPlayer.Builder(this, FfmpegRenderersFactory(this)) + .setRenderersFactory( + DefaultRenderersFactory(this).setEnableDecoderFallback( + true + ) + ) + player = builder?.build() + playerView = ViewUtils.layoutId2View(R.layout.include_detail_player_view) as PlayerView + playerView?.player = player + loadingLine = playerView?.findViewById(R.id.load_line) + tvName = playerView?.findViewById(R.id.example_tv_title_player_controller) + tvTime = playerView?.findViewById(R.id.tv_player_seek_time) + tvEpTotal = playerView?.findViewById(R.id.tv_ep_total) + tvCollectionNum = playerView?.findViewById(R.id.example_tv_collection_num_controller) + collection = playerView?.findViewById(R.id.example_iv_collection_controller) + play = playerView?.findViewById(R.id.example_iv_play_player_controller) + ivIconPlayer = playerView?.findViewById(R.id.iv_icon_player) + ivBackController = playerView?.findViewById(R.id.iv_back_controller) + exampleSeekbarPlayerController = + playerView?.findViewById(R.id.example_seekBar_player_controller) + play?.setOnClickListener { + singleClick { + if (isPlaying) { + pause() + } else { + play() + } + } + } + ivBackController?.setOnClickListener { + singleClick { + handleCustomLogic() + } + } + player?.addListener(object : Player.Listener { + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + when (playbackState) { + Player.STATE_BUFFERING -> { + loadingLine?.visibility = View.VISIBLE + loadingLine?.startAnimation() + ivIconPlayer?.visibility = View.VISIBLE + } + + Player.STATE_READY -> { +// if (!isLock) { + if (isCanPlay) { + if (seek && progress > 0) { + player?.seekTo(progress) + seek = false + } + canPlay() + } +// } else { +// loadingLine?.endAnimation() +// loadingLine?.visibility = View.INVISIBLE +// ivCover = currentView?.findViewById(R.id.iv_icon) +// ivCover?.visibility = View.INVISIBLE +// ivIconPlayer?.visibility = View.INVISIBLE +// pause() +// } + } + + Player.STATE_ENDED -> { + needRestart = true + uploadSeconds() + val plus = currentPosition.plus(1) + recyclerViewScrollerDetection.isFirstAttached = false + binding.rvDetail.scrollToPosition(plus) + } + + Player.STATE_IDLE -> { + } + } + } + + override fun onPlayerError(error: PlaybackException) { + super.onPlayerError(error) + ivIconPlayer?.visibility = View.VISIBLE + toast(getString(R.string.network_error)) + } + }) + collection?.setOnClickListener { + singleClick { + doCollect() + } + } + tvEpTotal?.setOnClickListener { + singleClick { + exampleSeriesDialogFragment = SeriesDialogFragment() + val bundle = Bundle() + dataRes?.episode?.let { it1 -> + bundle.putInt( + Constants.Constants_Episodes_Series_Data_currentPositionExample, + it1 + ) + } + bundle.putParcelable( + Constants.Constants_Episodes_Series_DataExample, + dataRes?.shortPlayInfo + ) + bundle.putParcelableArrayList( + Constants.Constants_Episodes_Series_Data_ListExample, + playerDetailAdapter?.items?.let { it1 -> ArrayList(it1) } + ) + exampleSeriesDialogFragment?.seriesCallBack = this + exampleSeriesDialogFragment?.arguments = bundle + exampleSeriesDialogFragment?.show( + supportFragmentManager, + "SeriesDialogFragment" + ) + } + } + mViewModel.getVideoDetails( + shortVideoId, + 0, + activityId, + revolution, + null + ) + onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (handleCustomLogic()) { + return + } + } + }) +// binding.tvLockEpisode.setOnClickListener { +// singleClick { +// if (currentPosition > 0) { +// val beforeItem = +// playerDetailAdapter?.getItem(currentPosition - 1) as PlayerDetailDataRes.Episode +// if (beforeItem.is_lock && !Memory.isVip()) { +// toast(getString(R.string.prequel_before)) +// return@singleClick +// } +// } +// val allCoin = Memory.getAllCoin() +// val coins = dataRes?.coins!! +// if (allCoin >= coins) { +// showLoading() +// dataRes?.short_play_id?.let { it1 -> +// dataRes?.short_play_video_id?.let { it2 -> +// mViewModel.doBuyVideo( +// it1, +// it2 +// ) +// } +// } +// } else { +// rechargeDialog(dataRes) +// } +// } +// } + binding?.viewVideoRecommend?.ivCloseRecommend?.setOnClickListener { finish() } + binding?.viewVideoRecommend?.tvWatchNowRecommend?.setOnClickListener { + singleClick { + val data = recommendBannerAdapter?.getData(recommendBannerPosition) + watchNow(data) + } + } + } + + private fun doCollect() { + if (dataRes?.shortPlayInfo?.is_collect == true) { + dataRes?.short_play_id?.let { it1 -> + dataRes?.short_play_video_id?.let { it2 -> + DialogUtils.unFavoriteDialog( + this, + it1, + it2, + mViewModel + ) + } + } + } else { + dataRes?.short_play_id?.let { + dataRes?.short_play_video_id.let { it1 -> + if (it1 != null) { + mViewModel.doCollect( + it, it1 + ) + } + } + } + } + } + + private fun canPlay() { + loadingLine?.endAnimation() + loadingLine?.visibility = View.INVISIBLE + ivCover = currentView?.findViewById(R.id.iv_icon) + play() + setProgress() + ivCover?.visibility = View.INVISIBLE + ivIconPlayer?.visibility = View.INVISIBLE + } +// private var storeDialogFragment: StoreDialogFragment? = null +// private fun rechargeDialog(dataRes: PlayerDetailDataRes.Episode?) { +// try { +// storeDialogFragment = +// StoreDialogFragment() +// val bundle = Bundle() +// bundle.putParcelable(Constants.CONSTANTS_Episode, dataRes) +// storeDialogFragment?.setStyle(DialogFragment.STYLE_NO_FRAME, 0) +// storeDialogFragment?.arguments = bundle +// storeDialogFragment?.show( +// supportFragmentManager, +// "StoreDialogFragment" +// ) +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } + + private fun watchNow( + data: DetailsRecommendRes.Item? + ) { + binding?.viewVideoRecommend?.exampleBannerRecommend?.postDelayed({ + if (data?.short_play_id != null) { + shortVideoId = data.short_play_id + recommendBannerAdapter?.releaseAllPlayers() + detailRefresh() + } + }, 300) + binding?.viewVideoRecommend?.root?.visibility = View.INVISIBLE + } + + private fun handleCustomLogic(): Boolean { + if (binding?.viewVideoRecommend?.root?.visibility == View.INVISIBLE) { + val currentTime = System.currentTimeMillis() + val duration = currentTime - startTime + if (duration > 3000) { + mViewModel.getDetailsRecommand() + binding.root.postDelayed({ + pause() + player?.stop() + }, 500) + } else { + finish() + } + } + return false + } + + private var recommendBannerPosition = 0 + + override fun center() { + mViewModel.getDetailsRecommandData.observe(this) { + if (it != null) { + if (it.data?.list?.isNotEmpty() == true) { + binding.viewVideoRecommend.root.visibility = View.VISIBLE + binding.viewVideoRecommend.bottom.setOnClickListener { } + recommendBannerAdapter = + NewRecommendBannerAdapter(it.data.list, this) + binding?.viewVideoRecommend?.exampleBannerRecommend?.setBannerGalleryEffect( + 52, + 12 + ) + binding?.viewVideoRecommend?.exampleBannerRecommend?.setAdapter( + recommendBannerAdapter + ) + val i = it.data.list.size / 2 + binding?.viewVideoRecommend?.exampleBannerRecommend?.setCurrentItem( + i, + false + ) + recommendBannerAdapter?.currentPlayingPosition = i + recommendBannerPosition = i + recommendBannerAdapter?.resumeCurrentPlayer() + binding?.viewVideoRecommend?.exampleBannerRecommend?.addOnPageChangeListener( + object : + OnPageChangeListener { + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int + ) { + } + + @SuppressLint("UnsafeOptInUsageError") + override fun onPageSelected(position: Int) { + recommendBannerPosition = position + recommendBannerAdapter?.pauseAllPlayers() + recommendBannerAdapter?.currentPlayingPosition = position + recommendBannerAdapter?.playerMap?.get(position)?.let { newPlayer -> + recommendBannerAdapter?.resumeCurrentPlayer() + } + } + + override fun onPageScrollStateChanged(state: Int) { + } + }) + } else { + finish() + } + } else { + finish() + } + } + mViewModel.infoData.observe(this) { + if (it != null) { + it.data?.let { it1 -> + Memory.saveUserInfo(it1) + if (needRefresh) { + detailRefresh() + } + } + } + } +// mViewModel.buy_videoData.observe(this) { +// if (it != null) { +// when (it.data?.status) { +// "not_enough" -> { +// rechargeDialog(dataRes) +// hideLoading() +// } +// +// "success" -> { +// needRefresh = false +// mViewModel.getInfo() +// toast(getString(R.string.success)) +// val get = playerDetailAdapter?.items?.get(currentPosition) +// get?.is_lock = false +// collection?.isEnabled = true +// tvEpTotal?.isEnabled = true +// play?.isEnabled = true +// binding.tvLockEpisode.postDelayed({ +// binding.tvLockEpisode.visibility = View.INVISIBLE +// binding.clLock.visibility = View.INVISIBLE +// canPlay() +// hideLoading() +// }, 1000) +// } +// +// else -> { +// hideLoading() +// toast(it.data?.status.toString()) +// } +// } +// } else { +// hideLoading() +// toast(getString(R.string.network_error)) +// } +// } + mViewModel.videoDetailsData.observe(this) { it -> + if (it != null) { + playerDetailAdapter = PlayerDetailAdapter() + val layoutManager = + LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding?.rvDetail?.layoutManager = layoutManager + binding?.rvDetail?.adapter = playerDetailAdapter + binding?.rvDetail?.isNestedScrollingEnabled = false + pagerSnapHelper.attachToRecyclerView(binding?.rvDetail) + recyclerViewScrollerDetection.setCurrentListener(this) + recyclerViewScrollerDetection.attachToSnapHelper(pagerSnapHelper) + binding?.rvDetail?.let { it1 -> + recyclerViewScrollerDetection.addOnScrollListener( + it1 + ) + } + it.data?.episodeList?.forEach { it1 -> + it1.shortPlayInfo = it.data.shortPlayInfo + } + ivIconPlayer?.let { it1 -> + Glide.with(this).load(it.data?.shortPlayInfo?.image_url) + .into(it1) + } + playerDetailAdapter?.submitList(it.data?.episodeList) + if (needRefresh) { + recyclerViewScrollerDetection.isFirstAttached = false + binding.rvDetail.scrollToPosition(payPosition) + needRefresh = false + } else { + if (it.data?.video_info != null) { + if (it.data.video_info.episode > 1) { + it.data.video_info.episode.minus(1) + .let { it1 -> + recyclerViewScrollerDetection.isFirstAttached = false + binding.rvDetail.scrollToPosition(it1) + } + } + } + } + hideLoading() + } else { + showNoDrama() + toast(getString(R.string.network_error)) + hideLoading() + } + } + mViewModel.collectData.observe(this) { + if (it != null) { + collection?.setImageResource(R.drawable.iv_example_collection_h) + dataRes?.shortPlayInfo?.collect_total = + dataRes?.shortPlayInfo?.collect_total?.plus(1)!! + tvCollectionNum?.text = dataRes?.shortPlayInfo?.collect_total.toString() + dataRes?.shortPlayInfo?.is_collect = true + EventBus.getDefault().post(Constants.CONSTANTS_collect_refresh) + toast(getString(R.string.success)) + } else { + toast(getString(R.string.network_error)) + } + } + mViewModel.cancelCollectData.observe(this) { + if (it != null) { + collection?.setImageResource(R.drawable.iv_example_collection_n) + dataRes?.shortPlayInfo?.collect_total = + dataRes?.shortPlayInfo?.collect_total?.minus(1)!! + tvCollectionNum?.text = dataRes?.shortPlayInfo?.collect_total.toString() + dataRes?.shortPlayInfo?.is_collect = false + toast(getString(R.string.success)) + EventBus.getDefault().post(Constants.CONSTANTS_collect_refresh) + } else { + toast(getString(R.string.network_error)) + } + } + } + + + @SuppressLint("UnsafeOptInUsageError") + private fun buildMediaSource(videoPath: String): MediaSource { + val dataSourceFactory: DataSource.Factory = + DefaultDataSourceFactory(this, "reelcrush") + + return if (videoPath.endsWith(".m3u8")) { + HlsMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(videoPath))) + } else { + ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(videoPath))) + } + } + + override fun onPause() { + super.onPause() + isCanPlay = false + binding.root.postDelayed({ player?.pause() }, 300) + recommendBannerAdapter?.pauseAllPlayers() + } + + override fun onResume() { + super.onResume() +// if (!isLock && player?.isPlaying == false) { + isCanPlay = true + binding.root.postDelayed({ canPlay() }, 300) +// } + recommendBannerAdapter?.resumeCurrentPlayer() + } + + private fun setProgress() { + exampleProgressJob?.cancel() + val duration = player!!.duration + exampleSeekbarPlayerController?.max = duration.toInt() + exampleSeekbarPlayerController?.progress = player!!.currentPosition.toInt() + exampleSeekbarPlayerController?.setOnSeekBarChangeListener(null) + exampleProgressJob = lifecycleScope.launch { + while (isActive) { + if (!isDragging) { + withContext(Dispatchers.Main) { + exampleSeekbarPlayerController?.progress = player!!.currentPosition.toInt() + } + } + delay(1000) + } + } + + exampleSeekbarPlayerController?.setOnSeekBarChangeListener(object : + SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + if (fromUser) { + seekTo(progress) + } + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) { + tvTime?.visibility = FrameLayout.VISIBLE + isDragging = true + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + isDragging = false + tvTime?.visibility = FrameLayout.INVISIBLE + } + }) + } + + fun play() { + player?.play() + play?.setImageResource(R.drawable.iv_example_stop) + isPlaying = true + isDragging = false + } + + fun pause() { + player?.pause() + play?.setImageResource(R.drawable.iv_example_play) + isPlaying = false + isDragging = true + } + + private fun seekTo(progress: Int) { + player?.seekTo(progress.toLong()) + seekTime() + } + + private fun seekTime() { + val currentPosition = player!!.currentPosition + val currentTime = formatTimestamp(currentPosition / 1000) + val totalDuration = player!!.duration + val totalTime = formatTimestamp(totalDuration / 1000) + tvTime?.text = "$currentTime/$totalTime" + } + + override fun getViewBinding(): ActivityPlayDetailBinding { + return ActivityPlayDetailBinding.inflate(layoutInflater) + } + + override fun onDestroy() { + isCanPlay = false + player?.stop() + player?.release() +// storeDialogFragment?.storeCallBack = null + exampleSeriesDialogFragment?.seriesCallBack = null + recommendBannerAdapter?.releaseAllPlayers() + super.onDestroy() + recyclerViewScrollerDetection.detachToSnapHelper() + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + EventBus.getDefault().unregister(this) + System.gc() + } + + @SuppressLint("UnsafeOptInUsageError") + override fun setActive( + currentView: View?, + position: Int, + previousView: View?, + previousPosition: Int + ) { + seek = true + isCanPlay = true + this.currentView = currentView + currentPosition = position + val frameLayout = currentView?.findViewById(R.id.video) + (playerView?.parent as FrameLayout?)?.removeView(playerView) + frameLayout?.removeAllViews() + frameLayout?.addView(playerView) + playerView?.showController() + dataRes = playerDetailAdapter?.getItem(position) + progress = if (dataRes?.play_seconds?.isNotEmpty() == true) { + dataRes?.play_seconds!!.toLong() + } else { + 0 + } +// isLock = (dataRes?.is_lock == true && !Memory.isVip()) +// collection?.isEnabled = !isLock +// tvEpTotal?.isEnabled = !isLock +// play?.isEnabled = !isLock +// binding.tvLockEpisode.visibility = if (isLock) View.VISIBLE else View.INVISIBLE +// binding.clLock.visibility = if (isLock) View.VISIBLE else View.INVISIBLE +// if (currentPosition > 0) { +// playerDetailAdapter?.items?.get(currentPosition - 1) +// ?.let { it2 -> +// if (!it2.is_lock) { +// binding.tvLockEpisode.text = +// "Unlocking costs ".plus(dataRes?.coins).plus(" coins") +// } else { +// binding.tvLockEpisode.text = "Prev. locked" +// } +// } +// } else { +// binding.tvLockEpisode.text = "Unlocking costs ".plus(dataRes?.coins).plus(" coins") +// } + dataRes?.video_url?.let { buildMediaSource(it) }?.let { player?.setMediaSource(it) } + player?.prepare() + tvName?.text = dataRes?.shortPlayInfo?.name + tvCollectionNum?.text = dataRes?.shortPlayInfo?.collect_total.toString() + val builder = SpannableStringBuilder() + builder.appendWithStyle( + "Ep.".plus(dataRes?.episode), + Color.parseColor("#FFDAA4"), + isBold = true + ) + .appendWithStyle( + "/Ep.".plus(dataRes?.shortPlayInfo?.episode_total), + Color.parseColor("#FFFFFF"), + isBold = false + ) + tvEpTotal?.text = builder + if (dataRes?.is_lock == false || Memory.isVip()) { + dataRes?.short_play_video_id?.let { + mViewModel.doCreateHistory( + shortVideoId, + it + ) + } + } + collection?.setImageResource(if (dataRes?.shortPlayInfo?.is_collect == true) R.drawable.iv_example_collection_h else R.drawable.iv_example_collection_n) + } + + + override fun disActive(detachedView: View?, detachedPosition: Int) { + if (null == detachedView) return + ivCover = detachedView.findViewById(R.id.iv_icon) + ivCover?.visibility = View.VISIBLE + if (player?.isPlaying == true) { + pause() + } + exampleSeekbarPlayerController?.progress = 0 + uploadSeconds() + } + + private fun uploadSeconds() { + if (dataRes?.is_lock == false) { + playerDetailAdapter?.getItem(currentPosition)?.play_seconds = + if (needRestart) "0" else lastProgress() + .toString() + mViewModel.uploadHistorySeconds( + UploadHistoryReq( + if (needRestart) 0 else lastProgress(), + shortVideoId, + dataRes?.short_play_video_id + ) + ) + needRestart = false + } + } + + override fun chooseSeries(episode: PlayerDetailDataRes.Episode) { +// isLock = episode.is_lock + uploadSeconds() + val index = playerDetailAdapter?.items?.withIndex()?.firstOrNull { + (episode.episode == it.value.episode) && (episode.shortPlayInfo?.name == it.value.shortPlayInfo?.name + ) + }?.index + recyclerViewScrollerDetection.isFirstAttached = false + index?.let { binding.rvDetail.scrollToPosition(it) } + } + + override fun collection() { + doCollect() + } + + override fun onStop() { + super.onStop() + if (dataRes?.is_lock == false || Memory.isVip()) { + dataRes?.short_play_id?.let { + UploadHistoryReq( + lastProgress(), + it, + dataRes?.short_play_video_id + ) + }?.let { + mViewModel.uploadHistorySeconds( + it + ) + } + } + if (dataRes?.shortPlayInfo?.image_url?.isNotEmpty() == true) { + val toJson = Gson().toJson( + dataRes?.short_play_id?.let { + MainDataHis( + dataRes?.shortPlayInfo?.episode_total.toString(), + it, + dataRes?.episode.toString(), + dataRes?.shortPlayInfo?.image_url.toString() + ) + } + ) + Memory.getMMKV() + .putString(Constants.Constants_Main_Video_info, toJson) + Memory.getMMKV() + .putBoolean(Constants.Constants_Main_Video_status, true) + } + } + + private fun lastProgress(): Long { + return player?.currentPosition ?: 0 + } + + private fun detailRefresh() { + recyclerViewScrollerDetection.isFirstAttached = false + player?.pause() + player?.stop() + mViewModel.getVideoDetails( + shortVideoId, + 0, + activityId, + revolution, + null + ) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEvent(event: String) { + if (Constants.CONSTANTS_pay_refresh == event) { + payPosition = dataRes?.episode?.minus(1)!! + needRefresh = true + mViewModel.getInfo() + } + } + + override fun onReturn() { + super.onReturn() + finish() + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailAdapter.kt new file mode 100644 index 0000000..dffdf2d --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/video/PlayerDetailAdapter.kt @@ -0,0 +1,29 @@ +package com.jia.er.nebuluxe.app.video + +import android.content.Context +import android.view.ViewGroup +import com.bumptech.glide.Glide +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes + +class PlayerDetailAdapter : + BaseQuickAdapter() { + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: PlayerDetailDataRes.Episode? + ) { + Glide.with(context).load(item?.shortPlayInfo?.image_url).placeholder(R.drawable.iv_placeholder_v) + .into(holder.getView(R.id.iv_icon)) + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_home_for_you, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDataAdapter.kt b/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDataAdapter.kt new file mode 100644 index 0000000..cd452c6 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDataAdapter.kt @@ -0,0 +1,48 @@ +package com.jia.er.nebuluxe.app.video + +import android.content.Context +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes + +class SeriesDataAdapter : + BaseQuickAdapter() { + var currentPosition = -1 + override fun onBindViewHolder( + holder: QuickViewHolder, + position: Int, + item: PlayerDetailDataRes.Episode? + ) { + val view = holder.getView(R.id.example_tv_num_data) + view.text = item?.episode.toString() + if (currentPosition == view.text.toString().toInt()) { + holder.setBackgroundResource(R.id.example_tv_num_data, R.drawable.bg_example_num_h) + holder.setTextColor( + R.id.example_tv_num_data, + context.getColor(R.color.black) + ) + } else { + holder.setBackgroundResource(R.id.example_tv_num_data, R.drawable.bg_example_num_n) + holder.setTextColor( + R.id.example_tv_num_data, + context.getColor(R.color.white) + ) + } +// if (item?.is_lock == true && !Memory.isVip()) { +// holder.setVisible(R.id.iv_example_lock, true) +// } else { +// holder.setVisible(R.id.iv_example_lock, false) +// } + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): QuickViewHolder { + return QuickViewHolder(R.layout.item_num_data, parent) + } +} diff --git a/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDialogFragment.kt b/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDialogFragment.kt new file mode 100644 index 0000000..a30a109 --- /dev/null +++ b/app/src/main/java/com/jia/er/nebuluxe/app/video/SeriesDialogFragment.kt @@ -0,0 +1,105 @@ +package com.jia.er.nebuluxe.app.video + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import android.view.Gravity +import android.view.View +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.GridLayoutManager +import com.jia.er.nebuluxe.app.R +import com.jia.er.nebuluxe.app.basics.Constants +import com.jia.er.nebuluxe.app.basics.Constants.Constants_Episodes_Series_DataExample +import com.jia.er.nebuluxe.app.basics.Constants.Constants_Episodes_Series_Data_ListExample +import com.jia.er.nebuluxe.app.basics.Constants.Constants_Episodes_Series_Data_currentPositionExample +import com.jia.er.nebuluxe.app.data.PlayerDetailDataRes +import com.jia.er.nebuluxe.app.databinding.DialogSeriesBinding +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class SeriesDialogFragment : DialogFragment() { + var seriesCallBack: SeriesCallBack? = null + var is_collect = false + var exampleDialogSeriesBinding: DialogSeriesBinding? = null + + interface SeriesCallBack { + fun chooseSeries(episode: PlayerDetailDataRes.Episode) + fun collection() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + EventBus.getDefault().register(this) + val builder = AlertDialog.Builder(requireActivity()) + val inflater = requireActivity().layoutInflater + val view = inflater.inflate(R.layout.dialog_series, null) + exampleDialogSeriesBinding = DialogSeriesBinding.bind(view) + val episodeList: List? = + arguments?.getParcelableArrayList(Constants_Episodes_Series_Data_ListExample) + val data: PlayerDetailDataRes.ShortPlayInfo? = + arguments?.getParcelable(Constants_Episodes_Series_DataExample) + val currentPosition = + arguments?.getInt(Constants_Episodes_Series_Data_currentPositionExample, 0) + val exampleSeriesDataAdapter = SeriesDataAdapter() + val spanCount = 6 + val layoutManager = GridLayoutManager(context, spanCount) + exampleDialogSeriesBinding?.rvDataDialogSeries?.layoutManager = layoutManager + exampleDialogSeriesBinding?.rvDataDialogSeries?.adapter = exampleSeriesDataAdapter + exampleSeriesDataAdapter.submitList(episodeList) + if (currentPosition != null) { + exampleSeriesDataAdapter.currentPosition = currentPosition + } + exampleDialogSeriesBinding?.tvDesDialogSeries?.text = data?.description + if (data?.category?.isNotEmpty() == true) { + exampleDialogSeriesBinding?.tvTag?.visibility = View.VISIBLE + exampleDialogSeriesBinding?.tvTag?.text = data?.category?.get(0) + } else { + exampleDialogSeriesBinding?.tvTag?.visibility = View.GONE + } + is_collect = data?.is_collect == true + exampleDialogSeriesBinding?.ivCollect?.setImageResource(if (data?.is_collect == true) R.drawable.iv_example_collection_h else R.drawable.iv_example_collection_n) + exampleDialogSeriesBinding?.ivCollect?.setOnClickListener { + seriesCallBack?.collection() + } + exampleSeriesDataAdapter.setOnItemClickListener { adapter, view, position -> + val item = adapter.getItem(position) as PlayerDetailDataRes.Episode +// if (position > 0) { +// val beforeItem = adapter.getItem(position - 1) as PlayerDetailDataRes.Episode +// if (beforeItem.is_lock && !Memory.isVip()) { +// toast(getString(R.string.prequel_before)) +// dismiss() +// return@setOnItemClickListener +// } +// } + seriesCallBack?.chooseSeries(item) + dismiss() + } + isCancelable = true + exampleDialogSeriesBinding?.tvContentDialogSeries?.text = data?.name + builder.setView(exampleDialogSeriesBinding?.root) + val dialog = builder.create() + dialog.window?.setBackgroundDrawableResource(android.R.color.transparent) + val window = dialog.window + window?.decorView?.setPadding(0, 0, 0, 0) + window?.setGravity(Gravity.BOTTOM) + val layoutParams = window?.attributes + layoutParams?.width = WindowManager.LayoutParams.MATCH_PARENT + layoutParams?.height = WindowManager.LayoutParams.WRAP_CONTENT + window?.attributes = layoutParams + return dialog + } + + override fun onStop() { + super.onStop() + EventBus.getDefault().unregister(this) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEvent(event: String) { + if (Constants.CONSTANTS_collect_refresh == event) { + is_collect = !is_collect + exampleDialogSeriesBinding?.ivCollect?.setImageResource(if (is_collect == true) R.drawable.iv_example_collection_h else R.drawable.iv_example_collection_n) + } + } +} diff --git a/app/src/main/res/drawable-xxhdpi/bg_rank_1.png b/app/src/main/res/drawable-xxhdpi/bg_rank_1.png new file mode 100644 index 0000000..91ff98f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_rank_1.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_rank_2.png b/app/src/main/res/drawable-xxhdpi/bg_rank_2.png new file mode 100644 index 0000000..8d2dc0a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_rank_2.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_rank_3.png b/app/src/main/res/drawable-xxhdpi/bg_rank_3.png new file mode 100644 index 0000000..9e62129 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_rank_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_rank_other.png b/app/src/main/res/drawable-xxhdpi/bg_rank_other.png new file mode 100644 index 0000000..f7f3a4d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_rank_other.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_me_top.png b/app/src/main/res/drawable-xxhdpi/ic_me_top.png new file mode 100644 index 0000000..a036c4b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_me_top.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_avatar.png b/app/src/main/res/drawable-xxhdpi/iv_avatar.png new file mode 100644 index 0000000..ccad7bf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_avatar.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_des_bg.png b/app/src/main/res/drawable-xxhdpi/iv_des_bg.png new file mode 100644 index 0000000..072c9b9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_des_bg.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_back.png b/app/src/main/res/drawable-xxhdpi/iv_example_back.png new file mode 100644 index 0000000..dd5cb3a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_back.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_collection_h.png b/app/src/main/res/drawable-xxhdpi/iv_example_collection_h.png new file mode 100644 index 0000000..2c62db6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_collection_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_collection_n.png b/app/src/main/res/drawable-xxhdpi/iv_example_collection_n.png new file mode 100644 index 0000000..2991786 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_collection_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_play.webp b/app/src/main/res/drawable-xxhdpi/iv_example_play.webp new file mode 100644 index 0000000..0f4a471 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_play.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_search.png b/app/src/main/res/drawable-xxhdpi/iv_example_search.png new file mode 100644 index 0000000..356a06a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_search.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_example_stop.webp b/app/src/main/res/drawable-xxhdpi/iv_example_stop.webp new file mode 100644 index 0000000..a25316b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_example_stop.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_fresh.png b/app/src/main/res/drawable-xxhdpi/iv_fresh.png new file mode 100644 index 0000000..a94ee89 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_fresh.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_fresh_play.png b/app/src/main/res/drawable-xxhdpi/iv_fresh_play.png new file mode 100644 index 0000000..954024a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_fresh_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_genre.png b/app/src/main/res/drawable-xxhdpi/iv_genre.png new file mode 100644 index 0000000..36ac62a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_genre.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_genres_1.png b/app/src/main/res/drawable-xxhdpi/iv_genres_1.png new file mode 100644 index 0000000..33321af Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_genres_1.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_genres_2.png b/app/src/main/res/drawable-xxhdpi/iv_genres_2.png new file mode 100644 index 0000000..998060a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_genres_2.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_genres_3.png b/app/src/main/res/drawable-xxhdpi/iv_genres_3.png new file mode 100644 index 0000000..94ab12b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_genres_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_genres_4.png b/app/src/main/res/drawable-xxhdpi/iv_genres_4.png new file mode 100644 index 0000000..86ee32f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_genres_4.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_home_bg.png b/app/src/main/res/drawable-xxhdpi/iv_home_bg.png new file mode 100644 index 0000000..c1f6128 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_home_bg.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_home_h.png b/app/src/main/res/drawable-xxhdpi/iv_home_h.png new file mode 100644 index 0000000..b8e8e80 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_home_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_home_n.png b/app/src/main/res/drawable-xxhdpi/iv_home_n.png new file mode 100644 index 0000000..0dbf50c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_home_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_hot.png b/app/src/main/res/drawable-xxhdpi/iv_hot.png new file mode 100644 index 0000000..554c5ef Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_hot.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_hot_top.png b/app/src/main/res/drawable-xxhdpi/iv_hot_top.png new file mode 100644 index 0000000..759e658 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_hot_top.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_hot_txt_l.png b/app/src/main/res/drawable-xxhdpi/iv_hot_txt_l.png new file mode 100644 index 0000000..b05891f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_hot_txt_l.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_hot_txt_r.png b/app/src/main/res/drawable-xxhdpi/iv_hot_txt_r.png new file mode 100644 index 0000000..29faba2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_hot_txt_r.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_indicator_h.png b/app/src/main/res/drawable-xxhdpi/iv_indicator_h.png new file mode 100644 index 0000000..03883c3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_indicator_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_indicator_n.png b/app/src/main/res/drawable-xxhdpi/iv_indicator_n.png new file mode 100644 index 0000000..8e242ec Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_indicator_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_item_play.png b/app/src/main/res/drawable-xxhdpi/iv_item_play.png new file mode 100644 index 0000000..eee5b5d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_item_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat.png b/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat.png new file mode 100644 index 0000000..7e6e40f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat_1.png b/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat_1.png new file mode 100644 index 0000000..91d8449 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_item_rank_heat_1.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_me_h.png b/app/src/main/res/drawable-xxhdpi/iv_me_h.png new file mode 100644 index 0000000..d44c2cb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_me_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_me_n.png b/app/src/main/res/drawable-xxhdpi/iv_me_n.png new file mode 100644 index 0000000..a247bb3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_me_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_me_right.png b/app/src/main/res/drawable-xxhdpi/iv_me_right.png new file mode 100644 index 0000000..a415179 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_me_right.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_no_data.png b/app/src/main/res/drawable-xxhdpi/iv_no_data.png new file mode 100644 index 0000000..b5c2e62 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_no_data.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_no_network.png b/app/src/main/res/drawable-xxhdpi/iv_no_network.png new file mode 100644 index 0000000..de8dfc8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_no_network.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_no_result.png b/app/src/main/res/drawable-xxhdpi/iv_no_result.png new file mode 100644 index 0000000..67ce18b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_no_result.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_placeholder_h.png b/app/src/main/res/drawable-xxhdpi/iv_placeholder_h.png new file mode 100644 index 0000000..46d8aa1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_placeholder_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_placeholder_v.png b/app/src/main/res/drawable-xxhdpi/iv_placeholder_v.png new file mode 100644 index 0000000..f271a8e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_placeholder_v.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_rank_top_bg.png b/app/src/main/res/drawable-xxhdpi/iv_rank_top_bg.png new file mode 100644 index 0000000..0ef74d5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_rank_top_bg.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_reels_h.png b/app/src/main/res/drawable-xxhdpi/iv_reels_h.png new file mode 100644 index 0000000..ecf1ae5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_reels_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_reels_n.png b/app/src/main/res/drawable-xxhdpi/iv_reels_n.png new file mode 100644 index 0000000..4b7b68a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_reels_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_saved_h.png b/app/src/main/res/drawable-xxhdpi/iv_saved_h.png new file mode 100644 index 0000000..d3ce6a7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_saved_h.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_saved_n.png b/app/src/main/res/drawable-xxhdpi/iv_saved_n.png new file mode 100644 index 0000000..dff14fc Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_saved_n.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_splash.png b/app/src/main/res/drawable-xxhdpi/iv_splash.png new file mode 100644 index 0000000..b8bae0f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_splash.png differ diff --git a/app/src/main/res/drawable-xxhdpi/iv_top.png b/app/src/main/res/drawable-xxhdpi/iv_top.png new file mode 100644 index 0000000..44b0efc Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/iv_top.png differ diff --git a/app/src/main/res/drawable/bg_circle.xml b/app/src/main/res/drawable/bg_circle.xml new file mode 100644 index 0000000..64346df --- /dev/null +++ b/app/src/main/res/drawable/bg_circle.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/bg_content.xml b/app/src/main/res/drawable/bg_content.xml new file mode 100644 index 0000000..39a3fcc --- /dev/null +++ b/app/src/main/res/drawable/bg_content.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_example_num_h.xml b/app/src/main/res/drawable/bg_example_num_h.xml new file mode 100644 index 0000000..73b9e4a --- /dev/null +++ b/app/src/main/res/drawable/bg_example_num_h.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_example_num_n.xml b/app/src/main/res/drawable/bg_example_num_n.xml new file mode 100644 index 0000000..5f323e4 --- /dev/null +++ b/app/src/main/res/drawable/bg_example_num_n.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_example_seekbar_player.xml b/app/src/main/res/drawable/bg_example_seekbar_player.xml new file mode 100644 index 0000000..93f7976 --- /dev/null +++ b/app/src/main/res/drawable/bg_example_seekbar_player.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_example_shape_seekbar_player.xml b/app/src/main/res/drawable/bg_example_shape_seekbar_player.xml new file mode 100644 index 0000000..c944060 --- /dev/null +++ b/app/src/main/res/drawable/bg_example_shape_seekbar_player.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_genres.xml b/app/src/main/res/drawable/bg_genres.xml new file mode 100644 index 0000000..81f13ad --- /dev/null +++ b/app/src/main/res/drawable/bg_genres.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_tab.xml b/app/src/main/res/drawable/bg_home_tab.xml new file mode 100644 index 0000000..01af242 --- /dev/null +++ b/app/src/main/res/drawable/bg_home_tab.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_top.xml b/app/src/main/res/drawable/bg_home_top.xml new file mode 100644 index 0000000..3a712cf --- /dev/null +++ b/app/src/main/res/drawable/bg_home_top.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..93dec38 --- /dev/null +++ b/app/src/main/res/layout/activity_about.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_fresh.xml b/app/src/main/res/layout/activity_fresh.xml new file mode 100644 index 0000000..fc28387 --- /dev/null +++ b/app/src/main/res/layout/activity_fresh.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_genres.xml b/app/src/main/res/layout/activity_genres.xml new file mode 100644 index 0000000..3a7d5b3 --- /dev/null +++ b/app/src/main/res/layout/activity_genres.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..5332af4 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_play_detail.xml b/app/src/main/res/layout/activity_play_detail.xml new file mode 100644 index 0000000..9c02f69 --- /dev/null +++ b/app/src/main/res/layout/activity_play_detail.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_rankings.xml b/app/src/main/res/layout/activity_rankings.xml new file mode 100644 index 0000000..2df9f06 --- /dev/null +++ b/app/src/main/res/layout/activity_rankings.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml new file mode 100644 index 0000000..cb75f0e --- /dev/null +++ b/app/src/main/res/layout/activity_setting.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 0000000..77d9ef6 --- /dev/null +++ b/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/close_episode_recommend.xml b/app/src/main/res/layout/close_episode_recommend.xml new file mode 100644 index 0000000..ef1188b --- /dev/null +++ b/app/src/main/res/layout/close_episode_recommend.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/custom_banner_view.xml b/app/src/main/res/layout/custom_banner_view.xml new file mode 100644 index 0000000..64309c6 --- /dev/null +++ b/app/src/main/res/layout/custom_banner_view.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/custom_banner_view_bottom.xml b/app/src/main/res/layout/custom_banner_view_bottom.xml new file mode 100644 index 0000000..e793de5 --- /dev/null +++ b/app/src/main/res/layout/custom_banner_view_bottom.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/detail_player_view_controller.xml b/app/src/main/res/layout/detail_player_view_controller.xml new file mode 100644 index 0000000..4eba406 --- /dev/null +++ b/app/src/main/res/layout/detail_player_view_controller.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000..861b1d9 --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_series.xml b/app/src/main/res/layout/dialog_series.xml new file mode 100644 index 0000000..0a1098c --- /dev/null +++ b/app/src/main/res/layout/dialog_series.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_un_collection.xml b/app/src/main/res/layout/dialog_un_collection.xml new file mode 100644 index 0000000..4cf86b6 --- /dev/null +++ b/app/src/main/res/layout/dialog_un_collection.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 0000000..9ccd33b --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_hot.xml b/app/src/main/res/layout/fragment_hot.xml new file mode 100644 index 0000000..044a5b8 --- /dev/null +++ b/app/src/main/res/layout/fragment_hot.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_me.xml b/app/src/main/res/layout/fragment_me.xml new file mode 100644 index 0000000..93466af --- /dev/null +++ b/app/src/main/res/layout/fragment_me.xml @@ -0,0 +1,446 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_reels.xml b/app/src/main/res/layout/fragment_reels.xml new file mode 100644 index 0000000..2b78b08 --- /dev/null +++ b/app/src/main/res/layout/fragment_reels.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_saved.xml b/app/src/main/res/layout/fragment_saved.xml new file mode 100644 index 0000000..77d9ef6 --- /dev/null +++ b/app/src/main/res/layout/fragment_saved.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/include_detail_player_view.xml b/app/src/main/res/layout/include_detail_player_view.xml new file mode 100644 index 0000000..8fdf332 --- /dev/null +++ b/app/src/main/res/layout/include_detail_player_view.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/include_player_view.xml b/app/src/main/res/layout/include_player_view.xml new file mode 100644 index 0000000..954175c --- /dev/null +++ b/app/src/main/res/layout/include_player_view.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dominant_ceo.xml b/app/src/main/res/layout/item_dominant_ceo.xml new file mode 100644 index 0000000..f25ab8a --- /dev/null +++ b/app/src/main/res/layout/item_dominant_ceo.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_fresh.xml b/app/src/main/res/layout/item_fresh.xml new file mode 100644 index 0000000..dad78f2 --- /dev/null +++ b/app/src/main/res/layout/item_fresh.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_genres.xml b/app/src/main/res/layout/item_genres.xml new file mode 100644 index 0000000..a229c69 --- /dev/null +++ b/app/src/main/res/layout/item_genres.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_genres_img.xml b/app/src/main/res/layout/item_genres_img.xml new file mode 100644 index 0000000..88489f7 --- /dev/null +++ b/app/src/main/res/layout/item_genres_img.xml @@ -0,0 +1,24 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_for_you.xml b/app/src/main/res/layout/item_home_for_you.xml new file mode 100644 index 0000000..0640670 --- /dev/null +++ b/app/src/main/res/layout/item_home_for_you.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_hot.xml b/app/src/main/res/layout/item_hot.xml new file mode 100644 index 0000000..a6430b3 --- /dev/null +++ b/app/src/main/res/layout/item_hot.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_num_data.xml b/app/src/main/res/layout/item_num_data.xml new file mode 100644 index 0000000..3f748bf --- /dev/null +++ b/app/src/main/res/layout/item_num_data.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_ranks.xml b/app/src/main/res/layout/item_ranks.xml new file mode 100644 index 0000000..ff535e8 --- /dev/null +++ b/app/src/main/res/layout/item_ranks.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_video.xml b/app/src/main/res/layout/item_video.xml new file mode 100644 index 0000000..f47bb86 --- /dev/null +++ b/app/src/main/res/layout/item_video.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_emptyview.xml b/app/src/main/res/layout/layout_emptyview.xml new file mode 100644 index 0000000..3488b4c --- /dev/null +++ b/app/src/main/res/layout/layout_emptyview.xml @@ -0,0 +1,49 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_network_error.xml b/app/src/main/res/layout/layout_network_error.xml new file mode 100644 index 0000000..8746d09 --- /dev/null +++ b/app/src/main/res/layout/layout_network_error.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_no_drama.xml b/app/src/main/res/layout/layout_no_drama.xml new file mode 100644 index 0000000..a3a83d8 --- /dev/null +++ b/app/src/main/res/layout/layout_no_drama.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/line_layout.xml b/app/src/main/res/layout/line_layout.xml new file mode 100644 index 0000000..cb4306f --- /dev/null +++ b/app/src/main/res/layout/line_layout.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/new_detail_player_banner_view.xml b/app/src/main/res/layout/new_detail_player_banner_view.xml new file mode 100644 index 0000000..1ec11fa --- /dev/null +++ b/app/src/main/res/layout/new_detail_player_banner_view.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recommend_player_view_controller.xml b/app/src/main/res/layout/recommend_player_view_controller.xml new file mode 100644 index 0000000..0c527ea --- /dev/null +++ b/app/src/main/res/layout/recommend_player_view_controller.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c4a603d --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..c4a603d --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..6bb968b Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..505eaf7 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..912fcfc Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..b9db059 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..aaf1b77 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..02e97c3 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..64e8920 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..027a366 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..021c8cd Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_about.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_about.png new file mode 100644 index 0000000..12b443d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_about.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_delete_account.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_delete_account.png new file mode 100644 index 0000000..1097db5 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_delete_account.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_help_center.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_help_center.png new file mode 100644 index 0000000..598c99c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_help_center.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_language.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_language.png new file mode 100644 index 0000000..69bcff8 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_language.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_privacy_policy.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_privacy_policy.png new file mode 100644 index 0000000..ffc9684 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_privacy_policy.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_btn_me_user_agreement.png b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_user_agreement.png new file mode 100644 index 0000000..13c3a40 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_btn_me_user_agreement.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..0d056fd Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..b5e3dd5 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..bdaa5cc Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..2a2cf19 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..f6e08b3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..e82bce7 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..920cef7 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,17 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #E3D4FF + #BDF5E2 + #FFFA80 + #F0C2E1 + + #D4D4D4 + #111111 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..28f4a0d --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ + + Nebuluxe + Check the network and try again~ + The service is abnormal. + No More Data + Success + Please log in first! + The prequel to this series is not unlocked. Please unlock the prequel before unlocking this series + Please enter keywords for search. + 1. ReelCrush offers free and paid content for everyone. \n 2. Paid content can be unlocked using coins or subscription. Member-only content is only accessible after subscribing. \n 3. Bonus coins expire after one day. \n 4. Coins will be used first when unlocking episodes. If the balance is insufficient, bonus coins will be used automatically. \n 5. During the subscription period, you will have unlimited access to all episodes on ReelCrush. \n 6. Subscription renewal will be automatically deducted from your Google account within 24 hours before your current subscription expires. \n 7. If you want to cancel your subscription, please go to your Google Play account and cancel your subscription at least 24 hours before the end of the current subscription period. \n 8. If the recharge payment is successful but your balance is not updated, please try to click Restore to refresh your balance. \n 9. Our subscription prices will be converted to the region where you pay. \nIf you want to manage your ReelCrush subscription, go to Google Subscription Management. + Google Pay Error + Google Pay Success + Google Pay Canceled + ReelCrush + No orders need to be restored + Language + Privacy Policy + Delet Account + Help Center + User Agreement + About Us + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..fb3f553 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..f2dfa28 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/net.xml b/app/src/main/res/xml/net.xml new file mode 100644 index 0000000..d11508d --- /dev/null +++ b/app/src/main/res/xml/net.xml @@ -0,0 +1,6 @@ + + + + api-nebuluxetv.nebuluxetv.com + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..922f551 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a747353 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,25 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.enableJetifier=true +android.buildFeatures.buildConfig=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..844e084 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,83 @@ +[versions] +agp = "8.7.3" +androidxLifecycleLifecycleExtensions2 = "2.2.0" +androidxMedia3ExoplayerVersion = "1.4.0" +androidxMedia3ExoplayerDashVersion = "1.4.0" +androidxMedia3UiVersion = "1.4.0" +androidxMedia3ExoplayerHlsVersion = "1.4.0" +androidxMedia3ExoplayerRtspVersion = "1.4.0" +getkeepsafeRelinker = "1.4.5" +githubFlycotablayout2Version = "Tag1.1.3" +githubBannerVersion = "2.2.3" +githubBaserecyclerviewadapterhelper4Version = "4.1.4" +githubGlideVersion = "4.12.0" +googleFlexbox = "3.0.0" +kotlin = "2.0.21" +coreKtx = "1.16.0" +lifecycleRuntimeKtx = "2.6.2" +appcompat = "1.7.1" +lifecycleViewmodelKtx = "2.8.4" +material = "1.12.0" +orgGreenrobotEventbus = "3.3.1" +refreshLayoutKernelVersion = "2.1.0" +refreshHeaderMaterialVersion = "2.1.0" +refreshFooterClassicsVersion = "2.1.0" +shapeblurview = "1.0.5" +squareupOkhttp = "4.10.0" +squareupLoggingInterceptor = "4.10.0" +squareupRetrofit = "2.9.0" +squareupConverterGson = "2.9.0" +tencentMmkv = "1.3.7" +utilcodex = "1.31.1" +playServicesAdsIdentifier = "18.2.0" +#androidBilling = "7.0.0" +#firebaseBomVersion = "32.3.1" +#firebaseMessagingKtxVersion = "24.0.0" +#androidInstallreferrer = "2.2" +#adjustAndroid = "5.2.0" +#adjustAndroidWebbridge = "5.2.0" + +[libraries] +#com-android-installreferrer-installreferrer = { module = "com.android.installreferrer:installreferrer", version.ref = "androidInstallreferrer" } +#adjust-android-webbridge = { module = "com.adjust.sdk:adjust-android-webbridge", version.ref = "adjustAndroidWebbridge" } +#adjust-android = { module = "com.adjust.sdk:adjust-android", version.ref = "adjustAndroid" } +#android-billing = { module = "com.android.billingclient:billing", version.ref = "androidBilling" } +#firebase-perf = { module = "com.google.firebase:firebase-perf" } +#google-firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" } +#google-firebase-messaging-ktx = { module = "com.google.firebase:firebase-messaging-ktx", version.ref = "firebaseMessagingKtxVersion" } +#google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBomVersion" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-lifecycle-lifecycle-extensions4 = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "androidxLifecycleLifecycleExtensions2" } +androidx-lifecycle-lifecycle-livedata-ktx3 = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +androidx-media3-media3-exoplayer-rtsp2 = { module = "androidx.media3:media3-exoplayer-rtsp", version.ref = "androidxMedia3ExoplayerRtspVersion" } +androidx-media3-media3-exoplayer-hls2 = { module = "androidx.media3:media3-exoplayer-hls", version.ref = "androidxMedia3ExoplayerHlsVersion" } +androidx-media3-media3-ui2 = { module = "androidx.media3:media3-ui", version.ref = "androidxMedia3UiVersion" } +androidx-media3-media3-exoplayer-dash2 = { module = "androidx.media3:media3-exoplayer-dash", version.ref = "androidxMedia3ExoplayerDashVersion" } +androidx-media3-media3-exoplayer2 = { module = "androidx.media3:media3-exoplayer", version.ref = "androidxMedia3ExoplayerVersion" } +com-getkeepsafe-relinker-relinker = { module = "com.getkeepsafe.relinker:relinker", version.ref = "getkeepsafeRelinker" } +com-google-android-flexbox-flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "googleFlexbox" } +com-github-bumptech-glide-glide2 = { module = "com.github.bumptech.glide:glide", version.ref = "githubGlideVersion" } +com-tencent-mmkv = { module = "com.tencent:mmkv", version.ref = "tencentMmkv" } +io-github-cymchad-baserecyclerviewadapterhelper4 = { module = "io.github.cymchad:BaseRecyclerViewAdapterHelper4", version.ref = "githubBaserecyclerviewadapterhelper4Version" } +io-github-youth5201314-banner = { module = "io.github.youth5201314:banner", version.ref = "githubBannerVersion" } +com-github-wuao-flycotablayout2 = { module = "com.github.wuao:FlycoTabLayout2", version.ref = "githubFlycotablayout2Version" } +material = { module = "com.google.android.material:material", version.ref = "material" } +androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } +org-greenrobot-eventbus3 = { module = "org.greenrobot:eventbus", version.ref = "orgGreenrobotEventbus" } +scwang90-refresh-footer-classics = { module = "io.github.scwang90:refresh-footer-classics", version.ref = "refreshFooterClassicsVersion" } +scwang90-refresh-header-material = { module = "io.github.scwang90:refresh-header-material", version.ref = "refreshHeaderMaterialVersion" } +scwang90-refresh-layout-kernel = { module = "io.github.scwang90:refresh-layout-kernel", version.ref = "refreshLayoutKernelVersion" } +shapeblurview = { module = "com.github.centerzx:ShapeBlurView", version.ref = "shapeblurview" } +squareup-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "squareupConverterGson" } +squareup-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "squareupRetrofit" } +squareup-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "squareupLoggingInterceptor" } +squareup-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "squareupOkhttp" } +utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" } +play-services-ads-identifier = { group = "com.google.android.gms", name = "play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e935866 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,8 @@ +#Wed Aug 27 11:01:55 CST 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +#distributionUrl=file\:/Users/raoqian156/Downloadsgradle-8.9-bin.zip +distributionUrl=https\://mirrors.aliyun.com/gradle/distributions/v8.9.0/gradle-8.9-bin.zip +#/Users/raoqian156/Downloads +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..65fdac2 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Wed Sep 17 15:07:17 CST 2025 +sdk.dir=/Users/raoqian156/Library/Android/sdk diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..6f3733f --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,35 @@ +pluginManagement { + repositories { + maven { url = uri("https://maven.aliyun.com/repository/releases") } + maven { url = uri("https://maven.aliyun.com/repository/google") } + maven { url = uri("https://maven.aliyun.com/repository/central") } + maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") } + maven { url = uri("https://maven.aliyun.com/repository/public") } + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + maven { url = uri("https://maven.aliyun.com/repository/releases") } + maven { url = uri("https://maven.aliyun.com/repository/google") } + maven { url = uri("https://maven.aliyun.com/repository/central") } + maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") } + maven { url = uri("https://maven.aliyun.com/repository/public") } + google() + mavenCentral() + maven { url = uri("https://jitpack.io") } + } +} + +rootProject.name = "Nebuluxe" +include(":app") + \ No newline at end of file