初次代码上传

This commit is contained in:
raoqian 2025-09-17 18:35:26 +08:00
commit 058e46bae8
228 changed files with 11578 additions and 0 deletions

109
app/build.gradle.kts Normal file
View File

@ -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")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -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

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application
android:name=".basics.MyApplication"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/net"
android:theme="@style/Theme.Nebuluxe"
tools:targetApi="31">
<provider
android:name=".basics.MyContentProvider"
android:authorities="${applicationId}.mycontentprovider"
android:exported="false" />
<activity
android:name=".main.SplashActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".main.MainActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter
android:autoVerify="true"
tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="nebuluxe" />
</intent-filter>
</activity>
<activity android:name="com.jia.er.nebuluxe.app.video.PlayerDetailActivity" />
<activity android:name="com.jia.er.nebuluxe.app.me.AboutActivity" />
<activity android:name="com.jia.er.nebuluxe.app.me.SettingActivity" />
<activity android:name="com.jia.er.nebuluxe.app.home.HotActivity" />
<activity android:name="com.jia.er.nebuluxe.app.home.RankActivity" />
<activity android:name="com.jia.er.nebuluxe.app.home.FreshActivity" />
<activity android:name="com.jia.er.nebuluxe.app.home.GenresActivity" />
</application>
</manifest>

View File

@ -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<BV : ViewBinding> : 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<AppCompatTextView>(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<AppCompatTextView>(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<TextView>(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)
}
}

View File

@ -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<VB : ViewBinding> : 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<AppCompatTextView>(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<TextView>(R.id.tv_loading_text)?.text = message
}
loadingDialog?.show()
}
protected open fun hideLoading() {
loadingDialog?.takeIf { it.isShowing }?.dismiss()
loadingDialog = null
}
}

View File

@ -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
}

View File

@ -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!")
// }
// }
}

View File

@ -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<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
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<out String>?): Int {
return 0
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package com.jia.er.nebuluxe.app.data
data class BaseEventBus<T>(val code: String, val data: T)

View File

@ -0,0 +1,6 @@
package com.jia.er.nebuluxe.app.data
data class BaseRes<T>(
val code: Int,
val msg: String,
val data: T?)

View File

@ -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
)

View File

@ -0,0 +1,11 @@
package com.jia.er.nebuluxe.app.data
class CategoriesDataRes(
val list: ArrayList<Item8>
) {
data class Item8(
val id: Int,
val name: String
)
}

View File

@ -0,0 +1,24 @@
package com.jia.er.nebuluxe.app.data
data class CollectionRes(
val list: List<CollectionData>,
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
)
}

View File

@ -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
)

View File

@ -0,0 +1,5 @@
package com.jia.er.nebuluxe.app.data
data class CreateOrderRes(
val order_code: String
)

View File

@ -0,0 +1,18 @@
package com.jia.er.nebuluxe.app.data
class CustomerBuyRecordsRes(
val list: List<Data>
) {
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
)
}

View File

@ -0,0 +1,20 @@
package com.jia.er.nebuluxe.app.data
data class CustomerOrderRes(
val list: List<Item0>,
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
)
}

View File

@ -0,0 +1,28 @@
package com.jia.er.nebuluxe.app.data
data class DetailsRecommendRes(
val brief: String,
val description: String,
val list: List<Item>,
val tag: String,
val title: String
) {
data class Item(
val all_coins: Int,
val buy_type: Int,
val collect_total: Int,
val description: String,
val episode_total: Int,
val horizontally_img: String,
val video_url: String,
val id: Int,
val image_url: String,
val name: String,
val process: Int,
val short_id: Int,
val short_play_id: Int?,
val tag_type: String,
val watch_total: Int
)
}

View File

@ -0,0 +1,30 @@
package com.jia.er.nebuluxe.app.data
class ExampleKeywordDataRes(
val list: List<KeywordData>,
) {
data class KeywordData(
val all_coins: Int,
val buy_type: Int,
val categoryList: List<Category>,
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
)
}

View File

@ -0,0 +1,3 @@
package com.jia.er.nebuluxe.app.data
data class FbNotificationReq(val is_open_notice: String)

View File

@ -0,0 +1,24 @@
package com.jia.er.nebuluxe.app.data
data class FreeSeriesMoreRes(
val list: List<Item9>
) {
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
)
}

View File

@ -0,0 +1,26 @@
package com.jia.er.nebuluxe.app.data
class HistoryDataRes(
val list: List<Data>,
val pagination: Pagination
) {
data class Data(
val description: String,
val episode_total: Int,
val category: List<String>,
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
)
}

View File

@ -0,0 +1,30 @@
package com.jia.er.nebuluxe.app.data
data class HomeBannerAndNineSquarepRes(
val arrangement: String,
val list: List<Item0>,
val title: String
) {
data class Item0(
val all_coins: Int,
val buy_type: Int,
val category: List<String>,
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
)
}

View File

@ -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<String>
)

View File

@ -0,0 +1,28 @@
package com.jia.er.nebuluxe.app.data
class HomeBestSellersData : ArrayList<HomeBestSellersData.HomeBestSellersDataItem>() {
data class HomeBestSellersDataItem(
val all_coins: String,
val buy_type: Int,
val categoryList: List<Category>,
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
)
}

View File

@ -0,0 +1,13 @@
package com.jia.er.nebuluxe.app.data
import com.google.gson.JsonElement
class HomeModuleBean(
val list: List<RecommandDataBean>,
) {
data class RecommandDataBean(
val module_key :String,
val data: JsonElement
)
}

View File

@ -0,0 +1,25 @@
package com.jia.er.nebuluxe.app.data
class HomeNewShortPlayNoPaginateRes(
val list: List<Item8>
) {
data class Item8(
val all_coins: Int,
val buy_type: Int,
val category: List<String>,
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
)
}

View File

@ -0,0 +1,61 @@
package com.jia.er.nebuluxe.app.data
class HomeNewShortPlayRes(
val category_list: List<Category>,
val pagination: Pagination,
val ranking_list: List<Ranking>,
val short_play_list: MutableList<ShortPlay>
) {
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<String>,
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<Ranking>,
var category_list: List<Category>
)
}

View File

@ -0,0 +1,25 @@
package com.jia.er.nebuluxe.app.data
class HomeRankingRes(
val list: List<Item7>
) {
data class Item7(
val all_coins: Int,
val buy_type: Int,
val category: List<String>,
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
)
}

View File

@ -0,0 +1,30 @@
package com.jia.er.nebuluxe.app.data
data class HomeTopRes(
val category: List<Category>,
val hotData: List<HotData>
) {
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
)
}

View File

@ -0,0 +1,25 @@
package com.jia.er.nebuluxe.app.data
data class HomeTopWeekRes(
val list: List<Item8>
){
data class Item8(
val all_coins: String,
val buy_type: Int,
val category: List<String>,
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
)}

View File

@ -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?
)
}

View File

@ -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
)

View File

@ -0,0 +1,6 @@
package com.jia.er.nebuluxe.app.data
data class LoginRes(
val customer_id: String,
val token: String
)

View File

@ -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
)

View File

@ -0,0 +1,5 @@
package com.jia.er.nebuluxe.app.data
data class NoticeNumRes(
val feedback_notice_num: Int
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -0,0 +1,68 @@
package com.jia.er.nebuluxe.app.data
class PaySettingRes(
val list_coins: List<Coins>,
val sort: List<String>,
val list_sub_vip: List<Vip>,
) {
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,
)
}

View File

@ -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<Episode>,
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<Episode> {
override fun createFromParcel(parcel: Parcel): Episode {
return Episode(parcel)
}
override fun newArray(size: Int): Array<Episode?> {
return arrayOfNulls(size)
}
}
}
data class ShortPlayInfo(
val all_coins: Int,
val buy_type: Int,
var collect_total: Int,
val description: String,
val category: ArrayList<String>?,
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<ShortPlayInfo> {
override fun createFromParcel(parcel: Parcel): ShortPlayInfo {
return ShortPlayInfo(parcel)
}
override fun newArray(size: Int): Array<ShortPlayInfo?> {
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
)
}

View File

@ -0,0 +1,39 @@
package com.jia.er.nebuluxe.app.data
data class RecommendDataRes(
val list: List<Data>,
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
)
}

View File

@ -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
)

View File

@ -0,0 +1,14 @@
package com.jia.er.nebuluxe.app.data
class RewardCoinsRes(val list: List<ExampleRewardCoinsResItem>) {
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
)
}

View File

@ -0,0 +1,32 @@
package com.jia.er.nebuluxe.app.data
class ShortListCategoryDataInfo(
val list: List<CategoryListData>,
) {
data class CategoryListData(
val id: Int,
val category_name: String,
val short_play_list: List<ShortPlayListData>
)
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
)
}

View File

@ -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
}
}

View File

@ -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?
)

View File

@ -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()
}
}
}

View File

@ -0,0 +1,4 @@
package com.jia.er.nebuluxe.app.data
data class UserRegisterRes(val customer_id: String,
val token: String)

View File

@ -0,0 +1,29 @@
package com.jia.er.nebuluxe.app.data
class VideoListDataRes(
val list: List<VideoListData>,
) {
data class VideoListData(
val all_coins: Int,
val buy_type: Int,
val categoryList: List<Category>,
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
)
}

View File

@ -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<ActivityFreshBinding>() {
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))
}
}
}
}

View File

@ -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<HomeRankingRes.Item7, QuickViewHolder>(),
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<AppCompatImageView>(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)
}
}

View File

@ -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<ActivityFreshBinding>() {
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))
}
}
}
}

View File

@ -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<ShortListCategoryDataInfo.CategoryListData, QuickViewHolder>(),
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<RecyclerView>(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)
}
}

View File

@ -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<ShortListCategoryDataInfo.ShortPlayListData, QuickViewHolder>(),
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)
}
}

View File

@ -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<HomeBannerAndNineSquarepRes.Item0?>?) :
BannerAdapter<HomeBannerAndNineSquarepRes.Item0?, HomeBannerAdapter.BannerViewHolder?>(
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<AppCompatImageView>(R.id.iv_icon_banner)
val tv_name = holder?.view?.findViewById<AppCompatTextView>(R.id.tv_name)
val tv_tag = holder?.view?.findViewById<AppCompatTextView>(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
)
}

View File

@ -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<HomeTopWeekRes.Item8?>?) :
BannerAdapter<HomeTopWeekRes.Item8?, HomeBannerBottomAdapter.BannerViewHolder?>(
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<AppCompatImageView>(R.id.iv_icon_banner)
val tv_name = holder?.view?.findViewById<AppCompatTextView>(R.id.tv_name)
val tv_tag = holder?.view?.findViewById<AppCompatTextView>(R.id.tv_tag)
val tv_des = holder?.view?.findViewById<AppCompatTextView>(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
)
}

View File

@ -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<RecommendDataRes.Data, QuickViewHolder>() {
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)
}
}

View File

@ -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<FragmentHomeBinding>() {
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<String> = 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<HomeBannerBean>::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<HomeBannerBean>::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))
}
}
}

View File

@ -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<HomeBannerBean, QuickViewHolder>() {
var currentPosition = 0
override fun onBindViewHolder(
holder: QuickViewHolder,
position: Int,
item: HomeBannerBean?
) {
val view = holder.getView<ShapeableImageView>(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)
}
}

View File

@ -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<FragmentHotBinding>() {
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))
}
}
}
}

View File

@ -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<VideoListDataRes.VideoListData, QuickViewHolder>() {
override fun onBindViewHolder(
holder: QuickViewHolder,
position: Int,
item: VideoListDataRes.VideoListData?
) {
val posterView = holder.getView<PosterStyleImageView>(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)
}
}

View File

@ -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<ActivityRankingsBinding>() {
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))
}
}
}
}

View File

@ -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<HomeRankingRes.Item7, QuickViewHolder>(),
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<AppCompatTextView>(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)
}
}

View File

@ -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<FragmentReelsBinding>(),
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<FrameLayout>(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()
}
}

View File

@ -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<FragmentSavedBinding>() {
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))
}
}
}
}

View File

@ -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<ActivityMainBinding>() {
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<CustomTabEntity>()
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<AppCompatTextView>(R.id.example_tv_unfavorite)
// val iv_close_notification =
// notificationDialog?.findViewById<AppCompatImageView>(R.id.iv_close_notification)
// val example_open =
// notificationDialog?.findViewById<AppCompatTextView>(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<Fragment> = 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<String?, String?> {
//
// 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<LoginResult> {
// override fun onSuccess(loginResult: LoginResult) {
// val enableButtons = AccessToken.getCurrentAccessToken() != null
// if (enableButtons) {
// val mGraphRequest = GraphRequest.newMeRequest(
// loginResult.accessToken
// ) { jsonObject, response ->
// if (response!!.error != null) {
// toast("Facebook login exception.${response.error?.exception.toString()}")
// 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<AppCompatTextView>(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)
}
}

View File

@ -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<ActivitySplashBinding>() {
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)
}
}

View File

@ -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<ActivityAboutBinding>() {
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)
}
}

View File

@ -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<FragmentMeBinding>() {
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)
}
}

View File

@ -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<ActivitySettingBinding>() {
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<AppCompatTextView>(R.id.example_tv_think_again)
// val tvUnfavorite =
// exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_unfavorite)
// val tvTitle =
// exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_title)
// val tvContent =
// exampleUnFavoriteDialog.findViewById<AppCompatTextView>(R.id.example_tv_content)
// tvThinkAgain.text = "Cancel"
// tvUnfavorite.text = "Confirm"
// tvTitle.text = "Tips"
// tvContent.text = "Are you sure you want to 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()
}
}

View File

@ -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<BaseRes<UserRegisterRes>>
@GET("customer/info")
fun getInfo(): Call<BaseRes<UserInfoRes>>
@POST("homeTop")
fun homeTop(): Call<BaseRes<HomeTopRes>>
@POST("newShortPlay")
fun newShortPlay(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<HomeNewShortPlayRes>>
@GET("myCollections")
fun getCollections(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<CollectionRes>>
@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<BaseRes<PlayerDetailDataRes>>
@POST("newShortPlayNoPaginate")
fun newShortPlayNoPaginate(): Call<BaseRes<HomeNewShortPlayNoPaginateRes>>
@FormUrlEncoded
@POST("createHistory")
fun createHistory(
@Field("short_play_id") short_play_id: Int,
@Field("video_id") video_id: Int,
): Call<BaseRes<Any>>
@GET("getRecommands")
fun getRecommands(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int,
@Query("revolution") revolution: String,
): Call<BaseRes<RecommendDataRes>>
@FormUrlEncoded
@POST("collect")
fun collect(
@Field("short_play_id") short_play_id: Int,
@Field("video_id") video_id: Int
): Call<BaseRes<Any>>
@FormUrlEncoded
@POST("cancelCollect")
fun cancelCollect(
@Field("short_play_id") short_play_id: Int,
@Field("video_id") video_id: Int
): Call<BaseRes<Any>>
@GET("myHistorys")
fun myHistorys(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<HistoryDataRes>>
@GET("search")
fun getVideoList(@Query("search") search: String): Call<BaseRes<VideoListDataRes>>
@GET("videoList")
fun getVideoList(
@Query("current_page") current_page: Int,
@Query("category_id") category_id: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<VideoListDataRes>>
@POST("homeRanking")
fun homeRanking(@Query("type") type: String): Call<BaseRes<HomeRankingRes>>
@POST("uploadHistorySeconds")
fun uploadHistorySeconds(
@Body uploadHistoryReq: UploadHistoryReq
): Call<BaseRes<Any>>
@GET("freeShorPlayListNoPaginate")
fun freeMoreVideo(): Call<BaseRes<FreeSeriesMoreRes>>
@GET("getDetailsRecommand")
fun getDetailsRecommand(
): Call<BaseRes<DetailsRecommendRes>>
@GET("search/hots")
fun hots(): Call<BaseRes<ExampleKeywordDataRes>>
@POST("search/click")
fun click(
@Query("short_play_id") short_play_id: Int,
): Call<BaseRes<Any>>
@GET("search")
fun keyword(@Query("search") search: String): Call<BaseRes<ExampleKeywordDataRes>>
@GET("home/all-modules")
fun allModules(): Call<BaseRes<HomeModuleBean>>
@FormUrlEncoded
@POST("buy_video")
fun buyVideo(
@Field("short_play_id") short_play_id: Int,
@Field("video_id") video_id: Int,
): Call<BaseRes<BuyVideoRes>>
@GET("getCategories")
fun getCategories(): Call<BaseRes<CategoriesDataRes>>
@POST("customer/onLine")
fun onLine(
): Call<BaseRes<Any>>
@POST("customer/enterTheApp")
fun enterTheApp(): Call<BaseRes<Any>>
@POST("customer/leaveApp")
fun leaveApp(): Call<BaseRes<Any>>
@FormUrlEncoded
@POST("customer/firebaseToken")
fun firebaseToken(@Field("fcm_token") fcm_token: String): Call<BaseRes<Any>>
@FormUrlEncoded
@POST("w2aSelfAttribution")
fun w2aSelfAttribution(
@Field("data") data: String
): Call<BaseRes<Any>>
@POST("openNotify")
fun openNotify(
): Call<BaseRes<Any>>
@POST("customer/uploadNoticeStatus")
fun uploadNoticeStatus(
@Body fbNotificationReq: FbNotificationReq
): Call<BaseRes<Any>>
@FormUrlEncoded
@POST("message/sendReport")
fun sendReport(
@Field("message_id") message_id: String, @Field("title") title: String
): Call<BaseRes<Any>>
@POST("noticeNum")
fun noticeNum(): Call<BaseRes<NoticeNumRes>>
@GET("getCustomerOrder")
fun getCustomerOrder(
@Query("current_page") current_page: Int,
@Query("buy_type") buy_type: String,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<CustomerOrderRes>>
@GET("getCustomerBuyRecords")
fun getCustomerBuyRecords(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<CustomerBuyRecordsRes>>
@POST("sendCoinList")
fun sendCoinList(
@Query("current_page") current_page: Int,
@Query("page_size") page_size: Int = 10
): Call<BaseRes<RewardCoinsRes>>
@GET("paySettingsV3")
fun getPaySetting(
@Query("short_play_id") short_play_id: Int?,
@Query("short_play_video_id") short_play_video_id: Int?
): Call<BaseRes<PaySettingRes>>
@POST("createOrder")
fun createOrder(@Body createOrderReq: CreateOrderReq): Call<BaseRes<CreateOrderRes>>
@POST("googlePaid")
fun googlePaid(@Body examplePayReq: PayReq?): Call<BaseRes<PayRes>>
@POST("event/add")
suspend fun upError(@Body body: Map<String, @JvmSuppressWildcards Any>): Call<BaseRes<Any>>
@POST("customer/login")
fun login(@Body exampleLoginReq: LoginReq): Call<BaseRes<LoginRes>>
@POST("customer/logoff")
fun logoff(): Call<BaseRes<Any>>
@POST("customer/signout")
fun signout(): Call<BaseRes<LoginRes>>
@GET("highestPaymentAndHottestVideo")
fun homeHot(
@Query("buy_count_num") buycount: Int,
@Query("hottest_num") hottestnum: Int,
): Call<BaseRes<VideoListDataRes>>
@GET("categoryListAppendShortPlay")
fun userCenterRecommend(
@Query("short_play_num") shortplaynum: Int,
): Call<BaseRes<ShortListCategoryDataInfo>>
}

View File

@ -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
}
}
}

View File

@ -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<Byte>()
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()
}
}
}

View File

@ -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<Result<BaseRes<UserRegisterRes>>> = handleData {
appService.register().response()
}
fun getInfo(): LiveData<Result<BaseRes<UserInfoRes>>> = handleData {
appService.getInfo().response()
}
fun homeTop(): LiveData<Result<BaseRes<HomeTopRes>>> = handleData {
appService.homeTop()
.response()
}
fun newShortPlay(current_page: Int): LiveData<Result<BaseRes<HomeNewShortPlayRes>>> =
handleData {
appService.newShortPlay(current_page)
.response()
}
fun getCollections(current_page: Int): LiveData<Result<BaseRes<CollectionRes>>> =
handleData {
appService.getCollections(current_page).response()
}
fun getVideoDetails(
short_play_id: Int, video_id: Int, activity_id: Int, revolution: String, no_ads: Boolean?
): LiveData<Result<BaseRes<PlayerDetailDataRes>>> = handleData {
appService.getVideoDetails(
short_play_id,
video_id,
activity_id,
revolution,
no_ads
)
.response()
}
fun newShortPlayNoPaginate(): LiveData<Result<BaseRes<HomeNewShortPlayNoPaginateRes>>> =
handleData {
appService.newShortPlayNoPaginate()
.response()
}
fun doCreateHistory(
short_play_id: Int, video_id: Int
): LiveData<Result<BaseRes<Any>>> = handleData {
appService.createHistory(short_play_id, video_id).response()
}
fun getRecommands(
current_page: Int, page_size: Int, revolution: String
): LiveData<Result<BaseRes<RecommendDataRes>>> = 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<Result<BaseRes<Any>>> = 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<Result<BaseRes<Any>>> = handleData {
cancelCollect(short_play_id, video_id)
}
fun myHistorys(
current_page: Int
): LiveData<Result<BaseRes<HistoryDataRes>>> = handleData {
appService.myHistorys(current_page).response()
}
fun getVideoList(search: String): LiveData<Result<BaseRes<VideoListDataRes>>> =
handleData {
appService.getVideoList(search).response()
}
fun getVideoList(
current_page: Int,
category_id: Int
): LiveData<Result<BaseRes<VideoListDataRes>>> =
handleData {
appService.getVideoList(current_page, category_id).response()
}
fun homeRanking(type: String): LiveData<Result<BaseRes<HomeRankingRes>>> = handleData {
appService.homeRanking(type)
.response()
}
fun freeMoreVideo(
): LiveData<Result<BaseRes<FreeSeriesMoreRes>>> =
handleData {
appService.freeMoreVideo().response()
}
fun getDetailsRecommand(
): LiveData<Result<BaseRes<DetailsRecommendRes>>> =
handleData {
appService.getDetailsRecommand().response()
}
fun uploadHistorySeconds(
uploadHistoryReq: UploadHistoryReq
): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.uploadHistorySeconds(uploadHistoryReq).response()
}
fun hots(): LiveData<Result<BaseRes<ExampleKeywordDataRes>>> =
handleData {
appService.hots().response()
}
fun click(short_play_id: Int): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.click(short_play_id).response()
}
fun keyword(search: String): LiveData<Result<BaseRes<ExampleKeywordDataRes>>> =
handleData {
appService.keyword(search).response()
}
fun allModules(): LiveData<Result<BaseRes<HomeModuleBean>>> = handleData {
appService.allModules().response()
}
fun doBuyVideo(
short_play_id: Int, video_id: Int
): LiveData<Result<BaseRes<BuyVideoRes>>> = handleData {
appService.buyVideo(short_play_id, video_id).response()
}
fun getCategories(): LiveData<Result<BaseRes<CategoriesDataRes>>> =
handleData {
appService.getCategories().response()
}
fun enterTheApp(): LiveData<Result<BaseRes<Any>>> = handleData {
appService.enterTheApp().response()
}
fun leaveApp(): LiveData<Result<BaseRes<Any>>> = handleData {
appService.leaveApp().response()
}
fun onLine(
): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.onLine().response()
}
fun firebaseToken(fcm_token: String): LiveData<Result<BaseRes<Any>>> = handleData {
appService.firebaseToken(fcm_token).response()
}
fun w2aSelfAttribution(
data: String
): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.w2aSelfAttribution(data).response()
}
fun openNotify(
): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.openNotify().response()
}
fun uploadNoticeStatus(fbNotificationReq: FbNotificationReq): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.uploadNoticeStatus(fbNotificationReq)
.response()
}
fun sendReport(
message_id: String, title: String
): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.sendReport(message_id, title).response()
}
fun noticeNum(): LiveData<Result<BaseRes<NoticeNumRes>>> =
handleData {
appService.noticeNum().response()
}
fun getCustomerOrder(
current_page: Int,
buy_type: String
): LiveData<Result<BaseRes<CustomerOrderRes>>> = handleData {
appService.getCustomerOrder(current_page, buy_type).response()
}
fun getCustomerBuyRecords(current_page: Int): LiveData<Result<BaseRes<CustomerBuyRecordsRes>>> =
handleData {
appService.getCustomerBuyRecords(current_page).response()
}
fun sendCoinList(current_page: Int): LiveData<Result<BaseRes<RewardCoinsRes>>> =
handleData {
appService.sendCoinList(current_page).response()
}
fun getPaySetting(
short_play_id: Int?,
video_id: Int?
): LiveData<Result<BaseRes<PaySettingRes>>> = handleData {
appService.getPaySetting(short_play_id, video_id).response()
}
fun doGooglePaid(examplePayReq: PayReq?): LiveData<Result<BaseRes<PayRes>>> =
handleData {
appService.googlePaid(examplePayReq).response()
}
fun doCreateOrder(createOrderReq: CreateOrderReq): LiveData<Result<BaseRes<CreateOrderRes>>> =
handleData {
appService.createOrder(createOrderReq).response()
}
fun upError(body: Map<String, @JvmSuppressWildcards Any>): LiveData<Result<BaseRes<Any>>> =
handleData {
appService.upError(body).response()
}
fun doLogin(exampleLoginReq: LoginReq): LiveData<Result<BaseRes<LoginRes>>> =
handleData {
appService.login(exampleLoginReq).response()
}
fun doLogoff(): LiveData<Result<BaseRes<Any>>> = handleData {
appService.logoff().response()
}
fun doSignout(): LiveData<Result<BaseRes<LoginRes>>> = handleData {
appService.signout().response()
}
fun homeHot(buycount: Int, hottestnum: Int): LiveData<Result<BaseRes<VideoListDataRes>>> =
handleData {
appService.homeHot(buycount, hottestnum)
.response()
}
fun userCenterRecommend(): LiveData<Result<BaseRes<ShortListCategoryDataInfo>>> =
handleData {
appService.userCenterRecommend(5)
.response()
}
}

View File

@ -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<BaseRes<UserRegisterRes>>()
val userRegisterData: MutableLiveData<BaseRes<UserRegisterRes>> get() = userRegisterLiveData
fun createUserAccount(
) {
MainRequest.userRegister().observeForever { result ->
userRegisterLiveData.value = result.getOrNull()
}
}
private val infoLiveData = MutableLiveData<BaseRes<UserInfoRes>>()
val infoData: MutableLiveData<BaseRes<UserInfoRes>> get() = infoLiveData
fun getInfo() {
MainRequest.getInfo().observeForever { result ->
infoLiveData.value = result.getOrNull()
}
}
private val newShortPlayLiveData = MutableLiveData<BaseRes<HomeNewShortPlayRes>>()
val newShortPlayData: MutableLiveData<BaseRes<HomeNewShortPlayRes>> get() = newShortPlayLiveData
fun newShortPlay(current_page: Int) {
MainRequest.newShortPlay(current_page)
.observeForever {
newShortPlayLiveData.value = it.getOrNull()
}
}
private val videoDetailsLiveData = MutableLiveData<BaseRes<PlayerDetailDataRes>>()
val videoDetailsData: MutableLiveData<BaseRes<PlayerDetailDataRes>> 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<BaseRes<HomeNewShortPlayNoPaginateRes>>()
val homeNewData: MutableLiveData<BaseRes<HomeNewShortPlayNoPaginateRes>> 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<BaseRes<VideoListDataRes>>()
val searchData: MutableLiveData<BaseRes<VideoListDataRes>> get() = searchLiveData
fun getVideoList(search: String) {
MainRequest.getVideoList(search).observeForever { result ->
searchLiveData.value = result.getOrNull()
}
}
private val videoListLiveData = MutableLiveData<BaseRes<VideoListDataRes>>()
val videoListData: MutableLiveData<BaseRes<VideoListDataRes>> 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<BaseRes<HomeRankingRes>>()
val homeRankingData: MutableLiveData<BaseRes<HomeRankingRes>> get() = homeRankingLiveData
fun homeRanking(type: String) {
MainRequest.homeRanking(type)
.observeForever {
homeRankingLiveData.value = it.getOrNull()
}
}
private val freeMoreLiveData = MutableLiveData<BaseRes<FreeSeriesMoreRes>>()
val freeMoreData: MutableLiveData<BaseRes<FreeSeriesMoreRes>> get() = freeMoreLiveData
fun freeMoreVideo() {
MainRequest.freeMoreVideo().observeForever { result ->
freeMoreLiveData.value = result.getOrNull()
}
}
private val recommendLiveData = MutableLiveData<BaseRes<RecommendDataRes>>()
val recommendData: MutableLiveData<BaseRes<RecommendDataRes>> 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<BaseRes<Any>>()
val collectData: MutableLiveData<BaseRes<Any>> 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<BaseRes<Any>>()
val cancelCollectData: MutableLiveData<BaseRes<Any>> 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<BaseRes<CollectionRes>>()
val collectionsData: MutableLiveData<BaseRes<CollectionRes>> get() = collectionsLiveData
fun getCollections(current_page: Int) {
MainRequest.getCollections(current_page).observeForever { result ->
collectionsLiveData.value = result.getOrNull()
}
}
private val historysLiveData = MutableLiveData<BaseRes<HistoryDataRes>>()
val historysData: MutableLiveData<BaseRes<HistoryDataRes>> get() = historysLiveData
fun myHistorys(
current_page: Int
) {
MainRequest.myHistorys(current_page).observeForever { result ->
historysLiveData.value = result.getOrNull()
}
}
private val getDetailsRecommandLiveData = MutableLiveData<BaseRes<DetailsRecommendRes>>()
val getDetailsRecommandData: MutableLiveData<BaseRes<DetailsRecommendRes>> get() = getDetailsRecommandLiveData
fun getDetailsRecommand() {
MainRequest.getDetailsRecommand().observeForever { result ->
getDetailsRecommandLiveData.value = result.getOrNull()
}
}
fun uploadHistorySeconds(uploadHistoryReq: UploadHistoryReq) {
MainRequest.uploadHistorySeconds(uploadHistoryReq).observeForever {}
}
private val hotsLiveData = MutableLiveData<BaseRes<ExampleKeywordDataRes>>()
val hotsData: MutableLiveData<BaseRes<ExampleKeywordDataRes>> 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<BaseRes<ExampleKeywordDataRes>>()
val keywordData: MutableLiveData<BaseRes<ExampleKeywordDataRes>> get() = keywordLiveData
fun keyword(search: String) {
MainRequest.keyword(search).observeForever { result ->
keywordLiveData.value = result.getOrNull()
}
}
private val allModulesLiveData = MutableLiveData<BaseRes<HomeModuleBean>>()
val allModulesData: MutableLiveData<BaseRes<HomeModuleBean>> get() = allModulesLiveData
fun allModules() {
MainRequest.allModules().observeForever { result ->
allModulesLiveData.value = result.getOrNull()
}
}
private val buy_videoLiveData = MutableLiveData<BaseRes<BuyVideoRes>>()
val buy_videoData: MutableLiveData<BaseRes<BuyVideoRes>> 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<BaseRes<CategoriesDataRes>>()
val categoriesData: MutableLiveData<BaseRes<CategoriesDataRes>> 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<BaseRes<Any>>()
fun w2aSelfAttribution(data: String) {
MainRequest.w2aSelfAttribution(data).observeForever { result ->
w2aSelfAttributionLiveData.value = result.getOrNull()
}
}
private val openNotifyLiveData = MutableLiveData<BaseRes<Any>>()
val openNotifyData: MutableLiveData<BaseRes<Any>> get() = openNotifyLiveData
fun openNotify() {
MainRequest.openNotify().observeForever { result ->
openNotifyLiveData.value = result.getOrNull()
}
}
private val notificationLiveData = MutableLiveData<BaseRes<Any>>()
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<BaseRes<NoticeNumRes>>()
val noticeNumData: MutableLiveData<BaseRes<NoticeNumRes>> get() = noticeNumLiveData
fun noticeNum() {
MainRequest.noticeNum().observeForever { result ->
noticeNumLiveData.value = result.getOrNull()
}
}
private val getPaySettingLiveData = MutableLiveData<BaseRes<PaySettingRes>>()
val getPaySettingData: MutableLiveData<BaseRes<PaySettingRes>> 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<BaseRes<PayRes>>()
val restorePaidData: MutableLiveData<BaseRes<PayRes>> get() = restorePaidLiveData
fun restorePaid(examplePayReq: PayReq?) {
MainRequest.doGooglePaid(examplePayReq).observeForever { result ->
restorePaidLiveData.value = result.getOrNull()
}
}
private val createOrderLiveData = MutableLiveData<BaseRes<CreateOrderRes>>()
val createOrderData: MutableLiveData<BaseRes<CreateOrderRes>> get() = createOrderLiveData
fun createOrder(createOrderReq: CreateOrderReq) {
MainRequest.doCreateOrder(createOrderReq).observeForever { result ->
createOrderLiveData.value = result.getOrNull()
}
}
private val googlePaidLiveData = MutableLiveData<BaseRes<PayRes>>()
val googlePaidData: MutableLiveData<BaseRes<PayRes>> get() = googlePaidLiveData
fun googlePaid(examplePayReq: PayReq?) {
MainRequest.doGooglePaid(examplePayReq).observeForever { result ->
googlePaidLiveData.value = result.getOrNull()
}
}
private val customerOrderLiveData = MutableLiveData<BaseRes<CustomerOrderRes>>()
val customerOrderData: MutableLiveData<BaseRes<CustomerOrderRes>> 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<BaseRes<CustomerBuyRecordsRes>>()
val customerBuyRecordsData: MutableLiveData<BaseRes<CustomerBuyRecordsRes>> get() = customerBuyRecordsLiveData
fun getCustomerBuyRecords(current_page: Int) {
MainRequest.getCustomerBuyRecords(current_page).observeForever { result ->
customerBuyRecordsLiveData.value = result.getOrNull()
}
}
private val sendCoinListLiveData =
MutableLiveData<BaseRes<RewardCoinsRes>>()
val sendCoinListData: MutableLiveData<BaseRes<RewardCoinsRes>> get() = sendCoinListLiveData
fun sendCoinList(current_page: Int) {
MainRequest.sendCoinList(current_page).observeForever { result ->
sendCoinListLiveData.value = result.getOrNull()
}
}
fun upError(body: Map<String, @JvmSuppressWildcards Any>) {
MainRequest.upError(body).observeForever {}
}
private val loginLiveData = MutableLiveData<BaseRes<LoginRes>>()
val loginData: MutableLiveData<BaseRes<LoginRes>> get() = loginLiveData
fun doLogin(exampleLoginReq: LoginReq) {
MainRequest.doLogin(exampleLoginReq).observeForever { result ->
loginLiveData.value = result.getOrNull()
}
}
private val logoffLiveData = MutableLiveData<BaseRes<Any>>()
val logoffData: MutableLiveData<BaseRes<Any>> get() = logoffLiveData
fun doLogoff() {
MainRequest.doLogoff().observeForever { result ->
logoffLiveData.value = result.getOrNull()
}
}
private val signoutLiveData = MutableLiveData<BaseRes<LoginRes>>()
val signoutData: MutableLiveData<BaseRes<LoginRes>> get() = signoutLiveData
fun doSignout() {
MainRequest.doSignout().observeForever { result ->
signoutLiveData.value = result.getOrNull()
}
}
private val homeHotLiveData = MutableLiveData<BaseRes<VideoListDataRes>?>()
val homeHotData: MutableLiveData<BaseRes<VideoListDataRes>?> get() = homeHotLiveData
fun homeHot(buycount: Int, hottestnum: Int) {
MainRequest.homeHot(buycount, hottestnum)
.observeForever {
homeHotLiveData.value = it.getOrNull()
}
}
private val userCenterRecommendLiveData = MutableLiveData<BaseRes<ShortListCategoryDataInfo>?>()
val userCenterRecommendData: MutableLiveData<BaseRes<ShortListCategoryDataInfo>?> get() = userCenterRecommendLiveData
fun userCenterRecommend(){
MainRequest.userCenterRecommend()
.observeForever {
userCenterRecommendLiveData.value = it.getOrNull()
}
}
}

View File

@ -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 <P> build(serviceClass: Class<P>): 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 <T> Call<T>.response(): T {
return suspendCoroutine { continuation ->
enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
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 <T> handleData(apiCall: suspend () -> BaseRes<T>): LiveData<Result<BaseRes<T>>> {
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);
}
}

View File

@ -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
}
}

View File

@ -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<Renderer>
) {
out.add(FfmpegAudioRenderer())
super.buildAudioRenderers(
context,
extensionRendererMode,
mediaCodecSelector,
enableDecoderFallback,
audioSink,
eventHandler,
eventListener,
out
)
}
}

View File

@ -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()
}

View File

@ -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<View>(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()
}
}

View File

@ -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)
}

View File

@ -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<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
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)
}
}

View File

@ -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) {}
}

View File

@ -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
}
}

View File

@ -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<String> = mutableListOf()
fun setData(data: MutableList<String>) {
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()
}
}

View File

@ -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)
}
}

View File

@ -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<AppCompatTextView>(R.id.example_tv_think_again)
val iv_close_notification =
exampleUnFavoriteDialog.findViewById<AppCompatImageView>(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()
}
}

View File

@ -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<String> {
val string = getMMKV().getString(CONSTANTS_SEARCH_STRINGExample, "[]")
return Gson().fromJson(string, Array<String>::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<PayReq> {
try {
val string = getMMKV().getString(Constants.CONSTANTS_examplePayReq, "[]")
if (null != string && "[]" != string) {
return Gson().fromJson(string, Array<PayReq>::class.java).toMutableList()
}
} catch (e: Exception) {
e.printStackTrace()
getMMKV().putString(
Constants.CONSTANTS_examplePayReq,
"[]"
)
}
return mutableListOf()
}
}

View File

@ -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
}
}

View File

@ -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<String, Any>()
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()
}

View File

@ -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<DetailsRecommendRes.Item?>?, context: Context) :
BannerAdapter<DetailsRecommendRes.Item?, NewRecommendBannerAdapter.BannerViewHolder?>(items) {
var currentPlayingPosition = -1
private var context: Context? = null
val playerMap = mutableMapOf<Int, ExoPlayer>()
val imageMap = mutableMapOf<Int, AppCompatImageView>()
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<PlayerView>(R.id.player_view)
val imageView = holder?.view?.findViewById<AppCompatImageView>(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()
}
}

View File

@ -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<ActivityPlayDetailBinding>(),
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<FrameLayout>(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()
}
}

View File

@ -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<PlayerDetailDataRes.Episode, QuickViewHolder>() {
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)
}
}

View File

@ -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<PlayerDetailDataRes.Episode, QuickViewHolder>() {
var currentPosition = -1
override fun onBindViewHolder(
holder: QuickViewHolder,
position: Int,
item: PlayerDetailDataRes.Episode?
) {
val view = holder.getView<AppCompatTextView>(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)
}
}

View File

@ -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<PlayerDetailDataRes.Episode>? =
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)
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

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