diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 56e5a334..fae7bb08 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 744203c4..5a97be8f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3
import android.app.PictureInPictureParams
+import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
@@ -36,6 +37,7 @@ class MainActivity : AppCompatActivity() {
var isInPlayer: Boolean = false
var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false
+ lateinit var mainContext : MainActivity
}
private fun enterPIPMode() {
@@ -83,6 +85,8 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ mainContext = this
+
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index b2e7d4a0..4eb3a7fb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -22,9 +22,10 @@ import kotlinx.android.synthetic.main.result_episode.view.episode_holder
import kotlinx.android.synthetic.main.result_episode.view.episode_text
import kotlinx.android.synthetic.main.result_episode_large.view.*
-const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
const val ACTION_RELOAD_EPISODE = 4
const val ACTION_CHROME_CAST_EPISODE = 2
+const val ACTION_DOWNLOAD_EPISODE = 3
+const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
@@ -145,10 +146,12 @@ class EpisodeAdapter(
if (castContext.castState == CastState.CONNECTED) {
clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
} else {
- clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
+ // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
+ clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
}
} else {
- clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
+ // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
+ clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index 675f985e..971ab207 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -45,7 +45,9 @@ import com.lagradost.cloudstream3.ui.player.PlayerData
import com.lagradost.cloudstream3.ui.player.PlayerFragment
import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.VideoDownloadManager
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.fragment_result.*
@@ -258,7 +260,6 @@ class ResultFragment : Fragment() {
currentLoadingCount++
when (episodeClick.action) {
ACTION_CHROME_CAST_EPISODE -> {
-
val skipLoading = if (apiName != null) {
getApiFromName(apiName).instantLinkLoading
} else false
@@ -327,7 +328,19 @@ class ResultFragment : Fragment() {
}
}*/
}
-
+ ACTION_DOWNLOAD_EPISODE -> {
+ val tempUrl = url
+ if (tempUrl != null) {
+ viewModel.loadEpisode(episodeClick.data, true) { data ->
+ if (data is Resource.Success) {
+ VideoDownloadManager.DownloadEpisode(requireContext(),
+ tempUrl,
+ episodeClick.data,
+ data.value)
+ }
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
index b9a9643a..e656fc4b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
@@ -148,7 +148,7 @@ class SearchFragment : Fragment() {
allApi.providersActive = requireActivity().getApiSettings()
//searchViewModel.search("iron man")
- //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
+ (activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
/*
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_anim,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadManager.kt
new file mode 100644
index 00000000..eb794f01
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadManager.kt
@@ -0,0 +1,310 @@
+package com.lagradost.cloudstream3.utils
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import androidx.annotation.DrawableRes
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.net.toUri
+import com.bumptech.glide.Glide
+import com.google.android.exoplayer2.database.ExoDatabaseProvider
+import com.google.android.exoplayer2.offline.DownloadManager
+import com.google.android.exoplayer2.offline.DownloadRequest
+import com.google.android.exoplayer2.offline.DownloadService
+import com.google.android.exoplayer2.offline.DownloadService.sendAddDownload
+import com.google.android.exoplayer2.offline.StreamKey
+import com.google.android.exoplayer2.scheduler.Requirements
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
+import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor
+import com.google.android.exoplayer2.upstream.cache.SimpleCache
+import com.lagradost.cloudstream3.MainActivity
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import java.io.File
+import java.util.concurrent.Executor
+
+
+const val CHANNEL_ID = "cloudstream3.general"
+const val CHANNEL_NAME = "Downloads"
+const val CHANNEL_DESCRIPT = "The download notification channel"
+
+object VideoDownloadManager {
+ @DrawableRes
+ const val imgDone = R.drawable.rddone
+
+ @DrawableRes
+ const val imgDownloading = R.drawable.rdload
+
+ @DrawableRes
+ const val imgPaused = R.drawable.rdpause
+
+ @DrawableRes
+ const val imgStopped = R.drawable.rderror
+
+ @DrawableRes
+ const val imgError = R.drawable.rderror
+
+ @DrawableRes
+ const val pressToPauseIcon = R.drawable.ic_baseline_pause_24
+
+ @DrawableRes
+ const val pressToResumeIcon = R.drawable.ic_baseline_play_arrow_24
+
+ @DrawableRes
+ const val pressToStopIcon = R.drawable.exo_icon_stop
+
+ enum class DownloadType {
+ IsPaused,
+ IsDownloading,
+ IsDone,
+ IsFailed,
+ IsStopped,
+ }
+
+ enum class DownloadActionType {
+ Pause,
+ Resume,
+ Stop,
+ }
+
+ private var hasCreatedNotChanel = false
+ private fun Context.createNotificationChannel() {
+ hasCreatedNotChanel = true
+ // Create the NotificationChannel, but only on API 26+ because
+ // the NotificationChannel class is new and not in the support library
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val name = CHANNEL_NAME //getString(R.string.channel_name)
+ val descriptionText = CHANNEL_DESCRIPT//getString(R.string.channel_description)
+ val importance = NotificationManager.IMPORTANCE_DEFAULT
+ val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
+ description = descriptionText
+ }
+ // Register the channel with the system
+ val notificationManager: NotificationManager =
+ this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.createNotificationChannel(channel)
+ }
+ }
+
+ private val cachedBitmaps = hashMapOf()
+ private fun Context.getImageBitmapFromUrl(url: String): Bitmap? {
+ if (cachedBitmaps.containsKey(url)) {
+ return cachedBitmaps[url]
+ }
+
+ val bitmap = Glide.with(this)
+ .asBitmap()
+ .load(url).into(720, 720)
+ .get()
+ if (bitmap != null) {
+ cachedBitmaps[url] = bitmap
+ }
+ return null
+ }
+
+ fun createNotification(
+ context: Context,
+ text: String,
+ source: String,
+ ep: ResultEpisode,
+ state: DownloadType,
+ progress: Long,
+ total: Long,
+ ) {
+ val intent = Intent(context, MainActivity::class.java).apply {
+ data = source.toUri()
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
+ val builder = NotificationCompat.Builder(context, CHANNEL_ID)
+ .setAutoCancel(true)
+ .setColorized(true)
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setColor(context.colorFromAttribute(R.attr.colorPrimary))
+ .setContentText(text)
+ .setSmallIcon(
+ when (state) {
+ DownloadType.IsDone -> imgDone
+ DownloadType.IsDownloading -> imgDownloading
+ DownloadType.IsPaused -> imgPaused
+ DownloadType.IsFailed -> imgError
+ DownloadType.IsStopped -> imgStopped
+ }
+ )
+ .setContentIntent(pendingIntent)
+
+ if (state == DownloadType.IsDownloading || state == DownloadType.IsPaused) {
+ builder.setProgress(total.toInt(), progress.toInt(), false)
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (ep.poster != null) {
+ val poster = context.getImageBitmapFromUrl(ep.poster)
+ if (poster != null)
+ builder.setLargeIcon(poster)
+ }
+ }
+ if ((state == DownloadType.IsDownloading || state == DownloadType.IsPaused) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val actionTypes: MutableList = ArrayList()
+ // INIT
+ if (state == DownloadType.IsDownloading) {
+ actionTypes.add(DownloadActionType.Pause)
+ actionTypes.add(DownloadActionType.Stop)
+ }
+
+ if (state == DownloadType.IsPaused) {
+ actionTypes.add(DownloadActionType.Resume)
+ actionTypes.add(DownloadActionType.Stop)
+ }
+
+ // ADD ACTIONS
+ for ((index, i) in actionTypes.withIndex()) {
+ val _resultIntent = Intent(context, DownloadService::class.java)
+
+ _resultIntent.putExtra(
+ "type", when (i) {
+ DownloadActionType.Resume -> "resume"
+ DownloadActionType.Pause -> "pause"
+ DownloadActionType.Stop -> "stop"
+ }
+ )
+
+ _resultIntent.putExtra("id", ep.id)
+
+ val pending: PendingIntent = PendingIntent.getService(
+ context, 4337 + index + ep.id,
+ _resultIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
+ builder.addAction(
+ NotificationCompat.Action(
+ when (i) {
+ DownloadActionType.Resume -> pressToResumeIcon
+ DownloadActionType.Pause -> pressToPauseIcon
+ DownloadActionType.Stop -> pressToStopIcon
+ }, when (i) {
+ DownloadActionType.Resume -> "Resume"
+ DownloadActionType.Pause -> "Pause"
+ DownloadActionType.Stop -> "Stop"
+ }, pending
+ )
+ )
+ }
+ }
+
+ if (!hasCreatedNotChanel) {
+ context.createNotificationChannel()
+ }
+
+ with(NotificationManagerCompat.from(context)) {
+ // notificationId is a unique int for each notification that you must define
+ notify(ep.id, builder.build())
+ }
+ }
+
+ //https://exoplayer.dev/downloading-media.html
+ fun DownloadSingleEpisode(context: Context, source: String, ep: ResultEpisode, link: ExtractorLink) {
+ val url = link.url
+ val headers = mapOf("User-Agent" to USER_AGENT, "Referer" to link.referer)
+
+ // Note: This should be a singleton in your app.
+ val databaseProvider = ExoDatabaseProvider(context)
+
+ val downloadDirectory = File(Environment.getExternalStorageDirectory().path + "/Download/" + (ep.name ?: "Episode ${ep.episode}")) // File(context.cacheDir, "video_${ep.id}")
+
+ // A download cache should not evict media, so should use a NoopCacheEvictor.
+ val downloadCache = SimpleCache(
+ downloadDirectory,
+ NoOpCacheEvictor(),
+ databaseProvider)
+
+ // Create a factory for reading the data from the network.
+ val dataSourceFactory = DefaultHttpDataSourceFactory()
+
+ // Choose an executor for downloading data. Using Runnable::run will cause each download task to
+ // download data on its own thread. Passing an executor that uses multiple threads will speed up
+ // download tasks that can be split into smaller parts for parallel execution. Applications that
+ // already have an executor for background downloads may wish to reuse their existing executor.
+ val downloadExecutor = Executor { obj: Runnable -> obj.run() }
+
+
+ // Create the download manager.
+ val downloadManager = DownloadManager(
+ context,
+ databaseProvider,
+ downloadCache,
+ dataSourceFactory,
+ downloadExecutor)
+
+ val requirements = Requirements(Requirements.NETWORK)
+ // Optionally, setters can be called to configure the download manager.
+ downloadManager.requirements = requirements
+ downloadManager.maxParallelDownloads = 3
+ val builder = DownloadRequest.Builder(ep.id.toString(), link.url.toUri())
+
+ val downloadRequest: DownloadRequest = builder.build()
+
+ DownloadService.sendAddDownload(
+ context,
+ VideoDownloadService::class.java,
+ downloadRequest,
+ /* foreground= */ true)
+/*
+ val disposable = url.download(header = headers)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeBy(
+ onNext = { progress ->
+ createNotification(
+ context,
+ "Downloading ${progress.downloadSizeStr()}/${progress.totalSizeStr()}",
+ source,
+ ep,
+ DownloadType.IsDownloading,
+ progress.downloadSize,
+ progress.totalSize
+ )
+ },
+ onComplete = {
+ createNotification(
+ context,
+ "Download Done",
+ source,
+ ep,
+ DownloadType.IsDone,
+ 0, 0
+ )
+ },
+ onError = {
+ createNotification(
+ context,
+ "Download Failed",
+ source,
+ ep,
+ DownloadType.IsFailed,
+ 0, 0
+ )
+ }
+ )*/
+ }
+
+ public fun DownloadEpisode(context: Context, source: String, ep: ResultEpisode, links: List) {
+ val validLinks = links.filter { !it.isM3u8 }
+ if (validLinks.isNotEmpty()) {
+ DownloadSingleEpisode(context, source, ep, validLinks.first())
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadService.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadService.kt
new file mode 100644
index 00000000..97b93ee2
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadService.kt
@@ -0,0 +1,97 @@
+package com.lagradost.cloudstream3.utils
+
+import android.app.IntentService
+import android.app.Notification
+import android.app.PendingIntent
+import android.content.Intent
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.net.toUri
+import com.google.android.exoplayer2.offline.Download
+import com.google.android.exoplayer2.offline.DownloadManager
+import com.google.android.exoplayer2.offline.DownloadService
+import com.google.android.exoplayer2.scheduler.PlatformScheduler
+import com.google.android.exoplayer2.scheduler.Scheduler
+import com.lagradost.cloudstream3.MainActivity
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
+import java.lang.Exception
+
+private const val JOB_ID = 1
+private const val FOREGROUND_NOTIFICATION_ID = 1
+
+class VideoDownloadService : DownloadService(
+ FOREGROUND_NOTIFICATION_ID,
+ DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
+ CHANNEL_ID,
+ R.string.exo_download_notification_channel_name, /* channelDescriptionResourceId= */
+ 0) {
+ override fun getDownloadManager(): DownloadManager {
+ val ctx = this
+ return ExoPlayerHelper.downloadManager.apply {
+ requirements = DownloadManager.DEFAULT_REQUIREMENTS
+ maxParallelDownloads = 3
+ addListener(
+ object : DownloadManager.Listener {
+
+ override fun onDownloadChanged(
+ downloadManager: DownloadManager,
+ download: Download,
+ finalException: Exception?,
+ ) {
+ val intent = Intent(ctx, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0)
+ val builder = NotificationCompat.Builder(ctx, CHANNEL_ID)
+ .setAutoCancel(true)
+ .setColorized(true)
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setColor(colorFromAttribute(R.attr.colorPrimary))
+ .setContentText("${download.bytesDownloaded} / ${download.contentLength}")
+ .setSmallIcon(
+ VideoDownloadManager.imgDownloading
+ )
+ .setProgress((download.bytesDownloaded / 1000).toInt(),
+ (download.contentLength / 1000).toInt(),
+ false) // in case the size is over 2gb / 1000
+ .setContentIntent(pendingIntent)
+ builder.build()
+ with(NotificationManagerCompat.from(ctx)) {
+ // notificationId is a unique int for each notification that you must define
+ notify(download.request.id.hashCode(), builder.build())
+ }
+ super.onDownloadChanged(downloadManager, download, finalException)
+ }
+ }
+ )
+ }
+ }
+
+ override fun getScheduler(): Scheduler =
+ PlatformScheduler(this, JOB_ID)
+
+ override fun getForegroundNotification(downloads: MutableList): Notification {
+ val intent = Intent(this, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
+ val builder = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setAutoCancel(true)
+ .setColorized(true)
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setColor(colorFromAttribute(R.attr.colorPrimary))
+ .setContentText("Downloading ${downloads.size} item${if (downloads.size == 1) "" else "s"}")
+ .setSmallIcon(
+ VideoDownloadManager.imgDownloading
+ )
+ .setContentIntent(pendingIntent)
+ return builder.build()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExoPlayerHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExoPlayerHelper.kt
new file mode 100644
index 00000000..960fac6c
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExoPlayerHelper.kt
@@ -0,0 +1,33 @@
+package com.lagradost.cloudstream3.utils
+
+import com.google.android.exoplayer2.database.ExoDatabaseProvider
+import com.google.android.exoplayer2.offline.DownloadManager
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
+import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
+import com.google.android.exoplayer2.upstream.cache.SimpleCache
+import com.lagradost.cloudstream3.MainActivity
+import java.util.concurrent.Executor
+
+object ExoPlayerHelper {
+ private val context = MainActivity.mainContext
+ val databaseProvider = ExoDatabaseProvider(context)
+ val downloadExecutor = Executor { obj: Runnable -> obj.run() }
+ val dataSourceFactory = DefaultHttpDataSourceFactory()
+
+ val downloadCache: SimpleCache by lazy {
+ SimpleCache(
+ context.cacheDir,
+ LeastRecentlyUsedCacheEvictor(20 * 1024 * 1024),
+ databaseProvider
+ )
+ }
+
+ val downloadManager: DownloadManager by lazy {
+ DownloadManager(context,
+ databaseProvider,
+ downloadCache,
+ dataSourceFactory,
+ downloadExecutor)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/.idea/misc.xml b/app/src/main/res/drawable/.idea/misc.xml
new file mode 100644
index 00000000..639900d1
--- /dev/null
+++ b/app/src/main/res/drawable/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/.idea/modules.xml b/app/src/main/res/drawable/.idea/modules.xml
new file mode 100644
index 00000000..1b046284
--- /dev/null
+++ b/app/src/main/res/drawable/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/.idea/vcs.xml b/app/src/main/res/drawable/.idea/vcs.xml
new file mode 100644
index 00000000..bc599707
--- /dev/null
+++ b/app/src/main/res/drawable/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rddone.xml b/app/src/main/res/drawable/rddone.xml
new file mode 100644
index 00000000..8657a246
--- /dev/null
+++ b/app/src/main/res/drawable/rddone.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rderror.xml b/app/src/main/res/drawable/rderror.xml
new file mode 100644
index 00000000..a29d2ada
--- /dev/null
+++ b/app/src/main/res/drawable/rderror.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rdload.xml b/app/src/main/res/drawable/rdload.xml
new file mode 100644
index 00000000..31988d83
--- /dev/null
+++ b/app/src/main/res/drawable/rdload.xml
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rdpause.xml b/app/src/main/res/drawable/rdpause.xml
new file mode 100644
index 00000000..f1fc1b9f
--- /dev/null
+++ b/app/src/main/res/drawable/rdpause.xml
@@ -0,0 +1,19 @@
+
+
+
+
\ No newline at end of file