From 0c40f03fd47acd0ef28d8edaa24882949f75981c Mon Sep 17 00:00:00 2001 From: Li ZongYing Date: Mon, 4 Dec 2023 19:36:29 +0800 Subject: [PATCH] init --- .gitignore | 15 ++ .idea/.gitignore | 3 + .idea/.name | 1 + .idea/compiler.xml | 6 + .idea/deploymentTargetDropDown.xml | 17 ++ .idea/gradle.xml | 20 ++ .idea/kotlinc.xml | 9 + .idea/misc.xml | 10 + app/.gitignore | 1 + app/build.gradle | 43 ++++ app/proguard-rules.pro | 21 ++ app/src/main/AndroidManifest.xml | 36 ++++ .../java/com/lizongying/mytv/CardPresenter.kt | 105 ++++++++++ app/src/main/java/com/lizongying/mytv/Info.kt | 10 + .../java/com/lizongying/mytv/MainActivity.kt | 168 ++++++++++++++++ .../java/com/lizongying/mytv/MainFragment.kt | 171 ++++++++++++++++ .../lizongying/mytv/PlaybackControlGlue.kt | 53 +++++ .../com/lizongying/mytv/PlaybackFragment.kt | 75 +++++++ app/src/main/java/com/lizongying/mytv/TV.kt | 25 +++ .../main/java/com/lizongying/mytv/TVList.kt | 115 +++++++++++ app/src/main/res/drawable/appreciate.jpg | Bin 0 -> 17984 bytes app/src/main/res/drawable/tv.png | Bin 0 -> 2438 bytes app/src/main/res/layout/activity_main.xml | 9 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes app/src/main/res/values/colors.xml | 3 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/styles.xml | 11 ++ app/src/main/res/values/themes.xml | 4 + build.gradle | 6 + gradle.properties | 25 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle | 16 ++ 39 files changed, 1261 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/lizongying/mytv/CardPresenter.kt create mode 100644 app/src/main/java/com/lizongying/mytv/Info.kt create mode 100644 app/src/main/java/com/lizongying/mytv/MainActivity.kt create mode 100644 app/src/main/java/com/lizongying/mytv/MainFragment.kt create mode 100644 app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt create mode 100644 app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt create mode 100644 app/src/main/java/com/lizongying/mytv/TV.kt create mode 100644 app/src/main/java/com/lizongying/mytv/TVList.kt create mode 100644 app/src/main/res/drawable/appreciate.jpg create mode 100644 app/src/main/res/drawable/tv.png create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..d0fb6256 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +My TV \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..b589d56e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..57272bc1 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 00000000..ae388c2a --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 00000000..183eebe5 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..0ad17cbd --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..b10e3e8b --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.lizongying.mytv' + compileSdk 33 + + defaultConfig { + applicationId "com.lizongying.mytv" + minSdk 23 + targetSdk 33 + versionCode 1 + versionName "1.0" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget=17 + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.11.0-beta02' + implementation 'androidx.leanback:leanback:1.0.0' + implementation 'com.github.bumptech.glide:glide:4.11.0' + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..92c788ad --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt new file mode 100644 index 00000000..7e5067b9 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt @@ -0,0 +1,105 @@ +package com.lizongying.mytv + +import android.graphics.Bitmap +import android.media.MediaMetadataRetriever +import android.util.Log +import android.view.ContextThemeWrapper +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.leanback.widget.ImageCardView +import androidx.leanback.widget.Presenter +import androidx.lifecycle.LifecycleCoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlin.properties.Delegates + + +/** + * A CardPresenter is used to generate Views and bind Objects to them on demand. + * It contains an ImageCardView. + */ +class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Presenter() { + + override fun onCreateViewHolder(parent: ViewGroup): ViewHolder { + Log.d(TAG, "onCreateViewHolder") + + val cardView = object : + ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) { + override fun setSelected(selected: Boolean) { +// updateCardBackgroundColor(this) + super.setSelected(selected) + } + } + + cardView.isFocusable = true + cardView.isFocusableInTouchMode = true + return ViewHolder(cardView) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) { + val tv = item as TV + val cardView = viewHolder.view as ImageCardView + + Log.d(TAG, "onBindViewHolder") + if (tv.videoUrl != null) { + cardView.titleText = tv.title + cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) + cardView.tag = tv.videoUrl + +// lifecycleScope.launch(Dispatchers.IO) { +// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) } +// +// withContext(Dispatchers.Main) { +// cardView.mainImageView.setImageBitmap(videoThumbnail) +// } +// } + } + } + + override fun onUnbindViewHolder(viewHolder: ViewHolder) { + Log.d(TAG, "onUnbindViewHolder") + val cardView = viewHolder.view as ImageCardView + // Remove references to images so that the garbage collector can free up memory + cardView.badgeImage = null + cardView.mainImage = null + } + + private fun updateCardBackgroundColor(view: ImageCardView) { + val currentTag = view.tag + lifecycleScope.launch(Dispatchers.IO) { + delay(1000) + if (view.isSelected && view.tag != null && currentTag == view.tag) { + val videoThumbnail = view.tag.toString().let { getVideoThumbnail(it) } + withContext(Dispatchers.Main) { + if (view.isSelected && currentTag == view.tag) { + view.mainImageView.setImageBitmap(videoThumbnail) + } + } + } + } + } + + private fun getVideoThumbnail(url: String): Bitmap? { + val mediaMetadataRetriever = MediaMetadataRetriever() + try { + val map = HashMap() + mediaMetadataRetriever.setDataSource(url, map) + return mediaMetadataRetriever.frameAtTime + } catch (e: Exception) { + e.printStackTrace() + } finally { + mediaMetadataRetriever.release() + } + return null + } + + companion object { + private const val TAG = "CardPresenter" + + private const val CARD_WIDTH = 313 + + private const val CARD_HEIGHT = 176 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/Info.kt b/app/src/main/java/com/lizongying/mytv/Info.kt new file mode 100644 index 00000000..07f7d6c7 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/Info.kt @@ -0,0 +1,10 @@ +package com.lizongying.mytv + +import java.io.Serializable + + +data class Info( + var rowPosition: Int = 0, + var itemPosition: Int = 0, + var item: TV? = null, +) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/MainActivity.kt b/app/src/main/java/com/lizongying/mytv/MainActivity.kt new file mode 100644 index 00000000..3783c6f2 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/MainActivity.kt @@ -0,0 +1,168 @@ +package com.lizongying.mytv + +import android.app.AlertDialog +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.KeyEvent +import android.widget.ImageView +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity + +/** + * Loads [MainFragment]. + */ +class MainActivity : FragmentActivity() { + + private val playbackFragment = PlaybackFragment() + private val mainFragment = MainFragment() + + private val handler = Handler(Looper.getMainLooper()) + private var hideTask: Runnable? = null + + private var doubleBackToExitPressedOnce = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .add(R.id.main_browse_fragment, playbackFragment) + .add(R.id.main_browse_fragment, mainFragment) + .commit() + + hideTask = Runnable { + Log.i(TAG, "hideTask") + hideMainFragment() + } + } + } + + fun play(tv: TV) { + playbackFragment.play(tv) + } + + fun prev() { + Log.i(TAG, "prev") + mainFragment.prev() + } + + fun next() { + Log.i(TAG, "next") + mainFragment.next() + } + + fun switchMainFragment() { + val transaction = supportFragmentManager.beginTransaction() + + if (mainFragment.isHidden) { + focusMainFragment() + transaction.show(mainFragment) + } else { + transaction.hide(mainFragment) + } + + transaction.commit() + } + + fun focusMainFragment() { + mainFragment.focus() + } + + fun fragmentIsHidden(): Boolean { + return mainFragment.isHidden + } + + fun hideMainFragment() { + if (!mainFragment.isHidden) { + supportFragmentManager.beginTransaction() + .hide(mainFragment) + .commit() + } + } + + fun startHideTask(delayMillis: Long) { + hideTask?.let { handler.postDelayed(it, delayMillis) } + } + + fun cancelHideTask() { + hideTask?.let { handler.removeCallbacks(it) } + } + + override fun onDestroy() { + cancelHideTask() + super.onDestroy() + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + when (keyCode) { + KeyEvent.KEYCODE_BACK -> { + if (doubleBackToExitPressedOnce) { + super.onBackPressed() + return true + } + + hideMainFragment() + this.doubleBackToExitPressedOnce = true + Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show() + + Handler(Looper.getMainLooper()).postDelayed({ + doubleBackToExitPressedOnce = false + }, 2000) + return true + } + + KeyEvent.KEYCODE_MENU -> { + val imageView = ImageView(this) + val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate) + imageView.setImageDrawable(drawable) + + val builder: AlertDialog.Builder = AlertDialog.Builder(this) + builder + .setMessage("地址: https://github.com/lizongying/my-tv/") + .setView(imageView) + + val dialog: AlertDialog = builder.create() + dialog.show() + return true + } + + KeyEvent.KEYCODE_DPAD_CENTER -> { + switchMainFragment() + } + + KeyEvent.KEYCODE_DPAD_UP -> { + if (mainFragment.isHidden) { + prev() + } + } + + KeyEvent.KEYCODE_DPAD_DOWN -> { + if (mainFragment.isHidden) { + next() + } + } + + KeyEvent.KEYCODE_DPAD_LEFT -> { + if (mainFragment.isHidden) { + prev() + } + } + + KeyEvent.KEYCODE_DPAD_RIGHT -> { + if (mainFragment.isHidden) { + next() + } + } + } + + return super.onKeyDown(keyCode, event) + } + + companion object { + private const val TAG = "MainActivity" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/MainFragment.kt b/app/src/main/java/com/lizongying/mytv/MainFragment.kt new file mode 100644 index 00000000..33a2011d --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/MainFragment.kt @@ -0,0 +1,171 @@ +package com.lizongying.mytv + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.leanback.app.BrowseSupportFragment +import androidx.leanback.widget.ArrayObjectAdapter +import androidx.leanback.widget.HeaderItem +import androidx.leanback.widget.ListRow +import androidx.leanback.widget.ListRowPresenter +import androidx.leanback.widget.ListRowPresenter.SelectItemViewHolderTask +import androidx.leanback.widget.OnItemViewClickedListener +import androidx.leanback.widget.OnItemViewSelectedListener +import androidx.leanback.widget.Presenter +import androidx.leanback.widget.Row +import androidx.leanback.widget.RowPresenter +import androidx.lifecycle.lifecycleScope + +/** + * Loads a grid of cards with movies to browse. + */ +class MainFragment : BrowseSupportFragment() { + + var itemPosition: Int = 0 + + private val list2: MutableList = mutableListOf() + + override fun onActivityCreated(savedInstanceState: Bundle?) { + Log.i(TAG, "onCreate") + super.onActivityCreated(savedInstanceState) + + setupUIElements() + + loadRows() + + setupEventListeners() + } + + fun show() { + if (!view?.isVisible!!) { + view?.visibility = View.VISIBLE + } + } + + private fun setupUIElements() { + // set fastLane (or headers) background color + brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background) +// headersState = HEADERS_DISABLED + } + + private fun loadRows() { + val list = TVList.list + val rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) + val cardPresenter = CardPresenter(lifecycleScope) + + var idx: Long = 0 + for ((k, v) in list) { + val listRowAdapter = ArrayObjectAdapter(cardPresenter) + for ((idx2, v1) in v.withIndex()) { + listRowAdapter.add(v1) + list2.add(Info(idx.toInt(), idx2, v1)) + } + val header = HeaderItem(idx, k) + rowsAdapter.add(ListRow(header, listRowAdapter)) + idx++ + } + + adapter = rowsAdapter + + (activity as? MainActivity)?.play(list.values.first()[0]) + (activity as? MainActivity)?.switchMainFragment() + } + + fun focus() { + if (!view?.isFocused!!) { + view?.requestFocus() + } + } + + fun prev() { + view?.post { + itemPosition-- + if (itemPosition == -1) { + itemPosition = list2.size - 1 + } + + val l = list2[itemPosition] + l.item?.let { (activity as? MainActivity)?.play(it) } + setSelectedPosition( + l.rowPosition, false, + SelectItemViewHolderTask(l.itemPosition) + ) +// Toast.makeText( +// activity, +// "${l.title} $selectedPosition $itemPosition", +// Toast.LENGTH_SHORT +// ).show() + } + } + + fun next() { + view?.post { + itemPosition++ + if (itemPosition == list2.size) { + itemPosition = 0 + } + + val l = list2[itemPosition] + l.item?.let { (activity as? MainActivity)?.play(it) } + setSelectedPosition( + l.rowPosition, false, + SelectItemViewHolderTask(l.itemPosition) + ) +// Toast.makeText( +// activity, +// "${l.title} $selectedPosition $itemPosition", +// Toast.LENGTH_SHORT +// ).show() + } + } + + private fun setupEventListeners() { + onItemViewClickedListener = ItemViewClickedListener() + onItemViewSelectedListener = ItemViewSelectedListener() + } + + private inner class ItemViewClickedListener : OnItemViewClickedListener { + override fun onItemClicked( + itemViewHolder: Presenter.ViewHolder, + item: Any, + rowViewHolder: RowPresenter.ViewHolder, + row: Row + ) { + Log.d(TAG, "onItemClicked") + if (item is TV) { + Log.d(TAG, "Item: $item") + (activity as? MainActivity)?.play(item) + (activity as? MainActivity)?.switchMainFragment() + itemPosition = item.id + } + } + } + + private inner class ItemViewSelectedListener : OnItemViewSelectedListener { + override fun onItemSelected( + itemViewHolder: Presenter.ViewHolder?, item: Any?, + rowViewHolder: RowPresenter.ViewHolder, row: Row + ) { + if (item is TV) { + Log.i(TAG, "Item: ${item.id}") + + } + if (itemViewHolder == null) { + view?.post { + val l = list2[itemPosition] + Log.i(TAG, "$l") + setSelectedPosition( + l.rowPosition, false, + SelectItemViewHolderTask(l.itemPosition) + ) + } + } + } + } + + companion object { + private const val TAG = "MainFragment" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt b/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt new file mode 100644 index 00000000..32b24287 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt @@ -0,0 +1,53 @@ +package com.lizongying.mytv + +import android.content.Context +import android.view.KeyEvent +import android.view.View +import androidx.leanback.media.MediaPlayerAdapter +import androidx.leanback.media.PlaybackTransportControlGlue + +class PlaybackControlGlue( + context: Context?, + playerAdapter: MediaPlayerAdapter?, +) : + PlaybackTransportControlGlue(context, playerAdapter) { + + override fun onKey(v: View?, keyCode: Int, event: KeyEvent?): Boolean { + if (event!!.action == KeyEvent.ACTION_DOWN) { + when (keyCode) { + KeyEvent.KEYCODE_DPAD_CENTER -> { + (context as? MainActivity)?.switchMainFragment() + } + + KeyEvent.KEYCODE_DPAD_UP -> { + if ((context as? MainActivity)?.fragmentIsHidden() == true) { + (context as? MainActivity)?.prev() + } + } + + KeyEvent.KEYCODE_DPAD_DOWN -> { + if ((context as? MainActivity)?.fragmentIsHidden() == true) { + (context as? MainActivity)?.next() + } + } + + KeyEvent.KEYCODE_DPAD_LEFT -> { + if ((context as? MainActivity)?.fragmentIsHidden() == true) { + (context as? MainActivity)?.prev() + } + } + + KeyEvent.KEYCODE_DPAD_RIGHT -> { + if ((context as? MainActivity)?.fragmentIsHidden() == true) { + (context as? MainActivity)?.next() + } + } + } + } + return super.onKey(v, keyCode, event) + } + + companion object { + private const val TAG = "CustomPlaybackControlGlue" + } +} diff --git a/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt b/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt new file mode 100644 index 00000000..03254cd6 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt @@ -0,0 +1,75 @@ +package com.lizongying.mytv + +import android.net.Uri +import android.os.Bundle +import android.util.Log +import androidx.leanback.app.VideoSupportFragment +import androidx.leanback.app.VideoSupportFragmentGlueHost +import androidx.leanback.media.MediaPlayerAdapter +import androidx.leanback.media.PlaybackTransportControlGlue +import androidx.leanback.widget.PlaybackControlsRow +import java.io.IOException + +/** Handles video playback with media controls. */ +class PlaybackFragment : VideoSupportFragment() { + + private lateinit var mTransportControlGlue: PlaybackTransportControlGlue + private var playerAdapter: MediaPlayerAdapter? = null + private var lastVideoUrl: String = "" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + playerAdapter = MediaPlayerAdapter(context) + playerAdapter?.setRepeatAction(PlaybackControlsRow.RepeatAction.INDEX_NONE) + + view?.isFocusable = false + view?.isFocusableInTouchMode = false + } + + override fun showControlsOverlay(runAnimation: Boolean) { + // We will do nothing here, and thus controls will never be shown + } + + fun play(tv: TV) { + if (tv.videoUrl.isNullOrBlank()) { + Log.e(TAG, "videoUrl is empty") + return + } + + if (tv.videoUrl.equals(lastVideoUrl)) { + Log.e(TAG, "videoUrl is duplication") + return + } + + lastVideoUrl = tv.videoUrl!! + + playerAdapter?.reset() + + val glueHost = VideoSupportFragmentGlueHost(this@PlaybackFragment) + mTransportControlGlue = PlaybackControlGlue(activity, playerAdapter) + mTransportControlGlue.host = glueHost + mTransportControlGlue.playWhenPrepared() + + try { + playerAdapter?.setDataSource(Uri.parse(tv.videoUrl)) + } catch (e: IOException) { + // Handle the exception + return + } + hideControlsOverlay(false) + } + + override fun onDestroy() { + if (playerAdapter?.mediaPlayer != null) { + playerAdapter?.release() + Log.d(TAG, "playerAdapter released") + } + + super.onDestroy() + } + + companion object { + private const val TAG = "PlaybackVideoFragment" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/TV.kt b/app/src/main/java/com/lizongying/mytv/TV.kt new file mode 100644 index 00000000..1fe2b6e0 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/TV.kt @@ -0,0 +1,25 @@ +package com.lizongying.mytv + +import java.io.Serializable + +/** + * Movie class represents video entity with title, description, image thumbs and video url. + */ +data class TV( + var id: Int = 0, + var title: String? = null, + var videoUrl: String? = null, +) : Serializable { + + override fun toString(): String { + return "TV{" + + "id=" + id + + ", title='" + title + '\'' + + ", videoUrl='" + videoUrl + '\'' + + '}' + } + + companion object { + internal const val serialVersionUID = 727566175075960653L + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/TVList.kt b/app/src/main/java/com/lizongying/mytv/TVList.kt new file mode 100644 index 00000000..91fcf095 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/TVList.kt @@ -0,0 +1,115 @@ +package com.lizongying.mytv + +object TVList { + val list: Map> by lazy { + setupTV() + } + private var count: Int = 0 + + private fun setupTV(): Map> { + val tv = arrayOf( + arrayOf( + "央视频道", + "CCTV1", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV2", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV3", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV4", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV4美洲", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV4欧洲", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV5", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频道", + "CCTV5+", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV1", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV2", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV3", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV4", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV4美洲", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV4欧洲", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV5", + "http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=", + ), + arrayOf( + "央视频", + "CCTV5+", + "http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=", + ), + ) + + val map: MutableMap> = mutableMapOf() + + for (i in tv) { + val channelName = i[0] + val movieList = map[channelName] ?: mutableListOf() + movieList.add(buildTV(i[1], i[2])) + map[channelName] = movieList + } + + return map + } + + private fun buildTV( + title: String, + videoUrl: String, + ): TV { + val tv = TV() + tv.id = count++ + tv.title = title + tv.videoUrl = videoUrl + return tv + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/appreciate.jpg b/app/src/main/res/drawable/appreciate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe31a80efc39e0ebc3eeeef2c768a9c5a55b1599 GIT binary patch literal 17984 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 G@zQ_(hi*Oq literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..86bbdaec --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!To6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..28d4b77f9f036a47549d47db79c16788749dca10 GIT binary patch literal 2884 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 + #30000000 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..8e397fad --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + My TV + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..51bdee83 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..eece25f9 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,4 @@ + + +