added stuff to MainAPI

This commit is contained in:
LagradOst 2021-07-24 01:44:54 +02:00
parent 79328502b4
commit 8c0a6dffe9
9 changed files with 152 additions and 102 deletions

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>

View file

@ -10,13 +10,10 @@ import com.lagradost.cloudstream3.animeproviders.ShiroProvider
import com.lagradost.cloudstream3.animeproviders.TenshiProvider import com.lagradost.cloudstream3.animeproviders.TenshiProvider
import com.lagradost.cloudstream3.animeproviders.WcoProvider import com.lagradost.cloudstream3.animeproviders.WcoProvider
import com.lagradost.cloudstream3.movieproviders.HDMProvider import com.lagradost.cloudstream3.movieproviders.HDMProvider
import com.lagradost.cloudstream3.movieproviders.LookMovieProvider
import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider
import com.lagradost.cloudstream3.movieproviders.TrailersToProvider import com.lagradost.cloudstream3.movieproviders.TrailersToProvider
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0" const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0"
val baseHeader = mapOf("User-Agent" to USER_AGENT) val baseHeader = mapOf("User-Agent" to USER_AGENT)
@ -38,7 +35,7 @@ object APIHolder {
MeloMovieProvider(), MeloMovieProvider(),
DubbedAnimeProvider(), DubbedAnimeProvider(),
HDMProvider(), HDMProvider(),
LookMovieProvider(), //LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...)
TrailersToProvider(), TrailersToProvider(),
) )
@ -60,13 +57,23 @@ object APIHolder {
} }
} }
/**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/
abstract class MainAPI { abstract class MainAPI {
open val name = "NONE" open val name = "NONE"
open val mainUrl = "NONE" open val mainUrl = "NONE"
open val instantLinkLoading = false // THIS IS IF THE LINK IS STORED IN THE "DATA"
/**If link is stored in the "data" string, so links can be instantly loaded*/
open val instantLinkLoading = false
open val hasQuickSearch = false open val hasQuickSearch = false
open fun search(query: String): ArrayList<SearchResponse>? { // SearchResponse
/**Set false if links require referer or for some reason cant be played on a chromecast*/
open val hasChromecastSupport = true
/**If all links are m3u8 then set this to false*/
open val hasDownloadSupport = true
open fun search(query: String): ArrayList<SearchResponse>? {
return null return null
} }
@ -78,7 +85,7 @@ abstract class MainAPI {
return null return null
} }
// callback is fired once a link is found, will return true if method is executed successfully /**Callback is fired once a link is found, will return true if method is executed successfully*/
open fun loadLinks( open fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,

View file

@ -10,6 +10,8 @@ class HDMProvider : MainAPI() {
get() = "HD Movies" get() = "HD Movies"
override val mainUrl: String override val mainUrl: String
get() = "https://hdm.to" get() = "https://hdm.to"
override val hasDownloadSupport: Boolean
get() = false
override fun search(query: String): ArrayList<SearchResponse> { override fun search(query: String): ArrayList<SearchResponse> {
val url = "$mainUrl/search/$query" val url = "$mainUrl/search/$query"

View file

@ -17,6 +17,8 @@ class MeloMovieProvider : MainAPI() {
get() = true get() = true
override val hasQuickSearch: Boolean override val hasQuickSearch: Boolean
get() = true get() = true
override val hasChromecastSupport: Boolean
get() = false // MKV FILES CANT BE PLAYED ON A CHROMECAST
data class MeloMovieSearchResult( data class MeloMovieSearchResult(
@JsonProperty("id") val id: Int, @JsonProperty("id") val id: Int,

View file

@ -1,28 +1,17 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.google.android.gms.cast.framework.CastContext import com.lagradost.cloudstream3.R
import com.google.android.gms.cast.framework.CastState
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
import com.lagradost.cloudstream3.UIHelper.isConnectedToChromecast
import com.lagradost.cloudstream3.utils.getId
import kotlinx.android.synthetic.main.result_episode.view.episode_holder 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.view.episode_text
import kotlinx.android.synthetic.main.result_episode_large.view.* import kotlinx.android.synthetic.main.result_episode_large.view.*
@ -42,10 +31,13 @@ const val ACTION_COPY_LINK = 9
const val ACTION_SHOW_OPTIONS = 10 const val ACTION_SHOW_OPTIONS = 10
const val ACTION_CLICK_DEFAULT = 11
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
class EpisodeAdapter( class EpisodeAdapter(
var cardList: List<ResultEpisode>, var cardList: List<ResultEpisode>,
val hasDownloadSupport : Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit, private val clickCallback: (EpisodeClickEvent) -> Unit,
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@ -64,6 +56,7 @@ class EpisodeAdapter(
return CardViewHolder( return CardViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false), LayoutInflater.from(parent.context).inflate(layout, parent, false),
hasDownloadSupport,
clickCallback clickCallback
) )
} }
@ -83,6 +76,7 @@ class EpisodeAdapter(
class CardViewHolder class CardViewHolder
constructor( constructor(
itemView: View, itemView: View,
private val hasDownloadSupport : Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit, private val clickCallback: (EpisodeClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView) { ) : RecyclerView.ViewHolder(itemView) {
private val episodeText: TextView = itemView.episode_text private val episodeText: TextView = itemView.episode_text
@ -131,13 +125,7 @@ class EpisodeAdapter(
} }
episodeHolder.setOnClickListener { episodeHolder.setOnClickListener {
episodeHolder.context?.let { ctx -> clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
if (ctx.isConnectedToChromecast()) {
clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
} else {
clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
}
}
} }
episodeHolder.setOnLongClickListener { episodeHolder.setOnLongClickListener {
@ -146,6 +134,8 @@ class EpisodeAdapter(
return@setOnLongClickListener true return@setOnLongClickListener true
} }
episodeDownload?.visibility = if(hasDownloadSupport) View.VISIBLE else View.GONE
episodeDownload?.setOnClickListener { episodeDownload?.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
} }

View file

@ -227,25 +227,37 @@ class ResultFragment : Fragment() {
) )
result_back.layoutParams = backParameter result_back.layoutParams = backParameter
if (activity?.isCastApiAvailable() == true) {
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state ->
if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
}
}
}
}
// activity?.fixPaddingStatusbar(result_toolbar) // activity?.fixPaddingStatusbar(result_toolbar)
url = arguments?.getString("url") url = arguments?.getString("url")
val slug = arguments?.getString("slug") val slug = arguments?.getString("slug")
val apiName = arguments?.getString("apiName") val apiName = arguments?.getString("apiName") ?: return
val api = getApiFromName(apiName)
if (media_route_button != null) {
val chromecastSupport = api.hasChromecastSupport
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
if (!chromecastSupport) {
media_route_button.setOnClickListener {
Toast.makeText(it.context, "This provider has no chromecast support", Toast.LENGTH_LONG).show()
}
}
if (activity?.isCastApiAvailable() == true) {
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state ->
if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
}
}
}
}
}
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ -> result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ ->
if (result_poster_blur == null) return@OnScrollChangeListener if (result_poster_blur == null) return@OnScrollChangeListener
result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f)) result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f))
@ -397,7 +409,10 @@ class ResultFragment : Fragment() {
val epData = episodeClick.data val epData = episodeClick.data
ctx.setKey( ctx.setKey(
getFolderName(DOWNLOAD_EPISODE_CACHE, (currentId ?: return@let).toString()), // 3 deep folder for faster acess getFolderName(
DOWNLOAD_EPISODE_CACHE,
(currentId ?: return@let).toString()
), // 3 deep folder for faster acess
epData.id.toString(), epData.id.toString(),
VideoDownloadHelper.DownloadEpisodeCached( VideoDownloadHelper.DownloadEpisodeCached(
epData.name, epData.name,
@ -431,6 +446,16 @@ class ResultFragment : Fragment() {
if (!isLoaded) return@main // CANT LOAD if (!isLoaded) return@main // CANT LOAD
when (episodeClick.action) { when (episodeClick.action) {
ACTION_CLICK_DEFAULT -> {
context?.let { ctx ->
if (ctx.isConnectedToChromecast()) {
handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, episodeClick.data))
} else {
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeClick.data))
}
}
}
ACTION_SHOW_OPTIONS -> { ACTION_SHOW_OPTIONS -> {
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
var dialog: AlertDialog? = null var dialog: AlertDialog? = null
@ -442,6 +467,8 @@ class ResultFragment : Fragment() {
val verifiedOptions = ArrayList<String>() val verifiedOptions = ArrayList<String>()
val verifiedOptionsValues = ArrayList<Int>() val verifiedOptionsValues = ArrayList<Int>()
val hasDownloadSupport = api.hasDownloadSupport
for (i in options.indices) { for (i in options.indices) {
val opv = optionsValues[i] val opv = optionsValues[i]
val op = options[i] val op = options[i]
@ -450,6 +477,8 @@ class ResultFragment : Fragment() {
val add = when (opv) { val add = when (opv) {
ACTION_CHROME_CAST_EPISODE -> isConnected ACTION_CHROME_CAST_EPISODE -> isConnected
ACTION_CHROME_CAST_MIRROR -> isConnected ACTION_CHROME_CAST_MIRROR -> isConnected
ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport
ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled(VLC_PACKAGE) ?: false ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled(VLC_PACKAGE) ?: false
else -> true else -> true
} }
@ -482,7 +511,7 @@ class ResultFragment : Fragment() {
ACTION_PLAY_EPISODE_IN_BROWSER -> { ACTION_PLAY_EPISODE_IN_BROWSER -> {
aquireSingeExtractorLink("Play in Browser") { link -> aquireSingeExtractorLink("Play in Browser") { link ->
val i = Intent(Intent.ACTION_VIEW) val i = Intent(ACTION_VIEW)
i.data = Uri.parse(link.url) i.data = Uri.parse(link.url)
startActivity(i) startActivity(i)
} }
@ -595,11 +624,11 @@ class ResultFragment : Fragment() {
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
EpisodeAdapter( EpisodeAdapter(
ArrayList(), ArrayList(),
api.hasDownloadSupport,
) { episodeClick -> ) { episodeClick ->
handleAction(episodeClick) handleAction(episodeClick)
} }
result_episodes.adapter = adapter result_episodes.adapter = adapter
result_episodes.layoutManager = GridLayoutManager(context, 1) result_episodes.layoutManager = GridLayoutManager(context, 1)
@ -678,17 +707,17 @@ class ResultFragment : Fragment() {
currentIsMovie = !d.isEpisodeBased() currentIsMovie = !d.isEpisodeBased()
result_openinbrower.setOnClickListener { result_openinbrower.setOnClickListener {
val i = Intent(Intent.ACTION_VIEW) val i = Intent(ACTION_VIEW)
i.data = Uri.parse(d.url) i.data = Uri.parse(d.url)
startActivity(i) startActivity(i)
} }
result_share.setOnClickListener { result_share.setOnClickListener {
val i = Intent(Intent.ACTION_SEND) val i = Intent(ACTION_SEND)
i.type = "text/plain" i.type = "text/plain"
i.putExtra(Intent.EXTRA_SUBJECT, d.name) i.putExtra(EXTRA_SUBJECT, d.name)
i.putExtra(Intent.EXTRA_TEXT, d.url) i.putExtra(EXTRA_TEXT, d.url)
startActivity(Intent.createChooser(i, d.name)) startActivity(createChooser(i, d.name))
} }
val metadataInfoArray = ArrayList<Pair<String, String>>() val metadataInfoArray = ArrayList<Pair<String, String>>()
@ -713,6 +742,8 @@ class ResultFragment : Fragment() {
val duration = d.duration val duration = d.duration
if (duration != null) metadataInfoArray.add(Pair("Duration", duration)) if (duration != null) metadataInfoArray.add(Pair("Duration", duration))
metadataInfoArray.add(Pair("Site", d.apiName))
if (metadataInfoArray.size > 0) { if (metadataInfoArray.size > 0) {
result_metadata.visibility = VISIBLE result_metadata.visibility = VISIBLE
val text = SpannableStringBuilder() val text = SpannableStringBuilder()
@ -777,37 +808,23 @@ class ResultFragment : Fragment() {
} }
} }
when (d.type) { if (d.type == TvType.Movie && d is MovieLoadResponse) {
TvType.Movie -> { result_movie_parent.visibility = VISIBLE
result_play_movie.visibility = VISIBLE result_episodes_text.visibility = GONE
result_episodes_text.visibility = GONE result_episodes.visibility = GONE
result_episodes.visibility = GONE
result_play_movie.setOnClickListener { result_play_movie.setOnClickListener {
val card = currentEpisodes?.first() ?: return@setOnClickListener val card = currentEpisodes?.first() ?: return@setOnClickListener
if (requireContext().isCastApiAvailable()) { handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
val castContext = CastContext.getSharedInstance(requireContext())
if (castContext.castState == CastState.CONNECTED) {
handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
} else {
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
}
} else {
handleAction(
EpisodeClickEvent(
ACTION_PLAY_EPISODE_IN_PLAYER,
card
)
) //TODO REDO TO MAIN
}
}
} }
else -> { result_options.setOnClickListener {
result_play_movie.visibility = GONE val card = currentEpisodes?.first() ?: return@setOnClickListener
result_episodes_text.visibility = VISIBLE handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
result_episodes.visibility = VISIBLE
} }
} else {
result_movie_parent.visibility = GONE
result_episodes_text.visibility = VISIBLE
result_episodes.visibility = VISIBLE
} }
when (d) { when (d) {
@ -842,7 +859,7 @@ class ResultFragment : Fragment() {
if (url != null) { if (url != null) {
result_reload_connection_open_in_browser.setOnClickListener { result_reload_connection_open_in_browser.setOnClickListener {
val i = Intent(Intent.ACTION_VIEW) val i = Intent(ACTION_VIEW)
i.data = Uri.parse(url) i.data = Uri.parse(url)
startActivity(i) startActivity(i)
} }

View file

@ -165,9 +165,10 @@ class ResultViewModel : ViewModel() {
updateEpisodes( updateEpisodes(
context, mainId, arrayListOf( context, mainId, arrayListOf(
context.buildResultEpisode( context.buildResultEpisode(
d.name,
null, null,
0,
null, null,
0, null,
d.dataUrl, d.dataUrl,
d.apiName, d.apiName,
(mainId + 1), (mainId + 1),
@ -211,7 +212,7 @@ class ResultViewModel : ViewModel() {
suspend fun loadEpisode( suspend fun loadEpisode(
episode: ResultEpisode, episode: ResultEpisode,
isCasting: Boolean, isCasting: Boolean,
) : Resource<ResultViewModel.EpisodeData> { ): Resource<EpisodeData> {
return loadEpisode(episode.id, episode.data, isCasting) return loadEpisode(episode.id, episode.data, isCasting)
} }
@ -219,7 +220,7 @@ class ResultViewModel : ViewModel() {
id: Int, id: Int,
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
): Resource<ResultViewModel.EpisodeData> { ): Resource<EpisodeData> {
if (_allEpisodes.value?.contains(id) == true) { if (_allEpisodes.value?.contains(id) == true) {
_allEpisodes.value?.remove(id) _allEpisodes.value?.remove(id)
} }

View file

@ -286,27 +286,56 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
</com.lagradost.cloudstream3.widget.FlowLayout> </com.lagradost.cloudstream3.widget.FlowLayout>
<com.google.android.material.button.MaterialButton <LinearLayout
android:visibility="visible" android:orientation="vertical"
android:layout_gravity="center_vertical" android:id="@+id/result_movie_parent"
app:cornerRadius="4dp"
android:id="@+id/result_play_movie"
android:text="@string/play_movie_button"
app:rippleColor="?attr/colorPrimary"
android:textColor="?attr/textColor"
app:iconTint="?attr/textColor"
android:textAllCaps="false"
android:clickable="true"
android:focusable="true"
app:iconGravity="textStart"
app:strokeColor="?attr/textColor"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:backgroundTint="@color/transparent"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="45dp"> android:layout_height="wrap_content">
</com.google.android.material.button.MaterialButton> <com.google.android.material.button.MaterialButton
android:visibility="visible"
android:layout_gravity="center_vertical"
app:cornerRadius="4dp"
android:id="@+id/result_play_movie"
android:text="@string/play_movie_button"
app:rippleColor="?attr/textColor"
android:textColor="?attr/textColor"
app:iconTint="?attr/textColor"
android:textAllCaps="false"
android:clickable="true"
android:focusable="true"
app:iconGravity="textStart"
app:strokeColor="?attr/textColor"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:backgroundTint="@color/transparent"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="45dp">
</com.google.android.material.button.MaterialButton>
<com.google.android.material.button.MaterialButton
android:visibility="visible"
android:layout_gravity="center_vertical"
app:cornerRadius="4dp"
android:id="@+id/result_options"
android:text="@string/options"
app:rippleColor="?attr/textColor"
android:textColor="?attr/textColor"
app:iconTint="?attr/textColor"
android:textAllCaps="false"
android:clickable="true"
android:focusable="true"
app:iconGravity="textStart"
app:strokeColor="?attr/textColor"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:backgroundTint="@color/transparent"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="45dp">
</com.google.android.material.button.MaterialButton>
</LinearLayout>
<LinearLayout android:orientation="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -314,7 +343,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
app:cornerRadius="4dp" app:cornerRadius="4dp"
android:id="@+id/result_season_button" android:id="@+id/result_season_button"
tools:text="Bookmark" tools:text="Subbed"
app:rippleColor="?attr/bitDarkerGrayBackground" app:rippleColor="?attr/bitDarkerGrayBackground"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"

View file

@ -41,4 +41,5 @@
<string name="download_descript">Download</string> <string name="download_descript">Download</string>
<string name="error_loading_links">Error Loading Links</string> <string name="error_loading_links">Error Loading Links</string>
<string name="download_storage_text">Internal Storage</string> <string name="download_storage_text">Internal Storage</string>
<string name="options">Options</string>
</resources> </resources>