Repository: Sovan22/Tokeii
Branch: master
Commit: 754b8503003b
Files: 185
Total size: 528.8 KB
Directory structure:
gitextract_qqda0tz5/
├── .gitignore
├── .idea/
│ ├── .gitignore
│ ├── compiler.xml
│ ├── gradle.xml
│ ├── kotlinc.xml
│ ├── misc.xml
│ └── vcs.xml
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── demomiru/
│ │ └── tokeiv2/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── demomiru/
│ │ │ └── tokeiv2/
│ │ │ ├── EpisodeAdapter.kt
│ │ │ ├── EpisodeAdapter2.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── Movie.kt
│ │ │ ├── MovieAdapter.kt
│ │ │ ├── MoviePlayActivity.kt
│ │ │ ├── MovieService.kt
│ │ │ ├── MoviesFragment.kt
│ │ │ ├── SearchFragment.kt
│ │ │ ├── TMDBService.kt
│ │ │ ├── TVShowAdapter.kt
│ │ │ ├── TVShowCardAdapter.kt
│ │ │ ├── TVShowDetails.kt
│ │ │ ├── TVShowFragment.kt
│ │ │ ├── TVshow.kt
│ │ │ ├── VideoPlayActivity.kt
│ │ │ ├── anime/
│ │ │ │ ├── AnimeAdapter.kt
│ │ │ │ ├── AnimeDetailsFragment.kt
│ │ │ │ ├── AnimeFragment.kt
│ │ │ │ └── AnimeInfo.kt
│ │ │ ├── extractors/
│ │ │ │ ├── CorrectTitleSelection.kt
│ │ │ │ ├── PrMovies.kt
│ │ │ │ └── Vidplay.kt
│ │ │ ├── history/
│ │ │ │ ├── QueryRepository.kt
│ │ │ │ ├── SearchApp.kt
│ │ │ │ ├── SearchDatabase.kt
│ │ │ │ ├── SearchHistory.kt
│ │ │ │ ├── SearchHistoryAdapter.kt
│ │ │ │ ├── SearchHistoryAdapter2.kt
│ │ │ │ └── SearchHistoryDao.kt
│ │ │ ├── subtitles/
│ │ │ │ ├── SubTrackAdapter.kt
│ │ │ │ └── Subtitles.kt
│ │ │ ├── tracks/
│ │ │ │ └── TrackAdapter.kt
│ │ │ ├── utils/
│ │ │ │ ├── DudeFilmsUtils.kt
│ │ │ │ ├── Extractor.kt
│ │ │ │ ├── GoMovies.kt
│ │ │ │ ├── GogoAnime.kt
│ │ │ │ ├── OpenSubtitle.kt
│ │ │ │ ├── SmashyStream.kt
│ │ │ │ ├── SuperstreamUtils.kt
│ │ │ │ ├── VidSrc.kt
│ │ │ │ ├── VidSrcUtils.kt
│ │ │ │ ├── ViewModelsTokei.kt
│ │ │ │ ├── colors.txt
│ │ │ │ └── retrofitBuilder.kt
│ │ │ └── watching/
│ │ │ ├── ContinueWatching.kt
│ │ │ ├── ContinueWatchingAdapter.kt
│ │ │ ├── ContinueWatchingDao.kt
│ │ │ ├── ContinueWatchingDatabase.kt
│ │ │ └── ContinueWatchingRepository.kt
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── enter_anim.xml
│ │ │ ├── enter_from_bottom.xml
│ │ │ ├── exit_anim.xml
│ │ │ ├── exit_to_top.xml
│ │ │ ├── go_left.xml
│ │ │ ├── go_right.xml
│ │ │ ├── layout_animation.xml
│ │ │ ├── nav_enter_anim.xml
│ │ │ ├── nav_exit_anim.xml
│ │ │ ├── nav_pop_enter.xml
│ │ │ ├── nav_pop_exit.xml
│ │ │ ├── pop_enter.xml
│ │ │ ├── pop_exit.xml
│ │ │ ├── rotate_around_center_point.xml
│ │ │ ├── rotate_left.xml
│ │ │ ├── rotate_right.xml
│ │ │ ├── slide_from_uo.xml
│ │ │ ├── slide_in.xml
│ │ │ ├── slide_in_right.xml
│ │ │ └── slide_out.xml
│ │ ├── drawable/
│ │ │ ├── anime_bottom_nav.xml
│ │ │ ├── background_search_history.xml
│ │ │ ├── background_text.xml
│ │ │ ├── baseline_cancel_24.xml
│ │ │ ├── baseline_double_arrow_24.xml
│ │ │ ├── baseline_history_24.xml
│ │ │ ├── baseline_keyboard_arrow_down_24.xml
│ │ │ ├── baseline_keyboard_arrow_up_24.xml
│ │ │ ├── baseline_lock_open_24.xml
│ │ │ ├── baseline_play_arrow_24.xml
│ │ │ ├── baseline_play_circle_24.xml
│ │ │ ├── baseline_search_24.xml
│ │ │ ├── baseline_zoom_out_map_24.xml
│ │ │ ├── custom_thumb.xml
│ │ │ ├── delete.xml
│ │ │ ├── down_arrow.xml
│ │ │ ├── edit.xml
│ │ │ ├── fill_screen.xml
│ │ │ ├── fit_screen.xml
│ │ │ ├── focus.xml
│ │ │ ├── focus_search.xml
│ │ │ ├── gradient_fill.xml
│ │ │ ├── gradient_fill_bottom.xml
│ │ │ ├── gradient_fill_text.xml
│ │ │ ├── ic_baseline_keyboard_backspace_24.xml
│ │ │ ├── ic_forward.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_menu.xml
│ │ │ ├── ic_rewind.xml
│ │ │ ├── ic_rotation.xml
│ │ │ ├── icon_play.xml
│ │ │ ├── image_view_round.xml
│ │ │ ├── more.xml
│ │ │ ├── netflix_audio_subtitles.xml
│ │ │ ├── netflix_brightness_four.xml
│ │ │ ├── netflix_brightness_one.xml
│ │ │ ├── netflix_brightness_three.xml
│ │ │ ├── netflix_brightness_two.xml
│ │ │ ├── netflix_lock_black.xml
│ │ │ ├── netflix_pause_button.xml
│ │ │ ├── netflix_speed.xml
│ │ │ ├── netflix_unlock.xml
│ │ │ ├── next_episode.xml
│ │ │ ├── play_ripple.xml
│ │ │ ├── player_controller_bg.xml
│ │ │ ├── properties.xml
│ │ │ ├── quality_24.xml
│ │ │ ├── radio_group_tab_bg.xml
│ │ │ ├── rectangle_box.xml
│ │ │ ├── rectangle_box_slim.xml
│ │ │ ├── selected_quality.xml
│ │ │ ├── selected_subtitle.xml
│ │ │ ├── share.xml
│ │ │ ├── share_link.xml
│ │ │ ├── tab_selector.xml
│ │ │ ├── tab_text_color_selector.xml
│ │ │ ├── volume_up_24.xml
│ │ │ └── white_round.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── font/
│ │ │ ├── exo_2.xml
│ │ │ └── exo_2_thin.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_movie_play.xml
│ │ │ ├── activity_video_play.xml
│ │ │ ├── card_view.xml
│ │ │ ├── custom_video_player2.xml
│ │ │ ├── dropdown_menu.xml
│ │ │ ├── episode_expanded_view.xml
│ │ │ ├── episode_item_viem.xml
│ │ │ ├── fragment_anime.xml
│ │ │ ├── fragment_anime_details.xml
│ │ │ ├── fragment_correct_title_selection.xml
│ │ │ ├── fragment_movies.xml
│ │ │ ├── fragment_search.xml
│ │ │ ├── fragment_tv_show.xml
│ │ │ ├── fragment_tv_show_details.xml
│ │ │ ├── item_view.xml
│ │ │ ├── search_item.xml
│ │ │ ├── sub_view.xml
│ │ │ ├── test.xml
│ │ │ └── track_item.xml
│ │ ├── menu/
│ │ │ └── bottom_nav.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── navigation/
│ │ │ └── nav_main.xml
│ │ ├── transition/
│ │ │ └── container_transform.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── font_certs.xml
│ │ │ ├── preloaded_fonts.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ ├── values-night/
│ │ │ └── themes.xml
│ │ └── xml/
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test/
│ └── java/
│ └── com/
│ └── demomiru/
│ └── tokeiv2/
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.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
================================================
FILE: .idea/.gitignore
================================================
# Default ignored files
/shelf/
/workspace.xml
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/kotlinc.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: README.md
================================================
# Tokei
Movie and TV Streaming App
## Download the app here:
# [tokei-release_v1.1.4](https://github.com/Sovan22/Tokeii/releases/download/v1.1.4-tokei/tokei-v1.1.4.apk)
Compatibility : Android 7+
## Features:
+ **AdFree**
+ No tracking/analytics
+ Lesser Loading Time
+ Good sources for Indian OTT Content
## App Demo
https://github.com/Sovan22/Tokeii/assets/12212201/b417be95-d728-4af8-9809-b2fcdfde1c5f
## DISCLAIMER
+ Tokei only scrapes links from various websites and makes it easier for users to find tvShows and movies.
+ Tokei doesn't host any of the contents found inside Tokei. Any and all images and movie/tv information found in the app are taken from various public APIs (TMDb).
+ Furthermore, all of the movie/tv links found in Tokei are taken from various 3rd party hosting websites.
+ Tokei or it's owners aren't liable for any misuse of any of the contents found inside or outside of the app and cannot be held accountable for the distribution of any of the contents found inside the app.
+ By using Tokei, you comply to the fact that the developer of the app is not responsible for any of the contents found in the app; nonetheless they may or may not be from their legitimate sources.
+ If the internet infringement issues are involved, please contact the source website. The developer does not assume any legal responsibility.
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id("androidx.navigation.safeargs.kotlin")
id("com.google.devtools.ksp")
}
def localProperties = new Properties()
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
android {
namespace 'com.demomiru.tokeiv2'
compileSdk 34
packagingOptions {
pickFirst "META-INF/DEPENDENCIES"
exclude 'mozilla/public-suffix-list.txt'
}
defaultConfig {
applicationId "com.demomiru.tokeiv2"
minSdk 26
targetSdk 34
versionCode 1
versionName "1.1.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "OPEN_SUBTITLE_API_KEY", "\"${localProperties["OPEN_SUBTITLE_API_KEY"]}\"")
buildConfigField("String", "TMDB_API_KEY", "\"${localProperties["TMDB_API_KEY"]}\"")
buildConfigField("String", "TMDB_TOKEN", "\"${localProperties["TMDB_TOKEN"]}\"")
buildConfigField("String", "SUPERSTREAM_API1", "\"${localProperties["SUPERSTREAM_API1"]}\"")
buildConfigField("String", "SUPERSTREAM_API2", "\"${localProperties["SUPERSTREAM_API2"]}\"")
buildConfigField("String", "PROXY_URL", "\"${localProperties["PROXY_URL"]}\"")
buildConfigField("String", "MAL_API", "\"${localProperties["MAL_API"]}\"")
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures{
viewBinding true
buildConfig true
}
}
dependencies {
def paging_version = "3.2.1"
implementation "androidx.paging:paging-runtime:$paging_version"
implementation 'com.github.ismaeldivita:chip-navigation-bar:1.4.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'at.blogc:expandabletextview:1.0.5'
implementation "androidx.navigation:navigation-fragment-ktx:2.7.1"
implementation "androidx.navigation:navigation-ui-ktx:2.7.1"
implementation 'com.google.firebase:firebase-firestore-ktx:24.1.0'
ksp("androidx.room:room-compiler:2.5.0")
implementation "androidx.activity:activity-ktx:1.7.2"
implementation 'androidx.appcompat:appcompat:1.6.1'
// implementation("it.skrape:skrapeit:1.2.2")
// implementation 'org.jsoup:jsoup:1.14.3'
// implementation 'org.danilopianini:khttp:1.3.1'
implementation 'com.github.Blatzar:NiceHttp:0.4.4'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2'
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation("io.coil-kt:coil:2.4.0")
// Room components
implementation "androidx.room:room-runtime:2.5.0"
// annotationProcessor "androidx.room:room-compiler:2.5.2"
implementation "androidx.room:room-ktx:2.5.0"
//ExoPlayer
implementation "androidx.media3:media3-exoplayer:1.1.1"
implementation "androidx.media3:media3-exoplayer-hls:1.1.1"
implementation "androidx.media3:media3-exoplayer-dash:1.1.1"
implementation "androidx.media3:media3-ui:1.1.1"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
================================================
FILE: app/proguard-rules.pro
================================================
# 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
================================================
FILE: app/src/androidTest/java/com/demomiru/tokeiv2/ExampleInstrumentedTest.kt
================================================
package com.demomiru.tokeiv2
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.demomiru.tokeiv2", appContext.packageName)
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/EpisodeAdapter.kt
================================================
package com.demomiru.tokeiv2
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class EpisodeAdapter(private val episodeNumber : List,
private val clickHandler : (Int) -> Unit
) : RecyclerView.Adapter() {
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
val episodeText : TextView = itemView.findViewById(R.id.episode_no_text)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.episode_item_viem,parent,false)
return ViewHolder(view)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val episodeN = episodeNumber[position]
holder.episodeText.text = "Episode $episodeN"
holder.itemView.setOnClickListener {
clickHandler(episodeN)
}
}
override fun getItemCount(): Int {
return episodeNumber.size
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/EpisodeAdapter2.kt
================================================
package com.demomiru.tokeiv2
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import at.blogc.android.views.ExpandableTextView
import coil.load
//class MyDiffUtilCallback : DiffUtil.ItemCallback() {
// override fun areItemsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
// // Return true if items are the same.
// return oldItem.id == newItem.id
// }
//
// override fun areContentsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
// // Return true if contents are the same.
// return oldItem == newItem
// }
//}
class EpisodeAdapter2(private val episodes : List,
private val clickHandler : (Episode) -> Unit
) : RecyclerView.Adapter() {
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
val episodeText : TextView = itemView.findViewById(R.id.episode_no_text)
val episodeImg : ImageView = itemView.findViewById(R.id.episode_img)
val episodeOverview : ExpandableTextView = itemView.findViewById(R.id.episode_overview_text)
val fl : FrameLayout = itemView.findViewById(R.id.expanded_episode_fl)
val epCard: CardView = itemView.findViewById(R.id.episode_card)
val expandText : ImageView = itemView.findViewById(R.id.expand_text)
val expandableTextView : ExpandableTextView = itemView.findViewById(R.id.episode_overview_text)
val episodeNumber : TextView = itemView.findViewById(R.id.episode_number)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.test,parent,false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val episode = episodes[position]
holder.episodeText.text = episode.name
holder.episodeNumber.text = episode.episode_number
holder.episodeOverview.text = episode.overview
holder.episodeImg.load("https://image.tmdb.org/t/p/w500${episode.still_path}")
holder.fl.setOnClickListener {
clickHandler(episode)
}
holder.epCard.setOnClickListener {
holder.fl.performClick()
// clickHandler(episode)
}
val etv = holder.expandableTextView
etv.setAnimationDuration(750L)
holder.expandText.setOnClickListener {
if(etv.isExpanded){
etv.collapse()
holder.expandText.load(R.drawable.baseline_keyboard_arrow_down_24)
}else{
etv.expand()
holder.expandText.load(R.drawable.baseline_keyboard_arrow_up_24)
}
}
}
override fun getItemCount(): Int {
return episodes.size
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/MainActivity.kt
================================================
package com.demomiru.tokeiv2
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavOptions
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModel2
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModelFactory2
import com.demomiru.tokeiv2.utils.addRecyclerAnimation
import com.demomiru.tokeiv2.utils.passData
import com.demomiru.tokeiv2.watching.ContinueWatching
import com.demomiru.tokeiv2.watching.ContinueWatchingAdapter
import com.demomiru.tokeiv2.watching.ContinueWatchingDatabase
import com.demomiru.tokeiv2.watching.ContinueWatchingRepository
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.common.primitives.UnsignedBytes.toInt
import com.google.gson.Gson
import com.lagradost.nicehttp.Requests
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONArray
import org.jsoup.Jsoup
@Suppress("DEPRECATION")
class MainActivity : AppCompatActivity() {
private val version = 114
private val gson = Gson()
private lateinit var watchHistoryRc : RecyclerView
private val database by lazy { ContinueWatchingDatabase.getInstance(this) }
private val watchHistoryDao by lazy { database.watchDao() }
private val app = Requests()
private lateinit var viewModelFactory: ContinueWatchingViewModelFactory2
private val viewModel: ContinueWatchingViewModel2 by viewModels(
factoryProducer = {
viewModelFactory
}
)
private var currentFragment = MutableLiveData(R.id.moviesFragment)
private lateinit var continueText: TextView
private lateinit var bottomNavigationView : BottomNavigationView
private var nestedScrollView : NestedScrollView? = null
private lateinit var adapter: ContinueWatchingAdapter
private lateinit var continueWatchingRepository: ContinueWatchingRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModelFactory = ContinueWatchingViewModelFactory2(watchHistoryDao)
val options = NavOptions.Builder()
.setEnterAnim(R.anim.enter_from_bottom)
.setExitAnim(R.anim.exit_to_top)
.build()
nestedScrollView = findViewById(R.id.nestedScrollView)
val navController = findNavController(R.id.nav_host_fragment)
bottomNavigationView = findViewById(R.id.bottom_nav_bar)
continueWatchingRepository = ContinueWatchingRepository(watchHistoryDao)
watchHistoryRc = findViewById(R.id.watch_history_rc)
watchHistoryRc.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false)
continueText = findViewById(R.id.continue_watching_text)
adapter = ContinueWatchingAdapter{it,delete->
if(delete){
lifecycleScope.launch (Dispatchers.IO) {
continueWatchingRepository.delete(it)
// continueWatchingRepository.loadData()
}
}
else {
startActivity(passData(it, this))
}
}
watchHistoryRc.adapter = adapter
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.moviesFragment -> {
navController.navigate(R.id.moviesFragment, null, options)
if (viewModel.allWatchHistory.value?.size !=0){
watchHistoryRc.visibility = View.VISIBLE
continueText.visibility = View.VISIBLE
currentFragment.value = R.id.moviesFragment
}
}
R.id.searchFragment -> {
navController.navigate(R.id.searchFragment, null, options)
watchHistoryRc.visibility = View.GONE
continueText.visibility = View.GONE
currentFragment.value = R.id.searchFragment
}
R.id.TVShowFragment -> {
navController.navigate(R.id.TVShowFragment, null, options)
if (viewModel.allWatchHistory.value?.size !=0){
watchHistoryRc.visibility = View.VISIBLE
continueText.visibility = View.VISIBLE
currentFragment.value = R.id.TVShowFragment
}
}
R.id.animeFragment ->{
navController.navigate(R.id.animeFragment,null,options)
if (viewModel.allWatchHistory.value?.size !=0){
watchHistoryRc.visibility = View.VISIBLE
continueText.visibility = View.VISIBLE
currentFragment.value = R.id.animeFragment
}
}
}
true
}
bottomNavigationView.setOnNavigationItemReselectedListener {
return@setOnNavigationItemReselectedListener
}
// bottomNavigationView.selectedItemId = R.id.animeFragment
lifecycleScope.launch (Dispatchers.IO){
// val update = app.get("https://github.com/Sovan22/Tokeii/").document.select("article.markdown-body.entry-content.container-lg .anchor")[2].attr("href").substringAfter("v").toInt()
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.github.com/repos/Sovan22/Tokeii/releases").build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val releases = JSONArray(response.body.string())
val update = gson.fromJson(releases.getJSONObject(0).toString(),Release::class.java).tag_name.replace(".","").replace("v","").replace("-tokei","").toInt()
// [2].attr("href").substringAfter("v").toInt()
println(update)
if (version < update)
withContext(Dispatchers.Main) {
showDialog()
}
}
}
}
data class Release(val tag_name:String)
fun triggerSearchKeyPress() {
val enterKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)
dispatchKeyEvent(enterKeyEvent)
}
@SuppressLint("SetTextI18n")
override fun onResume() {
// viewModel.currentFragment.observe(this){
// if(it == R.id.searchFragment ){
// watchHistoryRc.visibility = View.GONE
// continueText.visibility = View.GONE
// }
// else{
// watchHistoryRc.visibility = View.VISIBLE
// continueText.visibility = View.VISIBLE
// }
// }
// viewModel.showContinue.observe(this){
// if(!it){
// watchHistoryRc.visibility = View.GONE
// continueText.visibility = View.GONE
// }
// else{
// watchHistoryRc.visibility = View.VISIBLE
// continueText.visibility = View.VISIBLE
// }
// }
val viewStateObserver = Observer> {watchFrom ->
if(watchFrom.isNotEmpty()){
watchHistoryRc.visibility = View.VISIBLE
continueText.visibility = View.VISIBLE
adapter.submitList(watchFrom)
addRecyclerAnimation(watchHistoryRc,adapter)
viewModel.currentFragment.observe(this){
if(it == R.id.searchFragment || it == R.id.TVShowDetails || it == R.id.animeDetailsFragment ){
watchHistoryRc.visibility = View.GONE
continueText.visibility = View.GONE
}
else{
watchHistoryRc.visibility = View.VISIBLE
continueText.visibility = View.VISIBLE
}
}
// if(currentFragment.value == R.id.searchFragment){
// watchHistoryRc.visibility = View.GONE
// continueText.visibility = View.GONE
// }else
// {
// watchHistoryRc.visibility = View.VISIBLE
// continueText.visibility = View.VISIBLE
// }
}
else{
watchHistoryRc.visibility = View.GONE
continueText.visibility = View.GONE
}
}
viewModel.allWatchHistory.observe(this,viewStateObserver)
viewModel.currentFragment.observe(this){
bottomNavigationView.selectedItemId = it
}
super.onResume()
}
private fun showDialog(){
val builder = AlertDialog.Builder(this)
builder.setMessage("There is an update available to this app")
.setTitle("Update Found")
builder.setPositiveButton("Download"){ dialog, _ ->
// User clicked OK button
val url = "https://github.com/Sovan22/Tokeii/releases/"
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
startActivity(intent)
dialog.dismiss()
}
builder.setNegativeButton("Cancel"){ _, _ ->
// User cancelled the dialog
}
val dialog = builder.create()
dialog.show()
}
override fun onBackPressed() {
if(viewModel.currentFragment.value == R.id.searchFragment && viewModel.searchOpen.value == true) {
viewModel.searchOpen.value = false
}
else
super.onBackPressed()
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/Movie.kt
================================================
package com.demomiru.tokeiv2
import java.io.Serializable
data class Movie(
val id : String,
val original_language:String,
val production_countries:List,
val title: String,
val poster_path : String,
val release_date : String
) : Serializable
data class Prod(
val iso_3166_1 : String,
val name: String
)
data class MovieArray(
val movieFile : ArrayList
)
data class MovieFile(
val file: String, //episode file
val title: String// language
)
data class MovieIMDB(
val id: String,
val imdb_id : String,
val external_ids: ExternalIDs
)
data class Keys(
val file: String,
val key : String
)
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/MovieAdapter.kt
================================================
package com.demomiru.tokeiv2
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.paging.PagingDataAdapter
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.demomiru.tokeiv2.utils.retrofitBuilder
import com.demomiru.tokeiv2.utils.yearExtract
class MovieAdapter(private val clickHandler : (Movie) -> Unit) :
ListAdapter(differCallback) {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.image_view)
val titleTextView: TextView = itemView.findViewById(R.id.title_text_view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view,parent,false)
return ViewHolder(view)
}
companion object {
val differCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
return oldItem == newItem
}
}
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie = getItem(position)
holder.titleTextView.text = movie.title + " (${yearExtract(movie.release_date)})"
holder.imageView.load("https://image.tmdb.org/t/p/w500${movie.poster_path}")
holder.itemView.setOnClickListener {
clickHandler(movie)
}
}
}
class MoviesPagingSource(private val list: Int): PagingSource() {
private val retrofit = retrofitBuilder()
private val movieService = retrofit.create(MovieService::class.java)
override suspend fun load(params: LoadParams): LoadResult {
return try {
val currentPage = params.key ?: 1
val response = when(list){
1-> movieService.getPopularMovies(BuildConfig.TMDB_API_KEY,"en-US",currentPage)
2-> movieService.getTrendingMovies(BuildConfig.TMDB_API_KEY, "en-US",currentPage)
3-> movieService.getTopRatedMovies(BuildConfig.TMDB_API_KEY,"en-US",currentPage)
else -> throw Exception("wrong list parameter input")
}
val data = response.body()!!.results
val responseData = mutableListOf()
responseData.addAll(data)
LoadResult.Page(
data = responseData,
prevKey = if (currentPage == 1) null else -1,
nextKey = currentPage.plus(1)
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState): Int? {
return null
}
}
class MovieAdapter2(
private val clickHandler : (Movie) -> Unit
) :
PagingDataAdapter(differCallback) {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.image_view)
val titleTextView: TextView = itemView.findViewById(R.id.title_text_view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view,parent,false)
return ViewHolder(view)
}
companion object {
val differCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
return oldItem == newItem
}
}
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie = getItem(position)!!
holder.titleTextView.text = movie.title + " (${yearExtract(movie.release_date)})"
holder.imageView.load("https://image.tmdb.org/t/p/w500${movie.poster_path}")
holder.itemView.setOnClickListener {
clickHandler(movie)
}
holder.setIsRecyclable(false)
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/MoviePlayActivity.kt
================================================
package com.demomiru.tokeiv2
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.FrameLayout
import android.widget.ProgressBar
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.Player.*
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.demomiru.tokeiv2.extractors.PrMovies
import com.demomiru.tokeiv2.extractors.ResultsAdapter
import com.demomiru.tokeiv2.extractors.SearchResponse
import com.demomiru.tokeiv2.utils.Extractor
import com.demomiru.tokeiv2.utils.GoMovies
import com.demomiru.tokeiv2.utils.GogoAnime
import com.demomiru.tokeiv2.utils.SmashyStream
import com.demomiru.tokeiv2.utils.SuperstreamUtils
import com.demomiru.tokeiv2.utils.getMovieImdb
import com.demomiru.tokeiv2.utils.getMovieLink
import com.demomiru.tokeiv2.utils.getTvImdb
import com.demomiru.tokeiv2.utils.getTvLink
import com.demomiru.tokeiv2.utils.passVideoData
import com.demomiru.tokeiv2.watching.ContinueWatching
import com.demomiru.tokeiv2.watching.VideoData
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.Exception
@Suppress("DEPRECATION")
class MoviePlayActivity : AppCompatActivity(){
private val gson = Gson()
private lateinit var webView : WebView
private var fullscreenContainer: FullscreenHolder? = null
private var animeEp : List = listOf()
private var isSuper = false
private var superId: Int? = null
private var IMDBid: String? = null
private val superStream = SuperstreamUtils()
private lateinit var loading:ProgressBar
private lateinit var id:String
// private var clickedMiddle = false
private lateinit var resultRc : RecyclerView
private var animeUrl = ""
private var season: Int = 1
private var episode: Int = 1
private var origin : String = ""
private var year : String = ""
private var seekProgress : Int = 0
private var imgLink : String? = null
private lateinit var title:String
private var type : String? = null
private val videoUrl = MutableLiveData()
private var subUrl : MutableList = mutableListOf()
private var source : String? = null
private val COVER_SCREEN_PARAMS = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
private val prMovies = PrMovies()
private lateinit var url : String
// private val args : MoviePlayActivityArgs by navArgs()
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_play)
Log.i("Start", "Time")
resultRc = findViewById(R.id.results_rc)
val bundle = intent.extras
type = bundle?.getString("type")
when (type) {
"movie" -> {
val data = bundle?.getSerializable("Data") as? Movie
origin = data!!.original_language
year = data.release_date.substringBefore("-")
if(origin == "kn" || origin == "ml" || origin == "ta" || origin == "te")
origin = "hi"
id = data.id
title = data.title
imgLink = data.poster_path
}
"tvshow" -> {
val data = bundle?.getSerializable("Data") as? Episode
id = bundle?.getString("id")!!
title = bundle.getString("showTitle")!!
imgLink = bundle.getString("poster")
season = data!!.season_number.toInt()
episode = data.episode_number.toInt()
}
"anime" ->{
val data = bundle?.getParcelable("Data") as? GogoAnime.AnimeDetails
id = bundle?.getString("id")!!
title = data?.title!!
imgLink = data.poster
season = 1
episode = bundle.getInt("ep")
animeEp = data.episodes!!
animeUrl = data.episodes[episode].url
}
else -> {
val data = bundle?.getParcelable("Data") as? ContinueWatching
id = data!!.tmdbID.toString()
title = data.title
imgLink = data.imgLink
seekProgress = data.progress
type = data.type
year = data.year ?: ""
origin = data.origin ?: ""
println(data)
if (type != "movie"){
season = data.season
episode = data.episode
if(type == "anime"){
animeEp = data.animeEp!!
animeUrl = data.animeEp[episode].url
}
}
}
}
loading = findViewById(R.id.loading_content)
webView = findViewById(R.id.web_view)
loading.visibility = View.VISIBLE
videoUrl.observe(this) { hlsUri ->
if (!hlsUri.isNullOrEmpty()) {
webView.visibility = View.GONE
videoUrl.removeObservers(this)
val intent = passVideoData(VideoData(
seekProgress,
imgLink!!,
id.toInt(),
IMDBid,
title,
episode,
season,
type!!,
hlsUri,
superId,
subUrl,
animeEp,
origin,
year
),this)
intent.putExtra("origin", origin)
intent.putExtra("superstream",isSuper)
intent.putExtra("animeUrl",animeUrl)
// println(source)
intent.putExtra("source",source)
startActivity(intent)
finish()
}
}
// if(type == "tvshow"){
// lifecycleScope.launch {
// val imdbId = getTvImdb(id)
// if(imdbId.isNotBlank()){
// origin = "hi"
// IMDBid = imdbId
// val link = getTvLink(imdbId,season-1,episode-1)
// if(link.isNotBlank())
// videoUrl.value = link
// else{
// withContext(Dispatchers.Main){
// Toast.makeText(this@MoviePlayActivity,"No Links Available", Toast.LENGTH_SHORT).show()
// finish()
// }
// }
// }
// else{
// //Superstream add
// val mainData = superStream.search(title)
// superId = mainData.data.list[0].id
// if (superId != null) {
// isSuper = true
// val tvLinks = superStream.loadLinks(false, superId!!, season, episode)
// val urlMaps: MutableMap = mutableMapOf()
// tvLinks.data?.list?.forEach {
// if(!it.path.isNullOrBlank()){
// println("${it.quality} : ${it.path}")
// urlMaps[it.quality!!] = it.path
// if(it.quality == "720p") {
// val subtitle = superStream.loadSubtile(false,it.fid!!,superId!!,season,episode).data
////
// getSub(subtitle)
//
// return@forEach
// }
// }
// }
// if(urlMaps.isNotEmpty())
// videoUrl.value = gson.toJson(urlMaps)
// if(videoUrl.value.isNullOrBlank()){
//// withContext(Dispatchers.Main){
//// Toast.makeText(this@MoviePlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
//// finish()
//// }
// isSuper = false
// getGoMovieLink(false)
//// getSmashLink(false)
// }
// }
// else{
// isSuper = false
// getGoMovieLink(false)
//// getSmashLink(false)
// }
//
// }
// }
// }
if(type == "tvshow"){
lifecycleScope.launch (Dispatchers.IO){
val imdbId = getTvImdb(id)
if (imdbId.isNotBlank()) {
origin = "hi"
IMDBid = imdbId
}
val links = Extractor(origin).loadExtractor(title,id,year,season,episode,false)
println(links)
val list = prMovies.getPrMovieLink(title)
withContext(Dispatchers.Main) {
if (!links.videoUrl.isNullOrBlank()) {
println("if run")
subUrl.addAll(links.subs)
isSuper = links.isSuper
source = links.source
videoUrl.value = links.videoUrl
} else {
println("else run")
loading.visibility = View.GONE
try {
val rcAdapter = ResultsAdapter {
lifecycleScope.launch(Dispatchers.IO) {
val src = prMovies.loadLinks(it.link!!)
if (src.file.isNullOrBlank()) throw Exception("no video url found")
withContext(Dispatchers.Main) {
isSuper = false
source = "prmovies"
videoUrl.value = src.file
}
}
}
val width = Resources.getSystem().displayMetrics.widthPixels
val dpi = Resources.getSystem().displayMetrics.densityDpi
val grid = (width*160)/(165*dpi)
resultRc.apply {
layoutManager = GridLayoutManager(this@MoviePlayActivity,grid)
adapter = rcAdapter
}
if (list.isEmpty()) throw Exception("Empty search")
rcAdapter.submitList(list)
}catch (e:Exception){
Toast.makeText(
this@MoviePlayActivity,
"No available links",
Toast.LENGTH_SHORT
).show()
finish()
}
}
}
}
}
else if(type == "movie"){
println("Reached here")
// if (origin != "hi") {
lifecycleScope.launch(Dispatchers.IO) {
// try {
// val mainData = superStream.search(title)
//// println(mainData.data.list[0].year)
// val item = mainData.data.list[0]
// println(year + " ${item.year}")
// superId =
// if (item.title == title && item.year.toString() == year) item.id else null
// getMovieEn()
// }catch (e : Exception){
// getMovieEn()
// }
val links = Extractor(origin).loadExtractor(title,id,year, season,episode,true)
println(links)
val query = title.filter {
it.isLetterOrDigit() || it.isWhitespace()
}
val list = prMovies.getPrMovieLink(query)
withContext(Dispatchers.Main) {
if (!links.videoUrl.isNullOrBlank()) {
subUrl.addAll(links.subs)
isSuper = links.isSuper
source = links.source
videoUrl.value = links.videoUrl
} else {
println("else run")
println(list)
loading.visibility = View.GONE
try {
if (list.isEmpty()) throw Exception("Empty search")
val rcAdapter = ResultsAdapter {
lifecycleScope.launch(Dispatchers.IO) {
val src = prMovies.loadLinks(it.link!!)
if (src.file.isNullOrBlank()) throw Exception("no video url found")
withContext(Dispatchers.Main) {
isSuper = false
source = "prmovies"
videoUrl.value = src.file
}
}
}
val width = Resources.getSystem().displayMetrics.widthPixels
val dpi = Resources.getSystem().displayMetrics.densityDpi
val grid = (width*160)/(165*dpi)
resultRc.apply {
layoutManager = GridLayoutManager(this@MoviePlayActivity,grid)
adapter = rcAdapter
}
rcAdapter.submitList(list)
}catch (e:Exception){
Toast.makeText(
this@MoviePlayActivity,
"No available links",
Toast.LENGTH_SHORT
).show()
finish()
}
// Toast.makeText(
// this@MoviePlayActivity,
// "No available links",
// Toast.LENGTH_SHORT
// ).show()
// finish()
}
}
// webView.loadUrl(url)
}
// }
// else {
// lifecycleScope.launch {
// val imdbId = getMovieImdb(id)
// IMDBid = imdbId
// println(imdbId)
// lifecycleScope.launch {
// try {
// val mainData = superStream.search(title)
//// println(mainData.data.list[0].year)
// val item = mainData.data.list[0]
// println(year + " ${item.year}")
// superId =
// if (item.title == title && item.year.toString() == year) item.id else null
// getMovieHi(imdbId)
// } catch (e: Exception) {
// getMovieHi(imdbId)
// }
// }
// // videoUrl.value = getMovieLink(imdbId)
//
//
// }
// }
}
else{
lifecycleScope.launch (Dispatchers.IO){
val gogoSrc = GogoAnime()
val link = gogoSrc.extractVideos(animeUrl)
withContext(Dispatchers.Main){
videoUrl.value = link
}
}
}
}
private fun getPrMovie(list: List){
}
private suspend fun getGoMovieLink(isMovie: Boolean){
val goMovie = GoMovies()
val data = goMovie.search(season,episode,title,isMovie,year)
val vidLink = data.first
val subLinks = data.second
if(vidLink.isNullOrBlank()){
// Toast.makeText(this@MoviePlayActivity, "Not Available", Toast.LENGTH_SHORT).show()
// finish()
getSmashLink(isMovie)
}
else{
if (!subLinks.isNullOrEmpty())subUrl.add(subLinks)
videoUrl.value = vidLink
}
}
private fun getSub(subtitle: SuperstreamUtils.PrivateSubtitleData?){
subtitle?.list?.forEach { subList->
if(subList.language == "English"){
subList.subtitles.forEach { sub->
if (subUrl.size == 3) {
return
}
if (sub.lang == "en" && !sub.file_path.isNullOrBlank()) {
subUrl.add(sub.file_path)
// println("${sub.language} : ${sub.file_path}")
}
}
return
}
}
}
private fun getSub2(subtitle: SuperstreamUtils.PrivateSubtitleData?){
subtitle?.list?.forEach { subList->
subList.subtitles.forEach { sub->
if (!sub.file_path.isNullOrBlank()) {
subUrl.add("${subList.language} :${sub.file_path}")
}
}
}
}
private suspend fun getMovie()
{
if (superId != null) {
isSuper = true
val movieLinks = superStream.loadLinks(true, superId!!)
movieLinks.data?.list?.forEach {
if(!it.path.isNullOrBlank()){
println("${it.quality} : ${it.path}")
if(it.quality == "720p") {
val subtitle = superStream.loadSubtile(true,it.fid!!,superId!!).data
//
getSub(subtitle)
videoUrl.value = it.path
return
}
}
}
if(videoUrl.value.isNullOrBlank()){
withContext(Dispatchers.Main){
Toast.makeText(this@MoviePlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
finish()
}
}
}
else{
withContext(Dispatchers.Main){
Toast.makeText(this@MoviePlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
finish()
}
}
}
private suspend fun getMovieHi(imdbId: String)
{
if (superId != null) {
isSuper = true
val movieLinks = superStream.loadLinks(true, superId!!)
val urlMaps: MutableMap = mutableMapOf()
movieLinks.data?.list?.forEach {
if(!it.path.isNullOrBlank()){
println("${it.quality} : ${it.path}")
urlMaps[it.quality!!] = it.path
if(it.quality == "720p") {
val subtitle = superStream.loadSubtile(true,it.fid!!,superId!!).data
//
getSub(subtitle)
return@forEach
}
}
}
if(urlMaps.isNotEmpty())
videoUrl.value = gson.toJson(urlMaps)
if(videoUrl.value.isNullOrBlank()){
withContext(Dispatchers.Main){
// webView.loadUrl(url)
// getSmashLink(true)
isSuper = false
val link = getMovieLink(imdbId)
if (link.isBlank()) {
getSmashLink(true,"hi")
}
else
videoUrl.value = link
}
}
}
else{
withContext(Dispatchers.Main){
// webView.loadUrl(url)
// getSmashLink(true)
isSuper = false
val link = getMovieLink(imdbId)
if (link.isBlank())
getSmashLink(true,"hi")
else
videoUrl.value = link
}
}
}
private suspend fun getMovieEn()
{
if (superId != null) {
isSuper = true
val movieLinks = superStream.loadLinks(true, superId!!)
val urlMaps: MutableMap = mutableMapOf()
movieLinks.data?.list?.forEach {
if(!it.path.isNullOrBlank()){
println("${it.quality} : ${it.path}")
urlMaps[it.quality!!] = it.path
if(it.quality == "720p") {
val subtitle = superStream.loadSubtile(true,it.fid!!,superId!!).data
//
getSub(subtitle)
return@forEach
}
}
}
if(urlMaps.isNotEmpty())
videoUrl.value = gson.toJson(urlMaps)
if(videoUrl.value.isNullOrBlank()){
withContext(Dispatchers.Main){
// webView.loadUrl(url)
// getSmashLink(true)
isSuper = false
getGoMovieLink(true)
}
}
}
else{
withContext(Dispatchers.Main){
// webView.loadUrl(url)
// getSmashLink(true)
isSuper = false
getGoMovieLink(true)
}
}
}
private fun getSmashLink(isMovie:Boolean,src: String = "en")
{
val smashSrc = SmashyStream()
lifecycleScope.launch {
val links = smashSrc.getLink(isMovie,id, season, episode,src)
val vidLink = links.first
val subLink = links.second
if(vidLink.isNullOrBlank()){
if (isMovie && origin == "hi"){
val mainData = superStream.search(title)
superId = mainData.data.list[0].id
getMovie()
}
else {
withContext(Dispatchers.Main) {
Toast.makeText(this@MoviePlayActivity, "Not Available", Toast.LENGTH_SHORT)
.show()
finish()
}
}
}
else{
if (!subLink.isNullOrBlank())subUrl.add(subLink)
videoUrl.value = vidLink
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
webView.saveState(outState)
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
hideSystemUI()
}
}
private fun hideSystemUI() {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
}
@Suppress("DEPRECATION")
class FullscreenHolder(ctx: Context) : FrameLayout(ctx) {
init {
setBackgroundColor(ctx.resources.getColor(android.R.color.black))
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(evt: MotionEvent): Boolean {
return true
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/MovieService.kt
================================================
package com.demomiru.tokeiv2
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query
interface MovieService {
@GET("movie/popular")
suspend fun getPopularMovies(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response
@GET("trending/movie/day")
suspend fun getTrendingMovies(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response
@GET("search/movie")
suspend fun searchMovie(
@Query("query") query: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
) : Response
// @GET("movie/{movie_id}")
// suspend fun getImdbId(
// @Path("movie_id") movie_id : String,
// @Query("api_key") apiKey: String,
// @Query("language") language: String
// ) : Response
// @GET("scrape") // Replace with the actual endpoint path
// fun fetchDataFromServer(
// @Header("ngrok-skip-browser-warning") value: String
// ): Response
@GET("movie/top_rated")
suspend fun getTopRatedMovies(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
) : Response
}
//data class IdDB(
// val id: String,
// val imdb_id: String
//)
data class MovieResponse(
val results: List
)
//data class ServerResponse(
// val videoLink: String
// )
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/MoviesFragment.kt
================================================
package com.demomiru.tokeiv2
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.paging.CombinedLoadStates
import androidx.paging.LoadState
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModel2
import com.demomiru.tokeiv2.utils.addRecyclerAnimation
import com.demomiru.tokeiv2.utils.passData
import com.demomiru.tokeiv2.utils.retrofitBuilder
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [MoviesFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class MoviesFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private val activityViewModel: ContinueWatchingViewModel2 by activityViewModels()
private lateinit var popMovieRc: RecyclerView
private lateinit var trenMovieRc : RecyclerView
private lateinit var topMovieRc : RecyclerView
private var loading = MutableLiveData(true)
private val adapter = MovieAdapter2{
println(it.release_date)
startActivity(passData(it,requireContext()))
}
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
lifecycleScope.launch (Dispatchers.IO){
}
super.onViewCreated(view, savedInstanceState)
}
@DelicateCoroutinesApi
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_movies, container, false)
popMovieRc = view.findViewById(R.id.movie_recycler_view)
trenMovieRc = view.findViewById(R.id.trending_movie_rc)
topMovieRc = view.findViewById(R.id.topRatedMovie_recycler_view)
topMovieRc.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL,false)
trenMovieRc.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL,false)
popMovieRc.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL,false)
val retrofit = retrofitBuilder()
val movieService = retrofit.create(MovieService::class.java)
lifecycleScope.launch(Dispatchers.Main) {
withContext(Dispatchers.IO){
// val trmovies = Pager(PagingConfig(1)){MoviesPagingSource(3)}.flow.cachedIn(lifecycleScope)
val tradapter = MovieAdapter2{
// val action = MoviesFragmentDirections.actionMoviesFragmentToMoviePlayActivity(it.id, "movie")
// findNavController().navigate(play(it))
println(it.release_date)
startActivity(passData(it, requireContext()))
}
// val tmovies = Pager(PagingConfig(1)){MoviesPagingSource(2)}.flow.cachedIn(lifecycleScope)
val tadapter = MovieAdapter2 {
// findNavController().navigate(play(it))
println(it.release_date)
startActivity(passData(it, requireContext()))
}
// val movies = Pager(PagingConfig(1)){MoviesPagingSource(1)}.flow.cachedIn(lifecycleScope)
val adapter = MovieAdapter2 {
println(it.release_date)
startActivity(passData(it, requireContext()))
}
withContext(Dispatchers.Main) {
addRecyclerAnimation(topMovieRc, tradapter)
addRecyclerAnimation(trenMovieRc, tadapter)
addRecyclerAnimation(popMovieRc, adapter)
lifecycleScope.launch {
activityViewModel.topMovies.collect {
tradapter.submitData(it)
}
}
lifecycleScope.launch {
activityViewModel.popMovies.collect{
adapter.submitData(it)
}
}
lifecycleScope.launch {
tadapter.addLoadStateListener {
val state = it.refresh
val visible = state is LoadState.Loading
if(visible){
view.findViewById(R.id.loading_movies).visibility = View.VISIBLE
}
else{
view.findViewById(R.id.trending_text).visibility = View.VISIBLE
view.findViewById(R.id.movies_text).visibility = View.VISIBLE
view.findViewById(R.id.topmovies_text).visibility = View.VISIBLE
view.findViewById(R.id.loading_movies).visibility = View.GONE
}
}
// view.findViewById(R.id.loading_movies).visibility = View.GONE
// view.findViewById(R.id.trending_text).visibility = View.VISIBLE
// view.findViewById(R.id.movies_text).visibility = View.VISIBLE
// view.findViewById(R.id.topmovies_text).visibility = View.VISIBLE
activityViewModel.trenMovies.collect {
tadapter.submitData(it)
}
}
}
//
}
// withContext(Dispatchers.Main) {
// view.findViewById(R.id.loading_movies).visibility = View.GONE
// view.findViewById(R.id.trending_text).visibility = View.VISIBLE
// view.findViewById(R.id.movies_text).visibility = View.VISIBLE
// view.findViewById(R.id.topmovies_text).visibility = View.VISIBLE
// }
}
return view
}
// private inline fun CombinedLoadStates.decideOnState(
// showLoading: (Boolean) -> Unit,
// showEmptyState: (Boolean) -> Unit,
// showError: (String) -> Unit
// ) {
// showLoading(refresh is LoadState.Loading)
//
// showEmptyState(
// source.append is LoadState.NotLoading
// && source.append.endOfPaginationReached
// && adapter.itemCount == 0
// )
//
// val errorState = source.append as? LoadState.Error
// ?: source.prepend as? LoadState.Error
// ?: source.refresh as? LoadState.Error
// ?: append as? LoadState.Error
// ?: prepend as? LoadState.Error
// ?: refresh as? LoadState.Error
//
// errorState?.let { showError(it.error.toString()) }
// }
override fun onResume() {
activityViewModel.currentFragment.value = R.id.moviesFragment
super.onResume()
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MoviesFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
MoviesFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/SearchFragment.kt
================================================
@file:OptIn(DelicateCoroutinesApi::class)
package com.demomiru.tokeiv2
import android.content.Context
import android.content.res.Resources
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.SearchView
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.demomiru.tokeiv2.anime.AnimeAdapter
import com.demomiru.tokeiv2.history.QueryRepository
import com.demomiru.tokeiv2.history.SearchDatabase
import com.demomiru.tokeiv2.history.SearchHistory
import com.demomiru.tokeiv2.history.SearchHistoryAdapter2
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModel2
import com.demomiru.tokeiv2.utils.SearchVMFactory
import com.demomiru.tokeiv2.utils.SearchViewModel
import com.demomiru.tokeiv2.utils.addRecyclerAnimation
import com.demomiru.tokeiv2.utils.passData
import kotlinx.coroutines.DelicateCoroutinesApi
class SearchFragment : Fragment() {
// private lateinit var searchHistoryList: List
private lateinit var searchHistoryRC: RecyclerView
private lateinit var searchView : SearchView
private val activityViewModel: ContinueWatchingViewModel2 by activityViewModels()
private lateinit var searchHistoryFl : LinearLayout
private lateinit var deleteAll : TextView
private var isClicked = true
private lateinit var adapter: SearchHistoryAdapter2
private lateinit var searchResultsRc : RecyclerView
private lateinit var searchEt: EditText
private lateinit var movieChoice : RadioButton
private lateinit var tvChoice : RadioButton
private lateinit var animeChoice: RadioButton
private lateinit var choice : RadioGroup
private lateinit var searchLoading : ProgressBar
private val database by lazy { SearchDatabase.getInstance(requireContext()) }
private val searchHistoryDao by lazy { database.searchDao() }
private lateinit var queryRepository:QueryRepository
private lateinit var viewModelFactory: SearchVMFactory
private lateinit var mAdapter : MovieAdapter
private lateinit var tvAdapter : TVShowAdapter
private lateinit var aAdapter: AnimeAdapter
private val viewModel: SearchViewModel by viewModels(
factoryProducer = {
viewModelFactory
}
)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_search, container, false)
searchHistoryFl = view.findViewById(R.id.search_history_ll)
searchView = view.findViewById(R.id.searchView)
queryRepository = QueryRepository(searchHistoryDao)
viewModelFactory = SearchVMFactory(queryRepository)
deleteAll = view.findViewById(R.id.delete_all_button)
searchHistoryRC = view.findViewById(R.id.search_history_rc)
searchResultsRc = view.findViewById(R.id.search_results_rc)
searchLoading = view.findViewById(R.id.search_loading)
movieChoice = view.findViewById(R.id.movies_search)
tvChoice = view.findViewById(R.id.tvShows_search)
animeChoice = view.findViewById(R.id.anime_search)
choice = view.findViewById(R.id.choice)
activityViewModel.currentFragment.value = R.id.searchFragment
// println(activityViewModel.test)
mAdapter = MovieAdapter{
// val action = SearchFragmentDirections.actionSearchFragmentToMoviePlayActivity(it.id,"movie",title = it.title)
// findNavController().navigate(action)
startActivity(passData(it, requireContext()))
}
tvAdapter = TVShowAdapter{it, _ ->
val action = SearchFragmentDirections.actionSearchFragmentToTVShowDetails(it.id, title = it.name)
findNavController().navigate(action)
}
aAdapter = AnimeAdapter(requireContext()){
// val action = SearchFragmentDirections.actionSearchFragmentToTVShowDetails(
// encodeStringToInt(it.name).toString(), title = "",animeUrl = it.url)
// findNavController().navigate(action)
val action = SearchFragmentDirections.actionSearchFragmentToAnimeDetailsFragment(it.name,it.url)
findNavController().navigate(action)
}
// var start = true
// viewModel.queryText.observe(viewLifecycleOwner){
// if(it.isNotBlank() && start)searchView.setQuery(it,false)
// }
// searchView.setQuery(viewModel.queryText.value,false)
val width = Resources.getSystem().displayMetrics.widthPixels
val dpi = Resources.getSystem().displayMetrics.densityDpi
val grid = (width*160)/(165*dpi)
println(grid)
searchResultsRc.layoutManager = GridLayoutManager(requireContext(),grid)
searchHistoryRC.layoutManager = LinearLayoutManager(requireContext())
// start = false
adapter = SearchHistoryAdapter2{it,search->
if (search){
searchView.setQuery(it.query,true)
viewModel.queryText.value = it.query
viewModel.searchClicked.value = true
// (activity as MainActivity).triggerSearchKeyPress()
}
else {
viewModel.deleteRecord(it)
}
}
searchHistoryRC.adapter = adapter
searchHistoryRC.visibility = View.VISIBLE
// GlobalScope.launch (Dispatchers.IO){
// queryRepository.loadData()
// }
val searchHistoryObserver = Observer>{
adapter.submitList(it)
if (it.isEmpty()){
// searchHistoryRC.visibility = View.GONE
// deleteAll.visibility = View.GONE
searchHistoryFl.visibility = View.GONE
activityViewModel.searchOpen.value = false
isClicked = true
}
}
viewModel.searchClicked.observe(viewLifecycleOwner){
if(it){
// deleteAll.visibility = View.GONE
// searchHistoryRC.visibility = View.GONE
searchHistoryFl.visibility = View.GONE
activityViewModel.searchOpen.value = false
}
else{
// deleteAll.visibility = View.VISIBLE
// searchHistoryRC.visibility = View.VISIBLE
searchHistoryFl.visibility = View.VISIBLE
activityViewModel.searchOpen.value = true
}
}
// queryRepository.allQueries.observe(viewLifecycleOwner,searchHistoryObserver)
viewModel.queries.observe(viewLifecycleOwner,searchHistoryObserver)
deleteAll.setOnClickListener{
viewModel.deleteAll()
}
searchEt = view.findViewById(R.id.search_et)
searchEt.setOnClickListener{
if(isClicked) {
deleteAll.visibility = View.VISIBLE
searchHistoryRC.visibility = View.VISIBLE
isClicked = false
}
else{
deleteAll.visibility = View.GONE
searchHistoryRC.visibility = View.GONE
isClicked = true
}
}
// searchView.queryHint = "Search"
// searchView.setOnCloseListener {
// deleteAll.visibility = View.GONE
// searchHistoryRC.visibility = View.GONE
// false
// }
searchView.setOnClickListener {
val imm =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(it.findFocus(), 0)
}
// viewModel.queryText.observe(viewLifecycleOwner){
// if(it.isNotBlank())searchView.setQuery(it,false)
// }
searchView.setOnQueryTextFocusChangeListener { view, hasFocus ->
if(hasFocus) {
// deleteAll.visibility = View.VISIBLE
// searchHistoryRC.visibility = View.VISIBLE
view.postDelayed({
val imm =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(view.findFocus(), 0)
}, 200)
searchHistoryFl.visibility = View.VISIBLE
activityViewModel.searchOpen.value = true
}
// else{
//
// deleteAll.visibility = View.GONE
// viewModel.searchClicked.value = true
// searchHistoryRC.visibility = View.GONE
//
// }
}
activityViewModel.searchOpen.observe(viewLifecycleOwner){
if(it == false){
searchHistoryFl.visibility = View.GONE
searchView.clearFocus()
}
}
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
// Logic for when search button is clicked
if(query.isNullOrEmpty())return false
viewModel.searchClicked.value = true
if(movieChoice.isChecked)
{
viewModel.choice.value = 1
performMovieSearch(query)
}
else if(tvChoice.isChecked){
viewModel.choice.value = 2
performShowSearch(query)
}
else{
viewModel.choice.value = 3
performAnimeSearch(query)
}
// searchView.isIconified = true
// searchView.queryHint = query
viewModel.queryText.value = query
searchHistoryFl.visibility = View.GONE
searchView.clearFocus()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
// Logic for when text in search view changes
//add filter in searchHistory Rc
// deleteAll.visibility = View.VISIBLE
// searchHistoryRC.visibility = View.VISIBLE
searchHistoryFl.visibility = View.VISIBLE
viewModel.queryText.value = newText?:""
return false
}
})
// searchView.setOnClickListener {
// searchView.isIconified = false
// }
// searchEt.setOnKeyListener { _, actionId, event ->
// if(searchEt.text.toString().isEmpty()) return@setOnKeyListener true
// if (actionId == KeyEvent.ACTION_DOWN || event.keyCode == KeyEvent.KEYCODE_ENTER) {
// // The "Search" button on the keyboard was clicked
// viewModel.searchClicked.value = true
// if(movieChoice.isChecked)
// {
// viewModel.choice.value = 1
// performMovieSearch()
// }
// else if(tvChoice.isChecked){
// viewModel.choice.value = 2
// performShowSearch()
// }
// else{
// viewModel.choice.value = 3
// performAnimeSearch()
// }
// return@setOnKeyListener true
// }
// false
// }
choice.setOnCheckedChangeListener { _, _: Int ->
if(movieChoice.isChecked)
{
viewModel.choice.value = 1
}
else if(tvChoice.isChecked){
viewModel.choice.value = 2
}
else{
viewModel.choice.value = 3
}
searchResultsRc.visibility = View.GONE
// searchHistoryRC.visibility = View.VISIBLE
searchHistoryFl.visibility = View.VISIBLE
viewModel.searchClicked.value = false
}
when(viewModel.choice.value){
1 -> viewModel.movieList.observe(viewLifecycleOwner) { movies ->
if (movies.isNotEmpty()) {
mAdapter.submitList(movies)
addRecyclerAnimation(searchResultsRc,mAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
}
}
2 -> viewModel.tvList.observe(viewLifecycleOwner){shows->
if(shows.isNotEmpty()){
tvAdapter.submitList(shows)
addRecyclerAnimation(searchResultsRc,tvAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
}
}
3 -> viewModel.animeList.observe(viewLifecycleOwner){ anime->
if(anime.isNotEmpty()){
aAdapter.submitList(anime)
addRecyclerAnimation(searchResultsRc,aAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
}
}
else -> println(viewModel.choice.value)
}
viewModel.noMatches.observe(viewLifecycleOwner){
if(it) {
Toast.makeText(requireContext(), "No Matches", Toast.LENGTH_SHORT).show()
viewModel.noMatches.value = false
}
}
return view
}
private fun performAnimeSearch(query: String)
{
searchResultsRc.visibility = View.GONE
searchHistoryFl.visibility = View.GONE
isClicked = true
// deleteAll.visibility = View.GONE
searchLoading.visibility = View.VISIBLE
// val gogoSrc = GogoAnime()
// val query = searchEt.text.toString()
val history = SearchHistory(query = query)
viewModel.addToHistory(history)
viewModel.searchAnime(query)
// val adapter = AnimeAdapter(requireContext()){
// lifecycleScope.launch {
//
//// val action = SearchFragmentDirections.actionSearchFragmentToTVShowDetails(
//// encodeStringToInt(it.name).toString(), title = "",animeUrl = it.url)
//// findNavController().navigate(action)
//
// val action = SearchFragmentDirections.actionSearchFragmentToAnimeDetailsFragment(it.name,it.url)
// findNavController().navigate(action)
//
// }
// }
viewModel.animeList.observe(viewLifecycleOwner){anime->
if(anime.isNotEmpty()){
aAdapter.submitList(anime)
addRecyclerAnimation(searchResultsRc, aAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
searchResultsRc.requestFocus()
}
else{
// Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_SHORT).show()
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.GONE
}
}
// addRecyclerAnimation(searchResultsRc, aAdapter)
// lifecycleScope.launch(Dispatchers.IO) {
// val history = SearchHistory(query = query)
// queryRepository.insert(history)
// queryRepository.loadData()
// val animeList = gogoSrc.search(query)
//
// withContext(Dispatchers.Main)
// {
// if (animeList.isEmpty()) Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_LONG).show()
// adapter.submitList(animeList)
// searchLoading.visibility = View.GONE
// searchResultsRc.visibility = View.VISIBLE
// }
// }
}
@OptIn(DelicateCoroutinesApi::class)
private fun performMovieSearch(query:String)
{
searchResultsRc.visibility = View.GONE
// searchHistoryRC.visibility = View.GONE
isClicked = true
// deleteAll.visibility = View.GONE
searchHistoryFl.visibility = View.GONE
searchLoading.visibility = View.VISIBLE
// val query = searchEt.text.toString()
// GlobalScope.launch(Dispatchers.IO) {
//
// val history = SearchHistory(query = query)
// queryRepository.insert(history)
// queryRepository.loadData()
// }
val history = SearchHistory(query = query)
viewModel.addToHistory(history)
// val retrofit = retrofitBuilder()
//
// val movieService = retrofit.create(MovieService::class.java)
viewModel.searchMovie(query)
viewModel.movieList.observe(viewLifecycleOwner){movies->
if(movies.isNotEmpty()){
mAdapter.submitList(movies)
addRecyclerAnimation(searchResultsRc,mAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
searchResultsRc.requestFocus()
}
else{
// Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_SHORT).show()
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.GONE
}
}
// GlobalScope.launch (Dispatchers.Main){
//
// val searchResults = movieService.searchMovie(
// query,
// BuildConfig.TMDB_API_KEY,
// "en-US"
// )
//
// if (searchResults.isSuccessful)
// {
// val movies = searchResults.body()?.results ?: emptyList()
// if (movies.isEmpty()) Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_LONG).show()
// val adapter = MovieAdapter(movies){
//// val action = SearchFragmentDirections.actionSearchFragmentToMoviePlayActivity(it.id,"movie",title = it.title)
//// findNavController().navigate(action)
// startActivity(passData(it,requireContext()))
// }
// addRecyclerAnimation(searchResultsRc,adapter)
// }
//
// withContext(Dispatchers.Main) {
// searchLoading.visibility = View.GONE
// searchResultsRc.visibility = View.VISIBLE
// }
// }
}
@OptIn(DelicateCoroutinesApi::class)
private fun performShowSearch(query: String)
{
searchResultsRc.visibility = View.GONE
// searchHistoryRC.visibility = View.GONE
searchHistoryFl.visibility = View.GONE
isClicked = true
searchLoading.visibility = View.VISIBLE
// deleteAll.visibility = View.GONE
// val query = searchEt.text.toString()
// GlobalScope.launch(Dispatchers.IO) {
//// searchHistoryDao.deleteAll()
// val history = SearchHistory(query = query)
// queryRepository.insert(history)
// queryRepository.loadData()
// }
val history = SearchHistory(query = query)
viewModel.addToHistory(history)
viewModel.searchTv(query)
viewModel.tvList.observe(viewLifecycleOwner){shows->
if(shows.isNotEmpty()){
tvAdapter.submitList(shows)
addRecyclerAnimation(searchResultsRc,tvAdapter)
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.VISIBLE
searchResultsRc.requestFocus()
}
else{
// Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_SHORT).show()
searchLoading.visibility = View.GONE
searchResultsRc.visibility = View.GONE
}
}
// val retrofit = retrofitBuilder()
//
// val tvService = retrofit.create(TMDBService::class.java)
// GlobalScope.launch (Dispatchers.Main){
//
// val searchResults = tvService.searchShow(
// query,
// BuildConfig.TMDB_API_KEY,
// "en-US"
// )
//
// if (searchResults.isSuccessful)
// {
// val tvShows = searchResults.body()?.results ?: emptyList()
// if (tvShows.isEmpty()) Toast.makeText(requireContext(),"No Matches",Toast.LENGTH_LONG).show()
// val adapter = TVShowAdapter(tvShows){it, _ ->
// val action = SearchFragmentDirections.actionSearchFragmentToTVShowDetails(it.id, title = it.name)
// findNavController().navigate(action)
// }
// addRecyclerAnimation(searchResultsRc,adapter)
// }
// withContext(Dispatchers.Main) {
// searchLoading.visibility = View.GONE
// searchResultsRc.visibility = View.VISIBLE
// }
// }
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TMDBService.kt
================================================
package com.demomiru.tokeiv2
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
import java.io.Serializable
interface TMDBService {
@GET("tv/popular")
suspend fun getPopularTVShows(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response
@GET("trending/tv/day")
suspend fun getTrendingTVShows(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response
@GET("tv/{series_id}")
suspend fun getTVShowDetails(
@Path("series_id") seriesID: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
): Response
@GET("tv/{series_id}/season/{season_number}")
suspend fun getEpisodeDetails(
@Path("series_id") seriesID: String,
@Path("season_number") season: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
) : Response
@GET("search/tv")
suspend fun searchShow(
@Query("query") query: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
) : Response
@GET("tv/top_rated")
suspend fun getTopRatedTVShows(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
) : Response
}
data class TVShowResponse(
val results: List
)
data class TVShowDetailsResponse(
val backdrop_path : String,
val overview : String,
val original_name : String,
val number_of_seasons: String,
val poster_path : String,
val number_of_episodes : Int,
val tagline : String
)
data class TVShowEpisodeDetailsResponse(
val episodes : List
)
data class Episode(
val air_date: String?,
val season_number: String,
val episode_number: String,
val overview: String?,
val name: String?,
val still_path: String?,
) : Serializable
data class Season(
val id : String,
val title:String,
val folder: List
)
data class Folder(
val episode: String,
val folder: List
)
data class EpisodeID(
val file: String, //episode file
val title: String// language
)
data class TvIMDB(
val languages: List,
val external_ids : ExternalIDs,
val number_of_seasons: String,
val origin_country: List
)
data class ExternalIDs(
val imdb_id : String
)
data class ImdbEpisode(
val episodes : List
)
data class EP(
val episode_number: String
)
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TVShowAdapter.kt
================================================
package com.demomiru.tokeiv2
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.paging.PagingDataAdapter
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.demomiru.tokeiv2.MovieAdapter2.Companion.differCallback
import com.demomiru.tokeiv2.utils.retrofitBuilder
class TVShowAdapter(
private val clickHandler : (TVshow,Int) -> Unit
) :
ListAdapter(differCallback) {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.image_view)
val titleTextView: TextView = itemView.findViewById(R.id.title_text_view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tvShow = getItem(position)
holder.titleTextView.text = tvShow.name
holder.imageView
.load("https://image.tmdb.org/t/p/w500${tvShow.poster_path}")
ViewCompat.setTransitionName(holder.imageView, "image_$position")
holder.itemView.setOnClickListener {
clickHandler(tvShow,position)
}
}
companion object {
val differCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: TVshow, newItem: TVshow): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: TVshow, newItem: TVshow): Boolean {
return oldItem == newItem
}
}
}
}
class TvShowPagingSource(private val list: Int): PagingSource() {
private val retrofit = retrofitBuilder()
private val tvService = retrofit.create(TMDBService::class.java)
override suspend fun load(params: LoadParams): LoadResult {
return try {
val currentPage = params.key ?: 1
val response = when(list){
1-> tvService.getPopularTVShows(BuildConfig.TMDB_API_KEY,"en-US",currentPage)
2-> tvService.getTrendingTVShows(BuildConfig.TMDB_API_KEY, "en-US",currentPage)
3-> tvService.getTopRatedTVShows(BuildConfig.TMDB_API_KEY,"en-US",currentPage)
else -> throw Exception("wrong list parameter input")
}
val data = response.body()!!.results
val responseData = mutableListOf()
responseData.addAll(data)
LoadResult.Page(
data = responseData,
prevKey = if (currentPage == 1) null else -1,
nextKey = currentPage.plus(1)
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState): Int? {
return null
}
}
class TVShowAdapter2(private val clickHandler : (TVshow,Int) -> Unit) :
PagingDataAdapter(differCallback) {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.image_view)
val titleTextView: TextView = itemView.findViewById(R.id.title_text_view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tvShow = getItem(position)!!
holder.titleTextView.text = tvShow.name
holder.imageView
.load("https://image.tmdb.org/t/p/w500${tvShow.poster_path}")
ViewCompat.setTransitionName(holder.imageView, "image_$position")
holder.itemView.setOnClickListener {
clickHandler(tvShow,position)
}
holder.setIsRecyclable(false)
}
companion object {
val differCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: TVshow, newItem: TVshow): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: TVshow, newItem: TVshow): Boolean {
return oldItem == newItem
}
}
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TVShowCardAdapter.kt
================================================
package com.demomiru.tokeiv2
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import coil.load
class TVShowCardAdapter(private val tvShows: List>): RecyclerView.Adapter() {
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
val verticalImage: ImageView = itemView.findViewById(R.id.vertical_container)
val horizontalImage1: ImageView = itemView.findViewById(R.id.horizontal_container1)
val horizontalImage2: ImageView = itemView.findViewById(R.id.horizontal_container2)
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.card_view,parent,false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tvShow = tvShows[position % tvShows.size]
holder.verticalImage.load("https://image.tmdb.org/t/p/w500${tvShow[0].poster_path}")
holder.horizontalImage1.load("https://image.tmdb.org/t/p/original${tvShow[1].backdrop_path}")
holder.horizontalImage2.load("https://image.tmdb.org/t/p/original${tvShow[2].backdrop_path}")
}
override fun getItemCount(): Int {
return Int.MAX_VALUE
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TVShowDetails.kt
================================================
package com.demomiru.tokeiv2
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.demomiru.tokeiv2.anime.AnimeEpisodeAdapter
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModel
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModel2
import com.demomiru.tokeiv2.utils.ContinueWatchingViewModelFactory
import com.demomiru.tokeiv2.utils.GogoAnime
import com.demomiru.tokeiv2.utils.dateToUnixTime
import com.demomiru.tokeiv2.utils.dropDownMenu
import com.demomiru.tokeiv2.utils.passData
import com.demomiru.tokeiv2.utils.retrofitBuilder
import com.demomiru.tokeiv2.watching.ContinueWatching
import com.demomiru.tokeiv2.watching.ContinueWatchingDatabase
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class TVShowDetails : Fragment() {
private val args : TVShowDetailsArgs by navArgs()
private val activityViewModel: ContinueWatchingViewModel2 by activityViewModels()
private lateinit var id: String
private lateinit var title: String
private var isAnime: Boolean = false
private var animeDetails = MutableLiveData(null)
private lateinit var viewModelFactory: ContinueWatchingViewModelFactory
private val viewModel: ContinueWatchingViewModel by viewModels(
factoryProducer = {
viewModelFactory
}
)
private var lastPlayedSeason = 1
private lateinit var episodeProgress : ContinueWatching
private val database by lazy { ContinueWatchingDatabase.getInstance(requireContext()) }
private val watchHistoryDao by lazy { database.watchDao() }
private lateinit var episodesRc: RecyclerView
private lateinit var progressBar: ProgressBar
private lateinit var dropDownSpinner: Spinner
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewStateObserver = Observer {watchFrom ->
val continueButton = view.findViewById