Showing preview only (582K chars total). Download the full file or copy to clipboard to get everything.
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
================================================
FILE: .idea/kotlinc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
</component>
</project>
================================================
FILE: .idea/misc.xml
================================================
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="VisualizationToolProject">
<option name="state">
<ProjectState>
<option name="scale" value="0.025" />
</ProjectState>
</option>
</component>
</project>
================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
================================================
FILE: README.md
================================================
# Tokei <img src="https://github.com/Sovan22/Tokeii/blob/master/app/tokei_logo.jpeg" width="40" height="40" alt="Image Description">
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
<img src="https://github.com/Sovan22/Tokeii/blob/master/app/movies.jpg" width="400" height="900" alt="Movies"> <img src="https://github.com/Sovan22/Tokeii/blob/master/app/search.jpg" width="400" height="900" alt="Search">
<img src="https://github.com/Sovan22/Tokeii/blob/master/app/player.jpg" alt="Player">
## 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
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".VideoPlayActivity"
android:screenOrientation="landscape"
android:exported="false" />
<activity
android:name=".MoviePlayActivity"
android:exported="false"
android:screenOrientation="landscape" />
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="fullSensor"
tools:ignore="LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
================================================
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<Int>,
private val clickHandler : (Int) -> Unit
) : RecyclerView.Adapter<EpisodeAdapter.ViewHolder>() {
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<MyItem>() {
// 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<Episode>,
private val clickHandler : (Episode) -> Unit
) : RecyclerView.Adapter<EpisodeAdapter2.ViewHolder>() {
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<List<ContinueWatching>> {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<Prod>,
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<MovieFile>
)
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<Movie,MovieAdapter.ViewHolder>(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<Movie>() {
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<Int, Movie>() {
private val retrofit = retrofitBuilder()
private val movieService = retrofit.create(MovieService::class.java)
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movie> {
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<Movie>()
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, Movie>): Int? {
return null
}
}
class MovieAdapter2(
private val clickHandler : (Movie) -> Unit
) :
PagingDataAdapter<Movie,MovieAdapter2.ViewHolder>(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<Movie>() {
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<GogoAnime.Episode> = 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<String?>()
private var subUrl : MutableList<String> = 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<String,String> = 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<SearchResponse>){
}
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<String,String> = 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<String,String> = 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<MovieResponse>
@GET("trending/movie/day")
suspend fun getTrendingMovies(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response<MovieResponse>
@GET("search/movie")
suspend fun searchMovie(
@Query("query") query: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
) : Response<MovieResponse>
// @GET("movie/{movie_id}")
// suspend fun getImdbId(
// @Path("movie_id") movie_id : String,
// @Query("api_key") apiKey: String,
// @Query("language") language: String
// ) : Response<IdDB>
// @GET("scrape") // Replace with the actual endpoint path
// fun fetchDataFromServer(
// @Header("ngrok-skip-browser-warning") value: String
// ): Response<ServerResponse>
@GET("movie/top_rated")
suspend fun getTopRatedMovies(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
) : Response<MovieResponse>
}
//data class IdDB(
// val id: String,
// val imdb_id: String
//)
data class MovieResponse(
val results: List<Movie>
)
//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<ProgressBar>(R.id.loading_movies).visibility = View.VISIBLE
}
else{
view.findViewById<TextView>(R.id.trending_text).visibility = View.VISIBLE
view.findViewById<TextView>(R.id.movies_text).visibility = View.VISIBLE
view.findViewById<TextView>(R.id.topmovies_text).visibility = View.VISIBLE
view.findViewById<ProgressBar>(R.id.loading_movies).visibility = View.GONE
}
}
// view.findViewById<ProgressBar>(R.id.loading_movies).visibility = View.GONE
// view.findViewById<TextView>(R.id.trending_text).visibility = View.VISIBLE
// view.findViewById<TextView>(R.id.movies_text).visibility = View.VISIBLE
// view.findViewById<TextView>(R.id.topmovies_text).visibility = View.VISIBLE
activityViewModel.trenMovies.collect {
tadapter.submitData(it)
}
}
}
//
}
// withContext(Dispatchers.Main) {
// view.findViewById<ProgressBar>(R.id.loading_movies).visibility = View.GONE
// view.findViewById<TextView>(R.id.trending_text).visibility = View.VISIBLE
// view.findViewById<TextView>(R.id.movies_text).visibility = View.VISIBLE
// view.findViewById<TextView>(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<String>
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<List<SearchHistory>>{
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<TVShowResponse>
@GET("trending/tv/day")
suspend fun getTrendingTVShows(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
): Response<TVShowResponse>
@GET("tv/{series_id}")
suspend fun getTVShowDetails(
@Path("series_id") seriesID: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
): Response<TVShowDetailsResponse>
@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<TVShowEpisodeDetailsResponse>
@GET("search/tv")
suspend fun searchShow(
@Query("query") query: String,
@Query("api_key") apiKey: String,
@Query("language") language: String
) : Response<TVShowResponse>
@GET("tv/top_rated")
suspend fun getTopRatedTVShows(
@Query("api_key") apiKey: String,
@Query("language") language: String,
@Query("page") page: Int
) : Response<TVShowResponse>
}
data class TVShowResponse(
val results: List<TVshow>
)
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<Episode>
)
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<Folder>
)
data class Folder(
val episode: String,
val folder: List<EpisodeID>
)
data class EpisodeID(
val file: String, //episode file
val title: String// language
)
data class TvIMDB(
val languages: List<String>,
val external_ids : ExternalIDs,
val number_of_seasons: String,
val origin_country: List<String>
)
data class ExternalIDs(
val imdb_id : String
)
data class ImdbEpisode(
val episodes : List<EP>
)
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<TVshow,TVShowAdapter.ViewHolder>(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<TVshow>() {
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<Int, TVshow>() {
private val retrofit = retrofitBuilder()
private val tvService = retrofit.create(TMDBService::class.java)
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TVshow> {
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<TVshow>()
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, TVshow>): Int? {
return null
}
}
class TVShowAdapter2(private val clickHandler : (TVshow,Int) -> Unit) :
PagingDataAdapter<TVshow,TVShowAdapter2.ViewHolder>(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<TVshow>() {
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<List<TVshow>>): RecyclerView.Adapter<TVShowCardAdapter.ViewHolder>() {
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<GogoAnime.AnimeDetails>(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<ContinueWatching?> {watchFrom ->
val continueButton = view.findViewById<Button>(R.id.continue_button)
if (watchFrom != null) {
episodeProgress = watchFrom
continueButton.visibility = View.VISIBLE
continueButton.text =
"Continue Watching \t S${watchFrom.season} E${watchFrom.episode}"
lastPlayedSeason = watchFrom.season
}
continueButton.setOnClickListener {
startActivity(passData(watchFrom!!, requireContext()))
}
}
viewModel.watchFrom.observe(viewLifecycleOwner,viewStateObserver)
}
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
@OptIn(DelicateCoroutinesApi::class)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// postponeEnterTransition()
// Inflate the layout for this fragment
activityViewModel.currentFragment.value = R.id.TVShowDetails
val view = inflater.inflate(R.layout.fragment_tv_show_details, container, false)
// val animeDetails = arguments?.getSerializable("anime-details") as? GogoAnime.AnimeDetails
// if (animeDetails!= null) isAnime = true
val animeUrl: String = args.animeUrl
println(animeUrl)
isAnime = animeUrl.length > 5
if (!isAnime) {
id = args.tmdbID
viewModelFactory = ContinueWatchingViewModelFactory(watchHistoryDao, id.toInt())
title = args.title
val position = args.position
}
else
{
id=args.tmdbID
viewModelFactory = ContinueWatchingViewModelFactory(watchHistoryDao, id.toInt())
title = args.title
val gogoSrc = GogoAnime()
lifecycleScope.launch {
val details = gogoSrc.load(animeUrl)
withContext(Dispatchers.Main){
animeDetails.value = details
if (animeDetails!=null){
title = details.title!!
}
else{
Toast.makeText(requireContext(),"Error Loading", Toast.LENGTH_SHORT).show()
}
}
}
}
// val continueButton = view.findViewById<Button>(R.id.continue_button)
// hintTil = view.findViewById(R.id.dropdown_menu)
progressBar = view.findViewById(R.id.progress_circular)
episodesRc = view.findViewById(R.id.episode_display_rc)
val orientation = this.resources.configuration.orientation
if(orientation == Configuration.ORIENTATION_LANDSCAPE)
episodesRc.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
else
episodesRc.layoutManager = LinearLayoutManager(requireContext())
val expandView = view.findViewById<ConstraintLayout>(R.id.expand_tvshow_view)
val titleTv = view.findViewById<TextView>(R.id.title_show)
val backdropImg: ImageView = view.findViewById(R.id.show_backdrop)
val posterImg: ImageView = view.findViewById(R.id.show_poster)
val overview: TextView = view.findViewById(R.id.overview)
val retrofit = retrofitBuilder()
dropDownSpinner = view.findViewById(R.id.dropdown_spinner)
// viewModel.season.observe(viewLifecycleOwner){
// dropDownSpinner.setSelection(it)
// }
// expandView.transitionName = "image_$position"
if (!isAnime){
println(" NOt anime")
val tvService = retrofit.create(TMDBService::class.java)
lifecycleScope.launch(Dispatchers.IO) {
val tvDetailsResponse = tvService.getTVShowDetails(
id,
BuildConfig.TMDB_API_KEY,
"en-US"
)
if (tvDetailsResponse.isSuccessful) {
val tvShows = tvDetailsResponse.body()
withContext(Dispatchers.Main){
posterImg.load("https://image.tmdb.org/t/p/w500${tvShows?.poster_path}")
backdropImg.load("https://image.tmdb.org/t/p/original${tvShows?.backdrop_path}")
overview.text = tvShows?.overview
titleTv.text = title
}
val seasons = dropDownMenu(tvShows!!.number_of_seasons.toInt()) // Fetch the data
val arrayAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
seasons
) // Create an ArrayAdapter
// dropdownMenu = view.findViewById(R.id.autoCompleteTextView)
// dropdownMenu.setAdapter(arrayAdapter) // Set the adapter
withContext(Dispatchers.Main){
dropDownSpinner.adapter = arrayAdapter
dropDownSpinner.setSelection(lastPlayedSeason-1)
}
var seasonNumber: String
dropDownSpinner.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
p1: View?,
position: Int,
p3: Long
) {
val selectedItem = parent?.getItemAtPosition(position) as String
seasonNumber = selectedItem.substringAfter(" ")
Log.i("Season Number", seasonNumber)
viewModel.season.value = position
lifecycleScope.launch(Dispatchers.IO) {
val episodeResponse = tvService.getEpisodeDetails(
id, seasonNumber,
BuildConfig.TMDB_API_KEY,
"en-US"
)
if (episodeResponse.isSuccessful) {
var episodes = episodeResponse.body()?.episodes ?: emptyList()
if(episodes.isNotEmpty()){
val condition: (Episode) -> Boolean = { episode ->
if (episode.air_date == null) true
else
dateToUnixTime(episode.air_date) > System.currentTimeMillis()
}
val repisodes = episodes.toMutableList()
repisodes.removeIf(condition)
episodes = repisodes
}
val adapter = EpisodeAdapter2(episodes) {
startActivity(
passData(
it,
requireContext(),
title,
tvShows.poster_path,
id
)
)
}
val context = episodesRc.context
val controller = AnimationUtils.loadLayoutAnimation(
context,
R.anim.layout_animation
)
withContext(Dispatchers.Main) {
episodesRc.adapter = adapter
episodesRc.layoutAnimation = controller
adapter.notifyDataSetChanged()
episodesRc.scheduleLayoutAnimation()
view.findViewById<TextView>(R.id.episodes_text).visibility =
View.VISIBLE
}
}
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
withContext(Dispatchers.Main) {
view.findViewById<LinearLayout>(R.id.progress_layout).visibility = View.GONE
// view.findViewById<TextView>(R.id.episodes_text).visibility = View.VISIBLE
}
}
}
}else
{
dropDownSpinner.visibility = View.GONE
animeDetails.observe(viewLifecycleOwner) {animeDetails->
if(animeDetails!=null){
view.findViewById<LinearLayout>(R.id.progress_layout).visibility = View.GONE
posterImg.load(animeDetails.poster)
backdropImg.load(animeDetails.poster)
overview.text = animeDetails.description
titleTv.text = animeDetails.title
val episodes = animeDetails.episodes
val adapter = AnimeEpisodeAdapter() { _, epPos->
startActivity(
passData(
animeDetails,
requireContext(),
"0",
epPos,
animeDetails.title?:""
)
)
}
episodesRc.adapter = adapter
adapter.submitList(episodes)
val context = episodesRc.context
val controller = AnimationUtils.loadLayoutAnimation(
context,
R.anim.layout_animation
)
episodesRc.layoutAnimation = controller
adapter.notifyDataSetChanged()
episodesRc.scheduleLayoutAnimation()
view.findViewById<TextView>(R.id.episodes_text).visibility =
View.VISIBLE
}
}
}
return view
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TVShowFragment.kt
================================================
package com.demomiru.tokeiv2
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import androidx.navigation.fragment.findNavController
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
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.playShow
import com.demomiru.tokeiv2.utils.retrofitBuilder
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class TVShowFragment : Fragment() {
private val activityViewModel : ContinueWatchingViewModel2 by activityViewModels()
private lateinit var popTvRc: RecyclerView
private lateinit var trenTvRc: RecyclerView
private lateinit var topTvRc : RecyclerView
@OptIn(DelicateCoroutinesApi::class)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_tv_show, container, false)
popTvRc = view.findViewById(R.id.popular_tvshow_rc)
trenTvRc = view.findViewById(R.id.trending_tvshow_rc)
topTvRc = view.findViewById(R.id.toprated_tvshow_rc)
val retrofit = retrofitBuilder()
topTvRc.layoutManager = LinearLayoutManager(requireContext(),LinearLayoutManager.HORIZONTAL,false)
popTvRc.layoutManager = LinearLayoutManager(requireContext(),LinearLayoutManager.HORIZONTAL,false)
trenTvRc.layoutManager = LinearLayoutManager(requireContext(),LinearLayoutManager.HORIZONTAL,false)
val tvService = retrofit.create(TMDBService::class.java)
lifecycleScope.launch(Dispatchers.IO) {
// val tvPopularShows = Pager(PagingConfig(1)){TvShowPagingSource(1)}.flow.cachedIn(lifecycleScope)
//
//
// val tvTrendingShows = Pager(PagingConfig(1)){TvShowPagingSource(2)}.flow.cachedIn(lifecycleScope)
//
// val tvTopShows = Pager(PagingConfig(1)){TvShowPagingSource(3)}.flow.cachedIn(lifecycleScope)
val topAdapter = TVShowAdapter2{ it, position->
// val action = TVShowFragmentDirections.actionTVShowFragmentToTVShowDetails(it.id)
findNavController().navigate(playShow(it,position,it.name))
}
val trenAdapter = TVShowAdapter2{it, position->
// val action = TVShowFragmentDirections.actionTVShowFragmentToTVShowDetails(it.id)
findNavController().navigate(playShow(it,position,it.name))
}
val popAdapter = TVShowAdapter2{it, position->
// val action = TVShowFragmentDirections.actionTVShowFragmentToTVShowDetails(it.id)
findNavController().navigate(playShow(it,position,it.name))
}
withContext(Dispatchers.Main){
addRecyclerAnimation(popTvRc,popAdapter)
addRecyclerAnimation(trenTvRc,trenAdapter)
addRecyclerAnimation(topTvRc,topAdapter)
lifecycleScope.launch {
activityViewModel.tvTrendingShows.collect{
trenAdapter.submitData(it)
}
}
lifecycleScope.launch {
activityViewModel.tvPopularShows.collect{
popAdapter.submitData(it)
}
}
lifecycleScope.launch {
topAdapter.addLoadStateListener {
val state = it.refresh
val visible = state is LoadState.Loading
if(visible)
{
view.findViewById<ProgressBar>(R.id.loading_tvshow).visibility = View.VISIBLE
}else{
view.findViewById<ProgressBar>(R.id.loading_tvshow).visibility = View.GONE
view.findViewById<TextView>(R.id.trending_text).visibility = View.VISIBLE
view.findViewById<TextView>(R.id.popular_text).visibility = View.VISIBLE
view.findViewById<TextView>(R.id.topShows_text).visibility = View.VISIBLE
}
}
activityViewModel.tvTopShows.collect{
topAdapter.submitData(it)
}
}
}
}
return view
}
override fun onResume() {
activityViewModel.currentFragment.value = R.id.TVShowFragment
super.onResume()
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/TVshow.kt
================================================
package com.demomiru.tokeiv2
data class TVshow(
val id: String,
val name: String,
val poster_path: String,
val backdrop_path: String
)
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/VideoPlayActivity.kt
================================================
@file:Suppress("DEPRECATION")
@file:OptIn(DelicateCoroutinesApi::class)
package com.demomiru.tokeiv2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.demomiru.tokeiv2.watching.VideoData
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.health.connect.datatypes.units.Length
import android.media.AudioManager
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.provider.Settings
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.SeekBar
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Timeline
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.Tracks
import androidx.media3.common.text.CueGroup
import androidx.media3.datasource.DefaultDataSourceFactory
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.source.BaseMediaSource
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.MergingMediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.exoplayer.source.SingleSampleMediaSource
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import androidx.media3.ui.SubtitleView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.demomiru.tokeiv2.subtitles.SubTrackAdapter
import com.demomiru.tokeiv2.subtitles.Subtitle
import com.demomiru.tokeiv2.subtitles.SubtitleConfig
import com.demomiru.tokeiv2.tracks.SourceAdapter
import com.demomiru.tokeiv2.tracks.Track
import com.demomiru.tokeiv2.tracks.TrackAdapter
import com.demomiru.tokeiv2.utils.ExtractedData
import com.demomiru.tokeiv2.utils.Extractor
import com.demomiru.tokeiv2.utils.GoMovies
import com.demomiru.tokeiv2.utils.GogoAnime
import com.demomiru.tokeiv2.utils.OpenSubtitle
import com.demomiru.tokeiv2.utils.SmashyStream
import com.demomiru.tokeiv2.utils.SubAdapter
import com.demomiru.tokeiv2.utils.SuperstreamUtils
import com.demomiru.tokeiv2.utils.VideoViewModel
import com.demomiru.tokeiv2.utils.encodeStringToInt
import com.demomiru.tokeiv2.utils.getHiTvSeasons
import com.demomiru.tokeiv2.utils.getMovieImdb
import com.demomiru.tokeiv2.utils.getSeasonEpisodes
import com.demomiru.tokeiv2.utils.getTvIMDB
import com.demomiru.tokeiv2.utils.getTvImdb
import com.demomiru.tokeiv2.utils.getTvLink
import com.demomiru.tokeiv2.utils.getTvSeasons
import com.demomiru.tokeiv2.utils.setSeekBarTime
import com.demomiru.tokeiv2.watching.ContinueWatching
import com.demomiru.tokeiv2.watching.ContinueWatchingDatabase
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.lang.reflect.Type
import kotlin.Exception
import kotlin.math.abs
class VideoPlayActivity : AppCompatActivity(),AudioManager.OnAudioFocusChangeListener, GestureDetector.OnGestureListener {
private val gson = Gson()
private val superStream = SuperstreamUtils()
private var trackUpdate = 0L
private val fileList: MutableList<String> = mutableListOf()
private val fileLinks: MutableList<String> = mutableListOf()
private var bfapplyUrl = ""
private var setTrackAdapter = 1
private val openSubtitleAPI = BuildConfig.OPEN_SUBTITLE_API_KEY
private var subUpdateProgress = 0L
private var isShowFinished = false
private var animeEpList : List<GogoAnime.Episode>? = null
private lateinit var subSelectBg : ConstraintLayout
private lateinit var sourceSelectRc: RecyclerView
private lateinit var qualitySelectBg: ConstraintLayout
private lateinit var videoLoading: FrameLayout
private lateinit var showSubs: SwitchMaterial
private var totalSeasons = 1
private var totalEpisode = 0
private var superId: Int? = null
private var isNextEpisode = MutableLiveData(false)
// private var isControllerVisible = true
private lateinit var id:String
private var season: Int = 1
private var episode: Int = 1
private var progress : Int = 0
private var imgLink : String? = null
private var year : String = ""
private lateinit var title:String
private var type : String? = null
private var imdbId : String? = null
private var urlMaps: MutableMap<String,String> = mutableMapOf()
private lateinit var unlockIv : ImageView
private var isLocked = false
private lateinit var lockLL : LinearLayout
private val database by lazy { ContinueWatchingDatabase.getInstance(this) }
private val watchHistoryDao by lazy { database.watchDao() }
private lateinit var gestureDetectorCompat : GestureDetectorCompat
private var subList = MutableLiveData<List<String>>()
private lateinit var player : ExoPlayer
private lateinit var goBack : ImageView
private lateinit var videoUri: Uri
private var newVideoUrl = MutableLiveData<String>("")
private var isTrackChanged = false
private var maxVolume: Int = 0
private var brightness: Int = 0
private lateinit var brightnessLL: LinearLayout
private var animeUrl = ""
private lateinit var brightnessSeek : SeekBar
private lateinit var volumeLL: LinearLayout
private lateinit var mainPlayer:FrameLayout
private lateinit var volumeSeek : SeekBar
private var audioManager: AudioManager? = null
private lateinit var seekBar:SeekBar
private var q = ""
private var subUrl : List<String> = listOf()
private var newSubUrl: MutableList<String> = mutableListOf()
private lateinit var mediaSource: MediaSource
private lateinit var videoMediaSource: BaseMediaSource
private lateinit var playPause: ImageButton
private lateinit var titleTv : TextView
private lateinit var screenScale: LinearLayout
private var volume : Int = 0
private var minSwipeY: Float = 0f
private var fit = 1
private var mOrigin: String? = null
private var selectedUrl : String = ""
private var sourcesList = listOf<ExtractedData>()
private var source: String? = ""
private lateinit var powerManager: PowerManager
private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var openSubll : LinearLayout
private val oS = OpenSubtitle(this)
private val handler = Handler(Looper.getMainLooper())
private val updateSeekBarRunnable = object : Runnable {
override fun run() {
seekBar.progress = player.currentPosition.toInt()
subUpdateProgress = player.currentPosition
//TODO get link for next episode after progress > 95
handler.postDelayed(this, 1000)
}
}
@SuppressLint("UnsafeOptInUsageError", "ClickableViewAccessibility", "SetTextI18n",
"SetJavaScriptEnabled"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_play)
powerManager = getSystemService(POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "VideoPlayerActivity:wakelock")
showSubs = findViewById(R.id.switchcompat)
// val sub =PlayerControlView.findViewById<SubtitleView>(R.id.exo_subtitles)
val videoNext = findViewById<LinearLayout>(R.id.videoView_next_ep)
val buffer = findViewById<ProgressBar>(R.id.buffering)
val bundle = intent.extras
val origin = intent.getStringExtra("origin")
var isSuper = intent.getBooleanExtra("superstream",false)
source = intent.getStringExtra("source")
val data = bundle?.getParcelable("VidData") as? VideoData
type = data!!.type
mOrigin = data.origin
year = data.year?: ""
if(type == "anime") {
animeEpList = data.animeEpisode
totalEpisode = animeEpList!!.size
}
id = data.tmdbID.toString()
println("Video Url:" + data.videoUrl)
videoUri = if(isSuper){
q = "720p"
urlMaps = gson.fromJson(data.videoUrl,object : TypeToken<Map<String, String>>() {}.type)
selectedUrl = if(urlMaps["720p"].isNullOrEmpty()) {
q = urlMaps.keys.first()
urlMaps[q]!!
} else
urlMaps["720p"]!!
Uri.parse(selectedUrl)
} else Uri.parse(data.videoUrl)
bfapplyUrl = selectedUrl
progress = data.progress
title = data.title
imgLink = data.imgLink
superId = data.superId
subUrl = data.superSub
imdbId = data.imdbId
println("SuperID: $superId")
if (type != "movie"){
season = data.season
episode = data.episode
}
val videoQuality = findViewById<TextView>(R.id.videoView_quality)
videoQuality.text = if (isSuper) q else "Auto"
val sourceLoading = findViewById<ProgressBar>(R.id.source_loading)
val sourceAdapter = SourceAdapter{
newSubUrl.clear()
newSubUrl.addAll(it.subs)
if(fileLinks.isNotEmpty()){
val l = fileLinks.size
val fl = fileList.size
var j = 1
val map = mutableMapOf<String,String>()
for (i in (fl-l) until fl ){
val lang = fileList[i].substringAfter("lang-").substringBefore("N").replace("%20","")
map["$j OS - $lang"] = fileList[i]
}
val mapString = gson.toJson(map)
val prevSubs = newSubUrl
newSubUrl = (prevSubs + mapString).toMutableList()
}
source = it.source
isSuper = it.isSuper
isTrackChanged = true
setTrackAdapter = 1
newVideoUrl.value = if(!it.isSuper)it.videoUrl else{
urlMaps = gson.fromJson(it.videoUrl,object : TypeToken<Map<String, String>>() {}.type)
selectedUrl = urlMaps["720p"]!!
bfapplyUrl = selectedUrl
selectedUrl
}
subSelectBg.visibility = View.GONE
videoQuality.text = if (isSuper) q else "Auto"
}
sourceSelectRc = findViewById(R.id.source_change_rc)
sourceSelectRc.apply {
layoutManager = LinearLayoutManager(this@VideoPlayActivity)
adapter = sourceAdapter
}
if(type!="anime") {
lifecycleScope.launch(Dispatchers.IO) {
sourcesList = sourcesList + ExtractedData(data.videoUrl, subUrl, source!!, isSuper)
sourcesList = sourcesList + Extractor(mOrigin!!).loadSourceChange(
title,
id,
season,
episode,
year,
type == "movie",
source
)
withContext(Dispatchers.Main) {
sourceLoading.visibility = View.GONE
sourceAdapter.submitList(sourcesList)
}
}
}
window.decorView.apply {
systemUiVisibility = (
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_FULLSCREEN
)
}
gestureDetectorCompat = GestureDetectorCompat(this,this)
var play = true
videoLoading = findViewById(R.id.video_loading_fl)
val webView = findViewById<WebView>(R.id.web_view2)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
println("Superstream : $isSuper")
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
return request?.url.toString() != view?.url
}
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
if (request?.url.toString().endsWith("m3u8")){
// Log.i("Video Link","Found")
lifecycleScope.launch {
newVideoUrl.value = request?.url.toString()
}
}
return super.shouldInterceptRequest(view, request)
}
}
openSubll = findViewById<LinearLayout>(R.id.open_sub_ll)
val addOpenSub = findViewById<Button>(R.id.add_open_sub)
val spinner = findViewById<Spinner>(R.id.spinner)
val search = findViewById<Button>(R.id.search)
val apply = findViewById<Button>(R.id.apply)
val subRv = findViewById<RecyclerView>(R.id.sub_rc)
var langMap : Map<String,String> = mapOf()
var spinList :List<String>
var selectedItem = "English"
var fileLink = ""
var lI = 0
var openSubAdapter = SubAdapter{
fileLink = it
}
subRv.apply {
layoutManager = LinearLayoutManager(this@VideoPlayActivity)
adapter = openSubAdapter
}
lifecycleScope.launch(Dispatchers.IO) {
langMap = oS.getLang()
spinList = langMap.map{
it.key
}
val eI = spinList.indexOfFirst {
it == "English"
}
withContext(Dispatchers.Main){
val arrayAdapter = ArrayAdapter(
this@VideoPlayActivity,
android.R.layout.simple_dropdown_item_1line,
spinList
)
spinner.adapter = arrayAdapter
spinner.setSelection(eI)
}
}
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
p1: View?,
position: Int,
p3: Long
) {
val lang = parent?.getItemAtPosition(position) as String
if(selectedItem != lang){
openSubAdapter = SubAdapter{
fileLink = it
}
subRv.adapter = openSubAdapter
}
selectedItem = lang
}
override fun onNothingSelected(p0: AdapterView<*>?) {
selectedItem = "English"
}
}
search.setOnClickListener {
if (langMap.isEmpty())return@setOnClickListener
val langCode = langMap[selectedItem]!!
lifecycleScope.launch(Dispatchers.IO) {
val sid = if(type=="movie") getMovieImdb(id) else getTvIMDB(id)
val subRes = oS.searchSubs(sid,langCode,season,episode,type=="movie" )
withContext(Dispatchers.Main){
if (subRes.isEmpty()) Toast.makeText(this@VideoPlayActivity,"No Subs for this Language",Toast.LENGTH_SHORT).show()
openSubAdapter.submitList(subRes)
}
}
}
apply.setOnClickListener{
if(fileLink.isBlank()) return@setOnClickListener
if(fileLink in fileLinks) {
Toast.makeText(this@VideoPlayActivity,"Already added", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
lifecycleScope.launch(Dispatchers.IO) {
val fileName = "$title-S$season-E$episode-lang-$selectedItem N$lI"
oS.getSub2(fileLink, selectedItem, fileName)
fileLinks.add(fileLink)
trackUpdate = subUpdateProgress
isTrackChanged = true
withContext(Dispatchers.Main) {
val map: MutableMap<String, String> = mutableMapOf()
lI++
val link = oS.getSRT(fileName).toString()
map["$lI OS - $selectedItem"] = link
if(link !in fileList) {
val mapString = gson.toJson(map)
val prevSubs = subList.value
subList.value = if (prevSubs != null)
prevSubs + mapString
else
listOf(mapString)
fileList.add(link)
}
}
}
}
val remTimeTv = findViewById<TextView>(R.id.videoView_endtime)
val seekForward: ImageButton = findViewById(R.id.videoView_forward)
val seekBack : ImageButton = findViewById(R.id.videoView_rewind)
val subTracks : LinearLayout = findViewById(R.id.videoView_track)
val vidTracks : LinearLayout = findViewById(R.id.videoView_resolution)
// if (isSuper) vidTracks.visibility = View.GONE
if(type == "anime") subTracks.visibility = View.GONE
val skipOp : LinearLayout = findViewById(R.id.videoView_skip_op)
if(type != "tvshow") skipOp.visibility = View.VISIBLE
val skipOpText = findViewById<TextView>(R.id.skip_op_text)
skipOpText.text = if(type == "movie") "Skip CAM Ads" else "Skip OP"
val subSelectionView: RecyclerView = findViewById(R.id.sub_tracks_rc)
subSelectBg = findViewById(R.id.subtitle_select)
qualitySelectBg = findViewById(R.id.quality_select)
val applySub : Button = findViewById(R.id.apply_sub)
val applyQuality : Button = findViewById(R.id.apply_quality)
val screenResizeTv : TextView = findViewById(R.id.screen_resize_text)
val screenResizeIv : ImageView = findViewById(R.id.screen_resize_img)
mainPlayer = findViewById(R.id.main_player)
goBack = findViewById(R.id.videoView_go_back)
screenScale = findViewById(R.id.videoView_screen_size)
lockLL = findViewById(R.id.videoView_lock_screen)
unlockIv = findViewById(R.id.unlock_controls)
brightnessLL = findViewById(R.id.videoView_two_layout)
brightnessSeek = findViewById(R.id.videoView_brightness)
val contentResolver = contentResolver
val currbrightness = Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS)*3
brightnessSeek.max = 30
brightness = currbrightness / 10
if(audioManager == null) audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
maxVolume = audioManager!!.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
Log.i("maxVolume",maxVolume.toString())
volumeLL = findViewById(R.id.volume_ll)
volumeSeek = findViewById(R.id.volume_seek)
volumeSeek.max = maxVolume
volume = maxVolume/2
subSelectionView.layoutManager = LinearLayoutManager(this)
val subtitleView = findViewById<SubtitleView>(R.id.custom_subtitles)
val playerView = findViewById<PlayerView>(R.id.video_view)
if(type!="anime")
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
else {
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
fit = 3
screenResizeTv.text = "Fit"
screenResizeIv.setImageResource(R.drawable.fit_screen)
}
playerView.subtitleView?.visibility = View.GONE
subtitleView.setFractionalTextSize(0.05f)
subtitleView.setApplyEmbeddedStyles(false)
val trackSelector = DefaultTrackSelector(this)
player = ExoPlayer.Builder(this).setTrackSelector(trackSelector).build()
val tracksRv = findViewById<RecyclerView>(R.id.tracks_rc)
tracksRv.layoutManager = LinearLayoutManager(this)
showSubs.setOnClickListener {
if (showSubs.isChecked) subtitleView.visibility = View.VISIBLE
else subtitleView.visibility = View.GONE
}
val a = PlayerView.ControllerVisibilityListener { visibility ->
if(visibility == View.VISIBLE || !showSubs.isChecked) subtitleView.visibility = View.GONE
else subtitleView.visibility = View.VISIBLE
}
playerView.setControllerVisibilityListener(a)
try {
val gestureDetectorDouble =
GestureDetectorCompat(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
if (e.x > playerView.width / 2) {
// Double tapped on the right side - forward seek
player.seekTo(player.currentPosition + 10000)
seekBar.progress = player.currentPosition.toInt()
} else {
// Double tapped on the left side - backward seek
player.seekTo(player.currentPosition - 10000)
seekBar.progress = player.currentPosition.toInt()
}
return super.onDoubleTap(e)
}
// override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
// // Handle single tap event if needed
// if (isControllerVisible) {
// playerView.hideController()
// isControllerVisible = false
// } else {
// playerView.showController()
// isControllerVisible = true
// }
// return super.onSingleTapConfirmed(e)
// }
})
val dataSourceFactory = DefaultHttpDataSource.Factory()
.setAllowCrossProtocolRedirects(true)
.setConnectTimeoutMs(50000)
.setReadTimeoutMs(50000)
.setTransferListener(DefaultBandwidthMeter.Builder(this).build())
playPause = findViewById(R.id.videoView_play_pause_btn)
seekBar = findViewById(R.id.videoView_seekbar)
titleTv = findViewById(R.id.videoView_title)
val listener = object : Player.Listener {
override fun onTracksChanged(tracks: Tracks) {
if (qualitySelectBg.isVisible || subSelectBg.isVisible) return
// if (isSuper && setTrackAdapter == 1) {
// println("Called adapter")
// val trackData = superUrlSelector()
// val ta = TrackAdapter(trackData) {
// isTrackChanged = true
// newSubUrl = subUrl.toMutableList()
// selectedUrl = urlMaps[it.resolution.second]!!
// videoQuality.text = "${it.resolution.second}"
// }
// tracksRv.adapter = ta
// setTrackAdapter--
// }
if (setTrackAdapter == 0) return
else if(player.duration == C.TIME_UNSET) return
else {
val trackGroup = tracks.groups[0]
val trackRv: MutableList<Track> = mutableListOf()
for (i in 0 until trackGroup.length) {
val trackDetails = trackGroup.getTrackFormat(i)
// val selected = trackGroup.isTrackSelected(i)
trackRv.add(
Track(
trackDetails.id!!.toInt(),
trackDetails.label.toString(),
Pair(
trackDetails.width.toString(),
trackDetails.height.toString()
),
selected = false
)
)
}
trackRv.add(
Track(
trackGroup.length,
"Auto",
Pair("", ""),
selected = true
)
)
trackRv.reverse()
val trackAdapter = TrackAdapter(trackRv) { track ->
// val trackSelectionOverride = TrackSelectionOverride(0, track.id)
// track.selected = true
player.trackSelectionParameters = if (track.format != "Auto") {
videoQuality.text = "${track.resolution.second}p"
player.trackSelectionParameters
.buildUpon()
.setOverrideForType(
TrackSelectionOverride(trackGroup.mediaTrackGroup, track.id)
)
.build()
} else {
videoQuality.text = "Auto"
player.trackSelectionParameters
.buildUpon()
.setOverrideForType(
TrackSelectionOverride(
trackGroup.mediaTrackGroup,
MutableList(track.id) { it })
)
.build()
}
// for( i in 0 until ) println(player.currentTracks.groups[0].isTrackSelected(i))
// player.prepare()
}
for (track in trackRv) {
println(track)
}
tracksRv.adapter = trackAdapter
}
super.onTracksChanged(tracks)
}
// override fun onVideoSizeChanged(videoSize: VideoSize) {
// if(videoSize.width !=0 && videoSize.height!=0 ){
// videoQuality.text = "${videoSize.height}p"
// }
// super.onVideoSizeChanged(videoSize)
// }
override fun onPlayerError(error: PlaybackException) {
Toast.makeText(
this@VideoPlayActivity,
"Quality not available or Network Issue",
Toast.LENGTH_SHORT
).show()
super.onPlayerError(error)
}
@SuppressLint("UnsafeOptInUsageError")
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
if (isSuper && setTrackAdapter == 1) {
println("Called adapter")
val trackData = superUrlSelector()
val ta = TrackAdapter(trackData) {
isTrackChanged = true
newSubUrl = subList.value?.toMutableList() ?: mutableListOf()
selectedUrl = urlMaps[it.resolution.second]!!
videoQuality.text = "${it.resolution.second}"
}
tracksRv.adapter = ta
setTrackAdapter--
}
if (player.duration != C.TIME_UNSET) {
// Duration is available
seekBar.max = player.duration.toInt()
subUpdateProgress = trackUpdate
trackUpdate = 0L
isTrackChanged = false
// val seek = player.duration / 100 * progress
// println(subUpdateProgress)
val seek =
if (subUpdateProgress > 0) subUpdateProgress else player.duration / 100 * progress
player.seekTo(seek)
seekBar.progress = seek.toInt()
playerView.setOnTouchListener { _, motionEvent ->
if (!isLocked) {
gestureDetectorDouble.onTouchEvent(motionEvent)
gestureDetectorCompat.onTouchEvent(motionEvent)
if (motionEvent.action == MotionEvent.ACTION_UP) {
if (brightnessLL.visibility == View.VISIBLE || volumeLL.visibility == View.VISIBLE) {
playerView.useController = false
brightnessLL.visibility = View.GONE
volumeLL.visibility = View.GONE
}
}
Handler(Looper.getMainLooper()).postDelayed({
playerView.useController = true
}, 1000)
} else {
if (unlockIv.visibility == View.VISIBLE) {
unlockIv.visibility = View.GONE
} else {
unlockIv.visibility = View.VISIBLE
Handler(Looper.getMainLooper()).postDelayed({
unlockIv.visibility = View.GONE
}, 5000)
}
}
return@setOnTouchListener false
}
}
super.onTimelineChanged(timeline, reason)
}
override fun onPlaybackStateChanged(playbackState: Int) {
if (playbackState == Player.STATE_ENDED) {
if (type == "tvshow") {
if (isShowFinished || (season == totalSeasons && episode == totalEpisode)) finish()
isNextEpisode.value = true
} else finish()
}
else if(playbackState == Player.STATE_BUFFERING) {
buffer.visibility = View.VISIBLE
playPause.visibility = View.GONE
}
else {
playPause.visibility = View.VISIBLE
buffer.visibility = View.GONE
}
super.onPlaybackStateChanged(playbackState)
}
override fun onCues(cueGroup: CueGroup) {
subtitleView.setCues(cueGroup.cues)
super.onCues(cueGroup)
}
}
player.addListener(listener)
if (subUrl.isEmpty() && imdbId!=null) {
println("empty")
// lifecycleScope.launch(Dispatchers.IO) {
// val subs = oS.searchSubs(imdbId!!,"eng",season,episode,type=="movie")
// val fileName = "$title-$season-$episode-English"
// oS.getSub2(subs[0].SubDownloadLink,"English",fileName)
// subList.postValue(subUrl + listOf(oS.getSRT(fileName).toString()))
// }
subList.postValue(subUrl)
} else {
subList.postValue(subUrl)
}
isNextEpisode.observe(this) { isNext ->
if (isNext) {
setTrackAdapter = 1
if (type == "anime") {
getNextAnimeEp()
return@observe
}
println("Source: $source")
episodeNext()
fileLinks.clear()
openSubAdapter = SubAdapter {
fileLink = it
}
subRv.adapter = openSubAdapter
sourceLoading.visibility = View.VISIBLE
lifecycleScope.launch (Dispatchers.IO){
val vidData = Extractor(origin!!).loadExtractorNext(title,id,season,episode,source!!)
withContext(Dispatchers.Main){
if(vidData.videoUrl!=null){
newSubUrl.clear()
newSubUrl.addAll(vidData.subs)
newVideoUrl.value = if(!vidData.isSuper)vidData.videoUrl ?: "" else{
urlMaps = gson.fromJson(vidData.videoUrl,object : TypeToken<Map<String, String>>() {}.type)
selectedUrl = if(urlMaps[q].isNullOrEmpty()){
q = urlMaps.keys.first()
urlMaps[q]!!
}
else urlMaps[q]!!
selectedUrl
}
}
else
{
Toast.makeText(this@VideoPlayActivity,"Link not available", Toast.LENGTH_SHORT).show()
finish()
}
}
val sourceAdapter2 = SourceAdapter{
newSubUrl.clear()
newSubUrl.addAll(it.subs)
if(fileLinks.isNotEmpty()){
val l = fileLinks.size
val fl = fileList.size
var j = 1
val map = mutableMapOf<String,String>()
for (i in (fl-l) until fl ){
val lang = fileList[i].substringAfter("lang-").substringBefore(" ")
map["$j OS - $lang"] = fileList[i]
}
val mapString = gson.toJson(map)
val prevSubs = newSubUrl
newSubUrl = (prevSubs + mapString).toMutableList()
}
source = it.source
isSuper = it.isSuper
isTrackChanged = true
setTrackAdapter = 1
newVideoUrl.value = if(!it.isSuper)it.videoUrl else{
urlMaps = gson.fromJson(it.videoUrl,object : TypeToken<Map<String, String>>() {}.type)
selectedUrl = if(urlMaps[q].isNullOrEmpty()){
q = urlMaps.keys.first()
urlMaps[q]!!
}
else urlMaps[q]!!
bfapplyUrl = selectedUrl
selectedUrl
}
subSelectBg.visibility = View.GONE
videoQuality.text = if (isSuper) q else "Auto"
}
sourceSelectRc.adapter = sourceAdapter2
sourcesList = listOf(ExtractedData(newVideoUrl.value,newSubUrl,source!!,isSuper))
sourcesList = sourcesList + Extractor(mOrigin!!).loadSourceChange(title,id,season,episode,year,type == "movie",source)
withContext(Dispatchers.Main){
sourceLoading.visibility = View.GONE
sourceAdapter2.submitList(sourcesList)
}
}
// if (origin == "hi") {
// episodeNext()
// if (!isShowFinished)
// lifecycleScope.launch(Dispatchers.IO) {
// val res =
// imdbId?.let { getTvLink(it, season - 1, episode - 1) } ?: ""
//
// if (res.isNotBlank()) newVideoUrl.postValue(res)
// else {
// withContext(Dispatchers.Main) {
// Toast.makeText(
// this@VideoPlayActivity,
// "Not Available",
// Toast.LENGTH_SHORT
// ).show()
// finish()
// }
// }
// }
// } else {
// episodeNext()
// var isVideo = false
// if (!isShowFinished) {
// lifecycleScope.launch(Dispatchers.IO) {
// if (superId != null && isSuper) {
// val tvLinks =
// superStream.loadLinks(false, superId!!, season, episode)
// tvLinks.data?.list?.forEach {
// if (!it.path.isNullOrBlank()) {
// println("${it.quality} : ${it.path}")
// if (it.quality == "720p") {
// val subtitle = superStream.loadSubtile(
// false,
// it.fid!!,
// superId!!,
// season,
// episode
// ).data
// getSub(subtitle)
// if (it.path.isNullOrBlank()) isVideo = true
// newVideoUrl.postValue(it.path!!)
// }
// }
// }
// if (isVideo) {
//// withContext(Dispatchers.Main){
//// Toast.makeText(this@VideoPlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
//// finish()
//// }
// getGoMovieLink()
//// getSmashLink()
// }
// } else {
//// withContext(Dispatchers.Main){
//// Toast.makeText(this@VideoPlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
//// finish()
//// }
// getGoMovieLink()
//// getSmashLink()
// }
// }
// }
// }
isNextEpisode.value = false
}
}
// When Subtitles are available
subList.observe(this) {
fileLink = ""
val subtitleConfig: MutableList<SubtitleConfig> = mutableListOf()
var s = 0
// videoQuality.text = if (isSuper) "720p" else "Auto"
if (!it.isNullOrEmpty()) {
val map = it[0]
var subMap : MutableMap<String,String> = mutableMapOf()
if(!map.isNullOrBlank()) subMap = gson.fromJson(map, object : TypeToken<Map<String, String>>() {}.type)
for( key in subMap.keys){
val subs = subMap[key]!!.split(",")
var i = 1
for (element in subs) {
val subtitleMediaSource = if (element.contains("vtt")) {
SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.SubtitleConfiguration.Builder(Uri.parse(element))
.setMimeType(MimeTypes.TEXT_VTT)
.setLanguage("en")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.build(),
C.TIME_UNSET
)
}else if (element.contains("file://")) {
SingleSampleMediaSource.Factory(DefaultDataSourceFactory(this@VideoPlayActivity,"user-agent")).createMediaSource(
MediaItem.SubtitleConfiguration.Builder(Uri.parse(element))
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
.setLanguage("en")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.build(),
C.TIME_UNSET
)
}
else {
SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.SubtitleConfiguration.Builder(Uri.parse(element))
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
.setLanguage("en")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.build(),
C.TIME_UNSET
)
}
subtitleConfig.add(SubtitleConfig(subtitleMediaSource, language = "$key $i"))
if(i==6)break
i++
}
}
s = subtitleConfig.indexOfFirst {subConfig ->
subConfig.language.contains("English")
}
if(it.size > 1){
for( i in 1 until it.size){
val map = it[i]
var subMap : MutableMap<String,String> = mutableMapOf()
if(!map.isNullOrBlank()) subMap = gson.fromJson(map, object : TypeToken<Map<String, String>>() {}.type)
for( key in subMap.keys){
val subtitleMediaSource =
SingleSampleMediaSource.Factory(DefaultDataSourceFactory(this@VideoPlayActivity,"user-agent")).createMediaSource(
MediaItem.SubtitleConfiguration.Builder(Uri.parse(subMap[key]))
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
.setLanguage("en")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.build(),
C.TIME_UNSET
)
subtitleConfig.add(0,SubtitleConfig(subtitleMediaSource, language = "$key"))
s = 0
}
}
}
// change from here
// for (element in it) {
// println(element)
// val subtitleMediaSource = if (element.contains("vtt")) {
// SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(
// MediaItem.SubtitleConfiguration.Builder(Uri.parse(element))
// .setMimeType(MimeTypes.TEXT_VTT)
// .setLanguage("en")
// .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
// .build(),
// C.TIME_UNSET
// )
// } else {
// SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(
// MediaItem.SubtitleConfiguration.Builder(Uri.parse(element))
// .setMimeType(MimeTypes.APPLICATION_SUBRIP)
// .setLanguage("en")
// .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
// .build(),
// C.TIME_UNSET
// )
//
// }
// subtitleConfig.add(SubtitleConfig(subtitleMediaSource))
// }
// val
videoMediaSource = if (isSuper || source == "nowtv") {
if(source == "nowtv"){
val dsF = DefaultHttpDataSource.Factory()
.setDefaultRequestProperties(mapOf("Referer" to "https://bflix.gs/",
"Connection" to "keep-alive"
))
.setAllowCrossProtocolRedirects(true)
.setConnectTimeoutMs(50000)
.setReadTimeoutMs(50000)
.setTransferListener(DefaultBandwidthMeter.Builder(this).build())
println("NowTV insiide videomedia")
ProgressiveMediaSource.Factory(dsF)
.createMediaSource(MediaItem.fromUri(videoUri))
}else
ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(videoUri))
} else {
HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(videoUri))
}
// player.setMediaItem(mediaItem)
subSelectionView.adapter = SubTrackAdapter(subtitleConfig, s){ sub ->
val newMediaSource = MergingMediaSource(videoMediaSource, sub.subConfig)
subUpdateProgress = player.currentPosition
trackUpdate = subUpdateProgress
player.setMediaSource(newMediaSource)
player.prepare()
// player.seekTo(subUpdateProgress)
// seekBar.progress = subUpdateProgress.toInt()
}
mediaSource = MergingMediaSource(videoMediaSource, subtitleConfig[s].subConfig)
} else {
subSelectionView.adapter = SubTrackAdapter(subtitleConfig, s) {}
mediaSource = if (isSuper) {
ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(videoUri))
} else {
HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(videoUri))
}
}
player.setMediaSource(mediaSource)
if (type == "movie")
titleTv.text = title
else {
videoNext.visibility = View.VISIBLE
titleTv.text =
if (type == "tvshow") "$title S$season E$episode" else "$title E${episode + 1}"
}
mainPlayer.visibility = View.VISIBLE
playerView.player = player
videoLoading.visibility = View.GONE
player.prepare()
player.playWhenReady = true
println("${player.videoSize.width} x ${player.videoSize.height}")
playerPlay()
subTracks.setOnClickListener {
playerPause()
mainPlayer.visibility = View.GONE
subSelectBg.visibility = View.VISIBLE
}
addOpenSub.setOnClickListener {
openSubll.visibility = View.VISIBLE
subSelectBg.visibility = View.GONE
}
vidTracks.setOnClickListener {
playerPause()
mainPlayer.visibility = View.GONE
qualitySelectBg.visibility = View.VISIBLE
}
applyQuality.setOnClickListener {
qualitySelectBg.visibility = View.GONE
mainPlayer.visibility = View.VISIBLE
if(isSuper) {
if (bfapplyUrl != selectedUrl)
newVideoUrl.value = selectedUrl
}
player.seekTo(subUpdateProgress)
seekBar.progress = subUpdateProgress.toInt()
playerPlay()
bfapplyUrl = selectedUrl
}
applySub.setOnClickListener {
subSelectBg.visibility = View.GONE
mainPlayer.visibility = View.VISIBLE
player.seekTo(subUpdateProgress)
seekBar.progress = subUpdateProgress.toInt()
playerPlay()
}
playPause.setOnClickListener {
play = if (play) {
player.pause()
playPause.setImageResource(R.drawable.icon_play)
false
} else {
player.play()
playPause.setImageResource(R.drawable.netflix_pause_button)
true
}
}
seekForward.setOnClickListener {
val progress = (player.currentPosition + 10000)
player.seekTo(progress)
seekBar.progress = progress.toInt()
seekForward.rotation = 90f
Handler(Looper.getMainLooper()).postDelayed({
seekForward.rotation = 0f
}, 1000)
}
seekBack.setOnClickListener {
val progress = (player.currentPosition - 10000)
player.seekTo(progress)
seekBar.progress = progress.toInt()
seekBack.rotation = -90f
Handler(Looper.getMainLooper()).postDelayed({
seekBack.rotation = 0f
}, 1000) // Delay of 1 second
}
screenScale.setOnClickListener {
when (fit) {
1 -> {
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
screenResizeTv.text = "Fill"
screenResizeIv.setImageResource(R.drawable.fill_screen)
fit = 2
}
2 -> {
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
screenResizeTv.text = "Fit"
screenResizeIv.setImageResource(R.drawable.fit_screen)
fit = 3
}
else -> {
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
screenResizeTv.text = "Zoom"
screenResizeIv.setImageResource(R.drawable.baseline_zoom_out_map_24)
fit = 1
}
}
}
lockLL.setOnClickListener {
isLocked = true
playerView.useController = false
// unlockIv.visibility = View.VISIBLE
}
goBack.setOnClickListener {
finish()
}
unlockIv.setOnClickListener {
isLocked = false
playerView.useController = true
unlockIv.visibility = View.GONE
}
videoNext.setOnClickListener {
isNextEpisode.value = true
}
skipOp.setOnClickListener {
val progress = (player.currentPosition + 90000)
player.seekTo(progress)
seekBar.progress = progress.toInt()
}
}
newVideoUrl.observe(this) { newUrl ->
if (!newUrl.isNullOrEmpty()) {
videoUri = Uri.parse(newUrl)
if (!isTrackChanged) {
if(isSuper) videoQuality.text = "720p" else videoQuality.text = "Auto"
progress = 0
subUpdateProgress = 0
} else {
// newSubUrl = subUrl.toMutableList()
trackUpdate = subUpdateProgress
}
isTrackChanged = false
subList.value = newSubUrl.toList()
// if (newSubUrl.isEmpty() && type != "anime") {
// println("empty")
// lifecycleScope.launch(Dispatchers.IO) {
// val fileID: List<String> = if (type == "movie") {
// getSubtitles(id)
// } else {
// getSubtitles(id, season, episode)
// }
//
// subList.postValue(getAuthToken(fileID))
// }
// } else {
// subList.postValue(newSubUrl.toList())
// }
}
}
if (type == "tvshow") {
lifecycleScope.launch(Dispatchers.IO) {
totalSeasons =
if (origin != "hi") getTvSeasons(id) else getHiTvSeasons(imdbId!!)
totalEpisode = getSeasonEpisodes(id, season)
}
}
// if (seekBar.progress == seekBar.max)isNextEpisode.value = true
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
if (player.duration != C.TIME_UNSET) {
val rem = player.duration - progress.toLong()
if (fromUser) {
player.seekTo(progress.toLong())
subUpdateProgress = player.currentPosition
remTimeTv.text = setSeekBarTime(rem)
}
remTimeTv.text = setSeekBarTime(rem)
subUpdateProgress = player.currentPosition
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
// Optional: stop updating the SeekBar while the user is dragging it
handler.removeCallbacks(updateSeekBarRunnable)
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
// Optional: resume updating the SeekBar after the user finished dragging it
handler.post(updateSeekBarRunnable)
}
})
// Start updating the SeekBar
handler.post(updateSeekBarRunnable)
}catch (e :Exception){e.printStackTrace()}
}
// private suspend fun getGoMovieLink(){
// val goMovie = GoMovies()
// newSubUrl.clear()
// lifecycleScope.launch(Dispatchers.IO) {
// val data = goMovie.search(season, episode, title,false,year)
// val vidLink = data.first
// val subLinks = data.second
// if(vidLink.isNullOrBlank()){
//// withContext(Dispatchers.Main){
//// Toast.makeText(this@VideoPlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
//// finish()
//// }
// getSmashLink()
// }
// else{
// if (!subLinks.isNullOrEmpty())newSubUrl.addAll(subLinks)
// newVideoUrl.postValue(vidLink!!)
// }
// }
// }
// private fun getSmashLink()
// {
// newSubUrl.clear()
// val smashSrc = SmashyStream()
// lifecycleScope.launch(Dispatchers.IO) {
// val links = smashSrc.getLink(false,id, season, episode)
// val vidLink = links.first
// val subLink = links.second
// if(vidLink.isNullOrBlank()){
// withContext(Dispatchers.Main){
// Toast.makeText(this@VideoPlayActivity, "Not Available",Toast.LENGTH_SHORT).show()
// finish()
// }
// }
// else{
// if (!subLink.isNullOrBlank())newSubUrl.add(subLink)
// newVideoUrl.postValue(vidLink!!)
// }
// }
// }
//
// private fun getSub(subtitle: SuperstreamUtils.PrivateSubtitleData?){
// newSubUrl.clear()
// subtitle?.list?.forEach { subList->
// if(subList.language == "English"){
// subList.subtitles.forEach { sub->
//
// if (newSubUrl.size == 3) {
// return
// }
// if (sub.lang == "en" && !sub.file_path.isNullOrBlank()) {
// newSubUrl.add(sub.file_path)
// println("${sub.language} : ${sub.file_path}")
// }
//
//
// }
// return
// }
// }
// }
private fun getNextAnimeEp()
{
val gogoSrc = GogoAnime()
episode+=1
if (episode == totalEpisode) {
Toast.makeText(this, "No further episodes", Toast.LENGTH_SHORT).show()
isShowFinished = true
episode = totalEpisode-1
return
}
animeUrl = animeEpList?.get(episode)?.url ?: ""
mainPlayer.visibility = View.GONE
playerPause()
videoLoading.visibility = View.VISIBLE
lifecycleScope.launch {
newVideoUrl.postValue(gogoSrc.extractVideos(animeUrl))
}
}
private fun getAuthToken(fileId: List<String>): List<String>{
val client = OkHttpClient()
val mediaType = "application/json".toMediaTypeOrNull()
val body =
"{\n \"username\": \"bokaboy_20\",\n \"password\": \"d.omh.err.y61.7@gmail.com\"\n}".toRequestBody(mediaType)
try {
val request = Request.Builder()
.url("https://api.opensubtitles.com/api/v1/login")
.post(body)
.addHeader("Content-Type", "application/json")
.addHeader("User-Agent", "")
.addHeader("Accept", "application/json")
.addHeader("Api-Key", openSubtitleAPI)
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val gson = Gson()
val responseBody = response.body
val type: Type = object : TypeToken<Map<String?, Any?>?>() {}.type
val map: Map<String, Any> = gson.fromJson(responseBody.string(), type)
val token = map["token"].toString()
Log.i("response", token)
responseBody.close()
val downloadUrlList = mutableListOf<String>()
for (element in fileId) {
downloadUrlList.add(getSubtitleURl(token, element))
}
return downloadUrlList.toList()
} else
Log.i("response", "unsuccessful")
}catch (e :Exception){
e.printStackTrace()
return listOf()
}
return listOf()
}
private fun getSubtitleURl(token:String, fileId: String):String{
val client = OkHttpClient()
val mediaType = "application/json".toMediaTypeOrNull()
val body = "{\n \"file_id\": $fileId\n}".toRequestBody(mediaType)
try{
val request = Request.Builder()
.url("https://api.opensubtitles.com/api/v1/download")
.post(body)
.addHeader("User-Agent", "")
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("Api-Key", openSubtitleAPI)
.addHeader("Authorization", token)
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful){
val gson = Gson()
val responseBody = response.body
val type: Type = object : TypeToken<Map<String?, Any?>?>() {}.type
val map: Map<String, Any> = gson.fromJson(responseBody.string(), type)
responseBody.close()
return map["link"].toString()
}
}catch (e :Exception){
e.printStackTrace()
return ""
}
return ""
}
private fun getSubtitles(tmdbId:String, season:Int = 0, episode:Int = 0) : List<String>{
val client = OkHttpClient()
try{
val request : Request
if(type == "movie") {
request = Request.Builder()
.url("https://api.opensubtitles.com/api/v1/subtitles?tmdb_id=$tmdbId&type=movie&languages=en&order_by=ratings&page=1")
.get()
.addHeader("User-Agent", "")
.addHeader("Api-Key", openSubtitleAPI)
.build()
}
else{
request = Request.Builder()
.url("https://api.opensubtitles.com/api/v1/subtitles?languages=en&order_by=ratings&parent_tmdb_id=$tmdbId&season_number=$season&episode_number=$episode&page=1")
.get()
.addHeader("User-Agent", "")
.addHeader("Api-Key", openSubtitleAPI)
.build()
}
val response = client.newCall(request).execute()
if (response.isSuccessful){
val gson = Gson()
val responseBody = response.body
val subtitle: Subtitle = gson.fromJson(responseBody.string(), Subtitle::class.java)
responseBody.close()
val fileIDs = mutableListOf<String>()
val count = if(subtitle.data.size> 3) 3
else subtitle.data.size
for (i in 0 until count){
fileIDs.add(subtitle.data[i].attributes.files[0].file_id)
}
return fileIDs.toList()
}
}catch (e :Exception){
e.printStackTrace()
return listOf()
}
return listOf()
}
private fun playerPause(){
player.pause()
playPause.setImageResource(R.drawable.icon_play)
}
private fun playerPlay(){
player.play()
playPause.setImageResource(R.drawable.netflix_pause_button)
}
override fun onPause(){
wakeLock.release()
player.pause()
playPause.setImageResource(R.drawable.icon_play)
super.onPause()
}
override fun onDestroy() {
handler.removeCallbacks(updateSeekBarRunnable)
val currentPosition = player.currentPosition
val duration = player.duration
val progress = (currentPosition * 100 / duration).toInt()
GlobalScope.launch(Dispatchers.IO) {
if (type == "movie") {
if (progress in 3..96){
watchHistoryDao.insert(
ContinueWatching(
progress = progress,
imgLink = imgLink!!,
tmdbID = id.toInt(),
title = title,
type = type!!,
origin = mOrigin,
year = year
)
)
}
} else if (type == "tvshow") {
watchHistoryDao.insert(
ContinueWatching(
progress = progress,
imgLink = imgLink!!,
tmdbID = id.toInt(),
title = title,
season = season,
episode = episode,
type = type!!
)
)
}
else {
watchHistoryDao.insert(
ContinueWatching(
progress = progress,
imgLink = imgLink!!,
tmdbID = encodeStringToInt(title),
title = title,
season = season,
episode = episode,
type = type!!,
animeEp = animeEpList
)
)
}
}
Log.i("Progress", progress.toString())
player.playWhenReady = false
player.stop()
player.seekTo(0)
player.release() // or pause, depending on your requirements
lifecycleScope.launch (Dispatchers.IO) {
for (s in fileList) {
val path = Uri.parse(s).path
path?.let {
File(path).delete()
}
}
}
Log.i("Finish", "Called finish() go back pressed")
super.onDestroy()
}
override fun onDown(p0: MotionEvent): Boolean = false
override fun onShowPress(p0: MotionEvent) = Unit
override fun onSingleTapUp(p0: MotionEvent): Boolean = false
override fun onScroll(event: MotionEvent?, event1: MotionEvent, dX: Float, dY: Float): Boolean {
minSwipeY += dY
val sWidth = Resources.getSystem().displayMetrics.widthPixels
if(abs(dX)< abs(dY) && abs(minSwipeY) > 50){
if(event!!.x < sWidth/2){
brightnessLL.visibility = View.VISIBLE
volumeLL.visibility = View.GONE
val increase = dY > 0
val newValue = if(increase) brightness + 1 else brightness - 1
if(newValue in 0..30) brightness = newValue
brightnessSeek.progress = brightness
setScreenBrightness(brightness)
}
else{
brightnessLL.visibility = View.GONE
volumeLL.visibility = View.VISIBLE
val increase = dY > 0
val newValue = if(increase) volume + 5 else volume - 5
if(newValue in 0..maxVolume) volume = newValue
volumeSeek.progress = volume
audioManager!!.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0)
}
minSwipeY = 0f
}
return true
}
private fun setScreenBrightness(value: Int){
val d = 1.0f/30
val lp = this.window.attributes
lp.screenBrightness = d * value
this.window.attributes = lp
}
private fun episodeNext(){
if (episode < totalEpisode) {
mainPlayer.visibility = View.GONE
playerPause()
videoLoading.visibility = View.VISIBLE
episode += 1
} else {
if (season < totalSeasons) {
season += 1
playerPause()
lifecycleScope.launch(Dispatchers.IO) {
totalEpisode = getSeasonEpisodes(id, season)
}
mainPlayer.visibility = View.GONE
videoLoading.visibility = View.VISIBLE
episode = 1
} else {
Toast.makeText(
this@VideoPlayActivity,
"ShowFinished",
Toast.LENGTH_SHORT
).show()
isShowFinished = true
}
}
}
private fun superUrlSelector() : List<Track>
{
var id = 0
var selectPos = 0
var tracks = urlMaps.map {
val selected = it.key == q
val resolution = Pair("",it.key)
if(selected)selectPos = id
Track(id++,"super",resolution,selected)
}
val selectedQ = tracks.get(selectPos)
tracks = tracks - selectedQ
tracks = tracks + selectedQ
tracks = tracks.reversed()
println(tracks)
return tracks
}
@SuppressLint("WakelockTimeout")
override fun onResume() {
super.onResume()
wakeLock.acquire()
if(audioManager == null) audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager!!.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
if(brightness != 0) setScreenBrightness(brightness)
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if(subSelectBg.visibility != View.VISIBLE && !qualitySelectBg.isVisible && !openSubll.isVisible)
super.onBackPressed()
else if (openSubll.isVisible){
openSubll.visibility = View.GONE
subSelectBg.visibility = View.VISIBLE
}
else{
subSelectBg.visibility = View.GONE
qualitySelectBg.visibility = View.GONE
mainPlayer.visibility = View.VISIBLE
playerPlay()
}
}
override fun onLongPress(p0: MotionEvent) = Unit
override fun onFling(p0: MotionEvent?, p1: MotionEvent, p2: Float, p3: Float): Boolean = false
override fun onAudioFocusChange(focusChange: Int) {
if(focusChange <= 0) playerPause()
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/anime/AnimeAdapter.kt
================================================
package com.demomiru.tokeiv2.anime
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.ImageLoader
import coil.disk.DiskCache
import coil.load
import coil.memory.MemoryCache
import com.demomiru.tokeiv2.R
import com.demomiru.tokeiv2.utils.GogoAnime
class AnimeAdapter(context: Context, private val onClick: (GogoAnime.AnimeSearchResponse) -> Unit): ListAdapter<GogoAnime.AnimeSearchResponse, AnimeAdapter.ViewHolder>(
DiffCallBack
) {
private val imageLoader = ImageLoader.Builder(context)
.memoryCache { MemoryCache.Builder(context).maxSizePercent(0.25).build() }
.build()
class ViewHolder(view: View) : RecyclerView.ViewHolder(view){
val imageView : ImageView = view.findViewById(R.id.image_view)
val title : TextView = view.findViewById(R.id.title_text_view)
val dub: TextView = view.findViewById(R.id.tv_show_detail_tv)
}
object DiffCallBack: DiffUtil.ItemCallback<GogoAnime.AnimeSearchResponse>(){
override fun areItemsTheSame(
oldItem: GogoAnime.AnimeSearchResponse,
newItem: GogoAnime.AnimeSearchResponse
): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(
oldItem: GogoAnime.AnimeSearchResponse,
newItem: GogoAnime.AnimeSearchResponse
): Boolean {
return oldItem == newItem
}
}
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 anime = getItem(position)
holder.imageView.load(anime.posterUrl,imageLoader)
holder.title.text = anime.name
if(anime.dub!=null){
holder.dub.visibility = if(anime.dub) View.VISIBLE else View.GONE
}
holder.itemView.setOnClickListener {
onClick(anime)
}
}
}
class AnimeEpisodeAdapter(private val onClick: (GogoAnime.Episode,Int) -> Unit) : ListAdapter<GogoAnime.Episode, AnimeEpisodeAdapter.ViewHolder>(
DiffCallBack
){
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
val episodeText : TextView = itemView.findViewById(R.id.episode_no_text)
}
object DiffCallBack : DiffUtil.ItemCallback<GogoAnime.Episode>() {
override fun areItemsTheSame(
oldItem: GogoAnime.Episode,
newItem: GogoAnime.Episode
): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(
oldItem: GogoAnime.Episode,
newItem: GogoAnime.Episode
): Boolean {
return oldItem == newItem
}
}
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)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val episode = getItem(position)
holder.episodeText.text = episode.name
holder.itemView.setOnClickListener {
onClick(episode,position)
}
}
}
================================================
FILE: app/src/main/java/com/demomiru/tokeiv2/anime/AnimeDetailsFragment.kt
================================================
package com.demomiru.tokeiv2.anime
import android.annotation.SuppressLint
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.Button
import android.widget.ImageView
import android.widget.LinearLayout
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.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import coil.load
import com.demomiru.tokeiv2.R
import com.demomiru.tokeiv2.TVShowDetailsArgs
import com.demomiru.tokeiv2.databinding.FragmentAnimeDetailsBinding
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.encodeStringToInt
import com.demomiru.tokeiv2.utils.passData
import com.demomiru.tokeiv2.watching.ContinueWatchi
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
Condensed preview — 185 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (579K chars).
[
{
"path": ".gitignore",
"chars": 225,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
},
{
"path": ".idea/.gitignore",
"chars": 47,
"preview": "# Default ignored files\n/shelf/\n/workspace.xml\n"
},
{
"path": ".idea/compiler.xml",
"chars": 169,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"CompilerConfiguration\">\n <bytecodeTar"
},
{
"path": ".idea/gradle.xml",
"chars": 680,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"GradleMigrationSettings\" migrationVersio"
},
{
"path": ".idea/kotlinc.xml",
"chars": 175,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"KotlinJpsPluginSettings\">\n <option na"
},
{
"path": ".idea/misc.xml",
"chars": 584,
"preview": "<project version=\"4\">\n <component name=\"ExternalStorageConfigurationManager\" enabled=\"true\" />\n <component name=\"Proje"
},
{
"path": ".idea/vcs.xml",
"chars": 180,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"VcsDirectoryMappings\">\n <mapping dire"
},
{
"path": "README.md",
"chars": 1800,
"preview": "# Tokei <img src=\"https://github.com/Sovan22/Tokeii/blob/master/app/tokei_logo.jpeg\" width=\"40\" height=\"40\" alt=\"Imag"
},
{
"path": "app/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "app/build.gradle",
"chars": 4025,
"preview": "\n\nplugins {\n id 'com.android.application'\n id 'org.jetbrains.kotlin.android'\n id(\"androidx.navigation.safeargs."
},
{
"path": "app/proguard-rules.pro",
"chars": 750,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "app/src/androidTest/java/com/demomiru/tokeiv2/ExampleInstrumentedTest.kt",
"chars": 667,
"preview": "package com.demomiru.tokeiv2\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.r"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 1713,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:to"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/EpisodeAdapter.kt",
"chars": 1171,
"preview": "package com.demomiru.tokeiv2\n\n\n\nimport android.annotation.SuppressLint\nimport android.view.LayoutInflater\nimport android"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/EpisodeAdapter2.kt",
"chars": 3041,
"preview": "package com.demomiru.tokeiv2\n\n\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGrou"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/MainActivity.kt",
"chars": 10402,
"preview": "package com.demomiru.tokeiv2\n\nimport android.annotation.SuppressLint\nimport android.app.AlertDialog\nimport android.conte"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/Movie.kt",
"chars": 675,
"preview": "package com.demomiru.tokeiv2\n\nimport java.io.Serializable\n\n\ndata class Movie(\n val id : String,\n val original_lang"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/MovieAdapter.kt",
"chars": 4762,
"preview": "package com.demomiru.tokeiv2\n\nimport android.annotation.SuppressLint\nimport android.view.LayoutInflater\nimport android.v"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/MoviePlayActivity.kt",
"chars": 25617,
"preview": "package com.demomiru.tokeiv2\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.conte"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/MovieService.kt",
"chars": 1623,
"preview": "package com.demomiru.tokeiv2\n\nimport retrofit2.Response\nimport retrofit2.http.GET\n\n\nimport retrofit2.http.Query\n\ninterfa"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/MoviesFragment.kt",
"chars": 8931,
"preview": "package com.demomiru.tokeiv2\n\n\n\nimport android.os.Bundle\nimport androidx.fragment.app.Fragment\nimport android.view.Layou"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/SearchFragment.kt",
"chars": 21556,
"preview": "@file:OptIn(DelicateCoroutinesApi::class)\n\npackage com.demomiru.tokeiv2\n\n\nimport android.content.Context\nimport android."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TMDBService.kt",
"chars": 2770,
"preview": "package com.demomiru.tokeiv2\n\nimport retrofit2.Response\nimport retrofit2.http.GET\nimport retrofit2.http.Path\nimport retr"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TVShowAdapter.kt",
"chars": 4834,
"preview": "package com.demomiru.tokeiv2\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\n"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TVShowCardAdapter.kt",
"chars": 1440,
"preview": "package com.demomiru.tokeiv2\n\n\nimport androidx.recyclerview.widget.RecyclerView\nimport android.view.LayoutInflater\nimpor"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TVShowDetails.kt",
"chars": 13553,
"preview": "package com.demomiru.tokeiv2\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\n\nimport an"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TVShowFragment.kt",
"chars": 5199,
"preview": "package com.demomiru.tokeiv2\n\nimport android.os.Bundle\n\nimport androidx.fragment.app.Fragment\nimport android.view.Layout"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/TVshow.kt",
"chars": 152,
"preview": "package com.demomiru.tokeiv2\n\ndata class TVshow(\n val id: String,\n val name: String,\n val poster_path: String,\n"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/VideoPlayActivity.kt",
"chars": 73703,
"preview": "@file:Suppress(\"DEPRECATION\")\n@file:OptIn(DelicateCoroutinesApi::class)\n\npackage com.demomiru.tokeiv2\n\nimport androidx.a"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/anime/AnimeAdapter.kt",
"chars": 3593,
"preview": "package com.demomiru.tokeiv2.anime\n\nimport android.content.Context\nimport android.view.LayoutInflater\nimport android.vie"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/anime/AnimeDetailsFragment.kt",
"chars": 8415,
"preview": "package com.demomiru.tokeiv2.anime\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport android.util."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/anime/AnimeFragment.kt",
"chars": 4976,
"preview": "package com.demomiru.tokeiv2.anime\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/anime/AnimeInfo.kt",
"chars": 6571,
"preview": "package com.demomiru.tokeiv2.anime\n\nimport android.content.Context\nimport com.demomiru.tokeiv2.BuildConfig\nimport com.de"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/extractors/CorrectTitleSelection.kt",
"chars": 5446,
"preview": "package com.demomiru.tokeiv2.extractors\n\nimport android.annotation.SuppressLint\nimport android.app.Dialog\nimport android"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/extractors/PrMovies.kt",
"chars": 2244,
"preview": "package com.demomiru.tokeiv2.extractors\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/extractors/Vidplay.kt",
"chars": 7128,
"preview": "package com.demomiru.tokeiv2.extractors\n\nimport androidx.lifecycle.lifecycleScope\nimport com.demomiru.tokeiv2.utils.Vide"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/QueryRepository.kt",
"chars": 3402,
"preview": "package com.demomiru.tokeiv2.history\n\nimport android.view.View\nimport android.widget.Toast\nimport androidx.annotation.Wo"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchApp.kt",
"chars": 173,
"preview": "package com.demomiru.tokeiv2.history\n\nimport android.app.Application\n\nclass SearchApp : Application() {\n val db by la"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchDatabase.kt",
"chars": 976,
"preview": "package com.demomiru.tokeiv2.history\n\nimport android.content.Context\nimport androidx.room.Database\nimport androidx.room."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchHistory.kt",
"chars": 393,
"preview": "package com.demomiru.tokeiv2.history\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.I"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchHistoryAdapter.kt",
"chars": 1457,
"preview": "package com.demomiru.tokeiv2.history\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.Vi"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchHistoryAdapter2.kt",
"chars": 1824,
"preview": "package com.demomiru.tokeiv2.history\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.Vi"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/history/SearchHistoryDao.kt",
"chars": 778,
"preview": "package com.demomiru.tokeiv2.history\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\ni"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/subtitles/SubTrackAdapter.kt",
"chars": 1822,
"preview": "package com.demomiru.tokeiv2.subtitles\n\n\nimport android.annotation.SuppressLint\nimport android.view.LayoutInflater\nimpor"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/subtitles/Subtitles.kt",
"chars": 303,
"preview": "package com.demomiru.tokeiv2.subtitles\n\n\n\ndata class Subtitle(\n val data: List<Sub>\n)\n\ndata class Sub(\n val id : S"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/tracks/TrackAdapter.kt",
"chars": 3651,
"preview": "package com.demomiru.tokeiv2.tracks\n\nimport android.annotation.SuppressLint\nimport android.view.LayoutInflater\nimport an"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/DudeFilmsUtils.kt",
"chars": 11389,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.util.Log\nimport com.demomiru.tokeiv2.BuildConfig\nimport com.demomiru."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/Extractor.kt",
"chars": 16094,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport JsUnpacker\nimport android.net.Uri\nimport androidx.media3.common.MimeTypes\nimp"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/GoMovies.kt",
"chars": 10550,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.util.Base64\nimport com.demomiru.tokeiv2.BuildConfig\nimport com.google"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/GogoAnime.kt",
"chars": 17553,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.annotation.SuppressLint\nimport android.os.Parcel\nimport android.os.Pa"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/OpenSubtitle.kt",
"chars": 7914,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.content.Context\nimport android.net.Uri\nimport android.view.LayoutInfl"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/SmashyStream.kt",
"chars": 3220,
"preview": "package com.demomiru.tokeiv2.utils\n\n\nimport android.icu.text.CaseMap.Title\nimport com.demomiru.tokeiv2.BuildConfig\nimpor"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/SuperstreamUtils.kt",
"chars": 24509,
"preview": "package com.demomiru.tokeiv2.utils\n\n\nimport android.util.Base64\n\nimport com.demomiru.tokeiv2.utils.SuperstreamUtils.Ciph"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/VidSrc.kt",
"chars": 3526,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport com.google.gson.Gson\nimport com.lagradost.nicehttp.Requests\nimport org.checke"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/VidSrcUtils.kt",
"chars": 5884,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.os.Handler\nimport android.os.Looper\nimport android.os.SystemClock\nimp"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/ViewModelsTokei.kt",
"chars": 8705,
"preview": "@file:Suppress(\"UNCHECKED_CAST\")\n\npackage com.demomiru.tokeiv2.utils\n\nimport android.content.Context\nimport android.net."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/colors.txt",
"chars": 936,
"preview": "All hex value from 100% to 0% alpha\n\n100% — FF\n99% — FC\n98% — FA\n97% — F7\n96% — F5\n95% — F2\n94% — F0\n93% — ED\n92% — EB\n9"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/utils/retrofitBuilder.kt",
"chars": 9155,
"preview": "package com.demomiru.tokeiv2.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/watching/ContinueWatching.kt",
"chars": 5188,
"preview": "package com.demomiru.tokeiv2.watching\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimpo"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/watching/ContinueWatchingAdapter.kt",
"chars": 3353,
"preview": "package com.demomiru.tokeiv2.watching\n\nimport android.annotation.SuppressLint\n\nimport android.os.Handler\nimport android."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/watching/ContinueWatchingDao.kt",
"chars": 943,
"preview": "package com.demomiru.tokeiv2.watching\n\nimport androidx.lifecycle.LiveData\nimport androidx.room.Dao\nimport androidx.room."
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/watching/ContinueWatchingDatabase.kt",
"chars": 1680,
"preview": "package com.demomiru.tokeiv2.watching\n\nimport android.content.Context\nimport androidx.room.Database\nimport androidx.room"
},
{
"path": "app/src/main/java/com/demomiru/tokeiv2/watching/ContinueWatchingRepository.kt",
"chars": 1069,
"preview": "package com.demomiru.tokeiv2.watching\n\nimport androidx.annotation.WorkerThread\nimport androidx.lifecycle.LiveData\nimport"
},
{
"path": "app/src/main/res/anim/enter_anim.xml",
"chars": 689,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <scale\n "
},
{
"path": "app/src/main/res/anim/enter_from_bottom.xml",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "app/src/main/res/anim/exit_anim.xml",
"chars": 689,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <scale\n "
},
{
"path": "app/src/main/res/anim/exit_to_top.xml",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "app/src/main/res/anim/go_left.xml",
"chars": 322,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:inte"
},
{
"path": "app/src/main/res/anim/go_right.xml",
"chars": 321,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:inte"
},
{
"path": "app/src/main/res/anim/layout_animation.xml",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layoutAnimation\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "app/src/main/res/anim/nav_enter_anim.xml",
"chars": 321,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <alpha\n "
},
{
"path": "app/src/main/res/anim/nav_exit_anim.xml",
"chars": 321,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <alpha\n "
},
{
"path": "app/src/main/res/anim/nav_pop_enter.xml",
"chars": 321,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <alpha\n "
},
{
"path": "app/src/main/res/anim/nav_pop_exit.xml",
"chars": 321,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <alpha\n "
},
{
"path": "app/src/main/res/anim/pop_enter.xml",
"chars": 689,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <scale\n "
},
{
"path": "app/src/main/res/anim/pop_exit.xml",
"chars": 689,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <scale\n "
},
{
"path": "app/src/main/res/anim/rotate_around_center_point.xml",
"chars": 428,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:share"
},
{
"path": "app/src/main/res/anim/rotate_left.xml",
"chars": 1015,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n a"
},
{
"path": "app/src/main/res/anim/rotate_right.xml",
"chars": 1015,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n a"
},
{
"path": "app/src/main/res/anim/slide_from_uo.xml",
"chars": 223,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "app/src/main/res/anim/slide_in.xml",
"chars": 223,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "app/src/main/res/anim/slide_in_right.xml",
"chars": 222,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "app/src/main/res/anim/slide_out.xml",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <translate\n"
},
{
"path": "app/src/main/res/drawable/anime_bottom_nav.xml",
"chars": 623,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\" android:height"
},
{
"path": "app/src/main/res/drawable/background_search_history.xml",
"chars": 228,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "app/src/main/res/drawable/background_text.xml",
"chars": 191,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "app/src/main/res/drawable/baseline_cancel_24.xml",
"chars": 458,
"preview": "<vector android:height=\"24dp\" android:tint=\"#D51616\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_double_arrow_24.xml",
"chars": 422,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_history_24.xml",
"chars": 543,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_keyboard_arrow_down_24.xml",
"chars": 344,
"preview": "<vector android:height=\"24dp\" android:tint=\"@color/white\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n "
},
{
"path": "app/src/main/res/drawable/baseline_keyboard_arrow_up_24.xml",
"chars": 333,
"preview": "<vector android:height=\"24dp\" android:tint=\"@color/white\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n "
},
{
"path": "app/src/main/res/drawable/baseline_lock_open_24.xml",
"chars": 559,
"preview": "<vector android:height=\"48dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_play_arrow_24.xml",
"chars": 294,
"preview": "<vector android:height=\"40dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_play_circle_24.xml",
"chars": 353,
"preview": "<vector android:height=\"24dp\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n android:width=\"24dp\" xmlns:"
},
{
"path": "app/src/main/res/drawable/baseline_search_24.xml",
"chars": 528,
"preview": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/baseline_zoom_out_map_24.xml",
"chars": 502,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/custom_thumb.xml",
"chars": 346,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <ite"
},
{
"path": "app/src/main/res/drawable/delete.xml",
"chars": 1280,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/down_arrow.xml",
"chars": 339,
"preview": "<vector android:height=\"48dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/edit.xml",
"chars": 1348,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/fill_screen.xml",
"chars": 447,
"preview": "<vector android:alpha=\"0.9\" android:height=\"40dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewpor"
},
{
"path": "app/src/main/res/drawable/fit_screen.xml",
"chars": 487,
"preview": "<vector android:alpha=\"0.9\" android:height=\"40dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewpor"
},
{
"path": "app/src/main/res/drawable/focus.xml",
"chars": 451,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item a"
},
{
"path": "app/src/main/res/drawable/focus_search.xml",
"chars": 438,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item a"
},
{
"path": "app/src/main/res/drawable/gradient_fill.xml",
"chars": 338,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\""
},
{
"path": "app/src/main/res/drawable/gradient_fill_bottom.xml",
"chars": 338,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\""
},
{
"path": "app/src/main/res/drawable/gradient_fill_text.xml",
"chars": 340,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\""
},
{
"path": "app/src/main/res/drawable/ic_baseline_keyboard_backspace_24.xml",
"chars": 338,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/ic_forward.xml",
"chars": 954,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"90dp\"\n android:height=\"90dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_launcher_background.xml",
"chars": 5606,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:wi"
},
{
"path": "app/src/main/res/drawable/ic_menu.xml",
"chars": 337,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/ic_rewind.xml",
"chars": 989,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"90dp\"\n android:height=\"90dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_rotation.xml",
"chars": 751,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/icon_play.xml",
"chars": 314,
"preview": "<vector android:alpha=\"0.9\" android:height=\"40dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewpor"
},
{
"path": "app/src/main/res/drawable/image_view_round.xml",
"chars": 276,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "app/src/main/res/drawable/more.xml",
"chars": 493,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_audio_subtitles.xml",
"chars": 374,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"35dp\"\n android:height=\"35dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_brightness_four.xml",
"chars": 1827,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_brightness_one.xml",
"chars": 2145,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_brightness_three.xml",
"chars": 1422,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_brightness_two.xml",
"chars": 1042,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_lock_black.xml",
"chars": 985,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"20dp\"\n android:height=\"20dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_pause_button.xml",
"chars": 311,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"40dp\"\n android:height=\"40dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_speed.xml",
"chars": 1984,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/netflix_unlock.xml",
"chars": 1157,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"30dp\"\n android:height=\"30dp\"\n "
},
{
"path": "app/src/main/res/drawable/next_episode.xml",
"chars": 316,
"preview": "<vector android:height=\"40dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/play_ripple.xml",
"chars": 540,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <i"
},
{
"path": "app/src/main/res/drawable/player_controller_bg.xml",
"chars": 803,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item"
},
{
"path": "app/src/main/res/drawable/properties.xml",
"chars": 413,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/quality_24.xml",
"chars": 578,
"preview": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "app/src/main/res/drawable/radio_group_tab_bg.xml",
"chars": 310,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "app/src/main/res/drawable/rectangle_box.xml",
"chars": 304,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\""
},
{
"path": "app/src/main/res/drawable/rectangle_box_slim.xml",
"chars": 303,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\""
},
{
"path": "app/src/main/res/drawable/selected_quality.xml",
"chars": 293,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "app/src/main/res/drawable/selected_subtitle.xml",
"chars": 350,
"preview": "<vector android:alpha=\"0.9\" android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\" android:viewpor"
},
{
"path": "app/src/main/res/drawable/share.xml",
"chars": 740,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/share_link.xml",
"chars": 552,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"48dp\"\n android:height=\"48dp\"\n "
},
{
"path": "app/src/main/res/drawable/tab_selector.xml",
"chars": 1001,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item a"
},
{
"path": "app/src/main/res/drawable/tab_text_color_selector.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <item "
},
{
"path": "app/src/main/res/drawable/volume_up_24.xml",
"chars": 544,
"preview": "<vector android:alpha=\"0.9\" android:autoMirrored=\"true\"\n android:height=\"24dp\" android:tint=\"#FFFFFF\"\n android:vie"
},
{
"path": "app/src/main/res/drawable/white_round.xml",
"chars": 195,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <solid an"
},
{
"path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1702,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n "
},
{
"path": "app/src/main/res/font/exo_2.xml",
"chars": 354,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<font-family xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n app:font"
},
{
"path": "app/src/main/res/font/exo_2_thin.xml",
"chars": 374,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<font-family xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n app:font"
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 6362,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n xmlns:android=\"http://s"
},
{
"path": "app/src/main/res/layout/activity_movie_play.xml",
"chars": 1390,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n an"
},
{
"path": "app/src/main/res/layout/activity_video_play.xml",
"chars": 11709,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/card_view.xml",
"chars": 2250,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.cardview.widget.CardView xmlns:android=\"http://schemas.android.com/apk/"
},
{
"path": "app/src/main/res/layout/custom_video_player2.xml",
"chars": 18083,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://sche"
},
{
"path": "app/src/main/res/layout/dropdown_menu.xml",
"chars": 766,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "app/src/main/res/layout/episode_expanded_view.xml",
"chars": 4350,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n android:layout_height=\"wrap_content\"\n android:layout_width=\"m"
},
{
"path": "app/src/main/res/layout/episode_item_viem.xml",
"chars": 1306,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/fragment_anime.xml",
"chars": 4319,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/fragment_anime_details.xml",
"chars": 7437,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://sche"
},
{
"path": "app/src/main/res/layout/fragment_correct_title_selection.xml",
"chars": 315,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns"
},
{
"path": "app/src/main/res/layout/fragment_movies.xml",
"chars": 3518,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://sche"
},
{
"path": "app/src/main/res/layout/fragment_search.xml",
"chars": 5916,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/fragment_tv_show.xml",
"chars": 3538,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/fragment_tv_show_details.xml",
"chars": 6547,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://sche"
},
{
"path": "app/src/main/res/layout/item_view.xml",
"chars": 3035,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n x"
},
{
"path": "app/src/main/res/layout/search_item.xml",
"chars": 1638,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/sub_view.xml",
"chars": 811,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "app/src/main/res/layout/test.xml",
"chars": 4943,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n android:layout_height=\"wra"
},
{
"path": "app/src/main/res/layout/track_item.xml",
"chars": 716,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "app/src/main/res/menu/bottom_nav.xml",
"chars": 628,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n <item and"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 328,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <bac"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 343,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/navigation/nav_main.xml",
"chars": 5643,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:"
},
{
"path": "app/src/main/res/transition/container_transform.xml",
"chars": 333,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n and"
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 316,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"black\">#FF000000</color>\n <color name=\"white\">#FF"
},
{
"path": "app/src/main/res/values/font_certs.xml",
"chars": 3581,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <array name=\"com_google_android_gms_fonts_certs\">\n <item>@"
},
{
"path": "app/src/main/res/values/preloaded_fonts.xml",
"chars": 204,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <array name=\"preloaded_fonts\" translatable=\"false\">\n <item"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 863,
"preview": "<resources>\n <string name=\"app_name\">Tokei</string>\n <string name=\"movies\">Movies</string>\n <string name=\"trend"
},
{
"path": "app/src/main/res/values/themes.xml",
"chars": 927,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Base.Th"
},
{
"path": "app/src/main/res/values-night/themes.xml",
"chars": 327,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Base.Th"
},
{
"path": "app/src/main/res/xml/backup_rules.xml",
"chars": 478,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Sample backup rules file; uncomment and customize as necessary.\n See htt"
},
{
"path": "app/src/main/res/xml/data_extraction_rules.xml",
"chars": 551,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Sample data extraction rules file; uncomment and customize as necessary.\n "
},
{
"path": "app/src/test/java/com/demomiru/tokeiv2/ExampleUnitTest.kt",
"chars": 344,
"preview": "package com.demomiru.tokeiv2\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which wi"
},
{
"path": "build.gradle",
"chars": 534,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\n\nbuildscript {\n "
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Sat Aug 26 21:19:31 IST 2023\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://"
},
{
"path": "gradle.properties",
"chars": 1358,
"preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
},
{
"path": "gradlew",
"chars": 5766,
"preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
},
{
"path": "gradlew.bat",
"chars": 2674,
"preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "settings.gradle",
"chars": 386,
"preview": "pluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\ndepen"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the Sovan22/Tokeii GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 185 files (528.8 KB), approximately 132.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.