null safety tuning and bug fixes
This commit is contained in:
parent
0e36e6f39e
commit
45abbd34b9
|
@ -4,7 +4,7 @@ PodVinci is an open source podcast manager/player project.
|
|||
|
||||
This is based on a fork from the popular project AntennaPod (https://github.com/AntennaPod/AntennaPod) as of Feb 5 2024.
|
||||
|
||||
This project is purely Kotlin based, relies on the most recent dependencies, and most importantly has migrated the media player to androidx.media3, and added mechanism of AudioOffloadMode which is supposed to be kind to device battery. App build is also upgraded to target Android 14.
|
||||
Differing from the forked project, this project is purely Kotlin based, relies on the most recent dependencies, and most importantly has migrated the media player to androidx.media3, and added mechanism of AudioOffloadMode which is supposed to be kind to device battery. Efficiencies are also sought on running the app. App build is also upgraded to target Android 14.
|
||||
|
||||
## Privacy Policy
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<supports-screens
|
||||
|
@ -43,6 +42,13 @@
|
|||
android:allowAudioPlaybackCapture="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<!-- <service-->
|
||||
<!-- android:name=".core.service.playback.PlaybackService"-->
|
||||
<!-- android:foregroundServiceType="mediaPlayback"-->
|
||||
<!-- android:exported="false"-->
|
||||
<!-- tools:replace="android:exported">-->
|
||||
<!-- </service>-->
|
||||
|
||||
<activity
|
||||
android:name=".activity.PlaybackSpeedDialogActivity"
|
||||
android:noHistory="true"
|
||||
|
|
|
@ -104,20 +104,20 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
private var parser: Disposable? = null
|
||||
private var updater: Disposable? = null
|
||||
|
||||
private var headerBinding: OnlinefeedviewHeaderBinding? = null
|
||||
private var viewBinding: OnlinefeedviewActivityBinding? = null
|
||||
private lateinit var headerBinding: OnlinefeedviewHeaderBinding
|
||||
private lateinit var viewBinding: OnlinefeedviewActivityBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(getTranslucentTheme(this))
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewBinding = OnlinefeedviewActivityBinding.inflate(layoutInflater)
|
||||
setContentView(viewBinding!!.root)
|
||||
setContentView(viewBinding.root)
|
||||
|
||||
viewBinding!!.transparentBackground.setOnClickListener { v: View? -> finish() }
|
||||
viewBinding!!.closeButton.setOnClickListener { view: View? -> finish() }
|
||||
viewBinding!!.card.setOnClickListener(null)
|
||||
viewBinding!!.card.setCardBackgroundColor(getColorFromAttr(this, R.attr.colorSurface))
|
||||
viewBinding.transparentBackground.setOnClickListener { v: View? -> finish() }
|
||||
viewBinding.closeButton.setOnClickListener { view: View? -> finish() }
|
||||
viewBinding.card.setOnClickListener(null)
|
||||
viewBinding.card.setCardBackgroundColor(getColorFromAttr(this, R.attr.colorSurface))
|
||||
headerBinding = OnlinefeedviewHeaderBinding.inflate(layoutInflater)
|
||||
|
||||
var feedUrl: String? = null
|
||||
|
@ -165,8 +165,8 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
* Displays a progress indicator.
|
||||
*/
|
||||
private fun setLoadingLayout() {
|
||||
viewBinding!!.progressBar.visibility = View.VISIBLE
|
||||
viewBinding!!.feedDisplayContainer.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.VISIBLE
|
||||
viewBinding.feedDisplayContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -189,15 +189,9 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (updater != null) {
|
||||
updater!!.dispose()
|
||||
}
|
||||
if (download != null) {
|
||||
download!!.dispose()
|
||||
}
|
||||
if (parser != null) {
|
||||
parser!!.dispose()
|
||||
}
|
||||
updater?.dispose()
|
||||
download?.dispose()
|
||||
parser?.dispose()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -264,8 +258,9 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
val results = searcher.search(query)?.blockingGet()
|
||||
if (results.isNullOrEmpty()) return null
|
||||
for (result in results) {
|
||||
if (result?.feedUrl != null && result.author != null && result.author.equals(artistName,
|
||||
ignoreCase = true) && result.title.equals(trackName, ignoreCase = true)) {
|
||||
if (result?.feedUrl != null && result.author != null &&
|
||||
result.author.equals(artistName, ignoreCase = true) &&
|
||||
result.title.equals(trackName, ignoreCase = true)) {
|
||||
return result.feedUrl
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +296,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
if (username != null && password != null) {
|
||||
Toast.makeText(this, R.string.download_error_unauthorized, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
if (downloader!!.downloadRequest.source != null) {
|
||||
if (downloader?.downloadRequest?.source != null) {
|
||||
dialog = FeedViewAuthenticationDialog(this@OnlineFeedViewActivity,
|
||||
R.string.authentication_notification_title, downloader!!.downloadRequest.source!!).create()
|
||||
dialog?.show()
|
||||
|
@ -376,8 +371,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
} else {
|
||||
throw UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html))
|
||||
}
|
||||
}
|
||||
else null
|
||||
} else null
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
|
@ -395,18 +389,18 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
* This method is executed on the GUI thread.
|
||||
*/
|
||||
@UnstableApi private fun showFeedInformation(feed: Feed, alternateFeedUrls: Map<String, String>) {
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding!!.feedDisplayContainer.visibility = View.VISIBLE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
viewBinding.feedDisplayContainer.visibility = View.VISIBLE
|
||||
if (isFeedFoundBySearch) {
|
||||
val resId = R.string.no_feed_url_podcast_found_by_search
|
||||
Snackbar.make(findViewById(android.R.id.content), resId, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
viewBinding!!.backgroundImage.colorFilter = LightingColorFilter(-0x7d7d7e, 0x000000)
|
||||
viewBinding.backgroundImage.colorFilter = LightingColorFilter(-0x7d7d7e, 0x000000)
|
||||
|
||||
viewBinding!!.listView.addHeaderView(headerBinding!!.root)
|
||||
viewBinding!!.listView.setSelector(android.R.color.transparent)
|
||||
viewBinding!!.listView.adapter = FeedItemlistDescriptionAdapter(this, 0, feed.items)
|
||||
viewBinding.listView.addHeaderView(headerBinding.root)
|
||||
viewBinding.listView.setSelector(android.R.color.transparent)
|
||||
viewBinding.listView.adapter = FeedItemlistDescriptionAdapter(this, 0, feed.items)
|
||||
|
||||
if (StringUtils.isNotBlank(feed.imageUrl)) {
|
||||
Glide.with(this)
|
||||
|
@ -416,7 +410,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
.error(R.color.light_gray)
|
||||
.fitCenter()
|
||||
.dontAnimate())
|
||||
.into(viewBinding!!.coverImage)
|
||||
.into(viewBinding.coverImage)
|
||||
Glide.with(this)
|
||||
.load(feed.imageUrl)
|
||||
.apply(RequestOptions()
|
||||
|
@ -424,14 +418,14 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
.error(R.color.image_readability_tint)
|
||||
.transform(FastBlurTransformation())
|
||||
.dontAnimate())
|
||||
.into(viewBinding!!.backgroundImage)
|
||||
.into(viewBinding.backgroundImage)
|
||||
}
|
||||
|
||||
viewBinding!!.titleLabel.text = feed.title
|
||||
viewBinding!!.authorLabel.text = feed.author
|
||||
headerBinding!!.txtvDescription.text = HtmlToPlainText.getPlainText(feed.description)
|
||||
viewBinding.titleLabel.text = feed.title
|
||||
viewBinding.authorLabel.text = feed.author
|
||||
headerBinding.txtvDescription.text = HtmlToPlainText.getPlainText(feed.description)
|
||||
|
||||
viewBinding!!.subscribeButton.setOnClickListener { v: View? ->
|
||||
viewBinding.subscribeButton.setOnClickListener { v: View? ->
|
||||
if (feedInFeedlist()) {
|
||||
openFeed()
|
||||
} else {
|
||||
|
@ -441,29 +435,29 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
viewBinding!!.stopPreviewButton.setOnClickListener { v: View? ->
|
||||
viewBinding.stopPreviewButton.setOnClickListener { v: View? ->
|
||||
writeNoMediaPlaying()
|
||||
sendLocalBroadcast(this, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE)
|
||||
}
|
||||
|
||||
if (isEnableAutodownload) {
|
||||
val preferences = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
viewBinding!!.autoDownloadCheckBox.isChecked = preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)
|
||||
viewBinding.autoDownloadCheckBox.isChecked = preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)
|
||||
}
|
||||
|
||||
headerBinding!!.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED
|
||||
headerBinding!!.txtvDescription.setOnClickListener { v: View? ->
|
||||
if (headerBinding!!.txtvDescription.maxLines > DESCRIPTION_MAX_LINES_COLLAPSED) {
|
||||
headerBinding!!.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED
|
||||
headerBinding.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED
|
||||
headerBinding.txtvDescription.setOnClickListener { v: View? ->
|
||||
if (headerBinding.txtvDescription.maxLines > DESCRIPTION_MAX_LINES_COLLAPSED) {
|
||||
headerBinding.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED
|
||||
} else {
|
||||
headerBinding!!.txtvDescription.maxLines = 2000
|
||||
headerBinding.txtvDescription.maxLines = 2000
|
||||
}
|
||||
}
|
||||
|
||||
if (alternateFeedUrls.isEmpty()) {
|
||||
viewBinding!!.alternateUrlsSpinner.visibility = View.GONE
|
||||
viewBinding.alternateUrlsSpinner.visibility = View.GONE
|
||||
} else {
|
||||
viewBinding!!.alternateUrlsSpinner.visibility = View.VISIBLE
|
||||
viewBinding.alternateUrlsSpinner.visibility = View.VISIBLE
|
||||
|
||||
val alternateUrlsList: MutableList<String> = ArrayList()
|
||||
val alternateUrlsTitleList: MutableList<String?> = ArrayList()
|
||||
|
@ -486,8 +480,8 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
adapter.setDropDownViewResource(R.layout.alternate_urls_dropdown_item)
|
||||
viewBinding!!.alternateUrlsSpinner.adapter = adapter
|
||||
viewBinding!!.alternateUrlsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
viewBinding.alternateUrlsSpinner.adapter = adapter
|
||||
viewBinding.alternateUrlsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
selectedDownloadUrl = alternateUrlsList[position]
|
||||
}
|
||||
|
@ -510,13 +504,15 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
@UnstableApi private fun handleUpdatedFeedStatus() {
|
||||
if (DownloadServiceInterface.get() == null || selectedDownloadUrl == null) return
|
||||
if (DownloadServiceInterface.get()!!.isDownloadingEpisode(selectedDownloadUrl!!)) {
|
||||
viewBinding!!.subscribeButton.isEnabled = false
|
||||
viewBinding!!.subscribeButton.setText(R.string.subscribing_label)
|
||||
val dli = DownloadServiceInterface.get()
|
||||
if (dli == null || selectedDownloadUrl == null) return
|
||||
|
||||
if (dli.isDownloadingEpisode(selectedDownloadUrl!!)) {
|
||||
viewBinding.subscribeButton.isEnabled = false
|
||||
viewBinding.subscribeButton.setText(R.string.subscribing_label)
|
||||
} else if (feedInFeedlist()) {
|
||||
viewBinding!!.subscribeButton.isEnabled = true
|
||||
viewBinding!!.subscribeButton.setText(R.string.open_podcast)
|
||||
viewBinding.subscribeButton.isEnabled = true
|
||||
viewBinding.subscribeButton.setText(R.string.open_podcast)
|
||||
if (didPressSubscribe) {
|
||||
didPressSubscribe = false
|
||||
|
||||
|
@ -524,7 +520,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
val feedPreferences = feed1.preferences
|
||||
if (feedPreferences != null) {
|
||||
if (isEnableAutodownload) {
|
||||
val autoDownload = viewBinding!!.autoDownloadCheckBox.isChecked
|
||||
val autoDownload = viewBinding.autoDownloadCheckBox.isChecked
|
||||
feedPreferences.autoDownload = autoDownload
|
||||
|
||||
val preferences = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
|
@ -541,10 +537,10 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
openFeed()
|
||||
}
|
||||
} else {
|
||||
viewBinding!!.subscribeButton.isEnabled = true
|
||||
viewBinding!!.subscribeButton.setText(R.string.subscribe_label)
|
||||
viewBinding.subscribeButton.isEnabled = true
|
||||
viewBinding.subscribeButton.setText(R.string.subscribe_label)
|
||||
if (isEnableAutodownload) {
|
||||
viewBinding!!.autoDownloadCheckBox.visibility = View.VISIBLE
|
||||
viewBinding.autoDownloadCheckBox.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -622,7 +618,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun playbackStateChanged(event: PlayerStatusEvent?) {
|
||||
val isPlayingPreview = currentlyPlayingMediaType == RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA.toLong()
|
||||
viewBinding!!.stopPreviewButton.visibility = if (isPlayingPreview) View.VISIBLE else View.GONE
|
||||
viewBinding.stopPreviewButton.visibility = if (isPlayingPreview) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -631,7 +627,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
|||
*/
|
||||
private fun showFeedDiscoveryDialog(feedFile: File, baseUrl: String): Boolean {
|
||||
val fd = FeedDiscoverer()
|
||||
val urlsMap: Map<String, String>?
|
||||
val urlsMap: Map<String, String>
|
||||
try {
|
||||
urlsMap = fd.findLinks(feedFile, baseUrl)
|
||||
if (urlsMap.isEmpty()) {
|
||||
|
|
|
@ -45,54 +45,59 @@ import java.io.Reader
|
|||
*/
|
||||
class OpmlImportActivity : AppCompatActivity() {
|
||||
private var uri: Uri? = null
|
||||
private var viewBinding: OpmlSelectionBinding? = null
|
||||
private lateinit var viewBinding: OpmlSelectionBinding
|
||||
private lateinit var selectAll: MenuItem
|
||||
private lateinit var deselectAll: MenuItem
|
||||
|
||||
private var listAdapter: ArrayAdapter<String>? = null
|
||||
private var selectAll: MenuItem? = null
|
||||
private var deselectAll: MenuItem? = null
|
||||
private var readElements: ArrayList<OpmlElement>? = null
|
||||
|
||||
@UnstableApi override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(getTheme(this))
|
||||
super.onCreate(savedInstanceState)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
viewBinding = OpmlSelectionBinding.inflate(layoutInflater)
|
||||
setContentView(viewBinding!!.root)
|
||||
setContentView(viewBinding.root)
|
||||
|
||||
viewBinding!!.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE
|
||||
viewBinding!!.feedlist.onItemClickListener =
|
||||
viewBinding.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE
|
||||
viewBinding.feedlist.onItemClickListener =
|
||||
OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
val checked = viewBinding!!.feedlist.checkedItemPositions
|
||||
val checked = viewBinding.feedlist.checkedItemPositions
|
||||
var checkedCount = 0
|
||||
for (i in 0 until checked.size()) {
|
||||
if (checked.valueAt(i)) {
|
||||
checkedCount++
|
||||
}
|
||||
}
|
||||
if (checkedCount == listAdapter!!.count) {
|
||||
selectAll!!.setVisible(false)
|
||||
deselectAll!!.setVisible(true)
|
||||
} else {
|
||||
deselectAll!!.setVisible(false)
|
||||
selectAll!!.setVisible(true)
|
||||
if (listAdapter != null) {
|
||||
if (checkedCount == listAdapter!!.count) {
|
||||
selectAll.setVisible(false)
|
||||
deselectAll.setVisible(true)
|
||||
} else {
|
||||
deselectAll.setVisible(false)
|
||||
selectAll.setVisible(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewBinding!!.butCancel.setOnClickListener { v: View? ->
|
||||
viewBinding.butCancel.setOnClickListener { v: View? ->
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
viewBinding!!.butConfirm.setOnClickListener { v: View? ->
|
||||
viewBinding!!.progressBar.visibility = View.VISIBLE
|
||||
viewBinding.butConfirm.setOnClickListener { v: View? ->
|
||||
viewBinding.progressBar.visibility = View.VISIBLE
|
||||
Completable.fromAction {
|
||||
val checked = viewBinding!!.feedlist.checkedItemPositions
|
||||
val checked = viewBinding.feedlist.checkedItemPositions
|
||||
for (i in 0 until checked.size()) {
|
||||
if (!checked.valueAt(i)) {
|
||||
continue
|
||||
}
|
||||
val element = readElements!![checked.keyAt(i)]
|
||||
val feed = Feed(element.xmlUrl, null,
|
||||
if (element.text != null) element.text else "Unknown podcast")
|
||||
feed.items = mutableListOf()
|
||||
DBTasks.updateFeed(this, feed, false)
|
||||
if (!readElements.isNullOrEmpty()) {
|
||||
val element = readElements!![checked.keyAt(i)]
|
||||
val feed = Feed(element.xmlUrl, null,
|
||||
if (element.text != null) element.text else "Unknown podcast")
|
||||
feed.items = mutableListOf()
|
||||
DBTasks.updateFeed(this, feed, false)
|
||||
}
|
||||
}
|
||||
runOnce(this)
|
||||
}
|
||||
|
@ -100,14 +105,14 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
val intent = Intent(this@OpmlImportActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}, { e: Throwable ->
|
||||
e.printStackTrace()
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
|
||||
})
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
private val titleList: List<String>
|
||||
get() {
|
||||
val result: MutableList<String> = ArrayList()
|
||||
if (readElements != null) {
|
||||
if (!readElements.isNullOrEmpty()) {
|
||||
for (element in readElements!!) {
|
||||
if (element.text != null) result.add(element.text!!)
|
||||
}
|
||||
|
@ -153,7 +158,7 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
inflater.inflate(R.menu.opml_selection_options, menu)
|
||||
selectAll = menu.findItem(R.id.select_all_item)
|
||||
deselectAll = menu.findItem(R.id.deselect_all_item)
|
||||
deselectAll?.setVisible(false)
|
||||
deselectAll.setVisible(false)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -161,15 +166,15 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
val itemId = item.itemId
|
||||
when (itemId) {
|
||||
R.id.select_all_item -> {
|
||||
selectAll!!.setVisible(false)
|
||||
selectAll.setVisible(false)
|
||||
selectAllItems(true)
|
||||
deselectAll!!.setVisible(true)
|
||||
deselectAll.setVisible(true)
|
||||
return true
|
||||
}
|
||||
R.id.deselect_all_item -> {
|
||||
deselectAll!!.setVisible(false)
|
||||
deselectAll.setVisible(false)
|
||||
selectAllItems(false)
|
||||
selectAll!!.setVisible(true)
|
||||
selectAll.setVisible(true)
|
||||
return true
|
||||
}
|
||||
android.R.id.home -> {
|
||||
|
@ -180,8 +185,8 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun selectAllItems(b: Boolean) {
|
||||
for (i in 0 until viewBinding!!.feedlist.count) {
|
||||
viewBinding!!.feedlist.setItemChecked(i, b)
|
||||
for (i in 0 until viewBinding.feedlist.count) {
|
||||
viewBinding.feedlist.setItemChecked(i, b)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,13 +209,13 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
|
||||
/** Starts the import process. */
|
||||
private fun startImport() {
|
||||
viewBinding!!.progressBar.visibility = View.VISIBLE
|
||||
viewBinding.progressBar.visibility = View.VISIBLE
|
||||
|
||||
Observable.fromCallable {
|
||||
val opmlFileStream = contentResolver.openInputStream(uri!!)
|
||||
val bomInputStream = BOMInputStream(opmlFileStream)
|
||||
val bom = bomInputStream.bom
|
||||
val charsetName = if ((bom == null)) "UTF-8" else bom.charsetName
|
||||
val charsetName = if (bom == null) "UTF-8" else bom.charsetName
|
||||
val reader: Reader = InputStreamReader(bomInputStream, charsetName)
|
||||
val opmlReader = OpmlReader()
|
||||
val result = opmlReader.readDocument(reader)
|
||||
|
@ -221,13 +226,13 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: ArrayList<OpmlElement>? ->
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
Log.d(TAG, "Parsing was successful")
|
||||
readElements = result
|
||||
listAdapter = ArrayAdapter(this@OpmlImportActivity,
|
||||
android.R.layout.simple_list_item_multiple_choice,
|
||||
titleList)
|
||||
viewBinding!!.feedlist.adapter = listAdapter
|
||||
viewBinding.feedlist.adapter = listAdapter
|
||||
}, { e: Throwable ->
|
||||
Log.d(TAG, Log.getStackTraceString(e))
|
||||
val message = if (e.message == null) "" else e.message!!
|
||||
|
@ -240,7 +245,7 @@ class OpmlImportActivity : AppCompatActivity() {
|
|||
return@subscribe
|
||||
}
|
||||
}
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
val alert = MaterialAlertDialogBuilder(this)
|
||||
alert.setTitle(R.string.error_label)
|
||||
val userReadable = getString(R.string.opml_reader_error)
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
* PreferenceController.
|
||||
*/
|
||||
class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
||||
private var binding: SettingsActivityBinding? = null
|
||||
private lateinit var binding: SettingsActivityBinding
|
||||
|
||||
@SuppressLint("CommitTransaction")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -41,11 +41,11 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
|||
ab?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
binding = SettingsActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
setContentView(binding.root)
|
||||
|
||||
if (supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(binding!!.settingsContainer.id, MainPreferencesFragment(), FRAGMENT_TAG)
|
||||
.replace(binding.settingsContainer.id, MainPreferencesFragment(), FRAGMENT_TAG)
|
||||
.commit()
|
||||
}
|
||||
val intent = intent
|
||||
|
@ -96,7 +96,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
|||
startActivity(intent)
|
||||
} else {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(binding!!.settingsContainer.id, fragment!!)
|
||||
.replace(binding.settingsContainer.id, fragment!!)
|
||||
.addToBackStack(getString(getTitleOfPage(screen)))
|
||||
.commit()
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: MessageEvent) {
|
||||
Log.d(FRAGMENT_TAG, "onEvent($event)")
|
||||
val s = Snackbar.make(binding!!.root, event.message, Snackbar.LENGTH_LONG)
|
||||
val s = Snackbar.make(binding.root, event.message, Snackbar.LENGTH_LONG)
|
||||
if (event.action != null) {
|
||||
s.setAction(event.actionText) { v: View? -> event.action!!.accept(this) }
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
package ac.mdiq.podvinci.activity
|
||||
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.activity.MainActivity.Companion.EXTRA_FEED_ID
|
||||
import ac.mdiq.podvinci.core.preferences.ThemeSwitcher
|
||||
import ac.mdiq.podvinci.core.storage.DBReader
|
||||
import ac.mdiq.podvinci.core.storage.NavDrawerData
|
||||
import ac.mdiq.podvinci.databinding.SubscriptionSelectionActivityBinding
|
||||
import ac.mdiq.podvinci.model.feed.Feed
|
||||
import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
|
@ -15,19 +21,13 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.core.preferences.ThemeSwitcher
|
||||
import ac.mdiq.podvinci.core.storage.DBReader
|
||||
import ac.mdiq.podvinci.core.storage.NavDrawerData
|
||||
import ac.mdiq.podvinci.databinding.SubscriptionSelectionActivityBinding
|
||||
import ac.mdiq.podvinci.model.feed.Feed
|
||||
import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -39,29 +39,29 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
@Volatile
|
||||
private var listItems: List<Feed>? = null
|
||||
|
||||
private var viewBinding: SubscriptionSelectionActivityBinding? = null
|
||||
private lateinit var viewBinding: SubscriptionSelectionActivityBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(ThemeSwitcher.getTranslucentTheme(this))
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewBinding = SubscriptionSelectionActivityBinding.inflate(layoutInflater)
|
||||
setContentView(viewBinding!!.root)
|
||||
setSupportActionBar(viewBinding!!.toolbar)
|
||||
setContentView(viewBinding.root)
|
||||
setSupportActionBar(viewBinding.toolbar)
|
||||
setTitle(R.string.shortcut_select_subscription)
|
||||
|
||||
viewBinding!!.transparentBackground.setOnClickListener { v: View? -> finish() }
|
||||
viewBinding!!.card.setOnClickListener(null)
|
||||
viewBinding.transparentBackground.setOnClickListener { v: View? -> finish() }
|
||||
viewBinding.card.setOnClickListener(null)
|
||||
|
||||
loadSubscriptions()
|
||||
|
||||
val checkedPosition = arrayOfNulls<Int>(1)
|
||||
viewBinding!!.list.choiceMode = ListView.CHOICE_MODE_SINGLE
|
||||
viewBinding!!.list.onItemClickListener =
|
||||
viewBinding.list.choiceMode = ListView.CHOICE_MODE_SINGLE
|
||||
viewBinding.list.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { listView: AdapterView<*>?, view1: View?, position: Int, rowId: Long ->
|
||||
checkedPosition[0] = position
|
||||
}
|
||||
viewBinding!!.shortcutBtn.setOnClickListener { view: View? ->
|
||||
viewBinding.shortcutBtn.setOnClickListener { view: View? ->
|
||||
if (checkedPosition[0] != null && Intent.ACTION_CREATE_SHORTCUT == intent.action) {
|
||||
getBitmapFromUrl(listItems!![checkedPosition[0]!!])
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
return result
|
||||
}
|
||||
|
||||
private fun addShortcut(feed: Feed, bitmap: Bitmap?) {
|
||||
@UnstableApi private fun addShortcut(feed: Feed, bitmap: Bitmap?) {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.setAction(Intent.ACTION_MAIN)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
|
@ -114,15 +114,15 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
.load(feed.imageUrl)
|
||||
.apply(RequestOptions.overrideOf(iconSize, iconSize))
|
||||
.listener(object : RequestListener<Bitmap?> {
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?,
|
||||
target: Target<Bitmap?>, isFirstResource: Boolean
|
||||
@UnstableApi override fun onLoadFailed(e: GlideException?, model: Any?,
|
||||
target: Target<Bitmap?>, isFirstResource: Boolean
|
||||
): Boolean {
|
||||
addShortcut(feed, null)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: Bitmap, model: Any,
|
||||
target: Target<Bitmap?>, dataSource: DataSource, isFirstResource: Boolean
|
||||
@UnstableApi override fun onResourceReady(resource: Bitmap, model: Any,
|
||||
target: Target<Bitmap?>, dataSource: DataSource, isFirstResource: Boolean
|
||||
): Boolean {
|
||||
addShortcut(feed, resource)
|
||||
return true
|
||||
|
@ -131,9 +131,8 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
if (disposable != null) {
|
||||
disposable?.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable {
|
||||
val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
getFeedItems(data.items, ArrayList())
|
||||
|
@ -149,7 +148,7 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
}
|
||||
val adapter: ArrayAdapter<String> = ArrayAdapter<String>(this,
|
||||
R.layout.simple_list_item_multiple_choice_on_start, titles)
|
||||
viewBinding!!.list.adapter = adapter
|
||||
viewBinding.list.adapter = adapter
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,9 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
*/
|
||||
@UnstableApi
|
||||
class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
||||
|
||||
private lateinit var viewBinding: VideoplayerActivityBinding
|
||||
|
||||
/**
|
||||
* True if video controls are currently visible.
|
||||
*/
|
||||
|
@ -74,7 +77,6 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
private var destroyingDueToReload = false
|
||||
private var lastScreenTap: Long = 0
|
||||
private val videoControlsHider = Handler(Looper.getMainLooper())
|
||||
private var viewBinding: VideoplayerActivityBinding? = null
|
||||
private var controller: PlaybackController? = null
|
||||
private var showTimeLeft = false
|
||||
private var isFavorite = false
|
||||
|
@ -94,10 +96,10 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
|
||||
window.setFormat(PixelFormat.TRANSPARENT)
|
||||
viewBinding = VideoplayerActivityBinding.inflate(LayoutInflater.from(this))
|
||||
setContentView(viewBinding!!.root)
|
||||
setContentView(viewBinding.root)
|
||||
setupView()
|
||||
supportActionBar!!.setBackgroundDrawable(ColorDrawable(-0x80000000))
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setBackgroundDrawable(ColorDrawable(-0x80000000))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
|
@ -116,20 +118,17 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
|
||||
@UnstableApi
|
||||
override fun onStop() {
|
||||
if (controller != null) {
|
||||
controller!!.release()
|
||||
controller = null // prevent leak
|
||||
}
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
controller?.release()
|
||||
controller = null // prevent leak
|
||||
disposable?.dispose()
|
||||
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onStop()
|
||||
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||
videoControlsHider.removeCallbacks(hideVideoControls)
|
||||
}
|
||||
// Controller released; we will not receive buffering updates
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
public override fun onUserLeaveHint() {
|
||||
|
@ -172,7 +171,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
private fun newPlaybackController(): PlaybackController {
|
||||
return object : PlaybackController(this@VideoplayerActivity) {
|
||||
override fun updatePlayButtonShowsPlay(showPlay: Boolean) {
|
||||
viewBinding!!.playButton.setIsShowPlay(showPlay)
|
||||
viewBinding.playButton.setIsShowPlay(showPlay)
|
||||
if (showPlay) {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
|
@ -180,7 +179,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
setupVideoAspectRatio()
|
||||
if (videoSurfaceCreated && controller != null) {
|
||||
Log.d(TAG, "Videosurface already created, setting videosurface now")
|
||||
controller!!.setVideoSurface(viewBinding!!.videoView.holder)
|
||||
controller!!.setVideoSurface(viewBinding.videoView.holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,11 +198,11 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
@Suppress("unused")
|
||||
fun bufferUpdate(event: BufferUpdateEvent) {
|
||||
if (event.hasStarted()) {
|
||||
viewBinding!!.progressBar.visibility = View.VISIBLE
|
||||
viewBinding.progressBar.visibility = View.VISIBLE
|
||||
} else if (event.hasEnded()) {
|
||||
viewBinding!!.progressBar.visibility = View.INVISIBLE
|
||||
viewBinding.progressBar.visibility = View.INVISIBLE
|
||||
} else {
|
||||
viewBinding!!.sbPosition.secondaryProgress = (event.progress * viewBinding!!.sbPosition.max).toInt()
|
||||
viewBinding.sbPosition.secondaryProgress = (event.progress * viewBinding.sbPosition.max).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,9 +215,9 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
@UnstableApi
|
||||
protected fun loadMediaInfo() {
|
||||
private fun loadMediaInfo() {
|
||||
Log.d(TAG, "loadMediaInfo()")
|
||||
if (controller == null || controller!!.getMedia() == null) {
|
||||
if (controller?.getMedia() == null) {
|
||||
return
|
||||
}
|
||||
if (controller!!.status == PlayerStatus.PLAYING && !controller!!.isPlayingVideoLocally) {
|
||||
|
@ -239,12 +238,12 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
@UnstableApi
|
||||
protected fun setupView() {
|
||||
private fun setupView() {
|
||||
showTimeLeft = shouldShowRemainingTime()
|
||||
Log.d("timeleft", if (showTimeLeft) "true" else "false")
|
||||
viewBinding!!.durationLabel.setOnClickListener { v: View? ->
|
||||
viewBinding.durationLabel.setOnClickListener { v: View? ->
|
||||
showTimeLeft = !showTimeLeft
|
||||
val media = controller!!.getMedia() ?: return@setOnClickListener
|
||||
val media = controller?.getMedia() ?: return@setOnClickListener
|
||||
|
||||
val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier)
|
||||
val length: String
|
||||
|
@ -255,48 +254,48 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
val duration = converter.convert(media.getDuration())
|
||||
length = getDurationStringLong(duration)
|
||||
}
|
||||
viewBinding!!.durationLabel.text = length
|
||||
viewBinding.durationLabel.text = length
|
||||
|
||||
setShowRemainTimeSetting(showTimeLeft)
|
||||
Log.d("timeleft on click", if (showTimeLeft) "true" else "false")
|
||||
}
|
||||
|
||||
viewBinding!!.sbPosition.setOnSeekBarChangeListener(this)
|
||||
viewBinding!!.rewindButton.setOnClickListener { v: View? -> onRewind() }
|
||||
viewBinding!!.rewindButton.setOnLongClickListener { v: View? ->
|
||||
viewBinding.sbPosition.setOnSeekBarChangeListener(this)
|
||||
viewBinding.rewindButton.setOnClickListener { v: View? -> onRewind() }
|
||||
viewBinding.rewindButton.setOnLongClickListener { v: View? ->
|
||||
SkipPreferenceDialog.showSkipPreference(this@VideoplayerActivity,
|
||||
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null)
|
||||
true
|
||||
}
|
||||
viewBinding!!.playButton.setIsVideoScreen(true)
|
||||
viewBinding!!.playButton.setOnClickListener { v: View? -> onPlayPause() }
|
||||
viewBinding!!.fastForwardButton.setOnClickListener { v: View? -> onFastForward() }
|
||||
viewBinding!!.fastForwardButton.setOnLongClickListener { v: View? ->
|
||||
viewBinding.playButton.setIsVideoScreen(true)
|
||||
viewBinding.playButton.setOnClickListener { v: View? -> onPlayPause() }
|
||||
viewBinding.fastForwardButton.setOnClickListener { v: View? -> onFastForward() }
|
||||
viewBinding.fastForwardButton.setOnLongClickListener { v: View? ->
|
||||
SkipPreferenceDialog.showSkipPreference(this@VideoplayerActivity,
|
||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null)
|
||||
false
|
||||
}
|
||||
// To suppress touches directly below the slider
|
||||
viewBinding!!.bottomControlsContainer.setOnTouchListener { view: View?, motionEvent: MotionEvent? -> true }
|
||||
viewBinding!!.bottomControlsContainer.fitsSystemWindows = true
|
||||
viewBinding!!.videoView.holder.addCallback(surfaceHolderCallback)
|
||||
viewBinding!!.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
viewBinding.bottomControlsContainer.setOnTouchListener { view: View?, motionEvent: MotionEvent? -> true }
|
||||
viewBinding.bottomControlsContainer.fitsSystemWindows = true
|
||||
viewBinding.videoView.holder.addCallback(surfaceHolderCallback)
|
||||
viewBinding.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
|
||||
setupVideoControlsToggler()
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
|
||||
viewBinding!!.videoPlayerContainer.setOnTouchListener(onVideoviewTouched)
|
||||
viewBinding!!.videoPlayerContainer.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
viewBinding!!.videoView.setAvailableSize(
|
||||
viewBinding!!.videoPlayerContainer.width.toFloat(), viewBinding!!.videoPlayerContainer.height.toFloat())
|
||||
viewBinding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched)
|
||||
viewBinding.videoPlayerContainer.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
viewBinding.videoView.setAvailableSize(
|
||||
viewBinding.videoPlayerContainer.width.toFloat(), viewBinding.videoPlayerContainer.height.toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
private val hideVideoControls = Runnable {
|
||||
if (videoControlsShowing) {
|
||||
Log.d(TAG, "Hiding video controls")
|
||||
supportActionBar!!.hide()
|
||||
supportActionBar?.hide()
|
||||
hideVideoControls(true)
|
||||
videoControlsShowing = false
|
||||
}
|
||||
|
@ -320,7 +319,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
showSkipAnimation(false)
|
||||
}
|
||||
if (videoControlsShowing) {
|
||||
supportActionBar!!.hide()
|
||||
supportActionBar?.hide()
|
||||
hideVideoControls(false)
|
||||
videoControlsShowing = false
|
||||
}
|
||||
|
@ -344,24 +343,24 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
skipAnimation.fillAfter = false
|
||||
skipAnimation.duration = 800
|
||||
|
||||
val params = viewBinding!!.skipAnimationImage.layoutParams as FrameLayout.LayoutParams
|
||||
val params = viewBinding.skipAnimationImage.layoutParams as FrameLayout.LayoutParams
|
||||
if (isForward) {
|
||||
viewBinding!!.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white)
|
||||
viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white)
|
||||
params.gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
|
||||
} else {
|
||||
viewBinding!!.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white)
|
||||
viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white)
|
||||
params.gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
|
||||
}
|
||||
|
||||
viewBinding!!.skipAnimationImage.visibility = View.VISIBLE
|
||||
viewBinding!!.skipAnimationImage.layoutParams = params
|
||||
viewBinding!!.skipAnimationImage.startAnimation(skipAnimation)
|
||||
viewBinding.skipAnimationImage.visibility = View.VISIBLE
|
||||
viewBinding.skipAnimationImage.layoutParams = params
|
||||
viewBinding.skipAnimationImage.startAnimation(skipAnimation)
|
||||
skipAnimation.setAnimationListener(object : Animation.AnimationListener {
|
||||
override fun onAnimationStart(animation: Animation) {
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animation) {
|
||||
viewBinding!!.skipAnimationImage.visibility = View.GONE
|
||||
viewBinding.skipAnimationImage.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onAnimationRepeat(animation: Animation) {
|
||||
|
@ -380,7 +379,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
val videoSize = controller!!.videoSize
|
||||
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
|
||||
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second)
|
||||
viewBinding!!.videoView.setVideoSize(videoSize.first, videoSize.second)
|
||||
viewBinding.videoView.setVideoSize(videoSize.first, videoSize.second)
|
||||
} else {
|
||||
Log.e(TAG, "Could not determine video size")
|
||||
}
|
||||
|
@ -389,10 +388,10 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
|
||||
private fun toggleVideoControlsVisibility() {
|
||||
if (videoControlsShowing) {
|
||||
supportActionBar!!.hide()
|
||||
supportActionBar?.hide()
|
||||
hideVideoControls(true)
|
||||
} else {
|
||||
supportActionBar!!.show()
|
||||
supportActionBar?.show()
|
||||
showVideoControls()
|
||||
}
|
||||
videoControlsShowing = !videoControlsShowing
|
||||
|
@ -436,7 +435,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
Log.d(TAG, "Videoview holder created")
|
||||
videoSurfaceCreated = true
|
||||
if (controller != null && controller!!.status == PlayerStatus.PLAYING) {
|
||||
if (controller?.status == PlayerStatus.PLAYING) {
|
||||
controller!!.setVideoSurface(holder)
|
||||
}
|
||||
setupVideoAspectRatio()
|
||||
|
@ -452,31 +451,31 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
private fun showVideoControls() {
|
||||
viewBinding!!.bottomControlsContainer.visibility = View.VISIBLE
|
||||
viewBinding!!.controlsContainer.visibility = View.VISIBLE
|
||||
viewBinding.bottomControlsContainer.visibility = View.VISIBLE
|
||||
viewBinding.controlsContainer.visibility = View.VISIBLE
|
||||
val animation = AnimationUtils.loadAnimation(this, R.anim.fade_in)
|
||||
if (animation != null) {
|
||||
viewBinding!!.bottomControlsContainer.startAnimation(animation)
|
||||
viewBinding!!.controlsContainer.startAnimation(animation)
|
||||
viewBinding.bottomControlsContainer.startAnimation(animation)
|
||||
viewBinding.controlsContainer.startAnimation(animation)
|
||||
}
|
||||
viewBinding!!.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||
viewBinding.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||
}
|
||||
|
||||
private fun hideVideoControls(showAnimation: Boolean) {
|
||||
if (showAnimation) {
|
||||
val animation = AnimationUtils.loadAnimation(this, R.anim.fade_out)
|
||||
if (animation != null) {
|
||||
viewBinding!!.bottomControlsContainer.startAnimation(animation)
|
||||
viewBinding!!.controlsContainer.startAnimation(animation)
|
||||
viewBinding.bottomControlsContainer.startAnimation(animation)
|
||||
viewBinding.controlsContainer.startAnimation(animation)
|
||||
}
|
||||
}
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
viewBinding!!.bottomControlsContainer.fitsSystemWindows = true
|
||||
viewBinding.bottomControlsContainer.fitsSystemWindows = true
|
||||
|
||||
viewBinding!!.bottomControlsContainer.visibility = View.GONE
|
||||
viewBinding!!.controlsContainer.visibility = View.GONE
|
||||
viewBinding.bottomControlsContainer.visibility = View.GONE
|
||||
viewBinding.controlsContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -501,12 +500,10 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
Log.d(TAG, "onEvent($event)")
|
||||
val errorDialog = MaterialAlertDialogBuilder(this)
|
||||
errorDialog.setMessage(event.message)
|
||||
if (event.action != null) {
|
||||
errorDialog.setPositiveButton(event.actionText) { dialog: DialogInterface?, which: Int ->
|
||||
event.action!!.accept(
|
||||
this)
|
||||
}
|
||||
errorDialog.setPositiveButton(event.actionText) { dialog: DialogInterface?, which: Int ->
|
||||
event.action?.accept(this)
|
||||
}
|
||||
|
||||
errorDialog.show()
|
||||
}
|
||||
|
||||
|
@ -533,7 +530,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink)
|
||||
|
||||
val isItemAndHasLink = isFeedMedia && hasLinkToShare((media as FeedMedia).getItem())
|
||||
val isItemHasDownloadLink = isFeedMedia && (media as FeedMedia?)!!.download_url != null
|
||||
val isItemHasDownloadLink = isFeedMedia && (media as FeedMedia?)?.download_url != null
|
||||
menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink)
|
||||
|
||||
menu.findItem(R.id.add_to_favorites_item).setVisible(false)
|
||||
|
@ -555,56 +552,61 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.player_switch_to_audio_only) {
|
||||
switchToAudioOnly = true
|
||||
finish()
|
||||
return true
|
||||
} else if (item.itemId == android.R.id.home) {
|
||||
val intent = Intent(this@VideoplayerActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
return true
|
||||
} else if (item.itemId == R.id.player_show_chapters) {
|
||||
ChaptersFragment().show(supportFragmentManager, ChaptersFragment.TAG)
|
||||
return true
|
||||
// some options option requires FeedItem
|
||||
when {
|
||||
item.itemId == R.id.player_switch_to_audio_only -> {
|
||||
switchToAudioOnly = true
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
item.itemId == android.R.id.home -> {
|
||||
val intent = Intent(this@VideoplayerActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
item.itemId == R.id.player_show_chapters -> {
|
||||
ChaptersFragment().show(supportFragmentManager, ChaptersFragment.TAG)
|
||||
return true
|
||||
}
|
||||
controller == null -> {
|
||||
return false
|
||||
}
|
||||
else -> {
|
||||
val media = controller?.getMedia() ?: return false
|
||||
val feedItem = getFeedItem(media) // some options option requires FeedItem
|
||||
if (item.itemId == R.id.add_to_favorites_item && feedItem != null) {
|
||||
DBWriter.addFavoriteItem(feedItem)
|
||||
isFavorite = true
|
||||
invalidateOptionsMenu()
|
||||
} else if (item.itemId == R.id.remove_from_favorites_item && feedItem != null) {
|
||||
DBWriter.removeFavoriteItem(feedItem)
|
||||
isFavorite = false
|
||||
invalidateOptionsMenu()
|
||||
} else if (item.itemId == R.id.disable_sleeptimer_item
|
||||
|| item.itemId == R.id.set_sleeptimer_item) {
|
||||
SleepTimerDialog().show(supportFragmentManager, "SleepTimerDialog")
|
||||
} else if (item.itemId == R.id.audio_controls) {
|
||||
val dialog = PlaybackControlsDialog.newInstance()
|
||||
dialog.show(supportFragmentManager, "playback_controls")
|
||||
} else if (item.itemId == R.id.open_feed_item && feedItem != null) {
|
||||
val intent = MainActivity.getIntentToOpenFeed(this, feedItem.feedId)
|
||||
startActivity(intent)
|
||||
} else if (item.itemId == R.id.visit_website_item) {
|
||||
val url = getWebsiteLinkWithFallback(media)
|
||||
if (url != null) openInBrowser(this@VideoplayerActivity, url)
|
||||
} else if (item.itemId == R.id.share_item && feedItem != null) {
|
||||
val shareDialog = ShareDialog.newInstance(feedItem)
|
||||
shareDialog.show(supportFragmentManager, "ShareEpisodeDialog")
|
||||
} else if (item.itemId == R.id.playback_speed) {
|
||||
VariableSpeedDialog().show(supportFragmentManager, null)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (controller == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
val media = controller!!.getMedia() ?: return false
|
||||
val feedItem = getFeedItem(media) // some options option requires FeedItem
|
||||
if (item.itemId == R.id.add_to_favorites_item && feedItem != null) {
|
||||
DBWriter.addFavoriteItem(feedItem)
|
||||
isFavorite = true
|
||||
invalidateOptionsMenu()
|
||||
} else if (item.itemId == R.id.remove_from_favorites_item && feedItem != null) {
|
||||
DBWriter.removeFavoriteItem(feedItem)
|
||||
isFavorite = false
|
||||
invalidateOptionsMenu()
|
||||
} else if (item.itemId == R.id.disable_sleeptimer_item
|
||||
|| item.itemId == R.id.set_sleeptimer_item) {
|
||||
SleepTimerDialog().show(supportFragmentManager, "SleepTimerDialog")
|
||||
} else if (item.itemId == R.id.audio_controls) {
|
||||
val dialog = PlaybackControlsDialog.newInstance()
|
||||
dialog.show(supportFragmentManager, "playback_controls")
|
||||
} else if (item.itemId == R.id.open_feed_item && feedItem != null) {
|
||||
val intent = MainActivity.getIntentToOpenFeed(this, feedItem.feedId)
|
||||
startActivity(intent)
|
||||
} else if (item.itemId == R.id.visit_website_item) {
|
||||
val url = getWebsiteLinkWithFallback(media)
|
||||
if (url != null) openInBrowser(this@VideoplayerActivity, url)
|
||||
} else if (item.itemId == R.id.share_item && feedItem != null) {
|
||||
val shareDialog = ShareDialog.newInstance(feedItem)
|
||||
shareDialog.show(supportFragmentManager, "ShareEpisodeDialog")
|
||||
} else if (item.itemId == R.id.playback_speed) {
|
||||
VariableSpeedDialog().show(supportFragmentManager, null)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun onPositionObserverUpdate() {
|
||||
|
@ -623,11 +625,11 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
Log.w(TAG, "Could not react to position observer update because of invalid time")
|
||||
return
|
||||
}
|
||||
viewBinding!!.positionLabel.text = getDurationStringLong(currentPosition)
|
||||
viewBinding.positionLabel.text = getDurationStringLong(currentPosition)
|
||||
if (showTimeLeft) {
|
||||
viewBinding!!.durationLabel.text = "-" + getDurationStringLong(remainingTime)
|
||||
viewBinding.durationLabel.text = "-" + getDurationStringLong(remainingTime)
|
||||
} else {
|
||||
viewBinding!!.durationLabel.text = getDurationStringLong(duration)
|
||||
viewBinding.durationLabel.text = getDurationStringLong(duration)
|
||||
}
|
||||
updateProgressbarPosition(currentPosition, duration)
|
||||
}
|
||||
|
@ -635,7 +637,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
private fun updateProgressbarPosition(position: Int, duration: Int) {
|
||||
Log.d(TAG, "updateProgressbarPosition($position, $duration)")
|
||||
val progress = (position.toFloat()) / duration
|
||||
viewBinding!!.sbPosition.progress = (progress * viewBinding!!.sbPosition.max).toInt()
|
||||
viewBinding.sbPosition.progress = (progress * viewBinding.sbPosition.max).toInt()
|
||||
}
|
||||
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
@ -646,14 +648,14 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
prog = progress / (seekBar.max.toFloat())
|
||||
val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier)
|
||||
val position = converter.convert((prog * controller!!.duration).toInt())
|
||||
viewBinding!!.seekPositionLabel.text = getDurationStringLong(position)
|
||||
viewBinding.seekPositionLabel.text = getDurationStringLong(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
viewBinding!!.seekCardView.scaleX = .8f
|
||||
viewBinding!!.seekCardView.scaleY = .8f
|
||||
viewBinding!!.seekCardView.animate()
|
||||
viewBinding.seekCardView.scaleX = .8f
|
||||
viewBinding.seekCardView.scaleY = .8f
|
||||
viewBinding.seekCardView.animate()
|
||||
.setInterpolator(FastOutSlowInInterpolator())
|
||||
.alpha(1f).scaleX(1f).scaleY(1f)
|
||||
.setDuration(200)
|
||||
|
@ -665,9 +667,9 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
if (controller != null) {
|
||||
controller!!.seekTo((prog * controller!!.duration).toInt())
|
||||
}
|
||||
viewBinding!!.seekCardView.scaleX = 1f
|
||||
viewBinding!!.seekCardView.scaleY = 1f
|
||||
viewBinding!!.seekCardView.animate()
|
||||
viewBinding.seekCardView.scaleX = 1f
|
||||
viewBinding.seekCardView.scaleY = 1f
|
||||
viewBinding.seekCardView.animate()
|
||||
.setInterpolator(FastOutSlowInInterpolator())
|
||||
.alpha(0f).scaleX(.8f).scaleY(.8f)
|
||||
.setDuration(200)
|
||||
|
@ -676,26 +678,27 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
private fun checkFavorite() {
|
||||
val feedItem = getFeedItem(controller!!.getMedia()) ?: return
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
val feedItem = getFeedItem(controller?.getMedia()) ?: return
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable { DBReader.getFeedItem(feedItem.id) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ item: FeedItem? ->
|
||||
val isFav = item!!.isTagged(FeedItem.TAG_FAVORITE)
|
||||
if (isFavorite != isFav) {
|
||||
isFavorite = isFav
|
||||
invalidateOptionsMenu()
|
||||
if (item != null) {
|
||||
val isFav = item.isTagged(FeedItem.TAG_FAVORITE)
|
||||
if (isFavorite != isFav) {
|
||||
isFavorite = isFav
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
private fun compatEnterPictureInPicture() {
|
||||
if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
supportActionBar!!.hide()
|
||||
supportActionBar?.hide()
|
||||
hideVideoControls(false)
|
||||
enterPictureInPictureMode()
|
||||
}
|
||||
|
@ -753,7 +756,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
|
|||
}
|
||||
//Go to x% of video:
|
||||
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
|
||||
controller!!.seekTo((0.1f * (keyCode - KeyEvent.KEYCODE_0) * controller!!.duration).toInt())
|
||||
controller?.seekTo((0.1f * (keyCode - KeyEvent.KEYCODE_0) * controller!!.duration).toInt())
|
||||
return true
|
||||
}
|
||||
return super.onKeyUp(keyCode, event)
|
||||
|
|
|
@ -19,13 +19,13 @@ import ac.mdiq.podvinci.core.widget.WidgetUpdaterWorker
|
|||
class WidgetConfigActivity : AppCompatActivity() {
|
||||
private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
|
||||
|
||||
private var opacitySeekBar: SeekBar? = null
|
||||
private var opacityTextView: TextView? = null
|
||||
private var widgetPreview: View? = null
|
||||
private var ckPlaybackSpeed: CheckBox? = null
|
||||
private var ckRewind: CheckBox? = null
|
||||
private var ckFastForward: CheckBox? = null
|
||||
private var ckSkip: CheckBox? = null
|
||||
private lateinit var widgetPreview: View
|
||||
private lateinit var opacitySeekBar: SeekBar
|
||||
private lateinit var opacityTextView: TextView
|
||||
private lateinit var ckPlaybackSpeed: CheckBox
|
||||
private lateinit var ckRewind: CheckBox
|
||||
private lateinit var ckFastForward: CheckBox
|
||||
private lateinit var ckSkip: CheckBox
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(getTheme(this))
|
||||
|
@ -50,84 +50,82 @@ class WidgetConfigActivity : AppCompatActivity() {
|
|||
opacitySeekBar = findViewById(R.id.widget_opacity_seekBar)
|
||||
widgetPreview = findViewById(R.id.widgetLayout)
|
||||
findViewById<View>(R.id.butConfirm).setOnClickListener { v: View? -> confirmCreateWidget() }
|
||||
if (opacitySeekBar != null) {
|
||||
opacitySeekBar!!.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
||||
opacityTextView?.text = seekBar.progress.toString() + "%"
|
||||
val color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar!!.progress)
|
||||
widgetPreview?.setBackgroundColor(color)
|
||||
}
|
||||
opacitySeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
||||
opacityTextView.text = seekBar.progress.toString() + "%"
|
||||
val color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress)
|
||||
widgetPreview.setBackgroundColor(color)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
})
|
||||
|
||||
widgetPreview.findViewById<View>(R.id.txtNoPlaying).visibility = View.GONE
|
||||
val title = widgetPreview.findViewById<TextView>(R.id.txtvTitle)
|
||||
title.visibility = View.VISIBLE
|
||||
title.setText(R.string.app_name)
|
||||
val progress = widgetPreview.findViewById<TextView>(R.id.txtvProgress)
|
||||
progress.visibility = View.VISIBLE
|
||||
progress.setText(R.string.position_default_label)
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
})
|
||||
}
|
||||
if (widgetPreview != null) {
|
||||
widgetPreview!!.findViewById<View>(R.id.txtNoPlaying).visibility = View.GONE
|
||||
val title = widgetPreview!!.findViewById<TextView>(R.id.txtvTitle)
|
||||
title.visibility = View.VISIBLE
|
||||
title.setText(R.string.app_name)
|
||||
val progress = widgetPreview!!.findViewById<TextView>(R.id.txtvProgress)
|
||||
progress.visibility = View.VISIBLE
|
||||
progress.setText(R.string.position_default_label)
|
||||
}
|
||||
ckPlaybackSpeed = findViewById(R.id.ckPlaybackSpeed)
|
||||
ckPlaybackSpeed?.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckPlaybackSpeed.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckRewind = findViewById(R.id.ckRewind)
|
||||
ckRewind?.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckRewind.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckFastForward = findViewById(R.id.ckFastForward)
|
||||
ckFastForward?.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckFastForward.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckSkip = findViewById(R.id.ckSkip)
|
||||
ckSkip?.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
ckSkip.setOnClickListener { v: View? -> displayPreviewPanel() }
|
||||
|
||||
setInitialState()
|
||||
}
|
||||
|
||||
private fun setInitialState() {
|
||||
val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
||||
ckPlaybackSpeed!!.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, false)
|
||||
ckRewind!!.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, false)
|
||||
ckFastForward!!.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, false)
|
||||
ckSkip!!.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, false)
|
||||
ckPlaybackSpeed.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, false)
|
||||
ckRewind.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, false)
|
||||
ckFastForward.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, false)
|
||||
ckSkip.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, false)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val color = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, PlayerWidget.DEFAULT_COLOR)
|
||||
val opacity = Color.alpha(color) * 100 / 0xFF
|
||||
|
||||
opacitySeekBar!!.setProgress(opacity, false)
|
||||
opacitySeekBar.setProgress(opacity, false)
|
||||
}
|
||||
displayPreviewPanel()
|
||||
}
|
||||
|
||||
private fun displayPreviewPanel() {
|
||||
val showExtendedPreview =
|
||||
ckPlaybackSpeed!!.isChecked || ckRewind!!.isChecked || ckFastForward!!.isChecked || ckSkip!!.isChecked
|
||||
widgetPreview!!.findViewById<View>(R.id.extendedButtonsContainer).visibility =
|
||||
ckPlaybackSpeed.isChecked || ckRewind.isChecked || ckFastForward.isChecked || ckSkip.isChecked
|
||||
widgetPreview.findViewById<View>(R.id.extendedButtonsContainer).visibility =
|
||||
if (showExtendedPreview) View.VISIBLE else View.GONE
|
||||
widgetPreview!!.findViewById<View>(R.id.butPlay).visibility =
|
||||
widgetPreview.findViewById<View>(R.id.butPlay).visibility =
|
||||
if (showExtendedPreview) View.GONE else View.VISIBLE
|
||||
widgetPreview!!.findViewById<View>(R.id.butPlaybackSpeed).visibility =
|
||||
if (ckPlaybackSpeed!!.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview!!.findViewById<View>(R.id.butFastForward).visibility =
|
||||
if (ckFastForward!!.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview!!.findViewById<View>(R.id.butSkip).visibility =
|
||||
if (ckSkip!!.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview!!.findViewById<View>(R.id.butRew).visibility =
|
||||
if (ckRewind!!.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview.findViewById<View>(R.id.butPlaybackSpeed).visibility =
|
||||
if (ckPlaybackSpeed.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview.findViewById<View>(R.id.butFastForward).visibility =
|
||||
if (ckFastForward.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview.findViewById<View>(R.id.butSkip).visibility =
|
||||
if (ckSkip.isChecked) View.VISIBLE else View.GONE
|
||||
widgetPreview.findViewById<View>(R.id.butRew).visibility =
|
||||
if (ckRewind.isChecked) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun confirmCreateWidget() {
|
||||
val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar!!.progress)
|
||||
val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress)
|
||||
|
||||
val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed!!.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip!!.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, ckRewind!!.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, ckFastForward!!.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, ckRewind.isChecked)
|
||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, ckFastForward.isChecked)
|
||||
editor.apply()
|
||||
|
||||
val resultValue = Intent()
|
||||
|
|
|
@ -26,8 +26,8 @@ import ac.mdiq.podvinci.ui.common.CircularProgressBar
|
|||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class ChaptersListAdapter(private val context: Context, private val callback: Callback?) :
|
||||
RecyclerView.Adapter<ChapterHolder?>() {
|
||||
class ChaptersListAdapter(private val context: Context, private val callback: Callback?) : RecyclerView.Adapter<ChapterHolder>() {
|
||||
|
||||
private var media: Playable? = null
|
||||
private var currentChapterIndex = -1
|
||||
private var currentChapterPosition: Long = -1
|
||||
|
@ -45,18 +45,13 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChapterHolder, position: Int) {
|
||||
val sc = getItem(position)
|
||||
if (sc == null) {
|
||||
holder.title.text = "Error"
|
||||
return
|
||||
}
|
||||
val sc = getItem(position)?: return
|
||||
holder.title.text = sc.title
|
||||
holder.start.text = getDurationStringLong(sc
|
||||
.start.toInt())
|
||||
val duration = if (position + 1 < media!!.getChapters().size) {
|
||||
holder.start.text = getDurationStringLong(sc.start.toInt())
|
||||
val duration = if (position + 1 < itemCount) {
|
||||
media!!.getChapters()[position + 1].start - sc.start
|
||||
} else {
|
||||
media!!.getDuration() - sc.start
|
||||
(media?.getDuration()?:0) - sc.start
|
||||
}
|
||||
holder.duration.text = context.getString(R.string.chapter_duration,
|
||||
getDurationStringLocalized(context, duration.toInt().toLong()))
|
||||
|
@ -112,10 +107,7 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
if (media == null) {
|
||||
return 0
|
||||
}
|
||||
return media!!.getChapters().size
|
||||
return media?.getChapters()?.size?:0
|
||||
}
|
||||
|
||||
class ChapterHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
@ -131,7 +123,7 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
|
|||
|
||||
fun notifyChapterChanged(newChapterIndex: Int) {
|
||||
currentChapterIndex = newChapterIndex
|
||||
currentChapterPosition = getItem(newChapterIndex).start
|
||||
currentChapterPosition = getItem(newChapterIndex)?.start?:0
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
@ -142,8 +134,10 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
|
|||
notifyItemChanged(currentChapterIndex, "foo")
|
||||
}
|
||||
|
||||
fun getItem(position: Int): Chapter {
|
||||
return media!!.getChapters()[position]
|
||||
fun getItem(position: Int): Chapter? {
|
||||
val chapters = media?.getChapters()?: return null
|
||||
if (position < 0 || position >= chapters.size) return null
|
||||
return chapters[position]
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
|
|
|
@ -57,9 +57,9 @@ class CoverLoader(activity: MainActivity) {
|
|||
}
|
||||
|
||||
fun load() {
|
||||
if (fallbackTitle == null || imgvCover == null) return
|
||||
if (imgvCover == null) return
|
||||
|
||||
val coverTarget = CoverTarget(fallbackTitle!!, imgvCover!!, textAndImageCombined)
|
||||
val coverTarget = CoverTarget(fallbackTitle, imgvCover!!, textAndImageCombined)
|
||||
|
||||
if (resource != 0) {
|
||||
Glide.with(imgvCover!!).clear(coverTarget)
|
||||
|
@ -87,11 +87,12 @@ class CoverLoader(activity: MainActivity) {
|
|||
builder.into<CoverTarget>(coverTarget)
|
||||
}
|
||||
|
||||
internal class CoverTarget(fallbackTitle: TextView,
|
||||
internal class CoverTarget(fallbackTitle: TextView?,
|
||||
coverImage: ImageView,
|
||||
private val textAndImageCombined: Boolean
|
||||
) : CustomViewTarget<ImageView, Drawable>(coverImage) {
|
||||
private val fallbackTitle: WeakReference<TextView> = WeakReference<TextView>(fallbackTitle)
|
||||
|
||||
private val fallbackTitle: WeakReference<TextView?> = WeakReference<TextView?>(fallbackTitle)
|
||||
private val cover: WeakReference<ImageView> = WeakReference(coverImage)
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
|
|
|
@ -16,8 +16,8 @@ import ac.mdiq.podvinci.core.util.StorageUtils.getTotalSpaceAvailable
|
|||
import ac.mdiq.podvinci.storage.preferences.UserPreferences.getDataFolder
|
||||
import java.io.File
|
||||
|
||||
class DataFolderAdapter(context: Context, selectionHandler: Consumer<String>) :
|
||||
RecyclerView.Adapter<DataFolderAdapter.ViewHolder?>() {
|
||||
class DataFolderAdapter(context: Context, selectionHandler: Consumer<String>) : RecyclerView.Adapter<DataFolderAdapter.ViewHolder?>() {
|
||||
|
||||
private val selectionHandler: Consumer<String>
|
||||
private val currentPath: String?
|
||||
private val entries: List<StoragePath>
|
||||
|
@ -63,10 +63,7 @@ class DataFolderAdapter(context: Context, selectionHandler: Consumer<String>) :
|
|||
|
||||
private fun getCurrentPath(): String? {
|
||||
val dataFolder = getDataFolder(null)
|
||||
if (dataFolder != null) {
|
||||
return dataFolder.absolutePath
|
||||
}
|
||||
return null
|
||||
return dataFolder?.absolutePath
|
||||
}
|
||||
|
||||
private fun getStorageEntries(context: Context): List<StoragePath> {
|
||||
|
|
|
@ -19,6 +19,7 @@ import ac.mdiq.podvinci.model.feed.Feed
|
|||
import ac.mdiq.podvinci.model.feed.FeedMedia
|
||||
import ac.mdiq.podvinci.ui.common.ThemeUtils
|
||||
import ac.mdiq.podvinci.view.viewholder.DownloadLogItemViewHolder
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
/**
|
||||
* Displays a list of DownloadStatus entries.
|
||||
|
@ -31,7 +32,7 @@ class DownloadLogAdapter(private val context: Activity) : BaseAdapter() {
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
@UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val holder: DownloadLogItemViewHolder
|
||||
if (convertView == null) {
|
||||
holder = DownloadLogItemViewHolder(context, parent)
|
||||
|
@ -44,7 +45,7 @@ class DownloadLogAdapter(private val context: Activity) : BaseAdapter() {
|
|||
return holder.itemView
|
||||
}
|
||||
|
||||
private fun bind(holder: DownloadLogItemViewHolder, status: DownloadResult, position: Int) {
|
||||
@UnstableApi private fun bind(holder: DownloadLogItemViewHolder, status: DownloadResult, position: Int) {
|
||||
var statusText: String? = ""
|
||||
if (status.feedfileType == Feed.FEEDFILETYPE_FEED) {
|
||||
statusText += context.getString(R.string.download_type_feed)
|
||||
|
@ -56,7 +57,7 @@ class DownloadLogAdapter(private val context: Activity) : BaseAdapter() {
|
|||
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, 0)
|
||||
holder.status.text = statusText
|
||||
|
||||
if (status.title != null) {
|
||||
if (status.title.isNotEmpty()) {
|
||||
holder.title.text = status.title
|
||||
} else {
|
||||
holder.title.setText(R.string.download_log_title_unknown)
|
||||
|
@ -132,7 +133,7 @@ class DownloadLogAdapter(private val context: Activity) : BaseAdapter() {
|
|||
}
|
||||
|
||||
override fun getItem(position: Int): DownloadResult? {
|
||||
if (position < downloadLog.size) {
|
||||
if (position in downloadLog.indices) {
|
||||
return downloadLog[position]
|
||||
}
|
||||
return null
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
package ac.mdiq.podvinci.adapter
|
||||
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import android.R.color
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.view.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import ac.mdiq.podvinci.core.util.FeedItemUtil
|
||||
import ac.mdiq.podvinci.fragment.ItemPagerFragment
|
||||
import ac.mdiq.podvinci.menuhandler.FeedItemMenuHandler
|
||||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import ac.mdiq.podvinci.ui.common.ThemeUtils
|
||||
import ac.mdiq.podvinci.view.viewholder.EpisodeItemViewHolder
|
||||
import android.R.color
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.view.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
/**
|
||||
* List adapter for the list of new episodes.
|
||||
*/
|
||||
|
@ -53,7 +54,7 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapte
|
|||
}
|
||||
|
||||
@UnstableApi override fun onBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) {
|
||||
if (pos >= episodes.size) {
|
||||
if (pos >= episodes.size || pos < 0) {
|
||||
beforeBindViewHolder(holder, pos)
|
||||
holder.bindDummy()
|
||||
afterBindViewHolder(holder, pos)
|
||||
|
@ -72,10 +73,10 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapte
|
|||
|
||||
holder.itemView.setOnClickListener { v: View? ->
|
||||
val activity: MainActivity? = mainActivityRef.get()
|
||||
if (activity != null && !inActionMode()) {
|
||||
if (!inActionMode()) {
|
||||
val ids: LongArray = FeedItemUtil.getIds(episodes)
|
||||
val position = ArrayUtils.indexOf(ids, item.id)
|
||||
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position))
|
||||
activity?.loadChildFragment(ItemPagerFragment.newInstance(ids, position))
|
||||
} else {
|
||||
toggleSelection(holder.bindingAdapterPosition)
|
||||
}
|
||||
|
@ -88,8 +89,7 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapte
|
|||
}
|
||||
holder.itemView.setOnTouchListener(View.OnTouchListener { v: View?, e: MotionEvent ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (e.isFromSource(InputDevice.SOURCE_MOUSE)
|
||||
&& e.buttonState == MotionEvent.BUTTON_SECONDARY) {
|
||||
if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) {
|
||||
longPressedItem = item
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
return@OnTouchListener false
|
||||
|
@ -146,26 +146,28 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapte
|
|||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
if (position >= episodes.size) {
|
||||
return RecyclerView.NO_ID // Dummy views
|
||||
}
|
||||
val item: FeedItem = episodes[position]
|
||||
return item.id
|
||||
// if (position >= episodes.size) {
|
||||
// return RecyclerView.NO_ID // Dummy views
|
||||
// }
|
||||
// val item = episodes[position]
|
||||
// return item.id ?: RecyclerView.NO_POSITION.toLong()
|
||||
return getItem(position)?.id ?: RecyclerView.NO_ID
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dummyViews + episodes.size
|
||||
}
|
||||
|
||||
protected fun getItem(index: Int): FeedItem {
|
||||
return episodes[index]
|
||||
protected fun getItem(index: Int): FeedItem? {
|
||||
// return episodes[index]
|
||||
return if (index in episodes.indices) episodes[index] else null
|
||||
}
|
||||
|
||||
protected val activity: Activity?
|
||||
get() = mainActivityRef.get()
|
||||
|
||||
@UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater
|
||||
val inflater: MenuInflater = activity!!.menuInflater
|
||||
if (inActionMode()) {
|
||||
inflater.inflate(R.menu.multi_select_context_popup, menu)
|
||||
} else {
|
||||
|
@ -201,8 +203,9 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapte
|
|||
get() {
|
||||
val items: MutableList<FeedItem> = ArrayList()
|
||||
for (i in 0 until itemCount) {
|
||||
if (isSelected(i)) {
|
||||
items.add(getItem(i))
|
||||
if (i < episodes.size && isSelected(i)) {
|
||||
val item = getItem(i)
|
||||
if (item != null) items.add(item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
|
|
|
@ -17,9 +17,9 @@ class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() {
|
|||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||
private val data: MutableList<PodcastSearchResult> = ArrayList()
|
||||
|
||||
fun updateData(newData: List<PodcastSearchResult>?) {
|
||||
fun updateData(newData: List<PodcastSearchResult>) {
|
||||
data.clear()
|
||||
data.addAll(newData!!)
|
||||
data.addAll(newData)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() {
|
|||
return data.size
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): PodcastSearchResult {
|
||||
return data[position]
|
||||
override fun getItem(position: Int): PodcastSearchResult? {
|
||||
return if (position in data.indices) data[position] else null
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
|
@ -48,12 +48,11 @@ class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() {
|
|||
holder = convertView.tag as Holder
|
||||
}
|
||||
|
||||
|
||||
val podcast: PodcastSearchResult = getItem(position)
|
||||
holder.imageView!!.contentDescription = podcast.title
|
||||
val podcast: PodcastSearchResult? = getItem(position)
|
||||
holder.imageView!!.contentDescription = podcast?.title
|
||||
|
||||
Glide.with(mainActivityRef.get()!!)
|
||||
.load(podcast.imageUrl)
|
||||
.load(podcast?.imageUrl)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.color.light_gray)
|
||||
.transform(FitCenter(), RoundedCorners((8 * mainActivityRef.get()!!.resources.displayMetrics.density).toInt()))
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
package ac.mdiq.podvinci.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent
|
||||
import ac.mdiq.podvinci.core.util.DateFormatter.formatAbbrev
|
||||
|
@ -19,15 +11,20 @@ import ac.mdiq.podvinci.model.feed.FeedItem
|
|||
import ac.mdiq.podvinci.model.playback.MediaType
|
||||
import ac.mdiq.podvinci.model.playback.Playable
|
||||
import ac.mdiq.podvinci.model.playback.RemoteMedia
|
||||
import java.lang.Boolean
|
||||
import kotlin.Int
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
/**
|
||||
* List adapter for showing a list of FeedItems with their title and description.
|
||||
*/
|
||||
class FeedItemlistDescriptionAdapter(context: Context?, resource: Int, objects: List<FeedItem?>?) :
|
||||
ArrayAdapter<FeedItem?>(
|
||||
context!!, resource, objects!!) {
|
||||
class FeedItemlistDescriptionAdapter(context: Context, resource: Int, objects: List<FeedItem?>?) :
|
||||
ArrayAdapter<FeedItem?>(context, resource, objects!!) {
|
||||
@UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var convertView = convertView
|
||||
val holder: Holder
|
||||
|
@ -59,7 +56,7 @@ class FeedItemlistDescriptionAdapter(context: Context?, resource: Int, objects:
|
|||
holder.description!!.text = description
|
||||
holder.description!!.maxLines = MAX_LINES_COLLAPSED
|
||||
}
|
||||
holder.description!!.tag = Boolean.FALSE // not expanded
|
||||
holder.description!!.tag = false
|
||||
holder.preview!!.visibility = View.GONE
|
||||
holder.preview!!.setOnClickListener { v: View? ->
|
||||
if (item.media == null) {
|
||||
|
@ -79,13 +76,13 @@ class FeedItemlistDescriptionAdapter(context: Context?, resource: Int, objects:
|
|||
}
|
||||
}
|
||||
convertView!!.setOnClickListener { v: View? ->
|
||||
if (holder.description!!.tag === Boolean.TRUE) {
|
||||
if (holder.description!!.tag == true) {
|
||||
holder.description!!.maxLines = MAX_LINES_COLLAPSED
|
||||
holder.preview!!.visibility = View.GONE
|
||||
holder.description!!.tag = Boolean.FALSE
|
||||
holder.description!!.tag = false
|
||||
} else {
|
||||
holder.description!!.maxLines = 30
|
||||
holder.description!!.tag = Boolean.TRUE
|
||||
holder.description!!.tag = true
|
||||
|
||||
holder.preview!!.visibility = if (item.media != null) View.VISIBLE else View.GONE
|
||||
holder.preview!!.setText(R.string.preview_episode)
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.lang.ref.WeakReference
|
|||
open class HorizontalFeedListAdapter(mainActivity: MainActivity) :
|
||||
RecyclerView.Adapter<HorizontalFeedListAdapter.Holder>(), View.OnCreateContextMenuListener {
|
||||
|
||||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||
private val data: MutableList<Feed> = ArrayList()
|
||||
private var dummyViews = 0
|
||||
var longPressedItem: Feed? = null
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.lang.ref.WeakReference
|
|||
|
||||
open class HorizontalItemListAdapter(mainActivity: MainActivity) : RecyclerView.Adapter<HorizontalItemViewHolder?>(),
|
||||
View.OnCreateContextMenuListener {
|
||||
|
||||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||
private var data: List<FeedItem> = ArrayList()
|
||||
var longPressedItem: FeedItem? = null
|
||||
|
@ -70,10 +71,11 @@ open class HorizontalItemListAdapter(mainActivity: MainActivity) : RecyclerView.
|
|||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
if (position >= data.size) {
|
||||
return RecyclerView.NO_ID // Dummy views
|
||||
if (position in data.indices) {
|
||||
val item: FeedItem = data[position]
|
||||
return item.id
|
||||
}
|
||||
return data[position].id
|
||||
return RecyclerView.NO_ID // Dummy views
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
|
@ -108,9 +110,8 @@ open class HorizontalItemListAdapter(mainActivity: MainActivity) : RecyclerView.
|
|||
|
||||
@UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater
|
||||
if (longPressedItem == null) {
|
||||
return
|
||||
}
|
||||
if (longPressedItem == null) return
|
||||
|
||||
menu.clear()
|
||||
inflater.inflate(R.menu.feeditemlist_context, menu)
|
||||
menu.setHeaderTitle(longPressedItem!!.title)
|
||||
|
|
|
@ -31,6 +31,7 @@ import ac.mdiq.podvinci.storage.preferences.UserPreferences.hiddenDrawerItems
|
|||
import ac.mdiq.podvinci.storage.preferences.UserPreferences.isEnableAutodownload
|
||||
import ac.mdiq.podvinci.storage.preferences.UserPreferences.subscriptionsFilter
|
||||
import ac.mdiq.podvinci.ui.home.HomeFragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import java.lang.ref.WeakReference
|
||||
import java.text.NumberFormat
|
||||
|
@ -42,6 +43,7 @@ import kotlin.math.abs
|
|||
*/
|
||||
class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
||||
RecyclerView.Adapter<NavListAdapter.Holder>(), OnSharedPreferenceChangeListener {
|
||||
|
||||
private val fragmentTags: MutableList<String?> = ArrayList()
|
||||
private val titles: Array<String> = context.resources.getStringArray(R.array.nav_drawer_titles)
|
||||
private val activity = WeakReference(context)
|
||||
|
@ -87,7 +89,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
return titles[index]
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@UnstableApi @DrawableRes
|
||||
private fun getDrawable(tag: String?): Int {
|
||||
return when (tag) {
|
||||
HomeFragment.TAG -> R.drawable.ic_home
|
||||
|
@ -157,7 +159,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: Holder, position: Int) {
|
||||
@UnstableApi override fun onBindViewHolder(holder: Holder, position: Int) {
|
||||
val viewType = getItemViewType(position)
|
||||
|
||||
holder.itemView.setOnCreateContextMenuListener(null)
|
||||
|
@ -199,7 +201,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
}
|
||||
}
|
||||
|
||||
private fun bindNavView(title: String, position: Int, holder: NavHolder) {
|
||||
@UnstableApi private fun bindNavView(title: String, position: Int, holder: NavHolder) {
|
||||
val context = activity.get() ?: return
|
||||
holder.title.text = title
|
||||
|
||||
|
@ -209,43 +211,48 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
holder.count.isClickable = false
|
||||
|
||||
val tag = fragmentTags[position]
|
||||
if (tag == QueueFragment.TAG) {
|
||||
val queueSize = itemAccess.queueSize
|
||||
if (queueSize > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(queueSize.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
when {
|
||||
tag == QueueFragment.TAG -> {
|
||||
val queueSize = itemAccess.queueSize
|
||||
if (queueSize > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(queueSize.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
} else if (tag == InboxFragment.TAG) {
|
||||
val unreadItems = itemAccess.numberOfNewItems
|
||||
if (unreadItems > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(unreadItems.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
tag == InboxFragment.TAG -> {
|
||||
val unreadItems = itemAccess.numberOfNewItems
|
||||
if (unreadItems > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(unreadItems.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
} else if (tag == SubscriptionFragment.TAG) {
|
||||
val sum = itemAccess.feedCounterSum
|
||||
if (sum > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(sum.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
tag == SubscriptionFragment.TAG -> {
|
||||
val sum = itemAccess.feedCounterSum
|
||||
if (sum > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(sum.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
} else if (tag == CompletedDownloadsFragment.TAG && isEnableAutodownload) {
|
||||
val epCacheSize = episodeCacheSize
|
||||
// don't count episodes that can be reclaimed
|
||||
val spaceUsed = (itemAccess.numberOfDownloadedItems
|
||||
- itemAccess.reclaimableItems)
|
||||
if (epCacheSize in 1..spaceUsed) {
|
||||
holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_alert, 0)
|
||||
holder.count.visibility = View.VISIBLE
|
||||
holder.count.setOnClickListener { v: View? ->
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.episode_cache_full_title)
|
||||
.setMessage(R.string.episode_cache_full_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNeutralButton(R.string.open_autodownload_settings) { dialog: DialogInterface?, which: Int ->
|
||||
val intent = Intent(context, PreferenceActivity::class.java)
|
||||
intent.putExtra(PreferenceActivity.OPEN_AUTO_DOWNLOAD_SETTINGS, true)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
.show()
|
||||
tag == CompletedDownloadsFragment.TAG && isEnableAutodownload -> {
|
||||
val epCacheSize = episodeCacheSize
|
||||
// don't count episodes that can be reclaimed
|
||||
val spaceUsed = (itemAccess.numberOfDownloadedItems
|
||||
- itemAccess.reclaimableItems)
|
||||
if (epCacheSize in 1..spaceUsed) {
|
||||
holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_alert, 0)
|
||||
holder.count.visibility = View.VISIBLE
|
||||
holder.count.setOnClickListener { v: View? ->
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.episode_cache_full_title)
|
||||
.setMessage(R.string.episode_cache_full_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNeutralButton(R.string.open_autodownload_settings) { dialog: DialogInterface?, which: Int ->
|
||||
val intent = Intent(context, PreferenceActivity::class.java)
|
||||
intent.putExtra(PreferenceActivity.OPEN_AUTO_DOWNLOAD_SETTINGS, true)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,9 @@ import ac.mdiq.podvinci.view.viewholder.EpisodeItemViewHolder
|
|||
/**
|
||||
* List adapter for the queue.
|
||||
*/
|
||||
open class QueueRecyclerAdapter(mainActivity: MainActivity, swipeActions: SwipeActions) : EpisodeItemListAdapter(mainActivity) {
|
||||
private val swipeActions: SwipeActions = swipeActions
|
||||
open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeActions: SwipeActions) : EpisodeItemListAdapter(mainActivity) {
|
||||
private var dragDropEnabled: Boolean
|
||||
|
||||
|
||||
init {
|
||||
dragDropEnabled = !(UserPreferences.isQueueKeepSorted || UserPreferences.isQueueLocked)
|
||||
}
|
||||
|
@ -46,7 +44,7 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, swipeActions: SwipeA
|
|||
false
|
||||
}
|
||||
holder.coverHolder.setOnTouchListener { v1, event ->
|
||||
if (event.actionMasked === MotionEvent.ACTION_DOWN) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
val isLtr = holder.itemView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
|
||||
val factor = (if (isLtr) 1 else -1).toFloat()
|
||||
if (factor * event.x < factor * 0.5 * v1.width) {
|
||||
|
@ -75,10 +73,10 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, swipeActions: SwipeA
|
|||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
val keepSorted: Boolean = UserPreferences.isQueueKeepSorted
|
||||
if (getItem(0).id === longPressedItem?.id || keepSorted) {
|
||||
if (getItem(0)?.id === longPressedItem?.id || keepSorted) {
|
||||
menu.findItem(R.id.move_to_top_item).setVisible(false)
|
||||
}
|
||||
if (getItem(itemCount - 1).id === longPressedItem?.id || keepSorted) {
|
||||
if (getItem(itemCount - 1)?.id === longPressedItem?.id || keepSorted) {
|
||||
menu.findItem(R.id.move_to_bottom_item).setVisible(false)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -23,10 +23,7 @@ abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activ
|
|||
if (inActionMode()) {
|
||||
endSelectMode()
|
||||
}
|
||||
|
||||
if (onSelectModeListener != null) {
|
||||
onSelectModeListener!!.onStartSelectMode()
|
||||
}
|
||||
onSelectModeListener?.onStartSelectMode()
|
||||
|
||||
shouldSelectLazyLoadedItems = false
|
||||
selectedIds.clear()
|
||||
|
@ -75,7 +72,7 @@ abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activ
|
|||
fun endSelectMode() {
|
||||
if (inActionMode()) {
|
||||
callOnEndSelectMode()
|
||||
actionMode!!.finish()
|
||||
actionMode?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,9 +161,7 @@ abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activ
|
|||
}
|
||||
|
||||
private fun callOnEndSelectMode() {
|
||||
if (onSelectModeListener != null) {
|
||||
onSelectModeListener!!.onEndSelectMode()
|
||||
}
|
||||
onSelectModeListener?.onEndSelectMode()
|
||||
}
|
||||
|
||||
fun shouldSelectLazyLoadedItems(): Boolean {
|
||||
|
|
|
@ -12,7 +12,7 @@ abstract class SimpleChipAdapter(private val context: Context) : RecyclerView.Ad
|
|||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
protected abstract fun getChips(): List<String?>
|
||||
protected abstract fun getChips(): List<String>
|
||||
|
||||
protected abstract fun onRemoveClicked(position: Int)
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ import ac.mdiq.podvinci.R
|
|||
*/
|
||||
class SimpleIconListAdapter<T : SimpleIconListAdapter.ListItem>(private val context: Context,
|
||||
private val listItems: List<T>
|
||||
) : ArrayAdapter<T>(
|
||||
context, R.layout.simple_icon_list_item, listItems) {
|
||||
) : ArrayAdapter<T>(context, R.layout.simple_icon_list_item, listItems) {
|
||||
|
||||
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
|
||||
var view = view
|
||||
if (view == null) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.text.NumberFormat
|
|||
open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
||||
SelectableAdapter<SubscriptionsRecyclerAdapter.SubscriptionViewHolder?>(mainActivity),
|
||||
View.OnCreateContextMenuListener {
|
||||
|
||||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||
private var listItems: List<NavDrawerData.DrawerItem>
|
||||
private var selectedItem: NavDrawerData.DrawerItem? = null
|
||||
|
@ -54,8 +55,7 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
|
||||
val itemView: View =
|
||||
LayoutInflater.from(mainActivityRef.get()).inflate(R.layout.subscription_item, parent, false)
|
||||
itemView.findViewById<View>(R.id.titleLabel).visibility =
|
||||
if (viewType == COVER_WITH_TITLE) View.VISIBLE else View.GONE
|
||||
itemView.findViewById<View>(R.id.titleLabel).visibility = if (viewType == COVER_WITH_TITLE) View.VISIBLE else View.GONE
|
||||
return SubscriptionViewHolder(itemView)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import ac.mdiq.podvinci.R
|
|||
import ac.mdiq.podvinci.core.storage.DBWriter
|
||||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import ac.mdiq.podvinci.view.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
class DeleteActionButton(item: FeedItem) : ItemActionButton(item) {
|
||||
override fun getLabel(): Int {
|
||||
|
@ -14,11 +15,10 @@ class DeleteActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
override fun getDrawable(): Int {
|
||||
return R.drawable.ic_delete
|
||||
}
|
||||
override fun onClick(context: Context) {
|
||||
@UnstableApi override fun onClick(context: Context) {
|
||||
val media = item.media ?: return
|
||||
|
||||
showLocalFeedDeleteWarningIfNecessary(context, listOf(item)
|
||||
) { DBWriter.deleteFeedMediaOfItem(context, media.id) }
|
||||
showLocalFeedDeleteWarningIfNecessary(context, listOf(item)) { DBWriter.deleteFeedMediaOfItem(context, media.id) }
|
||||
}
|
||||
|
||||
override val visibility: Int
|
||||
|
|
|
@ -30,19 +30,29 @@ abstract class ItemActionButton internal constructor(@JvmField var item: FeedIte
|
|||
fun forItem(item: FeedItem): ItemActionButton {
|
||||
val media = item.media ?: return MarkAsPlayedActionButton(item)
|
||||
|
||||
val isDownloadingMedia = if (media.download_url==null) false else DownloadServiceInterface.get()?.isDownloadingEpisode(media.download_url!!)?:false
|
||||
return if (isCurrentlyPlaying(media)) {
|
||||
PauseActionButton(item)
|
||||
} else if (item.feed != null && item.feed!!.isLocalFeed) {
|
||||
PlayLocalActionButton(item)
|
||||
} else if (media.isDownloaded()) {
|
||||
PlayActionButton(item)
|
||||
} else if (isDownloadingMedia) {
|
||||
CancelDownloadActionButton(item)
|
||||
} else if (isStreamOverDownload) {
|
||||
StreamActionButton(item)
|
||||
} else {
|
||||
DownloadActionButton(item)
|
||||
val isDownloadingMedia = when (media.download_url) {
|
||||
null -> false
|
||||
else -> DownloadServiceInterface.get()?.isDownloadingEpisode(media.download_url!!)?:false
|
||||
}
|
||||
return when {
|
||||
isCurrentlyPlaying(media) -> {
|
||||
PauseActionButton(item)
|
||||
}
|
||||
item.feed != null && item.feed!!.isLocalFeed -> {
|
||||
PlayLocalActionButton(item)
|
||||
}
|
||||
media.isDownloaded() -> {
|
||||
PlayActionButton(item)
|
||||
}
|
||||
isDownloadingMedia -> {
|
||||
CancelDownloadActionButton(item)
|
||||
}
|
||||
isStreamOverDownload -> {
|
||||
StreamActionButton(item)
|
||||
}
|
||||
else -> {
|
||||
DownloadActionButton(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.view.View
|
|||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.core.storage.DBWriter
|
||||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
class MarkAsPlayedActionButton(item: FeedItem) : ItemActionButton(item) {
|
||||
override fun getLabel(): Int {
|
||||
|
@ -13,12 +14,12 @@ class MarkAsPlayedActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
override fun getDrawable(): Int {
|
||||
return R.drawable.ic_check
|
||||
}
|
||||
override fun onClick(context: Context) {
|
||||
@UnstableApi override fun onClick(context: Context) {
|
||||
if (!item.isPlayed()) {
|
||||
DBWriter.markItemPlayed(item, FeedItem.PLAYED, true)
|
||||
}
|
||||
}
|
||||
|
||||
override val visibility: Int
|
||||
get() = if ((item.isPlayed())) View.INVISIBLE else View.VISIBLE
|
||||
get() = if (item.isPlayed()) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
|
|
@ -18,5 +18,5 @@ class VisitWebsiteActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
}
|
||||
|
||||
override val visibility: Int
|
||||
get() = if ((item.link == null)) View.INVISIBLE else View.VISIBLE
|
||||
get() = if (item.link == null) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
|||
import com.bumptech.glide.request.RequestOptions
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.net.discovery.PodcastSearchResult
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
class ItunesAdapter(
|
||||
/**
|
||||
|
@ -26,7 +27,7 @@ class ItunesAdapter(
|
|||
*/
|
||||
private val data: List<PodcastSearchResult> = objects
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
@UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
//Current podcast
|
||||
val podcast: PodcastSearchResult = data[position]
|
||||
|
||||
|
@ -38,8 +39,7 @@ class ItunesAdapter(
|
|||
|
||||
//Handle view holder stuff
|
||||
if (convertView == null) {
|
||||
view = (context as MainActivity).layoutInflater
|
||||
.inflate(R.layout.itunes_podcast_listitem, parent, false)
|
||||
view = (context as MainActivity).layoutInflater.inflate(R.layout.itunes_podcast_listitem, parent, false)
|
||||
viewHolder = PodcastViewHolder(view)
|
||||
view.tag = viewHolder
|
||||
} else {
|
||||
|
@ -86,7 +86,7 @@ class ItunesAdapter(
|
|||
/**
|
||||
* TextView holding the Podcast title
|
||||
*/
|
||||
val titleView = view.findViewById<TextView>(R.id.txtvTitle)
|
||||
val titleView: TextView = view.findViewById(R.id.txtvTitle)
|
||||
|
||||
val authorView: TextView = view.findViewById(R.id.txtvAuthor)
|
||||
}
|
||||
|
|
|
@ -20,17 +20,15 @@ class DocumentFileExportWorker(private val exportWriter: ExportWriter,
|
|||
private val outputFileUri: Uri
|
||||
) {
|
||||
fun exportObservable(): Observable<DocumentFile?> {
|
||||
val output = DocumentFile.fromSingleUri(
|
||||
context, outputFileUri)
|
||||
val output = DocumentFile.fromSingleUri(context, outputFileUri)
|
||||
return Observable.create { subscriber: ObservableEmitter<DocumentFile?> ->
|
||||
var outputStream: OutputStream? = null
|
||||
var writer: OutputStreamWriter? = null
|
||||
try {
|
||||
val uri = output!!.uri
|
||||
if (output == null) throw IOException()
|
||||
val uri = output.uri
|
||||
outputStream = context.contentResolver.openOutputStream(uri, "wt")
|
||||
if (outputStream == null) {
|
||||
throw IOException()
|
||||
}
|
||||
if (outputStream == null) throw IOException()
|
||||
writer = OutputStreamWriter(outputStream, Charset.forName("UTF-8"))
|
||||
exportWriter.writeDocument(DBReader.getFeedList(), writer, context)
|
||||
subscriber.onNext(output)
|
||||
|
|
|
@ -8,6 +8,8 @@ import ac.mdiq.podvinci.R
|
|||
import ac.mdiq.podvinci.core.dialog.ConfirmationDialog
|
||||
import ac.mdiq.podvinci.core.storage.DBWriter
|
||||
import ac.mdiq.podvinci.model.feed.Feed
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
@ -28,7 +30,7 @@ object RemoveFeedDialog {
|
|||
|
||||
private fun showDialog(context: Context, feeds: List<Feed>, message: String, callback: Runnable?) {
|
||||
val dialog: ConfirmationDialog = object : ConfirmationDialog(context, R.string.remove_feed_label, message) {
|
||||
override fun onConfirmButtonPressed(clickedDialog: DialogInterface) {
|
||||
@OptIn(UnstableApi::class) override fun onConfirmButtonPressed(clickedDialog: DialogInterface) {
|
||||
callback?.run()
|
||||
|
||||
clickedDialog.dismiss()
|
||||
|
|
|
@ -43,7 +43,7 @@ class TagSettingsDialog : DialogFragment() {
|
|||
viewBinding!!.tagsRecycler.layoutManager = GridLayoutManager(context, 2)
|
||||
viewBinding!!.tagsRecycler.addItemDecoration(ItemOffsetDecoration(requireContext(), 4))
|
||||
adapter = object : SimpleChipAdapter(requireContext()) {
|
||||
override fun getChips(): List<String?> {
|
||||
override fun getChips(): List<String> {
|
||||
return displayedTags?: listOf()
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ import io.reactivex.schedulers.Schedulers
|
|||
*/
|
||||
@UnstableApi
|
||||
class AddFeedFragment : Fragment() {
|
||||
private var viewBinding: AddfeedBinding? = null
|
||||
private lateinit var viewBinding: AddfeedBinding
|
||||
private var activity: MainActivity? = null
|
||||
private var displayUpArrow = false
|
||||
|
||||
|
@ -51,60 +51,58 @@ class AddFeedFragment : Fragment() {
|
|||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding = AddfeedBinding.inflate(inflater)
|
||||
activity = getActivity() as MainActivity?
|
||||
activity = getActivity() as? MainActivity
|
||||
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
(getActivity() as MainActivity).setupToolbarToggle(viewBinding!!.toolbar, displayUpArrow)
|
||||
(getActivity() as MainActivity).setupToolbarToggle(viewBinding.toolbar, displayUpArrow)
|
||||
|
||||
viewBinding!!.searchItunesButton.setOnClickListener { v: View? ->
|
||||
viewBinding.searchItunesButton.setOnClickListener { v: View? ->
|
||||
activity?.loadChildFragment(OnlineSearchFragment.newInstance(
|
||||
ItunesPodcastSearcher::class.java))
|
||||
}
|
||||
viewBinding!!.searchFyydButton.setOnClickListener { v: View? ->
|
||||
viewBinding.searchFyydButton.setOnClickListener { v: View? ->
|
||||
activity?.loadChildFragment(OnlineSearchFragment.newInstance(
|
||||
FyydPodcastSearcher::class.java))
|
||||
}
|
||||
viewBinding!!.searchGPodderButton.setOnClickListener { v: View? ->
|
||||
viewBinding.searchGPodderButton.setOnClickListener { v: View? ->
|
||||
activity?.loadChildFragment(OnlineSearchFragment.newInstance(
|
||||
GpodnetPodcastSearcher::class.java))
|
||||
}
|
||||
viewBinding!!.searchPodcastIndexButton.setOnClickListener { v: View? ->
|
||||
viewBinding.searchPodcastIndexButton.setOnClickListener { v: View? ->
|
||||
activity?.loadChildFragment(OnlineSearchFragment.newInstance(
|
||||
PodcastIndexPodcastSearcher::class.java))
|
||||
}
|
||||
|
||||
viewBinding!!.combinedFeedSearchEditText.setOnEditorActionListener { v: TextView?, actionId: Int, event: KeyEvent? ->
|
||||
viewBinding.combinedFeedSearchEditText.setOnEditorActionListener { v: TextView?, actionId: Int, event: KeyEvent? ->
|
||||
performSearch()
|
||||
true
|
||||
}
|
||||
|
||||
viewBinding!!.addViaUrlButton.setOnClickListener { v: View? -> showAddViaUrlDialog() }
|
||||
viewBinding.addViaUrlButton.setOnClickListener { v: View? -> showAddViaUrlDialog() }
|
||||
|
||||
viewBinding!!.opmlImportButton.setOnClickListener { v: View? ->
|
||||
viewBinding.opmlImportButton.setOnClickListener { v: View? ->
|
||||
try {
|
||||
chooseOpmlImportPathLauncher.launch("*/*")
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
e.printStackTrace()
|
||||
(getActivity() as MainActivity)
|
||||
.showSnackbarAbovePlayer(R.string.unable_to_start_system_file_manager, Snackbar.LENGTH_LONG)
|
||||
activity?.showSnackbarAbovePlayer(R.string.unable_to_start_system_file_manager, Snackbar.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
|
||||
viewBinding!!.addLocalFolderButton.setOnClickListener { v: View? ->
|
||||
viewBinding.addLocalFolderButton.setOnClickListener { v: View? ->
|
||||
try {
|
||||
addLocalFolderLauncher.launch(null)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
e.printStackTrace()
|
||||
(getActivity() as MainActivity)
|
||||
.showSnackbarAbovePlayer(R.string.unable_to_start_system_file_manager, Snackbar.LENGTH_LONG)
|
||||
activity?.showSnackbarAbovePlayer(R.string.unable_to_start_system_file_manager, Snackbar.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
viewBinding!!.searchButton.setOnClickListener { view: View? -> performSearch() }
|
||||
viewBinding.searchButton.setOnClickListener { view: View? -> performSearch() }
|
||||
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -127,8 +125,7 @@ class AddFeedFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
builder.setView(dialogBinding.root)
|
||||
builder.setPositiveButton(R.string.confirm_label
|
||||
) { dialog: DialogInterface?, which: Int -> addUrl(dialogBinding.urlEditText.text.toString()) }
|
||||
builder.setPositiveButton(R.string.confirm_label) { dialog: DialogInterface?, which: Int -> addUrl(dialogBinding.urlEditText.text.toString()) }
|
||||
builder.setNegativeButton(R.string.cancel_label, null)
|
||||
builder.show()
|
||||
}
|
||||
|
@ -141,16 +138,16 @@ class AddFeedFragment : Fragment() {
|
|||
}
|
||||
|
||||
private fun performSearch() {
|
||||
viewBinding!!.combinedFeedSearchEditText.clearFocus()
|
||||
viewBinding.combinedFeedSearchEditText.clearFocus()
|
||||
val inVal = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inVal.hideSoftInputFromWindow(viewBinding!!.combinedFeedSearchEditText.windowToken, 0)
|
||||
val query = viewBinding!!.combinedFeedSearchEditText.text.toString()
|
||||
inVal.hideSoftInputFromWindow(viewBinding.combinedFeedSearchEditText.windowToken, 0)
|
||||
val query = viewBinding.combinedFeedSearchEditText.text.toString()
|
||||
if (query.matches("http[s]?://.*".toRegex())) {
|
||||
addUrl(query)
|
||||
return
|
||||
}
|
||||
activity?.loadChildFragment(OnlineSearchFragment.newInstance(CombinedSearcher::class.java, query))
|
||||
viewBinding!!.combinedFeedSearchEditText.post { viewBinding!!.combinedFeedSearchEditText.setText("") }
|
||||
viewBinding.combinedFeedSearchEditText.post { viewBinding.combinedFeedSearchEditText.setText("") }
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -205,8 +202,7 @@ class AddFeedFragment : Fragment() {
|
|||
|
||||
private class AddLocalFolder : ActivityResultContracts.OpenDocumentTree() {
|
||||
override fun createIntent(context: Context, input: Uri?): Intent {
|
||||
return super.createIntent(context, input)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
return super.createIntent(context, input).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,22 +64,23 @@ import kotlin.math.min
|
|||
*/
|
||||
@UnstableApi
|
||||
class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
|
||||
var butPlaybackSpeed: PlaybackSpeedIndicatorView? = null
|
||||
var txtvPlaybackSpeed: TextView? = null
|
||||
private var pager: ViewPager2? = null
|
||||
private var txtvPosition: TextView? = null
|
||||
private var txtvLength: TextView? = null
|
||||
private var sbPosition: ChapterSeekBar? = null
|
||||
private var butRev: ImageButton? = null
|
||||
private var txtvRev: TextView? = null
|
||||
private var butPlay: PlayButton? = null
|
||||
private var butFF: ImageButton? = null
|
||||
private var txtvFF: TextView? = null
|
||||
private var butSkip: ImageButton? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
private var progressIndicator: ProgressBar? = null
|
||||
private var cardViewSeek: CardView? = null
|
||||
private var txtvSeek: TextView? = null
|
||||
lateinit var butPlaybackSpeed: PlaybackSpeedIndicatorView
|
||||
lateinit var txtvPlaybackSpeed: TextView
|
||||
private lateinit var pager: ViewPager2
|
||||
private lateinit var txtvPosition: TextView
|
||||
private lateinit var txtvLength: TextView
|
||||
private lateinit var sbPosition: ChapterSeekBar
|
||||
private lateinit var butRev: ImageButton
|
||||
private lateinit var txtvRev: TextView
|
||||
private lateinit var butPlay: PlayButton
|
||||
private lateinit var butFF: ImageButton
|
||||
private lateinit var txtvFF: TextView
|
||||
private lateinit var butSkip: ImageButton
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
private lateinit var progressIndicator: ProgressBar
|
||||
private lateinit var cardViewSeek: CardView
|
||||
private lateinit var txtvSeek: TextView
|
||||
|
||||
private var controller: PlaybackController? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
@ -97,11 +98,11 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
val root: View = inflater.inflate(R.layout.audioplayer_fragment, container, false)
|
||||
root.setOnTouchListener { v: View?, event: MotionEvent? -> true } // Avoid clicks going through player to fragments below
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.title = ""
|
||||
toolbar?.setNavigationOnClickListener { v: View? ->
|
||||
toolbar.title = ""
|
||||
toolbar.setNavigationOnClickListener { v: View? ->
|
||||
(activity as MainActivity).bottomSheet?.setState(BottomSheetBehavior.STATE_COLLAPSED)
|
||||
}
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
val externalPlayerFragment = ExternalPlayerFragment()
|
||||
childFragmentManager.beginTransaction()
|
||||
|
@ -127,19 +128,19 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
|
||||
setupLengthTextView()
|
||||
setupControlButtons()
|
||||
butPlaybackSpeed?.setOnClickListener { v: View? ->
|
||||
butPlaybackSpeed.setOnClickListener { v: View? ->
|
||||
VariableSpeedDialog().show(
|
||||
childFragmentManager, null)
|
||||
}
|
||||
sbPosition?.setOnSeekBarChangeListener(this)
|
||||
sbPosition.setOnSeekBarChangeListener(this)
|
||||
|
||||
pager = root.findViewById(R.id.pager)
|
||||
pager?.adapter = AudioPlayerPagerAdapter(this@AudioPlayerFragment)
|
||||
pager.adapter = AudioPlayerPagerAdapter(this@AudioPlayerFragment)
|
||||
// Required for getChildAt(int) in ViewPagerBottomSheetBehavior to return the correct page
|
||||
pager?.offscreenPageLimit = NUM_CONTENT_FRAGMENTS
|
||||
pager?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
pager.offscreenPageLimit = NUM_CONTENT_FRAGMENTS
|
||||
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
pager?.post {
|
||||
pager.post {
|
||||
if (activity != null) {
|
||||
// By the time this is posted, the activity might be closed again.
|
||||
(activity as MainActivity).bottomSheet?.updateScrollingChild()
|
||||
|
@ -167,39 +168,39 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
}
|
||||
|
||||
sbPosition?.setDividerPos(dividerPos)
|
||||
sbPosition.setDividerPos(dividerPos)
|
||||
}
|
||||
|
||||
private fun setupControlButtons() {
|
||||
butRev?.setOnClickListener { v: View? ->
|
||||
butRev.setOnClickListener { v: View? ->
|
||||
if (controller != null) {
|
||||
val curr: Int = controller!!.position
|
||||
controller!!.seekTo(curr - UserPreferences.rewindSecs * 1000)
|
||||
}
|
||||
}
|
||||
butRev?.setOnLongClickListener { v: View? ->
|
||||
butRev.setOnLongClickListener { v: View? ->
|
||||
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
||||
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev)
|
||||
true
|
||||
}
|
||||
butPlay?.setOnClickListener { v: View? ->
|
||||
butPlay.setOnClickListener { v: View? ->
|
||||
if (controller != null) {
|
||||
controller!!.init()
|
||||
controller!!.playPause()
|
||||
}
|
||||
}
|
||||
butFF?.setOnClickListener { v: View? ->
|
||||
butFF.setOnClickListener { v: View? ->
|
||||
if (controller != null) {
|
||||
val curr: Int = controller!!.position
|
||||
controller!!.seekTo(curr + UserPreferences.fastForwardSecs * 1000)
|
||||
}
|
||||
}
|
||||
butFF?.setOnLongClickListener { v: View? ->
|
||||
butFF.setOnLongClickListener { v: View? ->
|
||||
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
||||
false
|
||||
}
|
||||
butSkip?.setOnClickListener { v: View? ->
|
||||
butSkip.setOnClickListener { v: View? ->
|
||||
activity?.sendBroadcast(
|
||||
MediaButtonReceiver.createIntent(requireContext(), KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||
}
|
||||
|
@ -222,7 +223,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
|
||||
private fun setupLengthTextView() {
|
||||
showTimeLeft = UserPreferences.shouldShowRemainingTime()
|
||||
txtvLength?.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
txtvLength.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
if (controller == null) {
|
||||
return@OnClickListener
|
||||
}
|
||||
|
@ -235,8 +236,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun updatePlaybackSpeedButton(event: SpeedChangedEvent) {
|
||||
val speedStr: String = DecimalFormat("0.00").format(event.newSpeed.toDouble())
|
||||
txtvPlaybackSpeed?.text = speedStr
|
||||
butPlaybackSpeed?.setSpeed(event.newSpeed)
|
||||
txtvPlaybackSpeed.text = speedStr
|
||||
butPlaybackSpeed.setSpeed(event.newSpeed)
|
||||
}
|
||||
|
||||
private fun loadMediaInfo(includingChapters: Boolean) {
|
||||
|
@ -268,7 +269,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
private fun newPlaybackController(): PlaybackController {
|
||||
return object : PlaybackController(activity) {
|
||||
override fun updatePlayButtonShowsPlay(showPlay: Boolean) {
|
||||
butPlay?.setIsShowPlay(showPlay)
|
||||
butPlay.setIsShowPlay(showPlay)
|
||||
}
|
||||
|
||||
override fun loadMediaInfo() {
|
||||
|
@ -311,41 +312,37 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
controller?.init()
|
||||
loadMediaInfo(false)
|
||||
EventBus.getDefault().register(this)
|
||||
txtvRev?.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||
txtvFF?.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
||||
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
controller?.release()
|
||||
controller = null
|
||||
progressIndicator?.visibility = View.GONE // Controller released; we will not receive buffering updates
|
||||
progressIndicator.visibility = View.GONE // Controller released; we will not receive buffering updates
|
||||
EventBus.getDefault().unregister(this)
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Suppress("unused")
|
||||
fun bufferUpdate(event: BufferUpdateEvent) {
|
||||
if (event.hasStarted()) {
|
||||
progressIndicator?.visibility = View.VISIBLE
|
||||
progressIndicator.visibility = View.VISIBLE
|
||||
} else if (event.hasEnded()) {
|
||||
progressIndicator?.visibility = View.GONE
|
||||
progressIndicator.visibility = View.GONE
|
||||
} else if (controller != null && controller!!.isStreaming) {
|
||||
sbPosition?.setSecondaryProgress((event.progress * sbPosition!!.max).toInt())
|
||||
sbPosition.setSecondaryProgress((event.progress * sbPosition.max).toInt())
|
||||
} else {
|
||||
sbPosition?.setSecondaryProgress(0)
|
||||
sbPosition.setSecondaryProgress(0)
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun updatePosition(event: PlaybackPositionEvent) {
|
||||
if (controller == null || txtvPosition == null || txtvLength == null || sbPosition == null) {
|
||||
return
|
||||
}
|
||||
if (controller == null) return
|
||||
|
||||
val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier)
|
||||
val currentPosition: Int = converter.convert(event.position)
|
||||
|
@ -357,23 +354,23 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
Log.w(TAG, "Could not react to position observer update because of invalid time")
|
||||
return
|
||||
}
|
||||
txtvPosition?.text = Converter.getDurationStringLong(currentPosition)
|
||||
txtvPosition?.setContentDescription(getString(R.string.position,
|
||||
txtvPosition.text = Converter.getDurationStringLong(currentPosition)
|
||||
txtvPosition.setContentDescription(getString(R.string.position,
|
||||
Converter.getDurationStringLocalized(requireContext(), currentPosition.toLong())))
|
||||
showTimeLeft = UserPreferences.shouldShowRemainingTime()
|
||||
if (showTimeLeft) {
|
||||
txtvLength?.setContentDescription(getString(R.string.remaining_time,
|
||||
txtvLength.setContentDescription(getString(R.string.remaining_time,
|
||||
Converter.getDurationStringLocalized(requireContext(), remainingTime.toLong())))
|
||||
txtvLength?.text = (if ((remainingTime > 0)) "-" else "") + Converter.getDurationStringLong(remainingTime)
|
||||
txtvLength.text = (if ((remainingTime > 0)) "-" else "") + Converter.getDurationStringLong(remainingTime)
|
||||
} else {
|
||||
txtvLength?.setContentDescription(getString(R.string.chapter_duration,
|
||||
txtvLength.setContentDescription(getString(R.string.chapter_duration,
|
||||
Converter.getDurationStringLocalized(requireContext(), duration.toLong())))
|
||||
txtvLength?.text = Converter.getDurationStringLong(duration)
|
||||
txtvLength.text = Converter.getDurationStringLong(duration)
|
||||
}
|
||||
|
||||
if (sbPosition == null || !sbPosition!!.isPressed) {
|
||||
if (!sbPosition.isPressed) {
|
||||
val progress: Float = (event.position.toFloat()) / event.duration
|
||||
sbPosition?.progress = (progress * sbPosition!!.max).toInt()
|
||||
sbPosition.progress = (progress * sbPosition.max).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,9 +385,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (controller == null || txtvLength == null) {
|
||||
return
|
||||
}
|
||||
if (controller == null) return
|
||||
|
||||
if (fromUser) {
|
||||
val prog: Float = progress / (seekBar.max.toFloat())
|
||||
|
@ -398,19 +393,19 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
var position: Int = converter.convert((prog * controller!!.duration).toInt())
|
||||
val newChapterIndex: Int = ChapterUtils.getCurrentChapterIndex(controller!!.getMedia(), position)
|
||||
if (newChapterIndex > -1) {
|
||||
if ((sbPosition == null || !sbPosition!!.isPressed) && currentChapterIndex != newChapterIndex) {
|
||||
if (!sbPosition.isPressed && currentChapterIndex != newChapterIndex) {
|
||||
currentChapterIndex = newChapterIndex
|
||||
val media = controller!!.getMedia()
|
||||
position = media?.getChapters()?.get(currentChapterIndex)?.start?.toInt() ?: 0
|
||||
seekedToChapterStart = true
|
||||
controller!!.seekTo(position)
|
||||
updateUi(controller!!.getMedia())
|
||||
sbPosition?.highlightCurrentChapter()
|
||||
sbPosition.highlightCurrentChapter()
|
||||
}
|
||||
txtvSeek?.text = controller!!.getMedia()?.getChapters()?.get(newChapterIndex)?.title ?: (""
|
||||
txtvSeek.text = controller!!.getMedia()?.getChapters()?.get(newChapterIndex)?.title ?: (""
|
||||
+ "\n" + Converter.getDurationStringLong(position))
|
||||
} else {
|
||||
txtvSeek?.text = Converter.getDurationStringLong(position)
|
||||
txtvSeek.text = Converter.getDurationStringLong(position)
|
||||
}
|
||||
} else if (duration != controller!!.duration) {
|
||||
updateUi(controller!!.getMedia())
|
||||
|
@ -419,9 +414,9 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
// interrupt position Observer, restart later
|
||||
cardViewSeek?.scaleX = .8f
|
||||
cardViewSeek?.scaleY = .8f
|
||||
cardViewSeek?.animate()
|
||||
cardViewSeek.scaleX = .8f
|
||||
cardViewSeek.scaleY = .8f
|
||||
cardViewSeek.animate()
|
||||
?.setInterpolator(FastOutSlowInInterpolator())
|
||||
?.alpha(1f)?.scaleX(1f)?.scaleY(1f)
|
||||
?.setDuration(200)
|
||||
|
@ -437,9 +432,9 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
controller!!.seekTo((prog * controller!!.duration).toInt())
|
||||
}
|
||||
}
|
||||
cardViewSeek?.scaleX = 1f
|
||||
cardViewSeek?.scaleY = 1f
|
||||
cardViewSeek?.animate()
|
||||
cardViewSeek.scaleX = 1f
|
||||
cardViewSeek.scaleY = 1f
|
||||
cardViewSeek.animate()
|
||||
?.setInterpolator(FastOutSlowInInterpolator())
|
||||
?.alpha(0f)?.scaleX(.8f)?.scaleY(.8f)
|
||||
?.setDuration(200)
|
||||
|
@ -447,29 +442,26 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
|
||||
fun setupOptionsMenu(media: Playable?) {
|
||||
if (toolbar == null) return
|
||||
if (toolbar!!.menu.size() == 0) {
|
||||
toolbar!!.inflateMenu(R.menu.mediaplayer)
|
||||
}
|
||||
if (controller == null) {
|
||||
return
|
||||
if (toolbar.menu.size() == 0) {
|
||||
toolbar.inflateMenu(R.menu.mediaplayer)
|
||||
}
|
||||
|
||||
val isFeedMedia = media is FeedMedia
|
||||
toolbar?.menu?.findItem(R.id.open_feed_item)?.setVisible(isFeedMedia)
|
||||
toolbar.menu?.findItem(R.id.open_feed_item)?.setVisible(isFeedMedia)
|
||||
if (isFeedMedia) {
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar!!.menu, (media as FeedMedia).getItem())
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar.menu, (media as FeedMedia).getItem())
|
||||
}
|
||||
|
||||
toolbar!!.menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller!!.sleepTimerActive())
|
||||
toolbar!!.menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller!!.sleepTimerActive())
|
||||
|
||||
(activity as CastEnabledActivity).requestCastButton(toolbar!!.menu)
|
||||
if (controller != null) {
|
||||
toolbar.menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller!!.sleepTimerActive())
|
||||
toolbar.menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller!!.sleepTimerActive())
|
||||
}
|
||||
(activity as CastEnabledActivity).requestCastButton(toolbar.menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
if (controller == null) {
|
||||
return false
|
||||
}
|
||||
if (controller == null) return false
|
||||
|
||||
val media: Playable = controller!!.getMedia() ?: return false
|
||||
|
||||
val feedItem: FeedItem? = if ((media is FeedMedia)) media.getItem() else null
|
||||
|
@ -497,8 +489,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
player.alpha = 1 - playerFadeProgress
|
||||
player.visibility = if (playerFadeProgress > 0.99f) View.INVISIBLE else View.VISIBLE
|
||||
val toolbarFadeProgress = (max(0.0, min(0.2, (slideOffset - 0.6f).toDouble())) / 0.2f).toFloat()
|
||||
toolbar?.setAlpha(toolbarFadeProgress)
|
||||
toolbar?.visibility = if (toolbarFadeProgress < 0.01f) View.INVISIBLE else View.VISIBLE
|
||||
toolbar.setAlpha(toolbarFadeProgress)
|
||||
toolbar.visibility = if (toolbarFadeProgress < 0.01f) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
||||
private class AudioPlayerPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
|
@ -522,11 +514,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
|
||||
@JvmOverloads
|
||||
fun scrollToPage(page: Int, smoothScroll: Boolean = false) {
|
||||
if (pager == null) {
|
||||
return
|
||||
}
|
||||
|
||||
pager?.setCurrentItem(page, smoothScroll)
|
||||
pager.setCurrentItem(page, smoothScroll)
|
||||
|
||||
val visibleChild = childFragmentManager.findFragmentByTag("f$POS_DESCRIPTION")
|
||||
if (visibleChild is ItemDescriptionFragment) {
|
||||
|
|
|
@ -37,14 +37,15 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
|
||||
@UnstableApi
|
||||
class ChaptersFragment : AppCompatDialogFragment() {
|
||||
private var adapter: ChaptersListAdapter? = null
|
||||
private lateinit var layoutManager: LinearLayoutManager
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var adapter: ChaptersListAdapter
|
||||
|
||||
private var controller: PlaybackController? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var focusedChapter = -1
|
||||
private var media: Playable? = null
|
||||
private var layoutManager: LinearLayoutManager? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.chapters_label))
|
||||
|
@ -55,7 +56,7 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
|||
dialog.show()
|
||||
dialog.getButton(DialogInterface.BUTTON_NEUTRAL).visibility = View.INVISIBLE
|
||||
dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener { v: View? ->
|
||||
progressBar!!.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
loadMediaInfo(true)
|
||||
}
|
||||
|
||||
|
@ -70,22 +71,21 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
|||
progressBar = root.findViewById(R.id.progLoading)
|
||||
layoutManager = LinearLayoutManager(activity)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(recyclerView.context,
|
||||
layoutManager!!.orientation))
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(recyclerView.context, layoutManager.orientation))
|
||||
|
||||
adapter = ChaptersListAdapter(requireContext(), object : ChaptersListAdapter.Callback {
|
||||
override fun onPlayChapterButtonClicked(pos: Int) {
|
||||
if (controller!!.status != PlayerStatus.PLAYING) {
|
||||
if (controller?.status != PlayerStatus.PLAYING) {
|
||||
controller!!.playPause()
|
||||
}
|
||||
val chapter = adapter!!.getItem(pos)
|
||||
controller!!.seekTo(chapter.start.toInt())
|
||||
val chapter = adapter.getItem(pos)
|
||||
if (chapter != null) controller!!.seekTo(chapter.start.toInt())
|
||||
updateChapterSelection(pos, true)
|
||||
}
|
||||
})
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
val wrapHeight = CoordinatorLayout.LayoutParams(
|
||||
CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.WRAP_CONTENT)
|
||||
|
@ -108,11 +108,8 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
controller!!.release()
|
||||
disposable?.dispose()
|
||||
controller?.release()
|
||||
controller = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
@ -120,20 +117,18 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
updateChapterSelection(getCurrentChapter(media), false)
|
||||
adapter!!.notifyTimeChanged(event.position.toLong())
|
||||
adapter.notifyTimeChanged(event.position.toLong())
|
||||
}
|
||||
|
||||
private fun getCurrentChapter(media: Playable?): Int {
|
||||
if (controller == null) {
|
||||
return -1
|
||||
}
|
||||
if (controller == null) return -1
|
||||
|
||||
return getCurrentChapterIndex(media, controller!!.position)
|
||||
}
|
||||
|
||||
private fun loadMediaInfo(forceRefresh: Boolean) {
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Maybe.create { emitter: MaybeEmitter<Any> ->
|
||||
val media = controller!!.getMedia()
|
||||
if (media != null) {
|
||||
|
@ -152,36 +147,29 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
|||
private fun onMediaChanged(media: Playable) {
|
||||
this.media = media
|
||||
focusedChapter = -1
|
||||
if (adapter == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (media.getChapters().isEmpty()) {
|
||||
dismiss()
|
||||
Toast.makeText(context, R.string.no_chapters_label, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
progressBar!!.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
}
|
||||
adapter!!.setMedia(media)
|
||||
(dialog as AlertDialog?)!!.getButton(DialogInterface.BUTTON_NEUTRAL).visibility =
|
||||
View.INVISIBLE
|
||||
adapter.setMedia(media)
|
||||
(dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEUTRAL).visibility = View.INVISIBLE
|
||||
if ((media is FeedMedia) && media.getItem() != null && !TextUtils.isEmpty(media.getItem()!!.podcastIndexChapterUrl)) {
|
||||
(dialog as AlertDialog?)!!.getButton(DialogInterface.BUTTON_NEUTRAL).visibility = View.VISIBLE
|
||||
(dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEUTRAL).visibility = View.VISIBLE
|
||||
}
|
||||
val positionOfCurrentChapter = getCurrentChapter(media)
|
||||
updateChapterSelection(positionOfCurrentChapter, true)
|
||||
}
|
||||
|
||||
private fun updateChapterSelection(position: Int, scrollTo: Boolean) {
|
||||
if (adapter == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (position != -1 && focusedChapter != position) {
|
||||
focusedChapter = position
|
||||
adapter!!.notifyChapterChanged(focusedChapter)
|
||||
if (scrollTo && (layoutManager!!.findFirstCompletelyVisibleItemPosition() >= position
|
||||
|| layoutManager!!.findLastCompletelyVisibleItemPosition() <= position)) {
|
||||
layoutManager!!.scrollToPositionWithOffset(position, 100)
|
||||
adapter.notifyChapterChanged(focusedChapter)
|
||||
if (scrollTo && (layoutManager.findFirstCompletelyVisibleItemPosition() >= position
|
||||
|| layoutManager.findLastCompletelyVisibleItemPosition() <= position)) {
|
||||
layoutManager.scrollToPositionWithOffset(position, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import ac.mdiq.podvinci.view.EmptyViewHandler
|
|||
import ac.mdiq.podvinci.view.EpisodeItemListRecyclerView
|
||||
import ac.mdiq.podvinci.view.LiftOnScrollListener
|
||||
import ac.mdiq.podvinci.view.viewholder.EpisodeItemViewHolder
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -52,75 +54,83 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
*/
|
||||
class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeListener, Toolbar.OnMenuItemClickListener {
|
||||
private var runningDownloads: Set<String>? = HashSet()
|
||||
private var items: MutableList<FeedItem>? = ArrayList()
|
||||
private var adapter: CompletedDownloadsListAdapter? = null
|
||||
private var recyclerView: EpisodeItemListRecyclerView? = null
|
||||
private var items: MutableList<FeedItem> = mutableListOf()
|
||||
|
||||
private lateinit var adapter: CompletedDownloadsListAdapter
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
private lateinit var swipeActions: SwipeActions
|
||||
private lateinit var speedDialView: SpeedDialView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var emptyView: EmptyViewHandler
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
private var emptyView: EmptyViewHandler? = null
|
||||
private var displayUpArrow = false
|
||||
private var speedDialView: SpeedDialView? = null
|
||||
private var swipeActions: SwipeActions? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val root: View = inflater.inflate(R.layout.simple_list_fragment, container, false)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.setTitle(R.string.downloads_label)
|
||||
toolbar?.inflateMenu(R.menu.downloads_completed)
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar?.setOnLongClickListener { v: View? ->
|
||||
recyclerView?.scrollToPosition(5)
|
||||
recyclerView?.post { recyclerView!!.smoothScrollToPosition(0) }
|
||||
toolbar.setTitle(R.string.downloads_label)
|
||||
toolbar.inflateMenu(R.menu.downloads_completed)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener { v: View? ->
|
||||
recyclerView.scrollToPosition(5)
|
||||
recyclerView.post { recyclerView.smoothScrollToPosition(0) }
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
if (toolbar != null) (activity as MainActivity).setupToolbarToggle(toolbar!!, displayUpArrow)
|
||||
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
|
||||
|
||||
recyclerView = root.findViewById(R.id.recyclerView)
|
||||
recyclerView?.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
adapter = CompletedDownloadsListAdapter(activity as MainActivity)
|
||||
adapter?.setOnSelectModeListener(this)
|
||||
recyclerView?.adapter = adapter
|
||||
recyclerView?.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
adapter.setOnSelectModeListener(this)
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
swipeActions = SwipeActions(this, TAG).attachTo(recyclerView)
|
||||
swipeActions?.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
||||
|
||||
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
|
||||
if (animator is SimpleItemAnimator) {
|
||||
animator.supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
progressBar = root.findViewById(R.id.progLoading)
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
speedDialView = root.findViewById(R.id.fabSD)
|
||||
speedDialView?.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView?.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView?.removeActionItemById(R.id.download_batch)
|
||||
speedDialView?.removeActionItemById(R.id.mark_read_batch)
|
||||
speedDialView?.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView?.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialView?.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView?.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
speedDialView.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView.removeActionItemById(R.id.download_batch)
|
||||
speedDialView.removeActionItemById(R.id.mark_read_batch)
|
||||
speedDialView.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && adapter?.selectedCount == 0) {
|
||||
if (open && adapter.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
speedDialView?.close()
|
||||
speedDialView.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialView?.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
adapter?.selectedItems?.let {
|
||||
speedDialView.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
adapter.selectedItems.let {
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItem.id)
|
||||
.handleAction(it.filterIsInstance<FeedItem>())
|
||||
}
|
||||
adapter?.endSelectMode()
|
||||
adapter.endSelectMode()
|
||||
true
|
||||
}
|
||||
if (arguments != null && requireArguments().getBoolean(ARG_SHOW_LOGS, false)) {
|
||||
|
@ -139,11 +149,10 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
|
||||
override fun onDestroyView() {
|
||||
EventBus.getDefault().unregister(this)
|
||||
adapter?.endSelectMode()
|
||||
if (toolbar != null) {
|
||||
toolbar!!.setOnMenuItemClickListener(null)
|
||||
toolbar!!.setOnLongClickListener(null)
|
||||
}
|
||||
adapter.endSelectMode()
|
||||
toolbar.setOnMenuItemClickListener(null)
|
||||
toolbar.setOnLongClickListener(null)
|
||||
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
@ -157,7 +166,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
disposable?.dispose()
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.refresh_item -> {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
|
@ -193,21 +202,20 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
return // Refreshed anyway
|
||||
}
|
||||
for (downloadUrl in event.urls) {
|
||||
var pos = -1
|
||||
items?.let { pos = FeedItemUtil.indexOfItemWithDownloadUrl(it.toList(), downloadUrl)}
|
||||
val pos = FeedItemUtil.indexOfItemWithDownloadUrl(items.toList(), downloadUrl)
|
||||
if (pos >= 0) {
|
||||
adapter?.notifyItemChangedCompat(pos)
|
||||
adapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
val selectedItem: FeedItem? = adapter?.longPressedItem
|
||||
val selectedItem: FeedItem? = adapter.longPressedItem
|
||||
if (selectedItem == null) {
|
||||
Log.i(TAG, "Selected item at current position was null, ignoring selection")
|
||||
return super.onContextItemSelected(item)
|
||||
}
|
||||
if (adapter != null && adapter!!.onContextItemSelected(item)) {
|
||||
if (adapter.onContextItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -216,51 +224,48 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
|
||||
private fun addEmptyView() {
|
||||
emptyView = EmptyViewHandler(activity)
|
||||
emptyView?.setIcon(R.drawable.ic_download)
|
||||
emptyView?.setTitle(R.string.no_comp_downloads_head_label)
|
||||
emptyView?.setMessage(R.string.no_comp_downloads_label)
|
||||
if (recyclerView != null) emptyView?.attachToRecyclerView(recyclerView!!)
|
||||
emptyView.setIcon(R.drawable.ic_download)
|
||||
emptyView.setTitle(R.string.no_comp_downloads_head_label)
|
||||
emptyView.setMessage(R.string.no_comp_downloads_label)
|
||||
emptyView.attachToRecyclerView(recyclerView)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
if (items == null) {
|
||||
return
|
||||
} else if (adapter == null) {
|
||||
loadItems()
|
||||
return
|
||||
}
|
||||
|
||||
var i = 0
|
||||
val size: Int = event.items.size
|
||||
while (i < size) {
|
||||
val item: FeedItem = event.items[i]
|
||||
var pos = -1
|
||||
items?.let { pos = FeedItemUtil.indexOfItemWithId(it.toList(), item.id) }
|
||||
val pos = FeedItemUtil.indexOfItemWithId(items.toList(), item.id)
|
||||
if (pos >= 0) {
|
||||
items?.removeAt(pos)
|
||||
items.removeAt(pos)
|
||||
val media = item.media
|
||||
if (media != null && media.isDownloaded()) {
|
||||
items?.add(pos, item)
|
||||
adapter?.notifyItemChangedCompat(pos)
|
||||
items.add(pos, item)
|
||||
// adapter.notifyItemChangedCompat(pos)
|
||||
} else {
|
||||
adapter?.notifyItemRemoved(pos)
|
||||
// adapter.notifyItemRemoved(pos)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
// have to do this as adapter.notifyItemRemoved(pos) when pos == 0 causes crash
|
||||
if (size > 0) {
|
||||
adapter.setDummyViews(0)
|
||||
adapter.updateItems(items)
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent?) {
|
||||
if (adapter != null) {
|
||||
for (i in 0 until adapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder =
|
||||
recyclerView?.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder
|
||||
if (holder.isCurrentlyPlayingItem) {
|
||||
if (event != null) holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
}
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
// if (event == null) return
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +288,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
private fun loadItems() {
|
||||
disposable?.dispose()
|
||||
|
||||
emptyView?.hide()
|
||||
emptyView.hide()
|
||||
disposable = Observable.fromCallable {
|
||||
val sortOrder: SortOrder? = UserPreferences.downloadsSortedOrder
|
||||
val downloadedItems: List<FeedItem> = DBReader.getEpisodes(0, Int.MAX_VALUE,
|
||||
|
@ -306,35 +311,35 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<FeedItem>? ->
|
||||
items = result?.toMutableList()
|
||||
adapter?.setDummyViews(0)
|
||||
progressBar?.visibility = View.GONE
|
||||
if (result != null) adapter?.updateItems(result)
|
||||
{ result: List<FeedItem> ->
|
||||
items = result.toMutableList()
|
||||
adapter.setDummyViews(0)
|
||||
progressBar.visibility = View.GONE
|
||||
adapter.updateItems(result)
|
||||
}, { error: Throwable? ->
|
||||
adapter?.setDummyViews(0)
|
||||
adapter?.updateItems(emptyList())
|
||||
adapter.setDummyViews(0)
|
||||
adapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
swipeActions?.detach()
|
||||
speedDialView?.visibility = View.VISIBLE
|
||||
swipeActions.detach()
|
||||
speedDialView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialView?.close()
|
||||
speedDialView?.visibility = View.GONE
|
||||
swipeActions?.attachTo(recyclerView)
|
||||
speedDialView.close()
|
||||
speedDialView.visibility = View.GONE
|
||||
swipeActions.attachTo(recyclerView)
|
||||
}
|
||||
|
||||
@UnstableApi private inner class CompletedDownloadsListAdapter(mainActivity: MainActivity) :
|
||||
EpisodeItemListAdapter(mainActivity) {
|
||||
@UnstableApi private inner class CompletedDownloadsListAdapter(mainActivity: MainActivity) : EpisodeItemListAdapter(mainActivity) {
|
||||
@UnstableApi override fun afterBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) {
|
||||
if (holder.feedItem != null && !inActionMode()) {
|
||||
if (holder.feedItem!!.isDownloaded) {
|
||||
val actionButton = DeleteActionButton(getItem(pos))
|
||||
val item = getItem(pos) ?: return
|
||||
val actionButton = DeleteActionButton(item)
|
||||
actionButton.configure(holder.secondaryActionButton, holder.secondaryActionIcon, requireContext())
|
||||
}
|
||||
}
|
||||
|
@ -345,8 +350,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
}
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem ->
|
||||
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem ->
|
||||
this@CompletedDownloadsFragment.onContextItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
package ac.mdiq.podvinci.fragment
|
||||
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import ac.mdiq.podvinci.core.feed.util.ImageResourceUtils
|
||||
import ac.mdiq.podvinci.core.util.ChapterUtils
|
||||
import ac.mdiq.podvinci.core.util.DateFormatter
|
||||
import ac.mdiq.podvinci.core.util.playback.PlaybackController
|
||||
import ac.mdiq.podvinci.databinding.CoverFragmentBinding
|
||||
import ac.mdiq.podvinci.event.playback.PlaybackPositionEvent
|
||||
import ac.mdiq.podvinci.model.feed.Chapter
|
||||
import ac.mdiq.podvinci.model.feed.EmbeddedChapterImage
|
||||
import ac.mdiq.podvinci.model.feed.FeedMedia
|
||||
import ac.mdiq.podvinci.model.playback.Playable
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
|
@ -13,7 +24,6 @@ import android.graphics.ColorFilter
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -30,17 +40,6 @@ import com.bumptech.glide.load.resource.bitmap.FitCenter
|
|||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.core.feed.util.ImageResourceUtils
|
||||
import ac.mdiq.podvinci.core.util.ChapterUtils
|
||||
import ac.mdiq.podvinci.core.util.DateFormatter
|
||||
import ac.mdiq.podvinci.core.util.playback.PlaybackController
|
||||
import ac.mdiq.podvinci.databinding.CoverFragmentBinding
|
||||
import ac.mdiq.podvinci.event.playback.PlaybackPositionEvent
|
||||
import ac.mdiq.podvinci.model.feed.Chapter
|
||||
import ac.mdiq.podvinci.model.feed.EmbeddedChapterImage
|
||||
import ac.mdiq.podvinci.model.feed.FeedMedia
|
||||
import ac.mdiq.podvinci.model.playback.Playable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
|
@ -55,7 +54,7 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
* Displays the cover and the title of a FeedItem.
|
||||
*/
|
||||
class CoverFragment : Fragment() {
|
||||
private var viewBinding: CoverFragmentBinding? = null
|
||||
private lateinit var viewBinding: CoverFragmentBinding
|
||||
private var controller: PlaybackController? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var displayedChapterIndex = -1
|
||||
|
@ -63,23 +62,23 @@ class CoverFragment : Fragment() {
|
|||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
viewBinding = CoverFragmentBinding.inflate(inflater)
|
||||
viewBinding!!.imgvCover.setOnClickListener { v: View? -> onPlayPause() }
|
||||
viewBinding!!.openDescription.setOnClickListener { view: View? ->
|
||||
viewBinding.imgvCover.setOnClickListener { v: View? -> onPlayPause() }
|
||||
viewBinding.openDescription.setOnClickListener { view: View? ->
|
||||
(requireParentFragment() as AudioPlayerFragment)
|
||||
.scrollToPage(AudioPlayerFragment.POS_DESCRIPTION, true)
|
||||
}
|
||||
val colorFilter: ColorFilter? = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
viewBinding!!.txtvPodcastTitle.currentTextColor, BlendModeCompat.SRC_IN)
|
||||
viewBinding!!.butNextChapter.colorFilter = colorFilter
|
||||
viewBinding!!.butPrevChapter.colorFilter = colorFilter
|
||||
viewBinding!!.descriptionIcon.colorFilter = colorFilter
|
||||
viewBinding!!.chapterButton.setOnClickListener { v: View? ->
|
||||
viewBinding.txtvPodcastTitle.currentTextColor, BlendModeCompat.SRC_IN)
|
||||
viewBinding.butNextChapter.colorFilter = colorFilter
|
||||
viewBinding.butPrevChapter.colorFilter = colorFilter
|
||||
viewBinding.descriptionIcon.colorFilter = colorFilter
|
||||
viewBinding.chapterButton.setOnClickListener { v: View? ->
|
||||
ChaptersFragment().show(
|
||||
childFragmentManager, ChaptersFragment.TAG)
|
||||
}
|
||||
viewBinding!!.butPrevChapter.setOnClickListener { v: View? -> seekToPrevChapter() }
|
||||
viewBinding!!.butNextChapter.setOnClickListener { v: View? -> seekToNextChapter() }
|
||||
return viewBinding!!.root
|
||||
viewBinding.butPrevChapter.setOnClickListener { v: View? -> seekToPrevChapter() }
|
||||
viewBinding.butNextChapter.setOnClickListener { v: View? -> seekToNextChapter() }
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -87,9 +86,8 @@ class CoverFragment : Fragment() {
|
|||
}
|
||||
|
||||
@UnstableApi private fun loadMediaInfo(includingChapters: Boolean) {
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Maybe.create<Playable> { emitter: MaybeEmitter<Playable?> ->
|
||||
val media: Playable? = controller?.getMedia()
|
||||
if (media != null) {
|
||||
|
@ -111,9 +109,9 @@ class CoverFragment : Fragment() {
|
|||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
private fun displayMediaInfo(media: Playable) {
|
||||
@UnstableApi private fun displayMediaInfo(media: Playable) {
|
||||
val pubDateStr = DateFormatter.formatAbbrev(activity, media.getPubDate())
|
||||
viewBinding!!.txtvPodcastTitle.text = (StringUtils.stripToEmpty(media.getFeedTitle())
|
||||
viewBinding.txtvPodcastTitle.text = (StringUtils.stripToEmpty(media.getFeedTitle())
|
||||
+ "\u00A0"
|
||||
+ "・"
|
||||
+ "\u00A0"
|
||||
|
@ -122,36 +120,36 @@ class CoverFragment : Fragment() {
|
|||
if (media.getItem() != null) {
|
||||
val openFeed: Intent =
|
||||
MainActivity.getIntentToOpenFeed(requireContext(), media.getItem()!!.feedId)
|
||||
viewBinding!!.txtvPodcastTitle.setOnClickListener { v: View? -> startActivity(openFeed) }
|
||||
viewBinding.txtvPodcastTitle.setOnClickListener { v: View? -> startActivity(openFeed) }
|
||||
}
|
||||
} else {
|
||||
viewBinding!!.txtvPodcastTitle.setOnClickListener(null)
|
||||
viewBinding.txtvPodcastTitle.setOnClickListener(null)
|
||||
}
|
||||
viewBinding!!.txtvPodcastTitle.setOnLongClickListener { v: View? -> copyText(media.getFeedTitle()) }
|
||||
viewBinding!!.txtvEpisodeTitle.text = media.getEpisodeTitle()
|
||||
viewBinding!!.txtvEpisodeTitle.setOnLongClickListener { v: View? -> copyText(media.getEpisodeTitle()) }
|
||||
viewBinding!!.txtvEpisodeTitle.setOnClickListener { v: View? ->
|
||||
val lines = viewBinding!!.txtvEpisodeTitle.lineCount
|
||||
viewBinding.txtvPodcastTitle.setOnLongClickListener { v: View? -> copyText(media.getFeedTitle()) }
|
||||
viewBinding.txtvEpisodeTitle.text = media.getEpisodeTitle()
|
||||
viewBinding.txtvEpisodeTitle.setOnLongClickListener { v: View? -> copyText(media.getEpisodeTitle()) }
|
||||
viewBinding.txtvEpisodeTitle.setOnClickListener { v: View? ->
|
||||
val lines = viewBinding.txtvEpisodeTitle.lineCount
|
||||
val animUnit = 1500
|
||||
if (lines > viewBinding!!.txtvEpisodeTitle.maxLines) {
|
||||
val titleHeight = (viewBinding!!.txtvEpisodeTitle.height
|
||||
- viewBinding!!.txtvEpisodeTitle.paddingTop
|
||||
- viewBinding!!.txtvEpisodeTitle.paddingBottom)
|
||||
if (lines > viewBinding.txtvEpisodeTitle.maxLines) {
|
||||
val titleHeight = (viewBinding.txtvEpisodeTitle.height
|
||||
- viewBinding.txtvEpisodeTitle.paddingTop
|
||||
- viewBinding.txtvEpisodeTitle.paddingBottom)
|
||||
val verticalMarquee: ObjectAnimator = ObjectAnimator.ofInt(
|
||||
viewBinding!!.txtvEpisodeTitle, "scrollY", 0,
|
||||
(lines - viewBinding!!.txtvEpisodeTitle.maxLines)
|
||||
* (titleHeight / viewBinding!!.txtvEpisodeTitle.maxLines))
|
||||
viewBinding.txtvEpisodeTitle, "scrollY", 0,
|
||||
(lines - viewBinding.txtvEpisodeTitle.maxLines)
|
||||
* (titleHeight / viewBinding.txtvEpisodeTitle.maxLines))
|
||||
.setDuration((lines * animUnit).toLong())
|
||||
val fadeOut: ObjectAnimator = ObjectAnimator.ofFloat(
|
||||
viewBinding!!.txtvEpisodeTitle, "alpha", 0f)
|
||||
viewBinding.txtvEpisodeTitle, "alpha", 0f)
|
||||
fadeOut.setStartDelay(animUnit.toLong())
|
||||
fadeOut.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
viewBinding!!.txtvEpisodeTitle.scrollTo(0, 0)
|
||||
viewBinding.txtvEpisodeTitle.scrollTo(0, 0)
|
||||
}
|
||||
})
|
||||
val fadeBackIn: ObjectAnimator = ObjectAnimator.ofFloat(
|
||||
viewBinding!!.txtvEpisodeTitle, "alpha", 1f)
|
||||
viewBinding.txtvEpisodeTitle, "alpha", 1f)
|
||||
val set = AnimatorSet()
|
||||
set.playSequentially(verticalMarquee, fadeOut, fadeBackIn)
|
||||
set.start()
|
||||
|
@ -173,9 +171,9 @@ class CoverFragment : Fragment() {
|
|||
chapterControlVisible = fm?.getItem() != null && fm.getItem()!!.hasChapters()
|
||||
}
|
||||
val newVisibility = if (chapterControlVisible) View.VISIBLE else View.GONE
|
||||
if (viewBinding!!.chapterButton.visibility != newVisibility) {
|
||||
viewBinding!!.chapterButton.visibility = newVisibility
|
||||
ObjectAnimator.ofFloat(viewBinding!!.chapterButton,
|
||||
if (viewBinding.chapterButton.visibility != newVisibility) {
|
||||
viewBinding.chapterButton.visibility = newVisibility
|
||||
ObjectAnimator.ofFloat(viewBinding.chapterButton,
|
||||
"alpha",
|
||||
(if (chapterControlVisible) 0 else 1).toFloat(),
|
||||
(if (chapterControlVisible) 1 else 0).toFloat())
|
||||
|
@ -187,10 +185,10 @@ class CoverFragment : Fragment() {
|
|||
if (media != null && chapterIndex > -1) {
|
||||
if (media!!.getPosition() > media!!.getDuration() || chapterIndex >= media!!.getChapters().size - 1) {
|
||||
displayedChapterIndex = media!!.getChapters().size - 1
|
||||
viewBinding!!.butNextChapter.visibility = View.INVISIBLE
|
||||
viewBinding.butNextChapter.visibility = View.INVISIBLE
|
||||
} else {
|
||||
displayedChapterIndex = chapterIndex
|
||||
viewBinding!!.butNextChapter.visibility = View.VISIBLE
|
||||
viewBinding.butNextChapter.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,10 +243,7 @@ class CoverFragment : Fragment() {
|
|||
|
||||
@UnstableApi override fun onStop() {
|
||||
super.onStop()
|
||||
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
controller?.release()
|
||||
controller = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
|
@ -277,14 +272,14 @@ class CoverFragment : Fragment() {
|
|||
.apply(options)
|
||||
|
||||
if (displayedChapterIndex == -1 || media!!.getChapters().isEmpty() || media!!.getChapters()[displayedChapterIndex].imageUrl.isNullOrEmpty()) {
|
||||
cover.into(viewBinding!!.imgvCover)
|
||||
cover.into(viewBinding.imgvCover)
|
||||
} else {
|
||||
Glide.with(this)
|
||||
.load(EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex))
|
||||
.apply(options)
|
||||
.thumbnail(cover)
|
||||
.error(cover)
|
||||
.into(viewBinding!!.imgvCover)
|
||||
.into(viewBinding.imgvCover)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,26 +291,26 @@ class CoverFragment : Fragment() {
|
|||
private fun configureForOrientation(newConfig: Configuration) {
|
||||
val isPortrait = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
viewBinding!!.coverFragment.orientation = if (isPortrait) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
|
||||
viewBinding.coverFragment.orientation = if (isPortrait) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
|
||||
|
||||
if (isPortrait) {
|
||||
viewBinding!!.coverHolder.layoutParams =
|
||||
viewBinding.coverHolder.layoutParams =
|
||||
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)
|
||||
viewBinding!!.coverFragmentTextContainer.layoutParams =
|
||||
viewBinding.coverFragmentTextContainer.layoutParams =
|
||||
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
} else {
|
||||
viewBinding!!.coverHolder.layoutParams =
|
||||
viewBinding.coverHolder.layoutParams =
|
||||
LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)
|
||||
viewBinding!!.coverFragmentTextContainer.layoutParams =
|
||||
viewBinding.coverFragmentTextContainer.layoutParams =
|
||||
LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)
|
||||
}
|
||||
|
||||
(viewBinding!!.episodeDetails.parent as ViewGroup).removeView(viewBinding!!.episodeDetails)
|
||||
(viewBinding.episodeDetails.parent as ViewGroup).removeView(viewBinding.episodeDetails)
|
||||
if (isPortrait) {
|
||||
viewBinding!!.coverFragment.addView(viewBinding!!.episodeDetails)
|
||||
viewBinding.coverFragment.addView(viewBinding.episodeDetails)
|
||||
} else {
|
||||
viewBinding!!.coverFragmentTextContainer.addView(viewBinding!!.episodeDetails)
|
||||
viewBinding.coverFragmentTextContainer.addView(viewBinding.episodeDetails)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +321,7 @@ class CoverFragment : Fragment() {
|
|||
controller!!.playPause()
|
||||
}
|
||||
|
||||
private fun copyText(text: String): Boolean {
|
||||
@UnstableApi private fun copyText(text: String): Boolean {
|
||||
val clipboardManager: ClipboardManager? = ContextCompat.getSystemService(requireContext(), ClipboardManager::class.java)
|
||||
clipboardManager?.setPrimaryClip(ClipData.newPlainText("PodVinci", text))
|
||||
if (Build.VERSION.SDK_INT <= 32) {
|
||||
|
|
|
@ -38,17 +38,18 @@ import java.util.*
|
|||
* Searches iTunes store for top podcasts and displays results in a list.
|
||||
*/
|
||||
class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||
private var prefs: SharedPreferences? = null
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var gridView: GridView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var txtvError: TextView
|
||||
private lateinit var butRetry: Button
|
||||
private lateinit var txtvEmpty: TextView
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
/**
|
||||
* Adapter responsible with the search results.
|
||||
*/
|
||||
private var adapter: ItunesAdapter? = null
|
||||
private var gridView: GridView? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private var txtvError: TextView? = null
|
||||
private var butRetry: Button? = null
|
||||
private var txtvEmpty: TextView? = null
|
||||
|
||||
/**
|
||||
* List of podcasts retreived from the search.
|
||||
|
@ -59,7 +60,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
private var countryCode: String? = "US"
|
||||
private var hidden = false
|
||||
private var needsConfirm = false
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
|
||||
|
||||
/**
|
||||
* Replace adapter data with provided search results from SearchTask.
|
||||
|
@ -68,28 +69,26 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
*/
|
||||
private fun updateData(result: List<PodcastSearchResult>?) {
|
||||
this.searchResults = result
|
||||
adapter!!.clear()
|
||||
if (result != null && result.size > 0) {
|
||||
gridView!!.visibility = View.VISIBLE
|
||||
txtvEmpty!!.visibility = View.GONE
|
||||
adapter?.clear()
|
||||
if (!result.isNullOrEmpty()) {
|
||||
gridView.visibility = View.VISIBLE
|
||||
txtvEmpty.visibility = View.GONE
|
||||
for (p in result) {
|
||||
adapter!!.add(p)
|
||||
}
|
||||
adapter!!.notifyDataSetInvalidated()
|
||||
adapter?.notifyDataSetInvalidated()
|
||||
} else {
|
||||
gridView!!.visibility = View.GONE
|
||||
txtvEmpty!!.visibility = View.VISIBLE
|
||||
gridView.visibility = View.GONE
|
||||
txtvEmpty.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
prefs = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
||||
if (prefs != null) {
|
||||
countryCode = prefs!!.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)
|
||||
hidden = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)
|
||||
needsConfirm = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)
|
||||
}
|
||||
countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)
|
||||
hidden = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)
|
||||
needsConfirm = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
|
@ -97,19 +96,18 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val root = inflater.inflate(R.layout.fragment_itunes_search, container, false)
|
||||
gridView = root.findViewById(R.id.gridView)
|
||||
adapter = ItunesAdapter(requireActivity(), ArrayList())
|
||||
gridView?.setAdapter(adapter)
|
||||
gridView.setAdapter(adapter)
|
||||
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.setNavigationOnClickListener(View.OnClickListener { v: View? -> parentFragmentManager.popBackStack() })
|
||||
toolbar?.inflateMenu(R.menu.countries_menu)
|
||||
if (toolbar != null) {
|
||||
val discoverHideItem = toolbar!!.getMenu().findItem(R.id.discover_hide_item)
|
||||
discoverHideItem.setChecked(hidden)
|
||||
}
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar.setNavigationOnClickListener { v: View? -> parentFragmentManager.popBackStack() }
|
||||
toolbar.inflateMenu(R.menu.countries_menu)
|
||||
val discoverHideItem = toolbar.menu.findItem(R.id.discover_hide_item)
|
||||
discoverHideItem.setChecked(hidden)
|
||||
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
//Show information about the podcast when the list item is clicked
|
||||
gridView?.setOnItemClickListener(OnItemClickListener { parent: AdapterView<*>?, view1: View?, position: Int, id: Long ->
|
||||
gridView.onItemClickListener = OnItemClickListener { parent: AdapterView<*>?, view1: View?, position: Int, id: Long ->
|
||||
val podcast = searchResults!![position]
|
||||
if (podcast.feedUrl == null) {
|
||||
return@OnItemClickListener
|
||||
|
@ -117,7 +115,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val intent = Intent(activity, OnlineFeedViewActivity::class.java)
|
||||
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl)
|
||||
startActivity(intent)
|
||||
})
|
||||
}
|
||||
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
txtvError = root.findViewById(R.id.txtvError)
|
||||
|
@ -130,45 +128,42 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
adapter = null
|
||||
}
|
||||
|
||||
private fun loadToplist(country: String?) {
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
gridView!!.visibility = View.GONE
|
||||
txtvError!!.visibility = View.GONE
|
||||
butRetry!!.visibility = View.GONE
|
||||
butRetry!!.setText(R.string.retry_label)
|
||||
txtvEmpty!!.visibility = View.GONE
|
||||
progressBar!!.visibility = View.VISIBLE
|
||||
gridView.visibility = View.GONE
|
||||
txtvError.visibility = View.GONE
|
||||
butRetry.visibility = View.GONE
|
||||
butRetry.setText(R.string.retry_label)
|
||||
txtvEmpty.visibility = View.GONE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
if (hidden) {
|
||||
gridView!!.visibility = View.GONE
|
||||
txtvError!!.visibility = View.VISIBLE
|
||||
txtvError!!.text = resources.getString(R.string.discover_is_hidden)
|
||||
butRetry!!.visibility = View.GONE
|
||||
txtvEmpty!!.visibility = View.GONE
|
||||
progressBar!!.visibility = View.GONE
|
||||
gridView.visibility = View.GONE
|
||||
txtvError.visibility = View.VISIBLE
|
||||
txtvError.text = resources.getString(R.string.discover_is_hidden)
|
||||
butRetry.visibility = View.GONE
|
||||
txtvEmpty.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
if (BuildConfig.FLAVOR == "free" && needsConfirm) {
|
||||
txtvError!!.visibility = View.VISIBLE
|
||||
txtvError!!.text = ""
|
||||
butRetry!!.visibility = View.VISIBLE
|
||||
butRetry!!.setText(R.string.discover_confirm)
|
||||
butRetry!!.setOnClickListener { v: View? ->
|
||||
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
||||
txtvError.visibility = View.VISIBLE
|
||||
txtvError.text = ""
|
||||
butRetry.visibility = View.VISIBLE
|
||||
butRetry.setText(R.string.discover_confirm)
|
||||
butRetry.setOnClickListener { v: View? ->
|
||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
||||
needsConfirm = false
|
||||
loadToplist(country)
|
||||
}
|
||||
txtvEmpty!!.visibility = View.GONE
|
||||
progressBar!!.visibility = View.GONE
|
||||
txtvEmpty.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -179,16 +174,16 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ podcasts: List<PodcastSearchResult>? ->
|
||||
progressBar!!.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
topList = podcasts
|
||||
updateData(topList)
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
progressBar!!.visibility = View.GONE
|
||||
txtvError!!.text = error.message
|
||||
txtvError!!.visibility = View.VISIBLE
|
||||
butRetry!!.setOnClickListener { v: View? -> loadToplist(country) }
|
||||
butRetry!!.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.GONE
|
||||
txtvError.text = error.message
|
||||
txtvError.visibility = View.VISIBLE
|
||||
butRetry.setOnClickListener { v: View? -> loadToplist(country) }
|
||||
butRetry.visibility = View.VISIBLE
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -200,7 +195,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
if (itemId == R.id.discover_hide_item) {
|
||||
item.setChecked(!item.isChecked)
|
||||
hidden = item.isChecked
|
||||
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||
|
||||
EventBus.getDefault().post(DiscoveryDefaultUpdateEvent())
|
||||
loadToplist(countryCode)
|
||||
|
@ -211,7 +206,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
builder.setView(selectCountryDialogView)
|
||||
|
||||
val countryCodeArray: List<String> = ArrayList(Arrays.asList(*Locale.getISOCountries()))
|
||||
val countryCodeArray: List<String> = listOf(*Locale.getISOCountries())
|
||||
val countryCodeNames: MutableMap<String?, String> = HashMap()
|
||||
val countryNameCodes: MutableMap<String, String> = HashMap()
|
||||
for (code in countryCodeArray) {
|
||||
|
@ -221,8 +216,8 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
countryNameCodes[countryName] = code
|
||||
}
|
||||
|
||||
val countryNamesSort: List<String> = ArrayList(countryCodeNames.values)
|
||||
Collections.sort(countryNamesSort)
|
||||
val countryNamesSort: MutableList<String> = ArrayList(countryCodeNames.values)
|
||||
countryNamesSort.sort()
|
||||
|
||||
val dataAdapter =
|
||||
ArrayAdapter(this.requireContext(), android.R.layout.simple_list_item_1, countryNamesSort)
|
||||
|
@ -231,7 +226,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
editText!!.setAdapter(dataAdapter)
|
||||
editText.setText(countryCodeNames[countryCode])
|
||||
editText.setOnClickListener { view: View? ->
|
||||
if (editText.text.length != 0) {
|
||||
if (editText.text.isNotEmpty()) {
|
||||
editText.setText("")
|
||||
editText.postDelayed({ editText.showDropDown() }, 100)
|
||||
}
|
||||
|
@ -247,13 +242,13 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val countryName = editText.text.toString()
|
||||
if (countryNameCodes.containsKey(countryName)) {
|
||||
countryCode = countryNameCodes[countryName]
|
||||
val discoverHideItem = toolbar!!.menu.findItem(R.id.discover_hide_item)
|
||||
val discoverHideItem = toolbar.menu.findItem(R.id.discover_hide_item)
|
||||
discoverHideItem.setChecked(false)
|
||||
hidden = false
|
||||
}
|
||||
|
||||
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||
prefs!!.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply()
|
||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||
prefs.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply()
|
||||
|
||||
EventBus.getDefault().post(DiscoveryDefaultUpdateEvent())
|
||||
loadToplist(countryCode)
|
||||
|
|
|
@ -28,10 +28,11 @@ import org.greenrobot.eventbus.Subscribe
|
|||
* Shows the download log
|
||||
*/
|
||||
class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, Toolbar.OnMenuItemClickListener {
|
||||
private lateinit var viewBinding: DownloadLogFragmentBinding
|
||||
private lateinit var adapter: DownloadLogAdapter
|
||||
|
||||
private var downloadLog: List<DownloadResult> = ArrayList()
|
||||
private var adapter: DownloadLogAdapter? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var viewBinding: DownloadLogFragmentBinding? = null
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
@ -40,30 +41,28 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = DownloadLogFragmentBinding.inflate(inflater)
|
||||
viewBinding!!.toolbar.inflateMenu(R.menu.download_log)
|
||||
viewBinding!!.toolbar.setOnMenuItemClickListener(this)
|
||||
viewBinding.toolbar.inflateMenu(R.menu.download_log)
|
||||
viewBinding.toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
val emptyView = EmptyViewHandler(activity)
|
||||
emptyView.setIcon(R.drawable.ic_download)
|
||||
emptyView.setTitle(R.string.no_log_downloads_head_label)
|
||||
emptyView.setMessage(R.string.no_log_downloads_label)
|
||||
emptyView.attachToListView(viewBinding!!.list)
|
||||
emptyView.attachToListView(viewBinding.list)
|
||||
|
||||
adapter = DownloadLogAdapter(requireActivity())
|
||||
viewBinding!!.list.adapter = adapter
|
||||
viewBinding!!.list.onItemClickListener = this
|
||||
viewBinding!!.list.isNestedScrollingEnabled = true
|
||||
viewBinding.list.adapter = adapter
|
||||
viewBinding.list.onItemClickListener = this
|
||||
viewBinding.list.isNestedScrollingEnabled = true
|
||||
EventBus.getDefault().register(this)
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -72,7 +71,7 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
|||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
val item = adapter!!.getItem(position)
|
||||
val item = adapter.getItem(position)
|
||||
if (item is DownloadResult) {
|
||||
DownloadLogDetailsDialog(requireContext(), item).show()
|
||||
}
|
||||
|
@ -98,16 +97,15 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
|||
}
|
||||
|
||||
private fun loadDownloadLog() {
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable { DBReader.getDownloadLog() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: List<DownloadResult>? ->
|
||||
if (result != null) {
|
||||
downloadLog = result
|
||||
adapter!!.setDownloadLog(downloadLog)
|
||||
adapter.setDownloadLog(downloadLog)
|
||||
}
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
|
|
@ -56,23 +56,20 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
protected var hasMoreItems: Boolean = false
|
||||
private var displayUpArrow = false
|
||||
|
||||
var recyclerView: EpisodeItemListRecyclerView? = null
|
||||
var listAdapter: EpisodeItemListAdapter? = null
|
||||
@JvmField
|
||||
var emptyView: EmptyViewHandler? = null
|
||||
@JvmField
|
||||
var speedDialView: SpeedDialView? = null
|
||||
@JvmField
|
||||
var toolbar: MaterialToolbar? = null
|
||||
var swipeRefreshLayout: SwipeRefreshLayout? = null
|
||||
var swipeActions: SwipeActions? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
lateinit var emptyView: EmptyViewHandler
|
||||
lateinit var speedDialView: SpeedDialView
|
||||
lateinit var toolbar: MaterialToolbar
|
||||
lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
lateinit var swipeActions: SwipeActions
|
||||
private lateinit var progressBar: ProgressBar
|
||||
lateinit var listAdapter: EpisodeItemListAdapter
|
||||
|
||||
@JvmField
|
||||
var episodes: MutableList<FeedItem> = ArrayList()
|
||||
|
||||
protected var disposable: Disposable? = null
|
||||
protected var txtvInformation: TextView? = null
|
||||
protected lateinit var txtvInformation: TextView
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
@ -82,24 +79,22 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (recyclerView != null) registerForContextMenu(recyclerView!!)
|
||||
registerForContextMenu(recyclerView)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (getPrefName() != null) recyclerView?.saveScrollPosition(getPrefName())
|
||||
if (recyclerView != null) unregisterForContextMenu(recyclerView!!)
|
||||
recyclerView.saveScrollPosition(getPrefName())
|
||||
unregisterForContextMenu(recyclerView)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
if (disposable != null) {
|
||||
disposable?.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@UnstableApi override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (super.onOptionsItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
|
@ -116,17 +111,17 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
Log.d(TAG, "onContextItemSelected() called with: item = [$item]")
|
||||
if (listAdapter == null || !userVisibleHint || !isVisible || !isMenuVisible) {
|
||||
if (!userVisibleHint || !isVisible || !isMenuVisible) {
|
||||
// The method is called on all fragments in a ViewPager, so this needs to be ignored in invisible ones.
|
||||
// Apparently, none of the visibility check method works reliably on its own, so we just use all.
|
||||
return false
|
||||
} else if (listAdapter?.longPressedItem == null) {
|
||||
} else if (listAdapter.longPressedItem == null) {
|
||||
Log.i(TAG, "Selected item or listAdapter was null, ignoring selection")
|
||||
return super.onContextItemSelected(item)
|
||||
} else if (listAdapter != null && listAdapter!!.onContextItemSelected(item)) {
|
||||
} else if (listAdapter.onContextItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
val selectedItem: FeedItem = listAdapter!!.longPressedItem ?: return false
|
||||
val selectedItem: FeedItem = listAdapter.longPressedItem ?: return false
|
||||
return FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem)
|
||||
}
|
||||
|
||||
|
@ -135,36 +130,34 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
val root: View = inflater.inflate(R.layout.episodes_list_fragment, container, false)
|
||||
txtvInformation = root.findViewById(R.id.txtvInformation)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar?.setOnLongClickListener { v: View? ->
|
||||
recyclerView?.scrollToPosition(5)
|
||||
recyclerView?.post { recyclerView?.smoothScrollToPosition(0) }
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener { v: View? ->
|
||||
recyclerView.scrollToPosition(5)
|
||||
recyclerView.post { recyclerView.smoothScrollToPosition(0) }
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
if (toolbar != null) (activity as MainActivity).setupToolbarToggle(toolbar!!, displayUpArrow)
|
||||
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
|
||||
|
||||
recyclerView = root.findViewById(R.id.recyclerView)
|
||||
recyclerView?.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
setupLoadMoreScrollListener()
|
||||
recyclerView?.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
recyclerView.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
|
||||
swipeActions = SwipeActions(this, getFragmentTag()).attachTo(recyclerView)
|
||||
swipeActions?.setFilter(getFilter())
|
||||
|
||||
if (recyclerView != null) {
|
||||
val animator: RecyclerView.ItemAnimator? = recyclerView!!.itemAnimator
|
||||
if (animator is SimpleItemAnimator) {
|
||||
animator.supportsChangeAnimations = false
|
||||
}
|
||||
swipeActions.setFilter(getFilter())
|
||||
|
||||
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
|
||||
if (animator is SimpleItemAnimator) {
|
||||
animator.supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
swipeRefreshLayout = root.findViewById(R.id.swipeRefresh)
|
||||
swipeRefreshLayout?.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout?.setOnRefreshListener {
|
||||
swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
}
|
||||
|
||||
|
@ -180,38 +173,38 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
}
|
||||
}
|
||||
}
|
||||
listAdapter?.setOnSelectModeListener(this)
|
||||
recyclerView?.adapter = listAdapter
|
||||
listAdapter.setOnSelectModeListener(this)
|
||||
recyclerView.adapter = listAdapter
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
emptyView = EmptyViewHandler(context)
|
||||
if (recyclerView != null) emptyView?.attachToRecyclerView(recyclerView!!)
|
||||
emptyView?.setIcon(R.drawable.ic_feed)
|
||||
emptyView?.setTitle(R.string.no_all_episodes_head_label)
|
||||
emptyView?.setMessage(R.string.no_all_episodes_label)
|
||||
emptyView?.updateAdapter(listAdapter)
|
||||
emptyView?.hide()
|
||||
emptyView.attachToRecyclerView(recyclerView)
|
||||
emptyView.setIcon(R.drawable.ic_feed)
|
||||
emptyView.setTitle(R.string.no_all_episodes_head_label)
|
||||
emptyView.setMessage(R.string.no_all_episodes_label)
|
||||
emptyView.updateAdapter(listAdapter)
|
||||
emptyView.hide()
|
||||
|
||||
speedDialView = root.findViewById(R.id.fabSD)
|
||||
speedDialView?.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView?.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView?.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
speedDialView.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && listAdapter != null && listAdapter!!.selectedCount == 0) {
|
||||
if (open && listAdapter.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
speedDialView?.close()
|
||||
speedDialView.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialView?.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
speedDialView.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
var confirmationString = 0
|
||||
if (listAdapter != null && (listAdapter!!.selectedItems.size >= 25 || listAdapter!!.shouldSelectLazyLoadedItems())) {
|
||||
if (listAdapter.selectedItems.size >= 25 || listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
// Should ask for confirmation
|
||||
if (actionItem.id == R.id.mark_read_batch) {
|
||||
confirmationString = R.string.multi_select_mark_played_confirmation
|
||||
|
@ -234,34 +227,32 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
return root
|
||||
}
|
||||
|
||||
private fun performMultiSelectAction(actionItemId: Int) {
|
||||
@UnstableApi private fun performMultiSelectAction(actionItemId: Int) {
|
||||
val handler =
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItemId)
|
||||
Completable.fromAction {
|
||||
if (listAdapter != null) {
|
||||
handler.handleAction(listAdapter!!.selectedItems.filterIsInstance<FeedItem>())
|
||||
if (listAdapter!!.shouldSelectLazyLoadedItems()) {
|
||||
var applyPage = page + 1
|
||||
var nextPage: List<FeedItem>
|
||||
do {
|
||||
nextPage = loadMoreData(applyPage)
|
||||
handler.handleAction(nextPage)
|
||||
applyPage++
|
||||
} while (nextPage.size == EPISODES_PER_PAGE)
|
||||
}
|
||||
handler.handleAction(listAdapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
if (listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
var applyPage = page + 1
|
||||
var nextPage: List<FeedItem>
|
||||
do {
|
||||
nextPage = loadMoreData(applyPage)
|
||||
handler.handleAction(nextPage)
|
||||
applyPage++
|
||||
} while (nextPage.size == EPISODES_PER_PAGE)
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ listAdapter?.endSelectMode() },
|
||||
.subscribe({ listAdapter.endSelectMode() },
|
||||
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
private fun setupLoadMoreScrollListener() {
|
||||
recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, deltaX: Int, deltaY: Int) {
|
||||
super.onScrolled(view, deltaX, deltaY)
|
||||
if (!isLoadingMore && hasMoreItems && recyclerView!!.isScrolledToBottom) {
|
||||
if (!isLoadingMore && hasMoreItems && recyclerView.isScrolledToBottom) {
|
||||
/* The end of the list has been reached. Load more data. */
|
||||
page++
|
||||
loadMoreItems()
|
||||
|
@ -272,12 +263,11 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
}
|
||||
|
||||
private fun loadMoreItems() {
|
||||
if (disposable != null) {
|
||||
disposable?.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
isLoadingMore = true
|
||||
listAdapter?.setDummyViews(1)
|
||||
listAdapter?.notifyItemInserted(listAdapter!!.itemCount - 1)
|
||||
listAdapter.setDummyViews(1)
|
||||
listAdapter.notifyItemInserted(listAdapter.itemCount - 1)
|
||||
disposable = Observable.fromCallable { loadMoreData(page) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -287,33 +277,33 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
hasMoreItems = false
|
||||
}
|
||||
episodes.addAll(data)
|
||||
listAdapter?.setDummyViews(0)
|
||||
listAdapter?.updateItems(episodes)
|
||||
if (listAdapter != null && listAdapter!!.shouldSelectLazyLoadedItems()) {
|
||||
listAdapter!!.setSelected(episodes.size - data.size, episodes.size, true)
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(episodes)
|
||||
if (listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
listAdapter.setSelected(episodes.size - data.size, episodes.size, true)
|
||||
}
|
||||
}, { error: Throwable? ->
|
||||
listAdapter?.setDummyViews(0)
|
||||
listAdapter?.updateItems(emptyList())
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
}, {
|
||||
// Make sure to not always load 2 pages at once
|
||||
recyclerView?.post { isLoadingMore = false }
|
||||
recyclerView.post { isLoadingMore = false }
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
listAdapter?.endSelectMode()
|
||||
listAdapter.endSelectMode()
|
||||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
speedDialView?.visibility = View.VISIBLE
|
||||
speedDialView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialView?.close()
|
||||
speedDialView?.visibility = View.GONE
|
||||
speedDialView.close()
|
||||
speedDialView.visibility = View.GONE
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -325,9 +315,9 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
episodes.removeAt(pos)
|
||||
if (getFilter().matches(item)) {
|
||||
episodes.add(pos, item)
|
||||
listAdapter?.notifyItemChangedCompat(pos)
|
||||
listAdapter.notifyItemChangedCompat(pos)
|
||||
} else {
|
||||
listAdapter?.notifyItemRemoved(pos)
|
||||
listAdapter.notifyItemRemoved(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,11 +325,9 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
if (listAdapter == null) return
|
||||
for (i in 0 until listAdapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder =
|
||||
recyclerView?.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
for (i in 0 until listAdapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder = recyclerView.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder
|
||||
if (holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
}
|
||||
|
@ -352,8 +340,8 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
return
|
||||
}
|
||||
when (event.keyCode) {
|
||||
KeyEvent.KEYCODE_T -> recyclerView?.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> recyclerView?.smoothScrollToPosition(listAdapter?.itemCount ?: (0 - 1))
|
||||
KeyEvent.KEYCODE_T -> recyclerView.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> recyclerView.smoothScrollToPosition(listAdapter.itemCount)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +351,7 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(episodes, downloadUrl)
|
||||
if (pos >= 0) {
|
||||
listAdapter?.notifyItemChangedCompat(pos)
|
||||
listAdapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,17 +385,17 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
val restoreScrollPosition = episodes.isEmpty()
|
||||
episodes = data.first
|
||||
hasMoreItems = !(page == 1 && episodes.size < EPISODES_PER_PAGE)
|
||||
progressBar?.visibility = View.GONE
|
||||
listAdapter?.setDummyViews(0)
|
||||
listAdapter?.updateItems(episodes)
|
||||
listAdapter?.setTotalNumberOfItems(data.second)
|
||||
if (restoreScrollPosition && getPrefName() != null) {
|
||||
recyclerView?.restoreScrollPosition(getPrefName())
|
||||
progressBar.visibility = View.GONE
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(episodes)
|
||||
listAdapter.setTotalNumberOfItems(data.second)
|
||||
if (restoreScrollPosition) {
|
||||
recyclerView.restoreScrollPosition(getPrefName())
|
||||
}
|
||||
updateToolbar()
|
||||
}, { error: Throwable? ->
|
||||
listAdapter?.setDummyViews(0)
|
||||
listAdapter?.updateItems(emptyList())
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
|
@ -429,7 +417,7 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedUpdateRunningEvent) {
|
||||
swipeRefreshLayout?.isRefreshing = event.isFeedUpdateRunning
|
||||
swipeRefreshLayout.isRefreshing = event.isFeedUpdateRunning
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
|
|
@ -37,11 +37,12 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
* Fragment which is supposed to be displayed outside of the MediaplayerActivity.
|
||||
*/
|
||||
class ExternalPlayerFragment : Fragment() {
|
||||
private var imgvCover: ImageView? = null
|
||||
private var txtvTitle: TextView? = null
|
||||
private var butPlay: PlayButton? = null
|
||||
private var feedName: TextView? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private lateinit var imgvCover: ImageView
|
||||
private lateinit var txtvTitle: TextView
|
||||
private lateinit var butPlay: PlayButton
|
||||
private lateinit var feedName: TextView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private var controller: PlaybackController? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
|
@ -73,7 +74,7 @@ class ExternalPlayerFragment : Fragment() {
|
|||
@UnstableApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
butPlay!!.setOnClickListener { v: View? ->
|
||||
butPlay.setOnClickListener { v: View? ->
|
||||
if (controller == null) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
@ -91,7 +92,7 @@ class ExternalPlayerFragment : Fragment() {
|
|||
private fun setupPlaybackController(): PlaybackController {
|
||||
return object : PlaybackController(activity) {
|
||||
override fun updatePlayButtonShowsPlay(showPlay: Boolean) {
|
||||
butPlay!!.setIsShowPlay(showPlay)
|
||||
butPlay.setIsShowPlay(showPlay)
|
||||
}
|
||||
|
||||
override fun loadMediaInfo() {
|
||||
|
@ -116,10 +117,9 @@ class ExternalPlayerFragment : Fragment() {
|
|||
@UnstableApi
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (controller != null) {
|
||||
controller!!.release()
|
||||
controller = null
|
||||
}
|
||||
controller?.release()
|
||||
controller = null
|
||||
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
|
@ -132,10 +132,10 @@ class ExternalPlayerFragment : Fragment() {
|
|||
|| controller!!.duration == Playable.INVALID_TIME) {
|
||||
return
|
||||
}
|
||||
progressBar!!.progress = (controller!!.position.toDouble() / controller!!.duration * 100).toInt()
|
||||
progressBar.progress = (controller!!.position.toDouble() / controller!!.duration * 100).toInt()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPlaybackServiceChanged(event: PlaybackServiceEvent) {
|
||||
if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) {
|
||||
(activity as MainActivity).setPlayerVisible(false)
|
||||
|
@ -145,17 +145,14 @@ class ExternalPlayerFragment : Fragment() {
|
|||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "Fragment is about to be destroyed")
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (controller != null) {
|
||||
controller!!.pause()
|
||||
}
|
||||
controller?.pause()
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
|
@ -166,9 +163,7 @@ class ExternalPlayerFragment : Fragment() {
|
|||
return
|
||||
}
|
||||
|
||||
if (disposable != null) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable?.dispose()
|
||||
disposable = Maybe.fromCallable<Playable?> { controller!!.getMedia() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -183,8 +178,8 @@ class ExternalPlayerFragment : Fragment() {
|
|||
return
|
||||
}
|
||||
(activity as MainActivity).setPlayerVisible(true)
|
||||
txtvTitle!!.text = media.getEpisodeTitle()
|
||||
feedName!!.text = media.getFeedTitle()
|
||||
txtvTitle.text = media.getEpisodeTitle()
|
||||
feedName.text = media.getFeedTitle()
|
||||
onPositionObserverUpdate(PlaybackPositionEvent(media.getPosition(), media.getDuration()))
|
||||
|
||||
val options = RequestOptions()
|
||||
|
@ -199,13 +194,13 @@ class ExternalPlayerFragment : Fragment() {
|
|||
.load(getFallbackImageLocation(media))
|
||||
.apply(options))
|
||||
.apply(options)
|
||||
.into(imgvCover!!)
|
||||
.into(imgvCover)
|
||||
|
||||
if (controller != null && controller!!.isPlayingVideoLocally) {
|
||||
(activity as MainActivity).bottomSheet?.setLocked(true)
|
||||
(activity as MainActivity).bottomSheet?.setState(BottomSheetBehavior.STATE_COLLAPSED)
|
||||
} else {
|
||||
butPlay!!.visibility = View.VISIBLE
|
||||
butPlay.visibility = View.VISIBLE
|
||||
(activity as MainActivity).bottomSheet?.setLocked(false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,17 +62,18 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
private var feed: Feed? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var imgvCover: ImageView? = null
|
||||
private var txtvTitle: TextView? = null
|
||||
private var txtvDescription: TextView? = null
|
||||
private var txtvFundingUrl: TextView? = null
|
||||
private var lblSupport: TextView? = null
|
||||
private var txtvUrl: TextView? = null
|
||||
private var txtvAuthorHeader: TextView? = null
|
||||
private var imgvBackground: ImageView? = null
|
||||
private var infoContainer: View? = null
|
||||
private var header: View? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
|
||||
private lateinit var imgvCover: ImageView
|
||||
private lateinit var txtvTitle: TextView
|
||||
private lateinit var txtvDescription: TextView
|
||||
private lateinit var txtvFundingUrl: TextView
|
||||
private lateinit var lblSupport: TextView
|
||||
private lateinit var txtvUrl: TextView
|
||||
private lateinit var txtvAuthorHeader: TextView
|
||||
private lateinit var imgvBackground: ImageView
|
||||
private lateinit var infoContainer: View
|
||||
private lateinit var header: View
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
private val copyUrlToClipboard = View.OnClickListener {
|
||||
if (feed != null && feed!!.download_url != null) {
|
||||
|
@ -91,28 +92,26 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
): View {
|
||||
val root: View = inflater.inflate(R.layout.feedinfo, null)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.title = ""
|
||||
toolbar?.inflateMenu(R.menu.feedinfo)
|
||||
toolbar?.setNavigationOnClickListener { v: View? -> parentFragmentManager.popBackStack() }
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar.title = ""
|
||||
toolbar.inflateMenu(R.menu.feedinfo)
|
||||
toolbar.setNavigationOnClickListener { v: View? -> parentFragmentManager.popBackStack() }
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
refreshToolbarState()
|
||||
|
||||
val appBar: AppBarLayout = root.findViewById(R.id.appBar)
|
||||
val collapsingToolbar: CollapsingToolbarLayout =
|
||||
root.findViewById(R.id.collapsing_toolbar)
|
||||
if (toolbar != null) {
|
||||
val iconTintManager: ToolbarIconTintManager =
|
||||
object : ToolbarIconTintManager(requireContext(), toolbar!!, collapsingToolbar) {
|
||||
override fun doTint(themedContext: Context?) {
|
||||
toolbar!!.menu.findItem(R.id.visit_website_item)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext!!, R.drawable.ic_web))
|
||||
toolbar!!.menu.findItem(R.id.share_item)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext, R.drawable.ic_share))
|
||||
}
|
||||
val collapsingToolbar: CollapsingToolbarLayout = root.findViewById(R.id.collapsing_toolbar)
|
||||
val iconTintManager: ToolbarIconTintManager =
|
||||
object : ToolbarIconTintManager(requireContext(), toolbar, collapsingToolbar) {
|
||||
override fun doTint(themedContext: Context?) {
|
||||
toolbar.menu.findItem(R.id.visit_website_item)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext!!, R.drawable.ic_web))
|
||||
toolbar.menu.findItem(R.id.share_item)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext, R.drawable.ic_share))
|
||||
}
|
||||
iconTintManager.updateTint()
|
||||
appBar.addOnOffsetChangedListener(iconTintManager)
|
||||
}
|
||||
}
|
||||
iconTintManager.updateTint()
|
||||
appBar.addOnOffsetChangedListener(iconTintManager)
|
||||
|
||||
imgvCover = root.findViewById(R.id.imgvCover)
|
||||
txtvTitle = root.findViewById(R.id.txtvTitle)
|
||||
txtvAuthorHeader = root.findViewById(R.id.txtvAuthor)
|
||||
|
@ -123,14 +122,14 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
root.findViewById<View>(R.id.butShowSettings).visibility = View.INVISIBLE
|
||||
root.findViewById<View>(R.id.butFilter).visibility = View.INVISIBLE
|
||||
// https://github.com/bumptech/glide/issues/529
|
||||
imgvBackground?.colorFilter = LightingColorFilter(-0x7d7d7e, 0x000000)
|
||||
imgvBackground.colorFilter = LightingColorFilter(-0x7d7d7e, 0x000000)
|
||||
|
||||
txtvDescription = root.findViewById(R.id.txtvDescription)
|
||||
txtvUrl = root.findViewById(R.id.txtvUrl)
|
||||
lblSupport = root.findViewById(R.id.lblSupport)
|
||||
txtvFundingUrl = root.findViewById(R.id.txtvFundingUrl)
|
||||
|
||||
txtvUrl?.setOnClickListener(copyUrlToClipboard)
|
||||
txtvUrl.setOnClickListener(copyUrlToClipboard)
|
||||
|
||||
val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
parentFragmentManager.beginTransaction().replace(R.id.statisticsFragmentContainer,
|
||||
|
@ -165,13 +164,10 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
if (header == null || infoContainer == null) {
|
||||
return
|
||||
}
|
||||
val horizontalSpacing = resources.getDimension(R.dimen.additional_horizontal_spacing).toInt()
|
||||
header!!.setPadding(horizontalSpacing, header!!.paddingTop, horizontalSpacing, header!!.paddingBottom)
|
||||
infoContainer!!.setPadding(horizontalSpacing, infoContainer!!.paddingTop,
|
||||
horizontalSpacing, infoContainer!!.paddingBottom)
|
||||
header.setPadding(horizontalSpacing, header.paddingTop, horizontalSpacing, header.paddingBottom)
|
||||
infoContainer.setPadding(horizontalSpacing, infoContainer.paddingTop,
|
||||
horizontalSpacing, infoContainer.paddingBottom)
|
||||
}
|
||||
|
||||
private fun showFeed() {
|
||||
|
@ -179,42 +175,42 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
Log.d(TAG, "Language is " + feed!!.language)
|
||||
Log.d(TAG, "Author is " + feed!!.author)
|
||||
Log.d(TAG, "URL is " + feed!!.download_url)
|
||||
if (imgvCover != null) Glide.with(this)
|
||||
Glide.with(this)
|
||||
.load(feed!!.imageUrl)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.color.light_gray)
|
||||
.error(R.color.light_gray)
|
||||
.fitCenter()
|
||||
.dontAnimate())
|
||||
.into(imgvCover!!)
|
||||
if (imgvBackground != null) Glide.with(this)
|
||||
.into(imgvCover)
|
||||
Glide.with(this)
|
||||
.load(feed!!.imageUrl)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.color.image_readability_tint)
|
||||
.error(R.color.image_readability_tint)
|
||||
.transform(FastBlurTransformation())
|
||||
.dontAnimate())
|
||||
.into(imgvBackground!!)
|
||||
.into(imgvBackground)
|
||||
|
||||
txtvTitle?.text = feed!!.title
|
||||
txtvTitle?.setMaxLines(6)
|
||||
txtvTitle.text = feed!!.title
|
||||
txtvTitle.setMaxLines(6)
|
||||
|
||||
val description: String = HtmlToPlainText.getPlainText(feed!!.description)?:""
|
||||
|
||||
txtvDescription?.text = description
|
||||
txtvDescription.text = description
|
||||
|
||||
if (!TextUtils.isEmpty(feed!!.author)) {
|
||||
txtvAuthorHeader?.text = feed!!.author
|
||||
if (!feed!!.author.isNullOrEmpty()) {
|
||||
txtvAuthorHeader.text = feed!!.author
|
||||
}
|
||||
|
||||
txtvUrl?.text = feed!!.download_url
|
||||
txtvUrl?.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0)
|
||||
txtvUrl.text = feed!!.download_url
|
||||
txtvUrl.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0)
|
||||
|
||||
if (feed!!.paymentLinks.isEmpty()) {
|
||||
lblSupport?.visibility = View.GONE
|
||||
txtvFundingUrl?.visibility = View.GONE
|
||||
lblSupport.visibility = View.GONE
|
||||
txtvFundingUrl.visibility = View.GONE
|
||||
} else {
|
||||
lblSupport?.visibility = View.VISIBLE
|
||||
lblSupport.visibility = View.VISIBLE
|
||||
val fundingList: ArrayList<FeedFunding> = feed!!.paymentLinks
|
||||
|
||||
// Filter for duplicates, but keep items in the order that they have in the feed.
|
||||
|
@ -238,7 +234,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
str.append("\n")
|
||||
}
|
||||
str = StringBuilder(StringUtils.trim(str.toString()))
|
||||
txtvFundingUrl?.text = str.toString()
|
||||
txtvFundingUrl.text = str.toString()
|
||||
}
|
||||
|
||||
refreshToolbarState()
|
||||
|
@ -250,12 +246,12 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
private fun refreshToolbarState() {
|
||||
toolbar?.menu?.findItem(R.id.reconnect_local_folder)?.setVisible(feed != null && feed!!.isLocalFeed)
|
||||
toolbar?.menu?.findItem(R.id.share_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
toolbar?.menu?.findItem(R.id.visit_website_item)
|
||||
toolbar.menu?.findItem(R.id.reconnect_local_folder)?.setVisible(feed != null && feed!!.isLocalFeed)
|
||||
toolbar.menu?.findItem(R.id.share_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
toolbar.menu?.findItem(R.id.visit_website_item)
|
||||
?.setVisible(feed != null && feed!!.link != null && IntentUtils.isCallable(requireContext(),
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(feed!!.link))))
|
||||
toolbar?.menu?.findItem(R.id.edit_feed_url_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
toolbar.menu?.findItem(R.id.edit_feed_url_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
|
@ -264,33 +260,39 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
R.string.please_wait_for_data, Toast.LENGTH_LONG)
|
||||
return false
|
||||
}
|
||||
if (item.itemId == R.id.visit_website_item) {
|
||||
if (feed!!.link != null) IntentUtils.openInBrowser(requireContext(), feed!!.link!!)
|
||||
} else if (item.itemId == R.id.share_item) {
|
||||
ShareUtils.shareFeedLink(requireContext(), feed!!)
|
||||
} else if (item.itemId == R.id.reconnect_local_folder) {
|
||||
val alert = MaterialAlertDialogBuilder(requireContext())
|
||||
alert.setMessage(R.string.reconnect_local_folder_warning)
|
||||
alert.setPositiveButton(string.ok
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
try {
|
||||
addLocalFolderLauncher.launch(null)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(TAG, "No activity found. Should never happen...")
|
||||
}
|
||||
when (item.itemId) {
|
||||
R.id.visit_website_item -> {
|
||||
if (feed!!.link != null) IntentUtils.openInBrowser(requireContext(), feed!!.link!!)
|
||||
}
|
||||
alert.setNegativeButton(string.cancel, null)
|
||||
alert.show()
|
||||
} else if (item.itemId == R.id.edit_feed_url_item) {
|
||||
object : EditUrlSettingsDialog(activity as Activity, feed!!) {
|
||||
override fun setUrl(url: String?) {
|
||||
feed!!.download_url = url
|
||||
txtvUrl?.text = feed!!.download_url
|
||||
txtvUrl?.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0)
|
||||
R.id.share_item -> {
|
||||
ShareUtils.shareFeedLink(requireContext(), feed!!)
|
||||
}
|
||||
R.id.reconnect_local_folder -> {
|
||||
val alert = MaterialAlertDialogBuilder(requireContext())
|
||||
alert.setMessage(R.string.reconnect_local_folder_warning)
|
||||
alert.setPositiveButton(string.ok
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
try {
|
||||
addLocalFolderLauncher.launch(null)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(TAG, "No activity found. Should never happen...")
|
||||
}
|
||||
}
|
||||
}.show()
|
||||
} else {
|
||||
return false
|
||||
alert.setNegativeButton(string.cancel, null)
|
||||
alert.show()
|
||||
}
|
||||
R.id.edit_feed_url_item -> {
|
||||
object : EditUrlSettingsDialog(activity as Activity, feed!!) {
|
||||
override fun setUrl(url: String?) {
|
||||
feed!!.download_url = url
|
||||
txtvUrl.text = feed!!.download_url
|
||||
txtvUrl.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -69,16 +69,18 @@ import java.util.concurrent.ExecutionException
|
|||
*/
|
||||
class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolbar.OnMenuItemClickListener,
|
||||
SelectableAdapter.OnSelectModeListener {
|
||||
private var adapter: FeedItemListAdapter? = null
|
||||
private var swipeActions: SwipeActions? = null
|
||||
private var nextPageLoader: MoreContentListFooterUtil? = null
|
||||
|
||||
private lateinit var adapter: FeedItemListAdapter
|
||||
private lateinit var swipeActions: SwipeActions
|
||||
private lateinit var viewBinding: FeedItemListFragmentBinding
|
||||
private lateinit var speedDialBinding: MultiSelectSpeedDialBinding
|
||||
private lateinit var nextPageLoader: MoreContentListFooterUtil
|
||||
|
||||
private var displayUpArrow = false
|
||||
private var feedID: Long = 0
|
||||
private var feed: Feed? = null
|
||||
private var headerCreated = false
|
||||
private var disposable: Disposable? = null
|
||||
private var viewBinding: FeedItemListFragmentBinding? = null
|
||||
private var speedDialBinding: MultiSelectSpeedDialBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -91,65 +93,65 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = FeedItemListFragmentBinding.inflate(inflater)
|
||||
speedDialBinding = MultiSelectSpeedDialBinding.bind(viewBinding!!.root)
|
||||
viewBinding!!.toolbar.inflateMenu(R.menu.feedlist)
|
||||
viewBinding!!.toolbar.setOnMenuItemClickListener(this)
|
||||
viewBinding!!.toolbar.setOnLongClickListener { v: View? ->
|
||||
viewBinding!!.recyclerView.scrollToPosition(5)
|
||||
viewBinding!!.recyclerView.post { viewBinding!!.recyclerView.smoothScrollToPosition(0) }
|
||||
viewBinding!!.appBar.setExpanded(true)
|
||||
speedDialBinding = MultiSelectSpeedDialBinding.bind(viewBinding.root)
|
||||
viewBinding.toolbar.inflateMenu(R.menu.feedlist)
|
||||
viewBinding.toolbar.setOnMenuItemClickListener(this)
|
||||
viewBinding.toolbar.setOnLongClickListener { v: View? ->
|
||||
viewBinding.recyclerView.scrollToPosition(5)
|
||||
viewBinding.recyclerView.post { viewBinding.recyclerView.smoothScrollToPosition(0) }
|
||||
viewBinding.appBar.setExpanded(true)
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
(activity as MainActivity).setupToolbarToggle(viewBinding!!.toolbar, displayUpArrow)
|
||||
(activity as MainActivity).setupToolbarToggle(viewBinding.toolbar, displayUpArrow)
|
||||
updateToolbar()
|
||||
|
||||
viewBinding!!.recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
viewBinding.recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
adapter = FeedItemListAdapter(activity as MainActivity)
|
||||
adapter?.setOnSelectModeListener(this)
|
||||
viewBinding!!.recyclerView.adapter = adapter
|
||||
swipeActions = SwipeActions(this, TAG).attachTo(viewBinding!!.recyclerView)
|
||||
viewBinding!!.progressBar.visibility = View.VISIBLE
|
||||
adapter.setOnSelectModeListener(this)
|
||||
viewBinding.recyclerView.adapter = adapter
|
||||
swipeActions = SwipeActions(this, TAG).attachTo(viewBinding.recyclerView)
|
||||
viewBinding.progressBar.visibility = View.VISIBLE
|
||||
|
||||
val iconTintManager: ToolbarIconTintManager = object : ToolbarIconTintManager(
|
||||
requireContext(), viewBinding!!.toolbar, viewBinding!!.collapsingToolbar) {
|
||||
requireContext(), viewBinding.toolbar, viewBinding.collapsingToolbar) {
|
||||
override fun doTint(themedContext: Context?) {
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.refresh_item)
|
||||
viewBinding.toolbar.menu.findItem(R.id.refresh_item)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext!!, R.drawable.ic_refresh))
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.action_search)
|
||||
viewBinding.toolbar.menu.findItem(R.id.action_search)
|
||||
.setIcon(AppCompatResources.getDrawable(themedContext, R.drawable.ic_search))
|
||||
}
|
||||
}
|
||||
iconTintManager.updateTint()
|
||||
viewBinding!!.appBar.addOnOffsetChangedListener(iconTintManager)
|
||||
viewBinding.appBar.addOnOffsetChangedListener(iconTintManager)
|
||||
|
||||
nextPageLoader = MoreContentListFooterUtil(viewBinding!!.moreContent.moreContentListFooter)
|
||||
nextPageLoader?.setClickListener(object : MoreContentListFooterUtil.Listener {
|
||||
nextPageLoader = MoreContentListFooterUtil(viewBinding.moreContent.moreContentListFooter)
|
||||
nextPageLoader.setClickListener(object : MoreContentListFooterUtil.Listener {
|
||||
override fun onClick() {
|
||||
if (feed != null) {
|
||||
FeedUpdateManager.runOnce(context, feed, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
viewBinding!!.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
viewBinding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, deltaX: Int, deltaY: Int) {
|
||||
super.onScrolled(view, deltaX, deltaY)
|
||||
val hasMorePages = feed != null && feed!!.isPaged && feed!!.nextPageLink != null
|
||||
val pageLoaderVisible = viewBinding!!.recyclerView.isScrolledToBottom && hasMorePages
|
||||
nextPageLoader?.root?.visibility = if (pageLoaderVisible) View.VISIBLE else View.GONE
|
||||
viewBinding!!.recyclerView.setPadding(
|
||||
viewBinding!!.recyclerView.paddingLeft, 0, viewBinding!!.recyclerView.paddingRight,
|
||||
if (pageLoaderVisible && nextPageLoader != null) nextPageLoader!!.root.measuredHeight else 0)
|
||||
val pageLoaderVisible = viewBinding.recyclerView.isScrolledToBottom && hasMorePages
|
||||
nextPageLoader.root.visibility = if (pageLoaderVisible) View.VISIBLE else View.GONE
|
||||
viewBinding.recyclerView.setPadding(
|
||||
viewBinding.recyclerView.paddingLeft, 0, viewBinding.recyclerView.paddingRight,
|
||||
if (pageLoaderVisible) nextPageLoader.root.measuredHeight else 0)
|
||||
}
|
||||
})
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
viewBinding!!.swipeRefresh.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
viewBinding!!.swipeRefresh.setOnRefreshListener {
|
||||
viewBinding.swipeRefresh.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
viewBinding.swipeRefresh.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext(),
|
||||
feed)
|
||||
}
|
||||
|
@ -157,28 +159,28 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
loadItems()
|
||||
|
||||
// Init action UI (via a FAB Speed Dial)
|
||||
speedDialBinding!!.fabSD.overlayLayout = speedDialBinding!!.fabSDOverlay
|
||||
speedDialBinding!!.fabSD.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialBinding!!.fabSD.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
speedDialBinding.fabSD.overlayLayout = speedDialBinding.fabSDOverlay
|
||||
speedDialBinding.fabSD.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialBinding.fabSD.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && adapter!!.selectedCount == 0) {
|
||||
if (open && adapter.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
speedDialBinding!!.fabSD.close()
|
||||
speedDialBinding.fabSD.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialBinding!!.fabSD.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
speedDialBinding.fabSD.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItem.id)
|
||||
.handleAction(adapter!!.selectedItems.filterIsInstance<FeedItem>())
|
||||
adapter?.endSelectMode()
|
||||
.handleAction(adapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
adapter.endSelectMode()
|
||||
true
|
||||
}
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -186,7 +188,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
adapter?.endSelectMode()
|
||||
adapter.endSelectMode()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -198,22 +200,22 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (feed == null) {
|
||||
return
|
||||
}
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.visit_website_item).setVisible(feed!!.link != null)
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.refresh_complete_item).setVisible(feed!!.isPaged)
|
||||
viewBinding.toolbar.menu.findItem(R.id.visit_website_item).setVisible(feed!!.link != null)
|
||||
viewBinding.toolbar.menu.findItem(R.id.refresh_complete_item).setVisible(feed!!.isPaged)
|
||||
if (StringUtils.isBlank(feed!!.link)) {
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.visit_website_item).setVisible(false)
|
||||
viewBinding.toolbar.menu.findItem(R.id.visit_website_item).setVisible(false)
|
||||
}
|
||||
if (feed!!.isLocalFeed) {
|
||||
viewBinding!!.toolbar.menu.findItem(R.id.share_item).setVisible(false)
|
||||
viewBinding.toolbar.menu.findItem(R.id.share_item).setVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val horizontalSpacing = resources.getDimension(R.dimen.additional_horizontal_spacing).toInt()
|
||||
viewBinding!!.header.headerContainer.setPadding(
|
||||
horizontalSpacing, viewBinding!!.header.headerContainer.paddingTop,
|
||||
horizontalSpacing, viewBinding!!.header.headerContainer.paddingBottom)
|
||||
viewBinding.header.headerContainer.setPadding(
|
||||
horizontalSpacing, viewBinding.header.headerContainer.paddingTop,
|
||||
horizontalSpacing, viewBinding.header.headerContainer.paddingBottom)
|
||||
}
|
||||
|
||||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
|
@ -270,12 +272,12 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
val selectedItem: FeedItem? = adapter!!.longPressedItem
|
||||
val selectedItem: FeedItem? = adapter.longPressedItem
|
||||
if (selectedItem == null) {
|
||||
Log.i(TAG, "Selected item at current position was null, ignoring selection")
|
||||
return super.onContextItemSelected(item)
|
||||
}
|
||||
if (adapter!!.onContextItemSelected(item)) {
|
||||
if (adapter.onContextItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
return FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem)
|
||||
|
@ -311,7 +313,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (pos >= 0) {
|
||||
feed?.items?.removeAt(pos)
|
||||
feed?.items?.add(pos, item)
|
||||
adapter?.notifyItemChangedCompat(pos)
|
||||
adapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
@ -325,16 +327,16 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(feed!!.items, downloadUrl)
|
||||
if (pos >= 0) {
|
||||
adapter?.notifyItemChangedCompat(pos)
|
||||
adapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
for (i in 0 until adapter!!.itemCount) {
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? =
|
||||
viewBinding!!.recyclerView.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder?
|
||||
viewBinding.recyclerView.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder?
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
|
@ -355,19 +357,19 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
swipeActions?.detach()
|
||||
swipeActions.detach()
|
||||
if (feed != null && feed!!.isLocalFeed) {
|
||||
speedDialBinding!!.fabSD.removeActionItemById(R.id.download_batch)
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.download_batch)
|
||||
}
|
||||
speedDialBinding!!.fabSD.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialBinding!!.fabSD.visibility = View.VISIBLE
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialBinding.fabSD.visibility = View.VISIBLE
|
||||
updateToolbar()
|
||||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialBinding!!.fabSD.close()
|
||||
speedDialBinding!!.fabSD.visibility = View.GONE
|
||||
swipeActions?.attachTo(viewBinding!!.recyclerView)
|
||||
speedDialBinding.fabSD.close()
|
||||
speedDialBinding.fabSD.visibility = View.GONE
|
||||
swipeActions.attachTo(viewBinding.recyclerView)
|
||||
}
|
||||
|
||||
@UnstableApi private fun updateUi() {
|
||||
|
@ -396,51 +398,51 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedUpdateRunningEvent) {
|
||||
nextPageLoader?.setLoadingState(event.isFeedUpdateRunning)
|
||||
nextPageLoader.setLoadingState(event.isFeedUpdateRunning)
|
||||
if (!event.isFeedUpdateRunning) {
|
||||
nextPageLoader?.root?.visibility = View.GONE
|
||||
nextPageLoader.root.visibility = View.GONE
|
||||
}
|
||||
viewBinding!!.swipeRefresh.isRefreshing = event.isFeedUpdateRunning
|
||||
viewBinding.swipeRefresh.isRefreshing = event.isFeedUpdateRunning
|
||||
}
|
||||
|
||||
@UnstableApi private fun refreshHeaderView() {
|
||||
setupHeaderView()
|
||||
if (viewBinding == null || feed == null) {
|
||||
if (feed == null) {
|
||||
Log.e(TAG, "Unable to refresh header view")
|
||||
return
|
||||
}
|
||||
loadFeedImage()
|
||||
if (feed != null && feed!!.hasLastUpdateFailed()) {
|
||||
viewBinding!!.header.txtvFailure.visibility = View.VISIBLE
|
||||
if (feed!!.hasLastUpdateFailed()) {
|
||||
viewBinding.header.txtvFailure.visibility = View.VISIBLE
|
||||
} else {
|
||||
viewBinding!!.header.txtvFailure.visibility = View.GONE
|
||||
viewBinding.header.txtvFailure.visibility = View.GONE
|
||||
}
|
||||
if (feed != null && feed!!.preferences != null && !feed!!.preferences!!.keepUpdated) {
|
||||
viewBinding!!.header.txtvUpdatesDisabled.text = ("{md-pause-circle-outline} "
|
||||
if (feed!!.preferences != null && !feed!!.preferences!!.keepUpdated) {
|
||||
viewBinding.header.txtvUpdatesDisabled.text = ("{md-pause-circle-outline} "
|
||||
+ this.getString(R.string.updates_disabled_label))
|
||||
Iconify.addIcons(viewBinding!!.header.txtvUpdatesDisabled)
|
||||
viewBinding!!.header.txtvUpdatesDisabled.visibility = View.VISIBLE
|
||||
Iconify.addIcons(viewBinding.header.txtvUpdatesDisabled)
|
||||
viewBinding.header.txtvUpdatesDisabled.visibility = View.VISIBLE
|
||||
} else {
|
||||
viewBinding!!.header.txtvUpdatesDisabled.visibility = View.GONE
|
||||
viewBinding.header.txtvUpdatesDisabled.visibility = View.GONE
|
||||
}
|
||||
viewBinding!!.header.txtvTitle.text = feed!!.title
|
||||
viewBinding!!.header.txtvAuthor.text = feed!!.author
|
||||
viewBinding.header.txtvTitle.text = feed!!.title
|
||||
viewBinding.header.txtvAuthor.text = feed!!.author
|
||||
if (feed != null && feed!!.itemFilter != null) {
|
||||
val filter: FeedItemFilter? = feed!!.itemFilter
|
||||
if (filter != null && filter.values.isNotEmpty()) {
|
||||
viewBinding!!.header.txtvInformation.text = ("{md-info-outline} "
|
||||
viewBinding.header.txtvInformation.text = ("{md-info-outline} "
|
||||
+ this.getString(R.string.filtered_label))
|
||||
Iconify.addIcons(viewBinding!!.header.txtvInformation)
|
||||
viewBinding!!.header.txtvInformation.setOnClickListener { l: View? ->
|
||||
Iconify.addIcons(viewBinding.header.txtvInformation)
|
||||
viewBinding.header.txtvInformation.setOnClickListener { l: View? ->
|
||||
FeedItemFilterDialog.newInstance(feed!!).show(
|
||||
childFragmentManager, null)
|
||||
}
|
||||
viewBinding!!.header.txtvInformation.visibility = View.VISIBLE
|
||||
viewBinding.header.txtvInformation.visibility = View.VISIBLE
|
||||
} else {
|
||||
viewBinding!!.header.txtvInformation.visibility = View.GONE
|
||||
viewBinding.header.txtvInformation.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
viewBinding!!.header.txtvInformation.visibility = View.GONE
|
||||
viewBinding.header.txtvInformation.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,19 +452,19 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
|
||||
// https://github.com/bumptech/glide/issues/529
|
||||
viewBinding!!.imgvBackground.colorFilter = LightingColorFilter(-0x99999a, 0x000000)
|
||||
viewBinding!!.header.butShowInfo.setOnClickListener { v: View? -> showFeedInfo() }
|
||||
viewBinding!!.header.imgvCover.setOnClickListener { v: View? -> showFeedInfo() }
|
||||
viewBinding!!.header.butShowSettings.setOnClickListener { v: View? ->
|
||||
viewBinding.imgvBackground.colorFilter = LightingColorFilter(-0x99999a, 0x000000)
|
||||
viewBinding.header.butShowInfo.setOnClickListener { v: View? -> showFeedInfo() }
|
||||
viewBinding.header.imgvCover.setOnClickListener { v: View? -> showFeedInfo() }
|
||||
viewBinding.header.butShowSettings.setOnClickListener { v: View? ->
|
||||
if (feed != null) {
|
||||
val fragment = FeedSettingsFragment.newInstance(feed!!)
|
||||
(activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE)
|
||||
}
|
||||
}
|
||||
viewBinding!!.header.butFilter.setOnClickListener { v: View? ->
|
||||
viewBinding.header.butFilter.setOnClickListener { v: View? ->
|
||||
if (feed != null) FeedItemFilterDialog.newInstance(feed!!).show(childFragmentManager, null)
|
||||
}
|
||||
viewBinding!!.header.txtvFailure.setOnClickListener { v: View? -> showErrorDetails() }
|
||||
viewBinding.header.txtvFailure.setOnClickListener { v: View? -> showErrorDetails() }
|
||||
headerCreated = true
|
||||
}
|
||||
|
||||
|
@ -502,7 +504,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
.error(R.color.image_readability_tint)
|
||||
.transform(FastBlurTransformation())
|
||||
.dontAnimate())
|
||||
.into(viewBinding!!.imgvBackground)
|
||||
.into(viewBinding.imgvBackground)
|
||||
|
||||
Glide.with(this)
|
||||
.load(feed!!.imageUrl)
|
||||
|
@ -511,7 +513,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
.error(R.color.light_gray)
|
||||
.fitCenter()
|
||||
.dontAnimate())
|
||||
.into(viewBinding!!.header.imgvCover)
|
||||
.into(viewBinding.header.imgvCover)
|
||||
}
|
||||
|
||||
@UnstableApi private fun loadItems() {
|
||||
|
@ -524,17 +526,17 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
{ result: Feed? ->
|
||||
feed = result
|
||||
Log.d(TAG, "loadItems subscribe called ${feed?.title}")
|
||||
swipeActions?.setFilter(feed?.itemFilter)
|
||||
swipeActions.setFilter(feed?.itemFilter)
|
||||
refreshHeaderView()
|
||||
viewBinding!!.progressBar.visibility = View.GONE
|
||||
adapter?.setDummyViews(0)
|
||||
if (feed != null && feed!!.items.isNotEmpty()) adapter?.updateItems(feed!!.items)
|
||||
viewBinding.progressBar.visibility = View.GONE
|
||||
adapter.setDummyViews(0)
|
||||
if (feed != null && feed!!.items.isNotEmpty()) adapter.updateItems(feed!!.items)
|
||||
updateToolbar()
|
||||
}, { error: Throwable? ->
|
||||
feed = null
|
||||
refreshHeaderView()
|
||||
adapter?.setDummyViews(0)
|
||||
adapter?.updateItems(emptyList())
|
||||
adapter.setDummyViews(0)
|
||||
adapter.updateItems(emptyList())
|
||||
updateToolbar()
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
|
@ -560,8 +562,8 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
return
|
||||
}
|
||||
when (event.keyCode) {
|
||||
KeyEvent.KEYCODE_T -> viewBinding!!.recyclerView.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> viewBinding!!.recyclerView.smoothScrollToPosition(adapter!!.itemCount - 1)
|
||||
KeyEvent.KEYCODE_T -> viewBinding.recyclerView.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> viewBinding.recyclerView.smoothScrollToPosition(adapter.itemCount - 1)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -576,8 +578,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
}
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem ->
|
||||
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem ->
|
||||
this@FeedItemlistFragment.onContextItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ import ac.mdiq.podvinci.model.feed.FeedPreferences.AutoDeleteAction
|
|||
import ac.mdiq.podvinci.model.feed.FeedPreferences.NewEpisodesAction
|
||||
import ac.mdiq.podvinci.model.feed.VolumeAdaptionSetting
|
||||
import ac.mdiq.podvinci.storage.preferences.UserPreferences.isEnableAutodownload
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.MaybeOnSubscribe
|
||||
|
@ -100,7 +102,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
|
||||
var notificationPermissionDenied: Boolean = false
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult<String, Boolean>(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
@ -123,7 +125,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
return view
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
@OptIn(UnstableApi::class) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.feed_settings)
|
||||
// To prevent displaying partially loaded data
|
||||
findPreference<Preference>(PREF_SCREEN)!!.isVisible = false
|
||||
|
@ -181,7 +183,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
object : FeedPreferenceSkipDialog(context,
|
||||
feedPreferences!!.feedSkipIntro,
|
||||
feedPreferences!!.feedSkipEnding) {
|
||||
override fun onConfirmed(skipIntro: Int, skipEnding: Int) {
|
||||
@UnstableApi override fun onConfirmed(skipIntro: Int, skipEnding: Int) {
|
||||
feedPreferences!!.feedSkipIntro = skipIntro
|
||||
feedPreferences!!.feedSkipEnding = skipEnding
|
||||
DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
|
@ -195,7 +197,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupPlaybackSpeedPreference() {
|
||||
@UnstableApi private fun setupPlaybackSpeedPreference() {
|
||||
val feedPlaybackSpeedPreference = findPreference<Preference>(PREF_FEED_PLAYBACK_SPEED)
|
||||
feedPlaybackSpeedPreference!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener { preference: Preference ->
|
||||
|
@ -237,7 +239,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
findPreference<Preference>(PREF_EPISODE_FILTER)!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener { preference: Preference ->
|
||||
object : EpisodeFilterDialog(context, feedPreferences!!.filter) {
|
||||
override fun onConfirmed(filter: FeedFilter) {
|
||||
@UnstableApi override fun onConfirmed(filter: FeedFilter) {
|
||||
feedPreferences!!.filter = filter
|
||||
DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
}
|
||||
|
@ -253,7 +255,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
object : AuthenticationDialog(context,
|
||||
R.string.authentication_label, true,
|
||||
feedPreferences!!.username, feedPreferences!!.password) {
|
||||
override fun onConfirmed(username: String, password: String) {
|
||||
@UnstableApi override fun onConfirmed(username: String, password: String) {
|
||||
feedPreferences!!.username = username
|
||||
feedPreferences!!.password = password
|
||||
val setPreferencesFuture = DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
|
@ -274,7 +276,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupAutoDeletePreference() {
|
||||
@UnstableApi private fun setupAutoDeletePreference() {
|
||||
if (feedPreferences == null) return
|
||||
findPreference<Preference>(PREF_AUTO_DELETE)!!.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any? ->
|
||||
|
@ -310,7 +312,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupVolumeAdaptationPreferences() {
|
||||
@UnstableApi private fun setupVolumeAdaptationPreferences() {
|
||||
if (feedPreferences == null) return
|
||||
val volumeAdaptationPreference = findPreference<ListPreference>("volumeReduction")
|
||||
volumeAdaptationPreference!!.onPreferenceChangeListener =
|
||||
|
@ -348,7 +350,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupNewEpisodesAction() {
|
||||
@OptIn(UnstableApi::class) private fun setupNewEpisodesAction() {
|
||||
if (feedPreferences == null) return
|
||||
|
||||
findPreference<Preference>(PREF_NEW_EPISODES_ACTION)!!.onPreferenceChangeListener =
|
||||
|
@ -374,14 +376,14 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupKeepUpdatedPreference() {
|
||||
@OptIn(UnstableApi::class) private fun setupKeepUpdatedPreference() {
|
||||
if (feedPreferences == null) return
|
||||
val pref = findPreference<SwitchPreferenceCompat>("keepUpdated")
|
||||
|
||||
pref!!.isChecked = feedPreferences!!.keepUpdated
|
||||
pref.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
|
||||
val checked = newValue === java.lang.Boolean.TRUE
|
||||
val checked = newValue == true
|
||||
feedPreferences!!.keepUpdated = checked
|
||||
DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
pref.isChecked = checked
|
||||
|
@ -399,7 +401,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupAutoDownloadPreference() {
|
||||
@OptIn(UnstableApi::class) private fun setupAutoDownloadPreference() {
|
||||
if (feedPreferences == null) return
|
||||
val pref = findPreference<SwitchPreferenceCompat>("autoDownload")
|
||||
|
||||
|
@ -413,7 +415,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
|
||||
pref.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
|
||||
val checked = newValue === java.lang.Boolean.TRUE
|
||||
val checked = newValue == true
|
||||
feedPreferences!!.autoDownload = checked
|
||||
DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
updateAutoDownloadEnabled()
|
||||
|
@ -439,7 +441,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupEpisodeNotificationPreference() {
|
||||
@OptIn(UnstableApi::class) private fun setupEpisodeNotificationPreference() {
|
||||
val pref = findPreference<SwitchPreferenceCompat>("episodeNotification")
|
||||
|
||||
pref!!.isChecked = feedPreferences!!.showEpisodeNotification
|
||||
|
@ -451,7 +453,7 @@ class FeedSettingsFragment : Fragment() {
|
|||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
val checked = newValue === java.lang.Boolean.TRUE
|
||||
val checked = newValue == true
|
||||
feedPreferences!!.showEpisodeNotification = checked
|
||||
if (feedPreferences != null) DBWriter.setFeedPreferences(feedPreferences!!)
|
||||
pref.isChecked = checked
|
||||
|
|
|
@ -22,6 +22,7 @@ import ac.mdiq.podvinci.model.feed.FeedItem
|
|||
import ac.mdiq.podvinci.model.feed.FeedItemFilter
|
||||
import ac.mdiq.podvinci.model.feed.SortOrder
|
||||
import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
||||
import androidx.annotation.OptIn
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
/**
|
||||
|
@ -39,16 +40,16 @@ class InboxFragment : EpisodesListFragment() {
|
|||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState)
|
||||
toolbar?.inflateMenu(R.menu.inbox)
|
||||
toolbar?.setTitle(R.string.inbox_label)
|
||||
toolbar.inflateMenu(R.menu.inbox)
|
||||
toolbar.setTitle(R.string.inbox_label)
|
||||
prefs = requireActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE)
|
||||
updateToolbar()
|
||||
emptyView?.setIcon(R.drawable.ic_inbox)
|
||||
emptyView?.setTitle(R.string.no_inbox_head_label)
|
||||
emptyView?.setMessage(R.string.no_inbox_label)
|
||||
speedDialView?.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView?.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialView?.removeActionItemById(R.id.delete_batch)
|
||||
emptyView.setIcon(R.drawable.ic_inbox)
|
||||
emptyView.setTitle(R.string.no_inbox_head_label)
|
||||
emptyView.setMessage(R.string.no_inbox_label)
|
||||
speedDialView.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialView.removeActionItemById(R.id.delete_batch)
|
||||
return root
|
||||
}
|
||||
|
||||
|
@ -56,7 +57,7 @@ class InboxFragment : EpisodesListFragment() {
|
|||
return FeedItemFilter(FeedItemFilter.NEW)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
@OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
if (super.onOptionsItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
|
@ -88,7 +89,7 @@ class InboxFragment : EpisodesListFragment() {
|
|||
return DBReader.getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.NEW))
|
||||
}
|
||||
|
||||
private fun removeAllFromInbox() {
|
||||
@OptIn(UnstableApi::class) private fun removeAllFromInbox() {
|
||||
DBWriter.removeAllNewFlags()
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.removed_all_inbox_msg, Toast.LENGTH_SHORT)
|
||||
}
|
||||
|
@ -102,8 +103,7 @@ class InboxFragment : EpisodesListFragment() {
|
|||
val checkNeverAskAgain: CheckBox = view.findViewById(R.id.checkbox_do_not_show_again)
|
||||
builder.setView(view)
|
||||
|
||||
builder.setPositiveButton(R.string.confirm_label
|
||||
) { dialog: DialogInterface, which: Int ->
|
||||
builder.setPositiveButton(R.string.confirm_label) { dialog: DialogInterface, which: Int ->
|
||||
dialog.dismiss()
|
||||
removeAllFromInbox()
|
||||
prefs?.edit()?.putBoolean(PREF_DO_NOT_PROMPT_REMOVE_ALL_FROM_INBOX, checkNeverAskAgain.isChecked)
|
||||
|
|
|
@ -27,7 +27,7 @@ import io.reactivex.schedulers.Schedulers
|
|||
*/
|
||||
@UnstableApi
|
||||
class ItemDescriptionFragment : Fragment() {
|
||||
private var webvDescription: ShownotesWebView? = null
|
||||
private lateinit var webvDescription: ShownotesWebView
|
||||
private var webViewLoader: Disposable? = null
|
||||
private var controller: PlaybackController? = null
|
||||
|
||||
|
@ -35,41 +35,39 @@ class ItemDescriptionFragment : Fragment() {
|
|||
Log.d(TAG, "Creating view")
|
||||
val root = inflater.inflate(R.layout.item_description_fragment, container, false)
|
||||
webvDescription = root.findViewById(R.id.webview)
|
||||
webvDescription?.setTimecodeSelectedListener { time: Int? ->
|
||||
webvDescription.setTimecodeSelectedListener { time: Int? ->
|
||||
if (controller != null) {
|
||||
controller!!.seekTo(time!!)
|
||||
}
|
||||
}
|
||||
webvDescription?.setPageFinishedListener {
|
||||
webvDescription.setPageFinishedListener {
|
||||
// Restoring the scroll position might not always work
|
||||
webvDescription!!.postDelayed({ this@ItemDescriptionFragment.restoreFromPreference() }, 50)
|
||||
webvDescription.postDelayed({ this@ItemDescriptionFragment.restoreFromPreference() }, 50)
|
||||
}
|
||||
|
||||
root.addOnLayoutChangeListener(object : OnLayoutChangeListener {
|
||||
override fun onLayoutChange(v: View, left: Int, top: Int, right: Int,
|
||||
bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int
|
||||
) {
|
||||
if (root.measuredHeight != webvDescription?.minimumHeight) {
|
||||
webvDescription?.setMinimumHeight(root.measuredHeight)
|
||||
if (root.measuredHeight != webvDescription.minimumHeight) {
|
||||
webvDescription.setMinimumHeight(root.measuredHeight)
|
||||
}
|
||||
root.removeOnLayoutChangeListener(this)
|
||||
}
|
||||
})
|
||||
if (webvDescription != null) registerForContextMenu(webvDescription!!)
|
||||
registerForContextMenu(webvDescription)
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "Fragment destroyed")
|
||||
if (webvDescription != null) {
|
||||
webvDescription!!.removeAllViews()
|
||||
webvDescription!!.destroy()
|
||||
}
|
||||
webvDescription.removeAllViews()
|
||||
webvDescription.destroy()
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
return webvDescription!!.onContextItemSelected(item)
|
||||
return webvDescription.onContextItemSelected(item)
|
||||
}
|
||||
|
||||
@UnstableApi private fun load() {
|
||||
|
@ -85,11 +83,10 @@ class ItemDescriptionFragment : Fragment() {
|
|||
return@create
|
||||
}
|
||||
if (media is FeedMedia) {
|
||||
val feedMedia = media
|
||||
if (feedMedia.getItem() == null) {
|
||||
feedMedia.setItem(DBReader.getFeedItem(feedMedia.itemId))
|
||||
if (media.getItem() == null) {
|
||||
media.setItem(DBReader.getFeedItem(media.itemId))
|
||||
}
|
||||
if (feedMedia.getItem() != null) DBReader.loadDescriptionOfFeedItem(feedMedia.getItem()!!)
|
||||
if (media.getItem() != null) DBReader.loadDescriptionOfFeedItem(media.getItem()!!)
|
||||
}
|
||||
val shownotesCleaner = ShownotesCleaner(context, media.getDescription()?:"", media.getDuration())
|
||||
emitter.onSuccess(shownotesCleaner.processShownotes())
|
||||
|
@ -97,7 +94,7 @@ class ItemDescriptionFragment : Fragment() {
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ data: String? ->
|
||||
webvDescription!!.loadDataWithBaseURL("https://127.0.0.1", data!!, "text/html",
|
||||
webvDescription.loadDataWithBaseURL("https://127.0.0.1", data!!, "text/html",
|
||||
"utf-8", "about:blank")
|
||||
Log.d(TAG, "Webview loaded")
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
@ -112,9 +109,9 @@ class ItemDescriptionFragment : Fragment() {
|
|||
Log.d(TAG, "Saving preferences")
|
||||
val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
if (controller != null && controller!!.getMedia() != null && webvDescription != null) {
|
||||
Log.d(TAG, "Saving scroll position: " + webvDescription!!.scrollY)
|
||||
editor.putInt(PREF_SCROLL_Y, webvDescription!!.scrollY)
|
||||
if (controller?.getMedia() != null) {
|
||||
Log.d(TAG, "Saving scroll position: " + webvDescription.scrollY)
|
||||
editor.putInt(PREF_SCROLL_Y, webvDescription.scrollY)
|
||||
editor.putString(PREF_PLAYABLE_ID, controller!!.getMedia()!!.getIdentifier()
|
||||
.toString())
|
||||
} else {
|
||||
|
@ -132,9 +129,9 @@ class ItemDescriptionFragment : Fragment() {
|
|||
val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||
val id = prefs.getString(PREF_PLAYABLE_ID, "")
|
||||
val scrollY = prefs.getInt(PREF_SCROLL_Y, -1)
|
||||
if (controller != null && scrollY != -1 && controller!!.getMedia() != null && id == controller!!.getMedia()!!.getIdentifier().toString() && webvDescription != null) {
|
||||
if (controller != null && scrollY != -1 && controller!!.getMedia() != null && id == controller!!.getMedia()!!.getIdentifier().toString()) {
|
||||
Log.d(TAG, "Restored scroll Position: $scrollY")
|
||||
webvDescription!!.scrollTo(webvDescription!!.scrollX, scrollY)
|
||||
webvDescription.scrollTo(webvDescription.scrollX, scrollY)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +139,7 @@ class ItemDescriptionFragment : Fragment() {
|
|||
}
|
||||
|
||||
fun scrollToTop() {
|
||||
webvDescription!!.scrollTo(0, 0)
|
||||
webvDescription.scrollTo(0, 0)
|
||||
savePreference()
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
|||
import ac.mdiq.podvinci.ui.common.CircularProgressBar
|
||||
import ac.mdiq.podvinci.ui.common.ThemeUtils
|
||||
import ac.mdiq.podvinci.view.ShownotesWebView
|
||||
import androidx.annotation.OptIn
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -65,24 +66,25 @@ class ItemFragment : Fragment() {
|
|||
private var item: FeedItem? = null
|
||||
private var webviewData: String? = null
|
||||
|
||||
private var root: ViewGroup? = null
|
||||
private var webvDescription: ShownotesWebView? = null
|
||||
private var txtvPodcast: TextView? = null
|
||||
private var txtvTitle: TextView? = null
|
||||
private var txtvDuration: TextView? = null
|
||||
private var txtvPublished: TextView? = null
|
||||
private var imgvCover: ImageView? = null
|
||||
private var progbarDownload: CircularProgressBar? = null
|
||||
private var progbarLoading: ProgressBar? = null
|
||||
private var butAction1Text: TextView? = null
|
||||
private var butAction2Text: TextView? = null
|
||||
private var butAction1Icon: ImageView? = null
|
||||
private var butAction2Icon: ImageView? = null
|
||||
private var butAction1: View? = null
|
||||
private var butAction2: View? = null
|
||||
private lateinit var root: ViewGroup
|
||||
private lateinit var webvDescription: ShownotesWebView
|
||||
private lateinit var txtvPodcast: TextView
|
||||
private lateinit var txtvTitle: TextView
|
||||
private lateinit var txtvDuration: TextView
|
||||
private lateinit var txtvPublished: TextView
|
||||
private lateinit var imgvCover: ImageView
|
||||
private lateinit var progbarDownload: CircularProgressBar
|
||||
private lateinit var progbarLoading: ProgressBar
|
||||
private lateinit var butAction1Text: TextView
|
||||
private lateinit var butAction2Text: TextView
|
||||
private lateinit var butAction1Icon: ImageView
|
||||
private lateinit var butAction2Icon: ImageView
|
||||
private lateinit var butAction1: View
|
||||
private lateinit var butAction2: View
|
||||
private lateinit var noMediaLabel: View
|
||||
|
||||
private var actionButton1: ItemActionButton? = null
|
||||
private var actionButton2: ItemActionButton? = null
|
||||
private var noMediaLabel: View? = null
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
private var controller: PlaybackController? = null
|
||||
|
@ -100,16 +102,16 @@ class ItemFragment : Fragment() {
|
|||
root = layout.findViewById(R.id.content_root)
|
||||
|
||||
txtvPodcast = layout.findViewById(R.id.txtvPodcast)
|
||||
txtvPodcast?.setOnClickListener { v: View? -> openPodcast() }
|
||||
txtvPodcast.setOnClickListener { v: View? -> openPodcast() }
|
||||
txtvTitle = layout.findViewById(R.id.txtvTitle)
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
txtvTitle?.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
|
||||
txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
|
||||
}
|
||||
txtvDuration = layout.findViewById(R.id.txtvDuration)
|
||||
txtvPublished = layout.findViewById(R.id.txtvPublished)
|
||||
txtvTitle?.ellipsize = TextUtils.TruncateAt.END
|
||||
txtvTitle.ellipsize = TextUtils.TruncateAt.END
|
||||
webvDescription = layout.findViewById(R.id.webvDescription)
|
||||
webvDescription?.setTimecodeSelectedListener { time: Int? ->
|
||||
webvDescription.setTimecodeSelectedListener { time: Int? ->
|
||||
if (controller != null && item != null && item!!.media != null && controller!!.getMedia() != null &&
|
||||
item!!.media!!.getIdentifier() == controller!!.getMedia()!!.getIdentifier()) {
|
||||
controller!!.seekTo(time ?: 0)
|
||||
|
@ -118,10 +120,10 @@ class ItemFragment : Fragment() {
|
|||
Snackbar.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
if (webvDescription != null) registerForContextMenu(webvDescription!!)
|
||||
registerForContextMenu(webvDescription)
|
||||
|
||||
imgvCover = layout.findViewById(R.id.imgvCover)
|
||||
imgvCover?.setOnClickListener { v: View? -> openPodcast() }
|
||||
imgvCover.setOnClickListener { v: View? -> openPodcast() }
|
||||
progbarDownload = layout.findViewById(R.id.circularProgressBar)
|
||||
progbarLoading = layout.findViewById(R.id.progbarLoading)
|
||||
butAction1 = layout.findViewById(R.id.butAction1)
|
||||
|
@ -132,7 +134,7 @@ class ItemFragment : Fragment() {
|
|||
butAction2Text = layout.findViewById(R.id.butAction2Text)
|
||||
noMediaLabel = layout.findViewById(R.id.noMediaLabel)
|
||||
|
||||
butAction1?.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
butAction1.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
if (actionButton1 is StreamActionButton && !UserPreferences.isStreamOverDownload
|
||||
&& UsageStatistics.hasSignificantBiasTo(UsageStatistics.ACTION_STREAM)) {
|
||||
showOnDemandConfigBalloon(true)
|
||||
|
@ -142,7 +144,7 @@ class ItemFragment : Fragment() {
|
|||
}
|
||||
actionButton1?.onClick(requireContext())
|
||||
})
|
||||
butAction2?.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
butAction2.setOnClickListener(View.OnClickListener { v: View? ->
|
||||
if (actionButton2 is DownloadActionButton && UserPreferences.isStreamOverDownload
|
||||
&& UsageStatistics.hasSignificantBiasTo(UsageStatistics.ACTION_DOWNLOAD)) {
|
||||
showOnDemandConfigBalloon(false)
|
||||
|
@ -155,7 +157,7 @@ class ItemFragment : Fragment() {
|
|||
return layout
|
||||
}
|
||||
|
||||
private fun showOnDemandConfigBalloon(offerStreaming: Boolean) {
|
||||
@OptIn(UnstableApi::class) private fun showOnDemandConfigBalloon(offerStreaming: Boolean) {
|
||||
val isLocaleRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL)
|
||||
val balloon: Balloon = Balloon.Builder(requireContext())
|
||||
.setArrowOrientation(ArrowOrientation.TOP)
|
||||
|
@ -186,7 +188,7 @@ class ItemFragment : Fragment() {
|
|||
UsageStatistics.doNotAskAgain(UsageStatistics.ACTION_STREAM) // Type does not matter. Both are silenced.
|
||||
balloon.dismiss()
|
||||
}
|
||||
balloon.showAlignBottom(butAction1!!, 0, (-12 * resources.displayMetrics.density).toInt())
|
||||
balloon.showAlignBottom(butAction1, 0, (-12 * resources.displayMetrics.density).toInt())
|
||||
}
|
||||
|
||||
@UnstableApi override fun onStart() {
|
||||
|
@ -204,7 +206,7 @@ class ItemFragment : Fragment() {
|
|||
@UnstableApi override fun onResume() {
|
||||
super.onResume()
|
||||
if (itemsLoaded) {
|
||||
progbarLoading?.visibility = View.GONE
|
||||
progbarLoading.visibility = View.GONE
|
||||
updateAppearance()
|
||||
}
|
||||
}
|
||||
|
@ -218,16 +220,13 @@ class ItemFragment : Fragment() {
|
|||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
disposable?.dispose()
|
||||
|
||||
if (webvDescription != null && root != null) {
|
||||
root!!.removeView(webvDescription)
|
||||
webvDescription!!.destroy()
|
||||
}
|
||||
root.removeView(webvDescription)
|
||||
webvDescription.destroy()
|
||||
}
|
||||
|
||||
@UnstableApi private fun onFragmentLoaded() {
|
||||
if (webviewData != null && !itemsLoaded) {
|
||||
webvDescription?.loadDataWithBaseURL("https://127.0.0.1", webviewData!!, "text/html", "utf-8", "about:blank")
|
||||
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData!!, "text/html", "utf-8", "about:blank")
|
||||
}
|
||||
updateAppearance()
|
||||
}
|
||||
|
@ -237,13 +236,13 @@ class ItemFragment : Fragment() {
|
|||
Log.d(TAG, "updateAppearance item is null")
|
||||
return
|
||||
}
|
||||
if (item!!.feed != null) txtvPodcast?.text = item!!.feed!!.title
|
||||
txtvTitle?.text = item!!.title
|
||||
if (item!!.feed != null) txtvPodcast.text = item!!.feed!!.title
|
||||
txtvTitle.text = item!!.title
|
||||
|
||||
if (item?.pubDate != null) {
|
||||
val pubDateStr = DateFormatter.formatAbbrev(activity, item!!.pubDate)
|
||||
txtvPublished?.text = pubDateStr
|
||||
txtvPublished?.setContentDescription(DateFormatter.formatForAccessibility(item!!.pubDate))
|
||||
txtvPublished.text = pubDateStr
|
||||
txtvPublished.setContentDescription(DateFormatter.formatForAccessibility(item!!.pubDate))
|
||||
}
|
||||
|
||||
val options: RequestOptions = RequestOptions()
|
||||
|
@ -252,25 +251,25 @@ class ItemFragment : Fragment() {
|
|||
RoundedCorners((8 * resources.displayMetrics.density).toInt()))
|
||||
.dontAnimate()
|
||||
|
||||
if (imgvCover != null) Glide.with(this)
|
||||
Glide.with(this)
|
||||
.load(item!!.imageLocation)
|
||||
.error(Glide.with(this)
|
||||
.load(ImageResourceUtils.getFallbackImageLocation(item!!))
|
||||
.apply(options))
|
||||
.apply(options)
|
||||
.into(imgvCover!!)
|
||||
.into(imgvCover)
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
@UnstableApi private fun updateButtons() {
|
||||
progbarDownload?.visibility = View.GONE
|
||||
progbarDownload.visibility = View.GONE
|
||||
val dls = DownloadServiceInterface.get()
|
||||
if (item != null && item!!.hasMedia() && item!!.media!!.download_url != null) {
|
||||
val url = item!!.media!!.download_url!!
|
||||
if (dls != null && dls.isDownloadingEpisode(url)) {
|
||||
progbarDownload?.visibility = View.VISIBLE
|
||||
progbarDownload?.setPercentage(0.01f * max(1.0, dls.getProgress(url).toDouble()).toFloat(), item)
|
||||
progbarDownload?.setIndeterminate(dls.isEpisodeQueued(url))
|
||||
progbarDownload.visibility = View.VISIBLE
|
||||
progbarDownload.setPercentage(0.01f * max(1.0, dls.getProgress(url).toDouble()).toFloat(), item)
|
||||
progbarDownload.setIndeterminate(dls.isEpisodeQueued(url))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,12 +279,12 @@ class ItemFragment : Fragment() {
|
|||
actionButton1 = MarkAsPlayedActionButton(item!!)
|
||||
actionButton2 = VisitWebsiteActionButton(item!!)
|
||||
}
|
||||
noMediaLabel!!.visibility = View.VISIBLE
|
||||
noMediaLabel.visibility = View.VISIBLE
|
||||
} else {
|
||||
noMediaLabel!!.visibility = View.GONE
|
||||
noMediaLabel.visibility = View.GONE
|
||||
if (media.getDuration() > 0) {
|
||||
txtvDuration?.text = Converter.getDurationStringLong(media.getDuration())
|
||||
txtvDuration?.setContentDescription(
|
||||
txtvDuration.text = Converter.getDurationStringLong(media.getDuration())
|
||||
txtvDuration.setContentDescription(
|
||||
Converter.getDurationStringLocalized(requireContext(), media.getDuration().toLong()))
|
||||
}
|
||||
if (item != null) {
|
||||
|
@ -309,25 +308,25 @@ class ItemFragment : Fragment() {
|
|||
}
|
||||
|
||||
if (actionButton1 != null) {
|
||||
butAction1Text?.setText(actionButton1!!.getLabel())
|
||||
butAction1Icon?.setImageResource(actionButton1!!.getDrawable())
|
||||
butAction1Text.setText(actionButton1!!.getLabel())
|
||||
butAction1Icon.setImageResource(actionButton1!!.getDrawable())
|
||||
}
|
||||
butAction1Text?.transformationMethod = null
|
||||
if (actionButton1 != null) butAction1!!.visibility = actionButton1!!.visibility
|
||||
butAction1Text.transformationMethod = null
|
||||
if (actionButton1 != null) butAction1.visibility = actionButton1!!.visibility
|
||||
|
||||
if (actionButton2 != null) {
|
||||
butAction2Text?.setText(actionButton2!!.getLabel())
|
||||
butAction2Icon?.setImageResource(actionButton2!!.getDrawable())
|
||||
butAction2Text.setText(actionButton2!!.getLabel())
|
||||
butAction2Icon.setImageResource(actionButton2!!.getDrawable())
|
||||
}
|
||||
butAction2Text?.transformationMethod = null
|
||||
if (actionButton2 != null) butAction2!!.visibility = actionButton2!!.visibility
|
||||
butAction2Text.transformationMethod = null
|
||||
if (actionButton2 != null) butAction2.visibility = actionButton2!!.visibility
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
return webvDescription?.onContextItemSelected(item)?: false
|
||||
return webvDescription.onContextItemSelected(item)
|
||||
}
|
||||
|
||||
private fun openPodcast() {
|
||||
@OptIn(UnstableApi::class) private fun openPodcast() {
|
||||
if (item == null) {
|
||||
return
|
||||
}
|
||||
|
@ -374,13 +373,13 @@ class ItemFragment : Fragment() {
|
|||
disposable?.dispose()
|
||||
|
||||
if (!itemsLoaded) {
|
||||
progbarLoading?.visibility = View.VISIBLE
|
||||
progbarLoading.visibility = View.VISIBLE
|
||||
}
|
||||
disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: FeedItem? ->
|
||||
progbarLoading?.visibility = View.GONE
|
||||
progbarLoading.visibility = View.GONE
|
||||
item = result
|
||||
onFragmentLoaded()
|
||||
itemsLoaded = true
|
||||
|
|
|
@ -30,22 +30,22 @@ import kotlin.math.max
|
|||
* Displays information about a list of FeedItems.
|
||||
*/
|
||||
class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||
private var pager: ViewPager2? = null
|
||||
private lateinit var pager: ViewPager2
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
private var feedItems: LongArray? = null
|
||||
private var item: FeedItem? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
val layout: View = inflater.inflate(R.layout.feeditem_pager_fragment, container, false)
|
||||
toolbar = layout.findViewById(R.id.toolbar)
|
||||
toolbar?.title = ""
|
||||
toolbar?.inflateMenu(R.menu.feeditem_options)
|
||||
toolbar?.setNavigationOnClickListener { v: View? -> parentFragmentManager.popBackStack() }
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar.title = ""
|
||||
toolbar.inflateMenu(R.menu.feeditem_options)
|
||||
toolbar.setNavigationOnClickListener { v: View? -> parentFragmentManager.popBackStack() }
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
feedItems = requireArguments().getLongArray(ARG_FEEDITEMS)
|
||||
val feedItemPos = max(0.0, requireArguments().getInt(ARG_FEEDITEM_POS).toDouble())
|
||||
|
@ -61,16 +61,18 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
// Restore state by using the same ID as before. ID collisions are prevented in MainActivity.
|
||||
newId = savedInstanceState.getInt(KEY_PAGER_ID, 0)
|
||||
}
|
||||
pager?.setId(newId)
|
||||
pager?.adapter = ItemPagerAdapter(this)
|
||||
pager?.setCurrentItem(feedItemPos, false)
|
||||
pager?.offscreenPageLimit = 1
|
||||
loadItem(feedItems!![feedItemPos])
|
||||
pager?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
loadItem(feedItems!![position])
|
||||
}
|
||||
})
|
||||
pager.setId(newId)
|
||||
pager.adapter = ItemPagerAdapter(this)
|
||||
pager.setCurrentItem(feedItemPos, false)
|
||||
pager.offscreenPageLimit = 1
|
||||
if (feedItems != null && feedItems!!.isNotEmpty()) {
|
||||
loadItem(feedItems!![feedItemPos])
|
||||
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
loadItem(feedItems!![position])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
return layout
|
||||
|
@ -78,7 +80,7 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
if (pager != null) outState.putInt(KEY_PAGER_ID, pager!!.id)
|
||||
outState.putInt(KEY_PAGER_ID, pager.id)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -100,19 +102,19 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
@UnstableApi fun refreshToolbarState() {
|
||||
if (item == null || toolbar == null) {
|
||||
if (item == null) {
|
||||
return
|
||||
}
|
||||
if (item!!.hasMedia()) {
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar!!.menu, item)
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item)
|
||||
} else {
|
||||
// these are already available via button1 and button2
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar!!.menu, item,
|
||||
FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item,
|
||||
R.id.mark_read_item, R.id.visit_website_item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(menuItem: MenuItem): Boolean {
|
||||
@UnstableApi override fun onMenuItemClick(menuItem: MenuItem): Boolean {
|
||||
if (menuItem.itemId == R.id.open_podcast) {
|
||||
openPodcast()
|
||||
return true
|
||||
|
@ -132,7 +134,7 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun openPodcast() {
|
||||
@UnstableApi private fun openPodcast() {
|
||||
if (item == null) {
|
||||
return
|
||||
}
|
||||
|
@ -142,11 +144,11 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
private inner class ItemPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return ItemFragment.newInstance(feedItems!![position])
|
||||
return ItemFragment.newInstance(if (feedItems!= null) feedItems!![position] else 0L)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return feedItems!!.size
|
||||
return feedItems?.size?:0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +167,7 @@ class ItemPagerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
fun newInstance(feeditems: LongArray?, feedItemPos: Int): ItemPagerFragment {
|
||||
val fragment = ItemPagerFragment()
|
||||
val args = Bundle()
|
||||
args.putLongArray(ARG_FEEDITEMS, feeditems)
|
||||
if (feeditems != null) args.putLongArray(ARG_FEEDITEMS, feeditems)
|
||||
args.putInt(ARG_FEEDITEM_POS, max(0.0, feedItemPos.toDouble()).toInt())
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
|
|
|
@ -42,6 +42,8 @@ import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
|||
import ac.mdiq.podvinci.ui.appstartintent.MainActivityStarter
|
||||
import ac.mdiq.podvinci.ui.common.ThemeUtils
|
||||
import ac.mdiq.podvinci.ui.home.HomeFragment
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -56,9 +58,11 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
private var navDrawerData: NavDrawerData? = null
|
||||
private var flatItemList: List<NavDrawerData.DrawerItem>? = null
|
||||
private var contextPressedItem: NavDrawerData.DrawerItem? = null
|
||||
private var navAdapter: NavListAdapter? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
|
||||
private lateinit var navAdapter: NavListAdapter
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private var openFolders: MutableSet<String> = HashSet()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
|
@ -83,12 +87,13 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
}
|
||||
|
||||
val preferences: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
// TODO: what is this?
|
||||
openFolders = HashSet(preferences.getStringSet(PREF_OPEN_FOLDERS, HashSet<String>())) // Must not modify
|
||||
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
val navList = root.findViewById<RecyclerView>(R.id.nav_list)
|
||||
navAdapter = NavListAdapter(itemAccess, requireActivity())
|
||||
navAdapter?.setHasStableIds(true)
|
||||
navAdapter.setHasStableIds(true)
|
||||
navList.adapter = navAdapter
|
||||
navList.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
|
@ -161,14 +166,14 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
}
|
||||
}
|
||||
|
||||
private fun onFeedContextMenuClicked(feed: Feed, item: MenuItem): Boolean {
|
||||
@OptIn(UnstableApi::class) private fun onFeedContextMenuClicked(feed: Feed, item: MenuItem): Boolean {
|
||||
val itemId = item.itemId
|
||||
when (itemId) {
|
||||
R.id.remove_all_inbox_item -> {
|
||||
val removeAllNewFlagsConfirmationDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(),
|
||||
R.string.remove_all_inbox_label,
|
||||
R.string.remove_all_inbox_confirmation_msg) {
|
||||
override fun onConfirmButtonPressed(dialog: DialogInterface) {
|
||||
@OptIn(UnstableApi::class) override fun onConfirmButtonPressed(dialog: DialogInterface) {
|
||||
dialog.dismiss()
|
||||
DBWriter.removeFeedNewFlag(feed.id)
|
||||
}
|
||||
|
@ -253,12 +258,12 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
|
||||
override fun isSelected(position: Int): Boolean {
|
||||
val lastNavFragment = getLastNavFragment(context)
|
||||
if (position < navAdapter!!.subscriptionOffset) {
|
||||
return navAdapter!!.getFragmentTags()[position] == lastNavFragment
|
||||
if (position < navAdapter.subscriptionOffset) {
|
||||
return navAdapter.getFragmentTags()[position] == lastNavFragment
|
||||
} else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed
|
||||
val feedId = lastNavFragment.toLong()
|
||||
if (navDrawerData != null) {
|
||||
val itemToCheck: NavDrawerData.DrawerItem = flatItemList!![position - navAdapter!!.subscriptionOffset]
|
||||
val itemToCheck: NavDrawerData.DrawerItem = flatItemList!![position - navAdapter.subscriptionOffset]
|
||||
if (itemToCheck.type == NavDrawerData.DrawerItem.Type.FEED) {
|
||||
// When the same feed is displayed multiple times, it should be highlighted multiple times.
|
||||
return (itemToCheck as NavDrawerData.FeedDrawerItem).feed.id == feedId
|
||||
|
@ -292,15 +297,15 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
return sum
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
val viewType: Int = navAdapter!!.getItemViewType(position)
|
||||
@OptIn(UnstableApi::class) override fun onItemClick(position: Int) {
|
||||
val viewType: Int = navAdapter.getItemViewType(position)
|
||||
if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
|
||||
if (position < navAdapter!!.subscriptionOffset) {
|
||||
val tag: String = navAdapter!!.getFragmentTags()[position] ?:""
|
||||
if (position < navAdapter.subscriptionOffset) {
|
||||
val tag: String = navAdapter.getFragmentTags()[position] ?:""
|
||||
(activity as MainActivity).loadFragment(tag, null)
|
||||
(activity as MainActivity).bottomSheet?.setState(BottomSheetBehavior.STATE_COLLAPSED)
|
||||
} else {
|
||||
val pos: Int = position - navAdapter!!.subscriptionOffset
|
||||
val pos: Int = position - navAdapter.subscriptionOffset
|
||||
val clickedItem: NavDrawerData.DrawerItem = flatItemList!![pos]
|
||||
|
||||
if (clickedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
|
||||
|
@ -327,20 +332,20 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
.subscribe(
|
||||
{ result: List<NavDrawerData.DrawerItem>? ->
|
||||
flatItemList = result
|
||||
navAdapter?.notifyDataSetChanged()
|
||||
navAdapter.notifyDataSetChanged()
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
}
|
||||
} else if (UserPreferences.subscriptionsFilter.isEnabled
|
||||
&& navAdapter!!.showSubscriptionList) {
|
||||
&& navAdapter.showSubscriptionList) {
|
||||
SubscriptionsFilterDialog().show(childFragmentManager, "filter")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemLongClick(position: Int): Boolean {
|
||||
if (position < navAdapter!!.getFragmentTags().size) {
|
||||
if (position < navAdapter.getFragmentTags().size) {
|
||||
DrawerPreferencesDialog.show(context!!) {
|
||||
navAdapter?.notifyDataSetChanged()
|
||||
navAdapter.notifyDataSetChanged()
|
||||
if (UserPreferences.hiddenDrawerItems != null && UserPreferences.hiddenDrawerItems!!.contains(
|
||||
getLastNavFragment(context))) {
|
||||
MainActivityStarter(context!!)
|
||||
|
@ -351,7 +356,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
}
|
||||
return true
|
||||
} else {
|
||||
contextPressedItem = flatItemList!![position - navAdapter!!.subscriptionOffset]
|
||||
contextPressedItem = flatItemList!![position - navAdapter.subscriptionOffset]
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -372,11 +377,11 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
{ result: Pair<NavDrawerData, List<NavDrawerData.DrawerItem>> ->
|
||||
navDrawerData = result.first
|
||||
flatItemList = result.second
|
||||
navAdapter?.notifyDataSetChanged()
|
||||
progressBar?.visibility = View.GONE // Stays hidden once there is something in the list
|
||||
navAdapter.notifyDataSetChanged()
|
||||
progressBar.visibility = View.GONE // Stays hidden once there is something in the list
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
progressBar?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -398,7 +403,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||
if (PREF_LAST_FRAGMENT_TAG == key) {
|
||||
navAdapter?.notifyDataSetChanged() // Update selection
|
||||
navAdapter.notifyDataSetChanged() // Update selection
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,6 +419,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
const val TAG: String = "NavDrawerFragment"
|
||||
|
||||
@JvmField
|
||||
@UnstableApi
|
||||
val NAV_DRAWER_TAGS: Array<String> = arrayOf(HomeFragment.TAG,
|
||||
QueueFragment.TAG,
|
||||
InboxFragment.TAG,
|
||||
|
|
|
@ -33,11 +33,12 @@ class OnlineSearchFragment
|
|||
*/
|
||||
private var adapter: ItunesAdapter? = null
|
||||
private var searchProvider: PodcastSearcher? = null
|
||||
private var gridView: GridView? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private var txtvError: TextView? = null
|
||||
private var butRetry: Button? = null
|
||||
private var txtvEmpty: TextView? = null
|
||||
|
||||
private lateinit var gridView: GridView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var txtvError: TextView
|
||||
private lateinit var butRetry: Button
|
||||
private lateinit var txtvEmpty: TextView
|
||||
|
||||
/**
|
||||
* List of podcasts retreived from the search
|
||||
|
@ -65,10 +66,10 @@ class OnlineSearchFragment
|
|||
val root: View = inflater.inflate(R.layout.fragment_itunes_search, container, false)
|
||||
gridView = root.findViewById(R.id.gridView)
|
||||
adapter = ItunesAdapter(requireContext(), ArrayList())
|
||||
gridView?.setAdapter(adapter)
|
||||
gridView.setAdapter(adapter)
|
||||
|
||||
//Show information about the podcast when the list item is clicked
|
||||
gridView?.onItemClickListener =
|
||||
gridView.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { parent: AdapterView<*>?, view1: View?, position: Int, id: Long ->
|
||||
val podcast = searchResults!![position]
|
||||
if (podcast != null) {
|
||||
|
@ -86,7 +87,7 @@ class OnlineSearchFragment
|
|||
if (searchProvider != null) txtvPoweredBy.text = getString(R.string.search_powered_by, searchProvider!!.name)
|
||||
setupToolbar(root.findViewById(R.id.toolbar))
|
||||
|
||||
gridView?.setOnScrollListener(object : AbsListView.OnScrollListener {
|
||||
gridView.setOnScrollListener(object : AbsListView.OnScrollListener {
|
||||
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
|
||||
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
|
||||
val imm = activity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
@ -158,29 +159,29 @@ class OnlineSearchFragment
|
|||
disposable = searchProvider?.search(query)
|
||||
?.subscribe({ result: List<PodcastSearchResult?>? ->
|
||||
searchResults = result
|
||||
progressBar?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
adapter?.clear()
|
||||
if (searchResults != null) adapter?.addAll(searchResults!!)
|
||||
adapter?.notifyDataSetInvalidated()
|
||||
gridView?.visibility = if (searchResults!!.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
txtvEmpty?.visibility = if (searchResults!!.isEmpty()) View.VISIBLE else View.GONE
|
||||
txtvEmpty?.text = getString(R.string.no_results_for_query) + query
|
||||
gridView.visibility = if (searchResults!!.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
txtvEmpty.visibility = if (searchResults!!.isEmpty()) View.VISIBLE else View.GONE
|
||||
txtvEmpty.text = getString(R.string.no_results_for_query) + query
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
progressBar?.visibility = View.GONE
|
||||
txtvError?.text = error.toString()
|
||||
txtvError?.visibility = View.VISIBLE
|
||||
butRetry?.setOnClickListener { v: View? -> search(query) }
|
||||
butRetry!!.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.GONE
|
||||
txtvError.text = error.toString()
|
||||
txtvError.visibility = View.VISIBLE
|
||||
butRetry.setOnClickListener { v: View? -> search(query) }
|
||||
butRetry.visibility = View.VISIBLE
|
||||
})
|
||||
}
|
||||
|
||||
private fun showOnlyProgressBar() {
|
||||
gridView?.visibility = View.GONE
|
||||
txtvError?.visibility = View.GONE
|
||||
butRetry!!.visibility = View.GONE
|
||||
txtvEmpty?.visibility = View.GONE
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
gridView.visibility = View.GONE
|
||||
txtvError.visibility = View.GONE
|
||||
butRetry.visibility = View.GONE
|
||||
txtvEmpty.visibility = View.GONE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun showInputMethod(view: View) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import ac.mdiq.podvinci.core.storage.DBWriter
|
|||
import ac.mdiq.podvinci.event.playback.PlaybackHistoryEvent
|
||||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import ac.mdiq.podvinci.model.feed.FeedItemFilter
|
||||
import androidx.annotation.OptIn
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
|
@ -27,19 +28,19 @@ class PlaybackHistoryFragment : EpisodesListFragment() {
|
|||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState)
|
||||
toolbar!!.inflateMenu(R.menu.playback_history)
|
||||
toolbar!!.setTitle(R.string.playback_history_label)
|
||||
toolbar.inflateMenu(R.menu.playback_history)
|
||||
toolbar.setTitle(R.string.playback_history_label)
|
||||
updateToolbar()
|
||||
emptyView!!.setIcon(R.drawable.ic_history)
|
||||
emptyView!!.setTitle(R.string.no_history_head_label)
|
||||
emptyView!!.setMessage(R.string.no_history_label)
|
||||
emptyView.setIcon(R.drawable.ic_history)
|
||||
emptyView.setTitle(R.string.no_history_head_label)
|
||||
emptyView.setMessage(R.string.no_history_label)
|
||||
return root
|
||||
}
|
||||
|
||||
override fun getFilter(): FeedItemFilter {
|
||||
return FeedItemFilter.unfiltered()
|
||||
}
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
@OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
if (super.onOptionsItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ class PlaybackHistoryFragment : EpisodesListFragment() {
|
|||
|
||||
override fun updateToolbar() {
|
||||
// Not calling super, as we do not have a refresh button that could be updated
|
||||
toolbar!!.menu.findItem(R.id.clear_history_item).setVisible(episodes.isNotEmpty())
|
||||
toolbar.menu.findItem(R.id.clear_history_item).setVisible(episodes.isNotEmpty())
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
|
|
@ -60,22 +60,21 @@ import java.util.*
|
|||
* Shows all items in the queue.
|
||||
*/
|
||||
class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
|
||||
private var infoBar: TextView? = null
|
||||
private var recyclerView: EpisodeItemListRecyclerView? = null
|
||||
private var recyclerAdapter: QueueRecyclerAdapter? = null
|
||||
private var emptyView: EmptyViewHandler? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
private var swipeRefreshLayout: SwipeRefreshLayout? = null
|
||||
private lateinit var infoBar: TextView
|
||||
private lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
private lateinit var emptyView: EmptyViewHandler
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
private lateinit var swipeActions: SwipeActions
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var speedDialView: SpeedDialView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private var displayUpArrow = false
|
||||
private var queue: MutableList<FeedItem> = mutableListOf()
|
||||
|
||||
private var queue: MutableList<FeedItem>? = null
|
||||
|
||||
private var recyclerAdapter: QueueRecyclerAdapter? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var swipeActions: SwipeActions? = null
|
||||
private var prefs: SharedPreferences? = null
|
||||
|
||||
private var speedDialView: SpeedDialView? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -83,10 +82,96 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
val root: View = inflater.inflate(R.layout.queue_fragment, container, false)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener { v: View? ->
|
||||
recyclerView.scrollToPosition(5)
|
||||
recyclerView.post { recyclerView.smoothScrollToPosition(0) }
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
|
||||
toolbar.inflateMenu(R.menu.queue)
|
||||
refreshToolbarState()
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
infoBar = root.findViewById(R.id.info_bar)
|
||||
recyclerView = root.findViewById(R.id.recyclerView)
|
||||
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
|
||||
if (animator != null && animator is SimpleItemAnimator) {
|
||||
animator.supportsChangeAnimations = false
|
||||
}
|
||||
recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
registerForContextMenu(recyclerView)
|
||||
recyclerView.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
|
||||
swipeActions = QueueSwipeActions()
|
||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.QUEUED))
|
||||
swipeActions.attachTo(recyclerView)
|
||||
|
||||
recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem -> this@QueueFragment.onContextItemSelected(item) }
|
||||
}
|
||||
}
|
||||
recyclerAdapter?.setOnSelectModeListener(this)
|
||||
recyclerView.adapter = recyclerAdapter
|
||||
|
||||
swipeRefreshLayout = root.findViewById(R.id.swipeRefresh)
|
||||
swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
}
|
||||
|
||||
emptyView = EmptyViewHandler(context)
|
||||
emptyView.attachToRecyclerView(recyclerView)
|
||||
emptyView.setIcon(R.drawable.ic_playlist_play)
|
||||
emptyView.setTitle(R.string.no_items_header_label)
|
||||
emptyView.setMessage(R.string.no_items_label)
|
||||
emptyView.updateAdapter(recyclerAdapter)
|
||||
|
||||
speedDialView = root.findViewById(R.id.fabSD)
|
||||
speedDialView.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView.removeActionItemById(R.id.mark_read_batch)
|
||||
speedDialView.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView.removeActionItemById(R.id.add_to_queue_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && recyclerAdapter!!.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
speedDialView.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialView.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItem.id)
|
||||
.handleAction(recyclerAdapter!!.selectedItems.filterIsInstance<FeedItem>())
|
||||
recyclerAdapter?.endSelectMode()
|
||||
true
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (queue != null) {
|
||||
recyclerView?.restoreScrollPosition(TAG)
|
||||
if (queue.isNotEmpty()) {
|
||||
recyclerView.restoreScrollPosition(TAG)
|
||||
}
|
||||
loadItems(true)
|
||||
EventBus.getDefault().register(this)
|
||||
|
@ -94,7 +179,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
recyclerView?.saveScrollPosition(TAG)
|
||||
recyclerView.saveScrollPosition(TAG)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
@ -106,15 +191,13 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: QueueEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
if (queue == null) {
|
||||
return
|
||||
} else if (recyclerAdapter == null) {
|
||||
if (recyclerAdapter == null) {
|
||||
loadItems(true)
|
||||
return
|
||||
}
|
||||
when (event.action) {
|
||||
QueueEvent.Action.ADDED -> {
|
||||
if (event.item != null) queue!!.add(event.position, event.item!!)
|
||||
if (event.item != null) queue.add(event.position, event.item!!)
|
||||
recyclerAdapter?.notifyItemInserted(event.position)
|
||||
}
|
||||
QueueEvent.Action.SET_QUEUE, QueueEvent.Action.SORTED -> {
|
||||
|
@ -125,14 +208,14 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
}
|
||||
QueueEvent.Action.REMOVED, QueueEvent.Action.IRREVERSIBLE_REMOVED -> {
|
||||
if (event.item != null) {
|
||||
val position: Int = FeedItemUtil.indexOfItemWithId(queue!!.toList(), event.item!!.id)
|
||||
queue!!.removeAt(position)
|
||||
val position: Int = FeedItemUtil.indexOfItemWithId(queue.toList(), event.item!!.id)
|
||||
queue.removeAt(position)
|
||||
recyclerAdapter?.notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
QueueEvent.Action.CLEARED -> {
|
||||
queue!!.clear()
|
||||
recyclerAdapter?.updateItems(queue!!)
|
||||
queue.clear()
|
||||
recyclerAdapter?.updateItems(queue)
|
||||
}
|
||||
QueueEvent.Action.MOVED -> return
|
||||
QueueEvent.Action.ADDED_ITEMS -> return
|
||||
|
@ -140,16 +223,14 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
}
|
||||
recyclerAdapter?.updateDragDropEnabled()
|
||||
refreshToolbarState()
|
||||
recyclerView?.saveScrollPosition(TAG)
|
||||
recyclerView.saveScrollPosition(TAG)
|
||||
refreshInfoBar()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
if (queue == null) {
|
||||
return
|
||||
} else if (recyclerAdapter == null) {
|
||||
if (recyclerAdapter == null) {
|
||||
loadItems(true)
|
||||
return
|
||||
}
|
||||
|
@ -157,10 +238,10 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
val size: Int = event.items.size
|
||||
while (i < size) {
|
||||
val item: FeedItem = event.items[i]
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithId(queue!!, item.id)
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithId(queue, item.id)
|
||||
if (pos >= 0) {
|
||||
queue!!.removeAt(pos)
|
||||
queue!!.add(pos, item)
|
||||
queue.removeAt(pos)
|
||||
queue.add(pos, item)
|
||||
recyclerAdapter?.notifyItemChangedCompat(pos)
|
||||
refreshInfoBar()
|
||||
}
|
||||
|
@ -170,11 +251,8 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||
if (queue == null) {
|
||||
return
|
||||
}
|
||||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue!!.toList(), downloadUrl)
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue.toList(), downloadUrl)
|
||||
if (pos >= 0) {
|
||||
recyclerAdapter?.notifyItemChangedCompat(pos)
|
||||
}
|
||||
|
@ -185,7 +263,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
if (recyclerAdapter != null) {
|
||||
for (i in 0 until recyclerAdapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = recyclerView?.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
|
@ -213,8 +291,8 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
return
|
||||
}
|
||||
when (event.keyCode) {
|
||||
KeyEvent.KEYCODE_T -> recyclerView!!.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> recyclerView!!.smoothScrollToPosition(recyclerAdapter!!.itemCount - 1)
|
||||
KeyEvent.KEYCODE_T -> recyclerView.smoothScrollToPosition(0)
|
||||
KeyEvent.KEYCODE_B -> recyclerView.smoothScrollToPosition(recyclerAdapter!!.itemCount - 1)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -224,19 +302,19 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
recyclerAdapter?.endSelectMode()
|
||||
recyclerAdapter = null
|
||||
|
||||
toolbar?.setOnMenuItemClickListener(null)
|
||||
toolbar?.setOnLongClickListener(null)
|
||||
toolbar.setOnMenuItemClickListener(null)
|
||||
toolbar.setOnLongClickListener(null)
|
||||
}
|
||||
|
||||
private fun refreshToolbarState() {
|
||||
val keepSorted: Boolean = UserPreferences.isQueueKeepSorted
|
||||
toolbar?.menu?.findItem(R.id.queue_lock)?.setChecked(UserPreferences.isQueueLocked)
|
||||
toolbar?.menu?.findItem(R.id.queue_lock)?.setVisible(!keepSorted)
|
||||
toolbar.menu?.findItem(R.id.queue_lock)?.setChecked(UserPreferences.isQueueLocked)
|
||||
toolbar.menu?.findItem(R.id.queue_lock)?.setVisible(!keepSorted)
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedUpdateRunningEvent) {
|
||||
swipeRefreshLayout?.isRefreshing = event.isFeedUpdateRunning
|
||||
swipeRefreshLayout.isRefreshing = event.isFeedUpdateRunning
|
||||
}
|
||||
|
||||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
|
@ -282,7 +360,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
if (isLocked) {
|
||||
setQueueLocked(false)
|
||||
} else {
|
||||
val shouldShowLockWarning: Boolean = prefs!!.getBoolean(PREF_SHOW_LOCK_WARNING, true)
|
||||
val shouldShowLockWarning: Boolean = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true)
|
||||
if (!shouldShowLockWarning) {
|
||||
setQueueLocked(true)
|
||||
} else {
|
||||
|
@ -296,7 +374,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
builder.setPositiveButton(R.string.lock_queue
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
prefs!!.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply()
|
||||
prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply()
|
||||
setQueueLocked(true)
|
||||
}
|
||||
builder.setNegativeButton(R.string.cancel_label, null)
|
||||
|
@ -311,7 +389,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
if (recyclerAdapter != null) {
|
||||
recyclerAdapter?.updateDragDropEnabled()
|
||||
}
|
||||
if (queue!!.size == 0) {
|
||||
if (queue.size == 0) {
|
||||
if (locked) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.queue_locked, Snackbar.LENGTH_SHORT)
|
||||
} else {
|
||||
|
@ -334,126 +412,38 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
return true
|
||||
}
|
||||
|
||||
if (queue != null) {
|
||||
val position: Int = FeedItemUtil.indexOfItemWithId(queue!!.toList(), selectedItem.id)
|
||||
if (position < 0) {
|
||||
Log.i(TAG, "Selected item no longer exist, ignoring selection")
|
||||
return super.onContextItemSelected(item)
|
||||
}
|
||||
|
||||
val itemId = item.itemId
|
||||
if (itemId == R.id.move_to_top_item) {
|
||||
queue!!.add(0, queue!!.removeAt(position))
|
||||
recyclerAdapter?.notifyItemMoved(position, 0)
|
||||
DBWriter.moveQueueItemToTop(selectedItem.id, true)
|
||||
return true
|
||||
} else if (itemId == R.id.move_to_bottom_item) {
|
||||
queue!!.add(queue!!.size - 1, queue!!.removeAt(position))
|
||||
recyclerAdapter?.notifyItemMoved(position, queue!!.size - 1)
|
||||
DBWriter.moveQueueItemToBottom(selectedItem.id, true)
|
||||
return true
|
||||
}
|
||||
val position: Int = FeedItemUtil.indexOfItemWithId(queue.toList(), selectedItem.id)
|
||||
if (position < 0) {
|
||||
Log.i(TAG, "Selected item no longer exist, ignoring selection")
|
||||
return super.onContextItemSelected(item)
|
||||
}
|
||||
|
||||
val itemId = item.itemId
|
||||
if (itemId == R.id.move_to_top_item) {
|
||||
queue.add(0, queue.removeAt(position))
|
||||
recyclerAdapter?.notifyItemMoved(position, 0)
|
||||
DBWriter.moveQueueItemToTop(selectedItem.id, true)
|
||||
return true
|
||||
} else if (itemId == R.id.move_to_bottom_item) {
|
||||
queue.add(queue.size - 1, queue.removeAt(position))
|
||||
recyclerAdapter?.notifyItemMoved(position, queue.size - 1)
|
||||
DBWriter.moveQueueItemToBottom(selectedItem.id, true)
|
||||
return true
|
||||
}
|
||||
|
||||
return FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem)
|
||||
}
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
val root: View = inflater.inflate(R.layout.queue_fragment, container, false)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar?.setOnLongClickListener { v: View? ->
|
||||
recyclerView?.scrollToPosition(5)
|
||||
recyclerView?.post { recyclerView?.smoothScrollToPosition(0) }
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
if (toolbar != null) (activity as MainActivity).setupToolbarToggle(toolbar!!, displayUpArrow)
|
||||
toolbar?.inflateMenu(R.menu.queue)
|
||||
refreshToolbarState()
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
|
||||
infoBar = root.findViewById(R.id.info_bar)
|
||||
recyclerView = root.findViewById(R.id.recyclerView)
|
||||
val animator: RecyclerView.ItemAnimator? = recyclerView!!.itemAnimator
|
||||
if (animator != null && animator is SimpleItemAnimator) {
|
||||
animator.supportsChangeAnimations = false
|
||||
}
|
||||
recyclerView?.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
registerForContextMenu(recyclerView!!)
|
||||
recyclerView?.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
|
||||
swipeActions = QueueSwipeActions()
|
||||
swipeActions?.setFilter(FeedItemFilter(FeedItemFilter.QUEUED))
|
||||
swipeActions?.attachTo(recyclerView)
|
||||
|
||||
recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions!!) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem -> this@QueueFragment.onContextItemSelected(item) }
|
||||
}
|
||||
}
|
||||
recyclerAdapter?.setOnSelectModeListener(this)
|
||||
recyclerView?.adapter = recyclerAdapter
|
||||
|
||||
swipeRefreshLayout = root.findViewById(R.id.swipeRefresh)
|
||||
swipeRefreshLayout?.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout?.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
}
|
||||
|
||||
emptyView = EmptyViewHandler(context)
|
||||
emptyView?.attachToRecyclerView(recyclerView!!)
|
||||
emptyView?.setIcon(R.drawable.ic_playlist_play)
|
||||
emptyView?.setTitle(R.string.no_items_header_label)
|
||||
emptyView?.setMessage(R.string.no_items_label)
|
||||
emptyView?.updateAdapter(recyclerAdapter)
|
||||
|
||||
speedDialView = root.findViewById(R.id.fabSD)
|
||||
speedDialView?.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView?.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialView?.removeActionItemById(R.id.mark_read_batch)
|
||||
speedDialView?.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView?.removeActionItemById(R.id.add_to_queue_batch)
|
||||
speedDialView?.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView?.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && recyclerAdapter!!.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
speedDialView?.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialView?.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItem.id)
|
||||
.handleAction(recyclerAdapter!!.selectedItems.filterIsInstance<FeedItem>())
|
||||
recyclerAdapter?.endSelectMode()
|
||||
true
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(KEY_UP_ARROW, displayUpArrow)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
private fun refreshInfoBar() {
|
||||
if (queue == null) return
|
||||
var info = String.format(Locale.getDefault(), "%d%s", queue!!.size, getString(R.string.episodes_suffix))
|
||||
if (queue!!.size > 0) {
|
||||
var info = String.format(Locale.getDefault(), "%d%s", queue.size, getString(R.string.episodes_suffix))
|
||||
if (queue.size > 0) {
|
||||
var timeLeft: Long = 0
|
||||
for (item in queue!!) {
|
||||
for (item in queue) {
|
||||
var playbackSpeed = 1f
|
||||
if (UserPreferences.timeRespectsSpeed()) {
|
||||
playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.media)
|
||||
|
@ -467,44 +457,44 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
info += getString(R.string.time_left_label)
|
||||
info += Converter.getDurationStringLocalized(requireActivity(), timeLeft)
|
||||
}
|
||||
infoBar?.text = info
|
||||
infoBar.text = info
|
||||
}
|
||||
|
||||
private fun loadItems(restoreScrollPosition: Boolean) {
|
||||
Log.d(TAG, "loadItems()")
|
||||
disposable?.dispose()
|
||||
|
||||
if (queue == null) {
|
||||
emptyView?.hide()
|
||||
if (queue.isEmpty()) {
|
||||
emptyView.hide()
|
||||
}
|
||||
disposable =
|
||||
Observable.fromCallable { DBReader.getQueue().toMutableList() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ items: MutableList<FeedItem>? ->
|
||||
.subscribe({ items: MutableList<FeedItem> ->
|
||||
queue = items
|
||||
progressBar?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
recyclerAdapter?.setDummyViews(0)
|
||||
if (queue != null) recyclerAdapter?.updateItems(queue!!)
|
||||
recyclerAdapter?.updateItems(queue)
|
||||
if (restoreScrollPosition) {
|
||||
recyclerView?.restoreScrollPosition(TAG)
|
||||
recyclerView.restoreScrollPosition(TAG)
|
||||
}
|
||||
refreshInfoBar()
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
swipeActions?.detach()
|
||||
speedDialView?.visibility = View.VISIBLE
|
||||
swipeActions.detach()
|
||||
speedDialView.visibility = View.VISIBLE
|
||||
refreshToolbarState()
|
||||
infoBar?.visibility = View.GONE
|
||||
infoBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialView?.close()
|
||||
speedDialView?.visibility = View.GONE
|
||||
infoBar?.visibility = View.VISIBLE
|
||||
swipeActions?.attachTo(recyclerView)
|
||||
speedDialView.close()
|
||||
speedDialView.visibility = View.GONE
|
||||
infoBar.visibility = View.VISIBLE
|
||||
swipeActions.attachTo(recyclerView)
|
||||
}
|
||||
|
||||
class QueueSortDialog : ItemSortDialog() {
|
||||
|
@ -565,10 +555,10 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
val from = viewHolder.bindingAdapterPosition
|
||||
val to = target.bindingAdapterPosition
|
||||
Log.d(TAG, "move($from, $to) in memory")
|
||||
if (queue == null || from >= queue!!.size || to >= queue!!.size || from < 0 || to < 0) {
|
||||
if (from >= queue.size || to >= queue.size || from < 0 || to < 0) {
|
||||
return false
|
||||
}
|
||||
queue!!.add(to, queue!!.removeAt(from))
|
||||
queue.add(to, queue.removeAt(from))
|
||||
recyclerAdapter?.notifyItemMoved(from, to)
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
package ac.mdiq.podvinci.fragment
|
||||
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import ac.mdiq.podvinci.BuildConfig
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import ac.mdiq.podvinci.activity.OnlineFeedViewActivity
|
||||
import ac.mdiq.podvinci.adapter.FeedDiscoverAdapter
|
||||
import ac.mdiq.podvinci.core.storage.DBReader
|
||||
import ac.mdiq.podvinci.event.DiscoveryDefaultUpdateEvent
|
||||
import ac.mdiq.podvinci.net.discovery.ItunesTopListLoader
|
||||
import ac.mdiq.podvinci.net.discovery.PodcastSearchResult
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -32,14 +33,15 @@ import java.util.*
|
|||
|
||||
class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||
private var disposable: Disposable? = null
|
||||
private var adapter: FeedDiscoverAdapter? = null
|
||||
private var discoverGridLayout: GridView? = null
|
||||
private var errorTextView: TextView? = null
|
||||
private var poweredByTextView: TextView? = null
|
||||
private var errorView: LinearLayout? = null
|
||||
private var errorRetry: Button? = null
|
||||
|
||||
private lateinit var adapter: FeedDiscoverAdapter
|
||||
private lateinit var discoverGridLayout: GridView
|
||||
private lateinit var errorTextView: TextView
|
||||
private lateinit var poweredByTextView: TextView
|
||||
private lateinit var errorView: LinearLayout
|
||||
private lateinit var errorRetry: Button
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
@OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
val root: View = inflater.inflate(R.layout.quick_feed_discovery, container, false)
|
||||
val discoverMore = root.findViewById<View>(R.id.discover_more)
|
||||
|
@ -52,15 +54,15 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
poweredByTextView = root.findViewById(R.id.discover_powered_by_itunes)
|
||||
|
||||
adapter = FeedDiscoverAdapter(activity as MainActivity)
|
||||
discoverGridLayout?.setAdapter(adapter)
|
||||
discoverGridLayout?.onItemClickListener = this
|
||||
discoverGridLayout.setAdapter(adapter)
|
||||
discoverGridLayout.onItemClickListener = this
|
||||
|
||||
val displayMetrics: DisplayMetrics = requireContext().resources.displayMetrics
|
||||
val screenWidthDp: Float = displayMetrics.widthPixels / displayMetrics.density
|
||||
if (screenWidthDp > 600) {
|
||||
discoverGridLayout?.numColumns = 6
|
||||
discoverGridLayout.numColumns = 6
|
||||
} else {
|
||||
discoverGridLayout?.numColumns = 4
|
||||
discoverGridLayout.numColumns = 4
|
||||
}
|
||||
|
||||
// Fill with dummy elements to have a fixed height and
|
||||
|
@ -70,7 +72,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
dummies.add(PodcastSearchResult.dummy())
|
||||
}
|
||||
|
||||
adapter?.updateData(dummies)
|
||||
adapter.updateData(dummies)
|
||||
loadToplist()
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
@ -90,31 +92,31 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
}
|
||||
|
||||
private fun loadToplist() {
|
||||
errorView?.visibility = View.GONE
|
||||
errorRetry!!.visibility = View.INVISIBLE
|
||||
errorRetry?.setText(R.string.retry_label)
|
||||
poweredByTextView?.visibility = View.VISIBLE
|
||||
errorView.visibility = View.GONE
|
||||
errorRetry.visibility = View.INVISIBLE
|
||||
errorRetry.setText(R.string.retry_label)
|
||||
poweredByTextView.visibility = View.VISIBLE
|
||||
|
||||
val loader = ItunesTopListLoader(requireContext())
|
||||
val prefs: SharedPreferences = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
||||
val countryCode: String = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE,
|
||||
Locale.getDefault().country)!!
|
||||
if (prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)) {
|
||||
errorTextView?.setText(R.string.discover_is_hidden)
|
||||
errorView?.visibility = View.VISIBLE
|
||||
discoverGridLayout?.visibility = View.GONE
|
||||
errorRetry!!.visibility = View.GONE
|
||||
poweredByTextView?.visibility = View.GONE
|
||||
errorTextView.setText(R.string.discover_is_hidden)
|
||||
errorView.visibility = View.VISIBLE
|
||||
discoverGridLayout.visibility = View.GONE
|
||||
errorRetry.visibility = View.GONE
|
||||
poweredByTextView.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
if (BuildConfig.FLAVOR == "free" && prefs.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)) {
|
||||
errorTextView?.text = ""
|
||||
errorView?.visibility = View.VISIBLE
|
||||
discoverGridLayout?.visibility = View.VISIBLE
|
||||
errorRetry!!.visibility = View.VISIBLE
|
||||
errorRetry?.setText(R.string.discover_confirm)
|
||||
poweredByTextView?.visibility = View.VISIBLE
|
||||
errorRetry!!.setOnClickListener { v: View? ->
|
||||
errorTextView.text = ""
|
||||
errorView.visibility = View.VISIBLE
|
||||
discoverGridLayout.visibility = View.VISIBLE
|
||||
errorRetry.visibility = View.VISIBLE
|
||||
errorRetry.setText(R.string.discover_confirm)
|
||||
poweredByTextView.visibility = View.VISIBLE
|
||||
errorRetry.setOnClickListener { v: View? ->
|
||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
||||
loadToplist()
|
||||
}
|
||||
|
@ -130,32 +132,32 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ podcasts: List<PodcastSearchResult> ->
|
||||
errorView?.visibility = View.GONE
|
||||
errorView.visibility = View.GONE
|
||||
if (podcasts.isEmpty()) {
|
||||
errorTextView?.text = resources.getText(R.string.search_status_no_results)
|
||||
errorView?.visibility = View.VISIBLE
|
||||
discoverGridLayout?.visibility = View.INVISIBLE
|
||||
errorTextView.text = resources.getText(R.string.search_status_no_results)
|
||||
errorView.visibility = View.VISIBLE
|
||||
discoverGridLayout.visibility = View.INVISIBLE
|
||||
} else {
|
||||
discoverGridLayout?.visibility = View.VISIBLE
|
||||
adapter?.updateData(podcasts)
|
||||
discoverGridLayout.visibility = View.VISIBLE
|
||||
adapter.updateData(podcasts)
|
||||
}
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
errorTextView?.text = error.localizedMessage
|
||||
errorView?.visibility = View.VISIBLE
|
||||
discoverGridLayout?.visibility = View.INVISIBLE
|
||||
errorRetry!!.visibility = View.VISIBLE
|
||||
errorRetry?.setOnClickListener { v: View? -> loadToplist() }
|
||||
errorTextView.text = error.localizedMessage
|
||||
errorView.visibility = View.VISIBLE
|
||||
discoverGridLayout.visibility = View.INVISIBLE
|
||||
errorRetry.visibility = View.VISIBLE
|
||||
errorRetry.setOnClickListener { v: View? -> loadToplist() }
|
||||
})
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
val podcast: PodcastSearchResult = adapter!!.getItem(position)
|
||||
if (TextUtils.isEmpty(podcast.feedUrl)) {
|
||||
val podcast: PodcastSearchResult? = adapter.getItem(position)
|
||||
if (podcast?.feedUrl.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
val intent = Intent(activity, OnlineFeedViewActivity::class.java)
|
||||
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl)
|
||||
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast!!.feedUrl)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
|
|
|
@ -55,18 +55,20 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
* Performs a search operation on all feeds or one specific feed and displays the search result.
|
||||
*/
|
||||
class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
||||
private var adapter: EpisodeItemListAdapter? = null
|
||||
private var adapterFeeds: HorizontalFeedListAdapter? = null
|
||||
private lateinit var adapter: EpisodeItemListAdapter
|
||||
private lateinit var adapterFeeds: HorizontalFeedListAdapter
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var emptyViewHandler: EmptyViewHandler
|
||||
private lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
private lateinit var searchView: SearchView
|
||||
private lateinit var speedDialBinding: MultiSelectSpeedDialBinding
|
||||
private lateinit var chip: Chip
|
||||
private lateinit var automaticSearchDebouncer: Handler
|
||||
|
||||
private var results: MutableList<FeedItem> = mutableListOf()
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private var emptyViewHandler: EmptyViewHandler? = null
|
||||
private var recyclerView: EpisodeItemListRecyclerView? = null
|
||||
private var results: MutableList<FeedItem>? = null
|
||||
private var chip: Chip? = null
|
||||
private var searchView: SearchView? = null
|
||||
private var automaticSearchDebouncer: Handler? = null
|
||||
private var lastQueryChange: Long = 0
|
||||
private var speedDialBinding: MultiSelectSpeedDialBinding? = null
|
||||
private var isOtherViewInFoucus = false
|
||||
|
||||
|
||||
|
@ -89,8 +91,8 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
speedDialBinding = MultiSelectSpeedDialBinding.bind(layout)
|
||||
progressBar = layout.findViewById(R.id.progressBar)
|
||||
recyclerView = layout.findViewById(R.id.recyclerView)
|
||||
recyclerView?.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
registerForContextMenu(recyclerView!!)
|
||||
recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
|
||||
registerForContextMenu(recyclerView)
|
||||
adapter = object : EpisodeItemListAdapter(activity as MainActivity) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
|
@ -101,9 +103,9 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
) { item: MenuItem -> this@SearchFragment.onContextItemSelected(item) }
|
||||
}
|
||||
}
|
||||
adapter?.setOnSelectModeListener(this)
|
||||
recyclerView?.adapter = adapter
|
||||
recyclerView?.addOnScrollListener(LiftOnScrollListener(layout.findViewById(R.id.appbar)))
|
||||
adapter.setOnSelectModeListener(this)
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.addOnScrollListener(LiftOnScrollListener(layout.findViewById(R.id.appbar)))
|
||||
|
||||
val recyclerViewFeeds = layout.findViewById<RecyclerView>(R.id.recyclerViewFeeds)
|
||||
val layoutManagerFeeds = LinearLayoutManager(activity)
|
||||
|
@ -121,28 +123,28 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
recyclerViewFeeds.adapter = adapterFeeds
|
||||
|
||||
emptyViewHandler = EmptyViewHandler(context)
|
||||
emptyViewHandler?.attachToRecyclerView(recyclerView!!)
|
||||
emptyViewHandler?.setIcon(R.drawable.ic_search)
|
||||
emptyViewHandler?.setTitle(R.string.search_status_no_results)
|
||||
emptyViewHandler?.setMessage(R.string.type_to_search)
|
||||
emptyViewHandler.attachToRecyclerView(recyclerView)
|
||||
emptyViewHandler.setIcon(R.drawable.ic_search)
|
||||
emptyViewHandler.setTitle(R.string.search_status_no_results)
|
||||
emptyViewHandler.setMessage(R.string.type_to_search)
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
chip = layout.findViewById(R.id.feed_title_chip)
|
||||
chip?.setOnCloseIconClickListener { v: View? ->
|
||||
chip.setOnCloseIconClickListener { v: View? ->
|
||||
requireArguments().putLong(ARG_FEED, 0)
|
||||
searchWithProgressBar()
|
||||
}
|
||||
chip?.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||
chip?.text = requireArguments().getString(ARG_FEED_NAME, "")
|
||||
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||
chip.text = requireArguments().getString(ARG_FEED_NAME, "")
|
||||
if (requireArguments().getString(ARG_QUERY, null) != null) {
|
||||
search()
|
||||
}
|
||||
searchView!!.setOnQueryTextFocusChangeListener { view: View, hasFocus: Boolean ->
|
||||
searchView.setOnQueryTextFocusChangeListener { view: View, hasFocus: Boolean ->
|
||||
if (hasFocus && !isOtherViewInFoucus) {
|
||||
showInputMethod(view.findFocus())
|
||||
}
|
||||
}
|
||||
recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
|
@ -151,24 +153,24 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
}
|
||||
}
|
||||
})
|
||||
speedDialBinding!!.fabSD.overlayLayout = speedDialBinding!!.fabSDOverlay
|
||||
speedDialBinding!!.fabSD.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialBinding!!.fabSD.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
speedDialBinding.fabSD.overlayLayout = speedDialBinding.fabSDOverlay
|
||||
speedDialBinding.fabSD.inflate(R.menu.episodes_apply_action_speeddial)
|
||||
speedDialBinding.fabSD.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onToggleChanged(open: Boolean) {
|
||||
if (open && adapter!!.selectedCount == 0) {
|
||||
if (open && adapter.selectedCount == 0) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(R.string.no_items_selected, Snackbar.LENGTH_SHORT)
|
||||
speedDialBinding!!.fabSD.close()
|
||||
speedDialBinding.fabSD.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
speedDialBinding!!.fabSD.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
speedDialBinding.fabSD.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
EpisodeMultiSelectActionHandler(activity as MainActivity, actionItem.id)
|
||||
.handleAction(adapter!!.selectedItems.filterIsInstance<FeedItem>())
|
||||
adapter?.endSelectMode()
|
||||
.handleAction(adapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
adapter.endSelectMode()
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -187,24 +189,24 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
val item: MenuItem = toolbar.menu.findItem(R.id.action_search)
|
||||
item.expandActionView()
|
||||
searchView = item.actionView as SearchView?
|
||||
searchView!!.queryHint = getString(R.string.search_label)
|
||||
searchView!!.setQuery(requireArguments().getString(ARG_QUERY), true)
|
||||
searchView!!.requestFocus()
|
||||
searchView!!.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
searchView = item.actionView as SearchView
|
||||
searchView.queryHint = getString(R.string.search_label)
|
||||
searchView.setQuery(requireArguments().getString(ARG_QUERY), true)
|
||||
searchView.requestFocus()
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
@UnstableApi override fun onQueryTextSubmit(s: String): Boolean {
|
||||
searchView!!.clearFocus()
|
||||
searchView.clearFocus()
|
||||
searchWithProgressBar()
|
||||
return true
|
||||
}
|
||||
|
||||
@UnstableApi override fun onQueryTextChange(s: String): Boolean {
|
||||
automaticSearchDebouncer!!.removeCallbacksAndMessages(null)
|
||||
automaticSearchDebouncer.removeCallbacksAndMessages(null)
|
||||
if (s.isEmpty() || s.endsWith(" ") || (lastQueryChange != 0L
|
||||
&& System.currentTimeMillis() > lastQueryChange + SEARCH_DEBOUNCE_INTERVAL)) {
|
||||
search()
|
||||
} else {
|
||||
automaticSearchDebouncer!!.postDelayed({
|
||||
automaticSearchDebouncer.postDelayed({
|
||||
search()
|
||||
lastQueryChange = 0 // Don't search instantly with first symbol after some pause
|
||||
}, (SEARCH_DEBOUNCE_INTERVAL / 2).toLong())
|
||||
|
@ -226,14 +228,14 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
val selectedFeedItem: Feed? = adapterFeeds?.longPressedItem
|
||||
val selectedFeedItem: Feed? = adapterFeeds.longPressedItem
|
||||
if (selectedFeedItem != null
|
||||
&& FeedMenuHandler.onMenuItemClicked(this, item.itemId, selectedFeedItem) {}) {
|
||||
return true
|
||||
}
|
||||
val selectedItem: FeedItem? = adapter?.longPressedItem
|
||||
val selectedItem: FeedItem? = adapter.longPressedItem
|
||||
if (selectedItem != null) {
|
||||
if (adapter!!.onContextItemSelected(item)) {
|
||||
if (adapter.onContextItemSelected(item)) {
|
||||
return true
|
||||
}
|
||||
if (FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem)) {
|
||||
|
@ -256,21 +258,16 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
if (results == null) {
|
||||
return
|
||||
} else if (adapter == null) {
|
||||
search()
|
||||
return
|
||||
}
|
||||
|
||||
var i = 0
|
||||
val size: Int = event.items.size
|
||||
while (i < size) {
|
||||
val item: FeedItem = event.items[i]
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithId(results!!, item.id)
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithId(results, item.id)
|
||||
if (pos >= 0) {
|
||||
results!!.removeAt(pos)
|
||||
results!!.add(pos, item)
|
||||
adapter!!.notifyItemChangedCompat(pos)
|
||||
results.removeAt(pos)
|
||||
results.add(pos, item)
|
||||
adapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
@ -278,27 +275,22 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||
if (results == null) {
|
||||
return
|
||||
}
|
||||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(results!!, downloadUrl)
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(results, downloadUrl)
|
||||
if (pos >= 0) {
|
||||
adapter?.notifyItemChangedCompat(pos)
|
||||
adapter.notifyItemChangedCompat(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
if (adapter != null) {
|
||||
for (i in 0 until adapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder =
|
||||
recyclerView!!.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder
|
||||
if (holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
}
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder =
|
||||
recyclerView.findViewHolderForAdapterPosition(i) as EpisodeItemViewHolder
|
||||
if (holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -309,40 +301,40 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
}
|
||||
|
||||
@UnstableApi private fun searchWithProgressBar() {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
emptyViewHandler?.hide()
|
||||
progressBar.visibility = View.VISIBLE
|
||||
emptyViewHandler.hide()
|
||||
search()
|
||||
}
|
||||
|
||||
@UnstableApi private fun search() {
|
||||
disposable?.dispose()
|
||||
|
||||
adapterFeeds?.setEndButton(R.string.search_online) { this.searchOnline() }
|
||||
chip?.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||
adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() }
|
||||
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||
disposable = Observable.fromCallable { this.performSearch() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ results: Pair<List<FeedItem>?, List<Feed?>?> ->
|
||||
progressBar?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
if (results.first != null) {
|
||||
this.results = results.first!!.toMutableList()
|
||||
adapter?.updateItems(results.first!!)
|
||||
adapter.updateItems(results.first!!)
|
||||
}
|
||||
if (requireArguments().getLong(ARG_FEED, 0) == 0L) {
|
||||
if (results.second != null) adapterFeeds?.updateData(results.second!!.filterNotNull())
|
||||
if (results.second != null) adapterFeeds.updateData(results.second!!.filterNotNull())
|
||||
} else {
|
||||
adapterFeeds?.updateData(emptyList())
|
||||
adapterFeeds.updateData(emptyList())
|
||||
}
|
||||
if (searchView!!.query.toString().isEmpty()) {
|
||||
emptyViewHandler?.setMessage(R.string.type_to_search)
|
||||
if (searchView.query.toString().isEmpty()) {
|
||||
emptyViewHandler.setMessage(R.string.type_to_search)
|
||||
} else {
|
||||
emptyViewHandler?.setMessage(getString(R.string.no_results_for_query) + searchView!!.query)
|
||||
emptyViewHandler.setMessage(getString(R.string.no_results_for_query) + searchView.query)
|
||||
}
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
@UnstableApi private fun performSearch(): Pair<List<FeedItem>?, List<Feed?>?> {
|
||||
val query = searchView!!.query.toString()
|
||||
val query = searchView.query.toString()
|
||||
if (query.isEmpty()) {
|
||||
return Pair<List<FeedItem>?, List<Feed?>?>(emptyList(), emptyList<Feed>())
|
||||
}
|
||||
|
@ -358,10 +350,10 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
}
|
||||
|
||||
@UnstableApi private fun searchOnline() {
|
||||
searchView!!.clearFocus()
|
||||
searchView.clearFocus()
|
||||
val inVal = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inVal.hideSoftInputFromWindow(searchView!!.windowToken, 0)
|
||||
val query = searchView!!.query.toString()
|
||||
inVal.hideSoftInputFromWindow(searchView.windowToken, 0)
|
||||
val query = searchView.query.toString()
|
||||
if (query.matches("http[s]?://.*".toRegex())) {
|
||||
val intent = Intent(activity, OnlineFeedViewActivity::class.java)
|
||||
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, query)
|
||||
|
@ -374,26 +366,26 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
override fun onStartSelectMode() {
|
||||
searchViewFocusOff()
|
||||
speedDialBinding!!.fabSD.removeActionItemById(R.id.remove_from_inbox_batch)
|
||||
speedDialBinding!!.fabSD.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialBinding!!.fabSD.removeActionItemById(R.id.delete_batch)
|
||||
speedDialBinding!!.fabSD.visibility = View.VISIBLE
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.remove_from_inbox_batch)
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.delete_batch)
|
||||
speedDialBinding.fabSD.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialBinding!!.fabSD.close()
|
||||
speedDialBinding!!.fabSD.visibility = View.GONE
|
||||
speedDialBinding.fabSD.close()
|
||||
speedDialBinding.fabSD.visibility = View.GONE
|
||||
searchViewFocusOn()
|
||||
}
|
||||
|
||||
private fun searchViewFocusOff() {
|
||||
isOtherViewInFoucus = true
|
||||
searchView!!.clearFocus()
|
||||
searchView.clearFocus()
|
||||
}
|
||||
|
||||
private fun searchViewFocusOn() {
|
||||
isOtherViewInFoucus = false
|
||||
searchView!!.requestFocus()
|
||||
searchView.requestFocus()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -52,22 +52,21 @@ import java.util.*
|
|||
* Fragment for displaying feed subscriptions
|
||||
*/
|
||||
class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
|
||||
private var subscriptionRecycler: RecyclerView? = null
|
||||
private var subscriptionAdapter: SubscriptionsRecyclerAdapter? = null
|
||||
private var emptyView: EmptyViewHandler? = null
|
||||
private var feedsFilteredMsg: LinearLayout? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
private var swipeRefreshLayout: SwipeRefreshLayout? = null
|
||||
private var progressBar: ProgressBar? = null
|
||||
private var displayedFolder: String? = null
|
||||
private lateinit var subscriptionRecycler: RecyclerView
|
||||
private lateinit var subscriptionAdapter: SubscriptionsRecyclerAdapter
|
||||
private lateinit var emptyView: EmptyViewHandler
|
||||
private lateinit var feedsFilteredMsg: LinearLayout
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var displayedFolder: String
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var speedDialView: SpeedDialView
|
||||
|
||||
private var displayUpArrow = false
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
private var prefs: SharedPreferences? = null
|
||||
|
||||
private var speedDialView: SpeedDialView? = null
|
||||
|
||||
private var listItems: List<NavDrawerData.DrawerItem?>? = null
|
||||
private var listItems: List<NavDrawerData.DrawerItem> = mutableListOf()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -81,36 +80,34 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
): View {
|
||||
val root: View = inflater.inflate(R.layout.fragment_subscriptions, container, false)
|
||||
toolbar = root.findViewById(R.id.toolbar)
|
||||
toolbar?.setOnMenuItemClickListener(this)
|
||||
toolbar?.setOnLongClickListener { v: View? ->
|
||||
subscriptionRecycler!!.scrollToPosition(5)
|
||||
subscriptionRecycler!!.post { subscriptionRecycler!!.smoothScrollToPosition(0) }
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener { v: View? ->
|
||||
subscriptionRecycler.scrollToPosition(5)
|
||||
subscriptionRecycler.post { subscriptionRecycler.smoothScrollToPosition(0) }
|
||||
false
|
||||
}
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
if (toolbar != null) (activity as MainActivity).setupToolbarToggle(toolbar!!, displayUpArrow)
|
||||
toolbar?.inflateMenu(R.menu.subscriptions)
|
||||
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
|
||||
toolbar.inflateMenu(R.menu.subscriptions)
|
||||
for (i in COLUMN_CHECKBOX_IDS.indices) {
|
||||
// Do this in Java to localize numbers
|
||||
toolbar?.menu?.findItem(COLUMN_CHECKBOX_IDS[i])
|
||||
toolbar.menu?.findItem(COLUMN_CHECKBOX_IDS[i])
|
||||
?.setTitle(String.format(Locale.getDefault(), "%d", i + MIN_NUM_COLUMNS))
|
||||
}
|
||||
refreshToolbarState()
|
||||
|
||||
if (arguments != null) {
|
||||
displayedFolder = requireArguments().getString(ARGUMENT_FOLDER, null)
|
||||
if (displayedFolder != null) {
|
||||
toolbar?.title = displayedFolder
|
||||
}
|
||||
toolbar.title = displayedFolder
|
||||
}
|
||||
|
||||
subscriptionRecycler = root.findViewById(R.id.subscriptions_grid)
|
||||
subscriptionRecycler?.addItemDecoration(SubscriptionsRecyclerAdapter.GridDividerItemDecorator())
|
||||
if (subscriptionRecycler != null) registerForContextMenu(subscriptionRecycler!!)
|
||||
subscriptionRecycler?.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
subscriptionRecycler.addItemDecoration(SubscriptionsRecyclerAdapter.GridDividerItemDecorator())
|
||||
registerForContextMenu(subscriptionRecycler)
|
||||
subscriptionRecycler.addOnScrollListener(LiftOnScrollListener(root.findViewById(R.id.appbar)))
|
||||
subscriptionAdapter = object : SubscriptionsRecyclerAdapter(activity as MainActivity) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
|
@ -120,13 +117,13 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
}
|
||||
}
|
||||
setColumnNumber(prefs!!.getInt(PREF_NUM_COLUMNS, defaultNumOfColumns))
|
||||
subscriptionAdapter?.setOnSelectModeListener(this)
|
||||
subscriptionRecycler?.adapter = subscriptionAdapter
|
||||
setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, defaultNumOfColumns))
|
||||
subscriptionAdapter.setOnSelectModeListener(this)
|
||||
subscriptionRecycler.adapter = subscriptionAdapter
|
||||
setupEmptyView()
|
||||
|
||||
progressBar = root.findViewById(R.id.progressBar)
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
val subscriptionAddButton: FloatingActionButton =
|
||||
root.findViewById(R.id.subscriptions_add)
|
||||
|
@ -137,21 +134,21 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
|
||||
feedsFilteredMsg = root.findViewById(R.id.feeds_filtered_message)
|
||||
feedsFilteredMsg?.setOnClickListener { l: View? ->
|
||||
feedsFilteredMsg.setOnClickListener { l: View? ->
|
||||
SubscriptionsFilterDialog().show(
|
||||
childFragmentManager, "filter")
|
||||
}
|
||||
|
||||
swipeRefreshLayout = root.findViewById(R.id.swipeRefresh)
|
||||
swipeRefreshLayout?.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout?.setOnRefreshListener {
|
||||
swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
}
|
||||
|
||||
speedDialView = root.findViewById(R.id.fabSD)
|
||||
speedDialView?.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView?.inflate(R.menu.nav_feed_action_speeddial)
|
||||
speedDialView?.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
speedDialView.overlayLayout = root.findViewById(R.id.fabSDOverlay)
|
||||
speedDialView.inflate(R.menu.nav_feed_action_speeddial)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
@ -159,9 +156,9 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
override fun onToggleChanged(isOpen: Boolean) {
|
||||
}
|
||||
})
|
||||
speedDialView?.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
speedDialView.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
FeedMultiSelectActionHandler(activity as MainActivity,
|
||||
subscriptionAdapter!!.selectedItems.filterIsInstance<Feed>()).handleAction(actionItem.id)
|
||||
subscriptionAdapter.selectedItems.filterIsInstance<Feed>()).handleAction(actionItem.id)
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -174,13 +171,13 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
|
||||
private fun refreshToolbarState() {
|
||||
val columns: Int = prefs!!.getInt(PREF_NUM_COLUMNS, defaultNumOfColumns)
|
||||
toolbar?.menu?.findItem(COLUMN_CHECKBOX_IDS[columns - MIN_NUM_COLUMNS])?.setChecked(true)
|
||||
val columns: Int = prefs.getInt(PREF_NUM_COLUMNS, defaultNumOfColumns)
|
||||
toolbar.menu?.findItem(COLUMN_CHECKBOX_IDS[columns - MIN_NUM_COLUMNS])?.setChecked(true)
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedUpdateRunningEvent) {
|
||||
swipeRefreshLayout?.isRefreshing = event.isFeedUpdateRunning
|
||||
swipeRefreshLayout.isRefreshing = event.isFeedUpdateRunning
|
||||
}
|
||||
|
||||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
|
@ -229,18 +226,18 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
private fun setColumnNumber(columns: Int) {
|
||||
val gridLayoutManager = GridLayoutManager(context,
|
||||
columns, RecyclerView.VERTICAL, false)
|
||||
subscriptionAdapter?.setColumnCount(columns)
|
||||
subscriptionRecycler!!.layoutManager = gridLayoutManager
|
||||
prefs!!.edit().putInt(PREF_NUM_COLUMNS, columns).apply()
|
||||
subscriptionAdapter.setColumnCount(columns)
|
||||
subscriptionRecycler.layoutManager = gridLayoutManager
|
||||
prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply()
|
||||
refreshToolbarState()
|
||||
}
|
||||
|
||||
private fun setupEmptyView() {
|
||||
emptyView = EmptyViewHandler(context)
|
||||
emptyView?.setIcon(R.drawable.ic_subscriptions)
|
||||
emptyView?.setTitle(R.string.no_subscriptions_head_label)
|
||||
emptyView?.setMessage(R.string.no_subscriptions_label)
|
||||
if (subscriptionRecycler != null) emptyView?.attachToRecyclerView(subscriptionRecycler!!)
|
||||
emptyView.setIcon(R.drawable.ic_subscriptions)
|
||||
emptyView.setTitle(R.string.no_subscriptions_head_label)
|
||||
emptyView.setMessage(R.string.no_subscriptions_label)
|
||||
emptyView.attachToRecyclerView(subscriptionRecycler)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -253,13 +250,12 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
subscriptionAdapter?.endSelectMode()
|
||||
subscriptionAdapter.endSelectMode()
|
||||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
disposable?.dispose()
|
||||
|
||||
emptyView?.hide()
|
||||
emptyView.hide()
|
||||
disposable = Observable.fromCallable {
|
||||
val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
val items: List<NavDrawerData.DrawerItem> = data.items
|
||||
|
@ -273,23 +269,23 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<NavDrawerData.DrawerItem?> ->
|
||||
if (listItems != null && listItems!!.size > result.size) {
|
||||
{ result: List<NavDrawerData.DrawerItem> ->
|
||||
if ( listItems.size > result.size) {
|
||||
// We have fewer items. This can result in items being selected that are no longer visible.
|
||||
subscriptionAdapter?.endSelectMode()
|
||||
subscriptionAdapter.endSelectMode()
|
||||
}
|
||||
listItems = result
|
||||
progressBar?.visibility = View.GONE
|
||||
subscriptionAdapter?.setItems(result.filterNotNull())
|
||||
emptyView?.updateVisibility()
|
||||
progressBar.visibility = View.GONE
|
||||
subscriptionAdapter.setItems(result)
|
||||
emptyView.updateVisibility()
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
|
||||
if (UserPreferences.subscriptionsFilter.isEnabled) {
|
||||
feedsFilteredMsg?.visibility = View.VISIBLE
|
||||
feedsFilteredMsg.visibility = View.VISIBLE
|
||||
} else {
|
||||
feedsFilteredMsg?.visibility = View.GONE
|
||||
feedsFilteredMsg.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,7 +293,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
get() = resources.getInteger(R.integer.subscriptions_default_num_of_columns)
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
val drawerItem: NavDrawerData.DrawerItem = subscriptionAdapter!!.getSelectedItem() ?: return false
|
||||
val drawerItem: NavDrawerData.DrawerItem = subscriptionAdapter.getSelectedItem() ?: return false
|
||||
val itemId = item.itemId
|
||||
if (drawerItem.type == NavDrawerData.DrawerItem.Type.TAG && itemId == R.id.rename_folder_item) {
|
||||
RenameItemDialog(activity as Activity, drawerItem).show()
|
||||
|
@ -306,8 +302,8 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
|
||||
val feed: Feed = (drawerItem as NavDrawerData.FeedDrawerItem).feed
|
||||
if (itemId == R.id.multi_select) {
|
||||
speedDialView?.visibility = View.VISIBLE
|
||||
return subscriptionAdapter!!.onContextItemSelected(item)
|
||||
speedDialView.visibility = View.VISIBLE
|
||||
return subscriptionAdapter.onContextItemSelected(item)
|
||||
}
|
||||
return FeedMenuHandler.onMenuItemClicked(this, item.itemId, feed) { this.loadSubscriptions() }
|
||||
}
|
||||
|
@ -323,20 +319,19 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
|
||||
override fun onEndSelectMode() {
|
||||
speedDialView?.close()
|
||||
speedDialView?.visibility = View.GONE
|
||||
if (listItems != null) subscriptionAdapter?.setItems(listItems!!.filterNotNull())
|
||||
speedDialView.close()
|
||||
speedDialView.visibility = View.GONE
|
||||
subscriptionAdapter.setItems(listItems)
|
||||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
val feedsOnly: MutableList<NavDrawerData.DrawerItem> = ArrayList<NavDrawerData.DrawerItem>()
|
||||
if (listItems != null) for (item in listItems!!) {
|
||||
if (item == null) continue
|
||||
for (item in listItems) {
|
||||
if (item.type == NavDrawerData.DrawerItem.Type.FEED) {
|
||||
feedsOnly.add(item)
|
||||
}
|
||||
}
|
||||
subscriptionAdapter?.setItems(feedsOnly)
|
||||
subscriptionAdapter.setItems(feedsOnly)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -10,7 +10,9 @@ import ac.mdiq.podvinci.core.util.LongList
|
|||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import ac.mdiq.podvinci.net.download.serviceinterface.DownloadServiceInterface
|
||||
import ac.mdiq.podvinci.view.LocalDeleteModal
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
@UnstableApi
|
||||
class EpisodeMultiSelectActionHandler(private val activity: MainActivity, private val actionId: Int) {
|
||||
private var totalNumItems = 0
|
||||
private var snackbar: Snackbar? = null
|
||||
|
|
|
@ -17,8 +17,10 @@ import ac.mdiq.podvinci.fragment.preferences.dialog.PreferenceListDialog
|
|||
import ac.mdiq.podvinci.fragment.preferences.dialog.PreferenceSwitchDialog
|
||||
import ac.mdiq.podvinci.model.feed.Feed
|
||||
import ac.mdiq.podvinci.model.feed.FeedPreferences
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import java.util.*
|
||||
|
||||
@UnstableApi
|
||||
class FeedMultiSelectActionHandler(private val activity: MainActivity, private val selectedItems: List<Feed>) {
|
||||
|
||||
fun handleAction(id: Int) {
|
||||
|
@ -56,7 +58,7 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
activity.getString(R.string.episode_notification_summary))
|
||||
|
||||
preferenceSwitchDialog.setOnPreferenceChangedListener(object: PreferenceSwitchDialog.OnPreferenceChangedListener {
|
||||
override fun preferenceChanged(enabled: Boolean) {
|
||||
@UnstableApi override fun preferenceChanged(enabled: Boolean) {
|
||||
saveFeedPreferences { feedPreferences: FeedPreferences ->
|
||||
feedPreferences.showEpisodeNotification = enabled
|
||||
}
|
||||
|
@ -69,7 +71,7 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
val preferenceSwitchDialog = PreferenceSwitchDialog(activity,
|
||||
activity.getString(R.string.auto_download_settings_label),
|
||||
activity.getString(R.string.auto_download_label))
|
||||
preferenceSwitchDialog.setOnPreferenceChangedListener(object: PreferenceSwitchDialog.OnPreferenceChangedListener {
|
||||
preferenceSwitchDialog.setOnPreferenceChangedListener(@UnstableApi object: PreferenceSwitchDialog.OnPreferenceChangedListener {
|
||||
override fun preferenceChanged(enabled: Boolean) {
|
||||
saveFeedPreferences { feedPreferences: FeedPreferences -> feedPreferences.autoDownload = enabled }
|
||||
}
|
||||
|
@ -77,7 +79,7 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
preferenceSwitchDialog.openDialog()
|
||||
}
|
||||
|
||||
private fun playbackSpeedPrefHandler() {
|
||||
@UnstableApi private fun playbackSpeedPrefHandler() {
|
||||
val viewBinding =
|
||||
PlaybackSpeedFeedSettingDialogBinding.inflate(activity.layoutInflater)
|
||||
viewBinding.seekBar.setProgressChangedListener { speed: Float? ->
|
||||
|
@ -110,7 +112,7 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
val items: Array<String> = activity.resources.getStringArray(R.array.spnAutoDeleteItems)
|
||||
preferenceListDialog.openDialog(items)
|
||||
preferenceListDialog.setOnPreferenceChangedListener(object: PreferenceListDialog.OnPreferenceChangedListener {
|
||||
override fun preferenceChanged(which: Int) {
|
||||
@UnstableApi override fun preferenceChanged(which: Int) {
|
||||
val autoDeleteAction: FeedPreferences.AutoDeleteAction = FeedPreferences.AutoDeleteAction.fromCode(which)
|
||||
saveFeedPreferences { feedPreferences: FeedPreferences ->
|
||||
feedPreferences.currentAutoDelete = autoDeleteAction
|
||||
|
@ -124,7 +126,7 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
activity.getString(R.string.kept_updated),
|
||||
activity.getString(R.string.keep_updated_summary))
|
||||
preferenceSwitchDialog.setOnPreferenceChangedListener(object: PreferenceSwitchDialog.OnPreferenceChangedListener {
|
||||
override fun preferenceChanged(keepUpdated: Boolean) {
|
||||
@UnstableApi override fun preferenceChanged(keepUpdated: Boolean) {
|
||||
saveFeedPreferences { feedPreferences: FeedPreferences ->
|
||||
feedPreferences.keepUpdated = keepUpdated
|
||||
}
|
||||
|
@ -133,12 +135,12 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
|
|||
preferenceSwitchDialog.openDialog()
|
||||
}
|
||||
|
||||
private fun showMessage(@PluralsRes msgId: Int, numItems: Int) {
|
||||
@UnstableApi private fun showMessage(@PluralsRes msgId: Int, numItems: Int) {
|
||||
activity.showSnackbarAbovePlayer(activity.resources
|
||||
.getQuantityString(msgId, numItems, numItems), Snackbar.LENGTH_LONG)
|
||||
}
|
||||
|
||||
private fun saveFeedPreferences(preferencesConsumer: Consumer<FeedPreferences>) {
|
||||
@UnstableApi private fun saveFeedPreferences(preferencesConsumer: Consumer<FeedPreferences>) {
|
||||
for (feed in selectedItems) {
|
||||
if (feed.preferences == null) continue
|
||||
preferencesConsumer.accept(feed.preferences)
|
||||
|
|
|
@ -30,9 +30,8 @@ class DeleteSwipeAction : SwipeAction {
|
|||
if (!item.isDownloaded && !item.feed!!.isLocalFeed) {
|
||||
return
|
||||
}
|
||||
showLocalFeedDeleteWarningIfNecessary(
|
||||
fragment.requireContext(), listOf(item)
|
||||
) { DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), item.media!!.id) }
|
||||
showLocalFeedDeleteWarningIfNecessary(fragment.requireContext(), listOf(item)) {
|
||||
DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), item.media!!.id) }
|
||||
}
|
||||
|
||||
override fun willRemove(filter: FeedItemFilter, item: FeedItem): Boolean {
|
||||
|
|
|
@ -21,11 +21,13 @@ import ac.mdiq.podvinci.model.feed.FeedItem
|
|||
import ac.mdiq.podvinci.model.feed.FeedMedia
|
||||
import ac.mdiq.podvinci.net.sync.model.EpisodeAction
|
||||
import ac.mdiq.podvinci.view.LocalDeleteModal
|
||||
import androidx.annotation.OptIn
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* Handles interactions with the FeedItemMenu.
|
||||
*/
|
||||
@OptIn(UnstableApi::class)
|
||||
object FeedItemMenuHandler {
|
||||
private const val TAG = "FeedItemMenuHandler"
|
||||
|
||||
|
@ -115,7 +117,7 @@ object FeedItemMenuHandler {
|
|||
return false
|
||||
}
|
||||
val rc = onPrepareMenu(menu, selectedItem)
|
||||
if (rc && excludeIds != null) {
|
||||
if (rc && excludeIds.isNotEmpty()) {
|
||||
for (id in excludeIds) {
|
||||
setItemVisibility(menu, id, false)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import ac.mdiq.podvinci.storage.preferences.UserPreferences
|
|||
import ac.mdiq.podvinci.ui.echo.EchoActivity
|
||||
import ac.mdiq.podvinci.ui.home.sections.*
|
||||
import ac.mdiq.podvinci.view.LiftOnScrollListener
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
@ -43,33 +44,32 @@ import java.util.*
|
|||
*/
|
||||
class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||
private var displayUpArrow = false
|
||||
private var viewBinding: HomeFragmentBinding? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
private lateinit var viewBinding: HomeFragmentBinding
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding = HomeFragmentBinding.inflate(inflater)
|
||||
viewBinding!!.toolbar.inflateMenu(R.menu.home)
|
||||
viewBinding!!.toolbar.setOnMenuItemClickListener(this)
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
}
|
||||
viewBinding!!.homeScrollView.setOnScrollChangeListener(LiftOnScrollListener(
|
||||
viewBinding!!.appbar))
|
||||
(requireActivity() as MainActivity).setupToolbarToggle(viewBinding!!.toolbar, displayUpArrow)
|
||||
viewBinding.toolbar.inflateMenu(R.menu.home)
|
||||
viewBinding.toolbar.setOnMenuItemClickListener(this)
|
||||
displayUpArrow = savedInstanceState?.getBoolean(KEY_UP_ARROW)?:false
|
||||
|
||||
viewBinding.homeScrollView.setOnScrollChangeListener(LiftOnScrollListener(viewBinding.appbar))
|
||||
(requireActivity() as MainActivity).setupToolbarToggle(viewBinding.toolbar, displayUpArrow)
|
||||
populateSectionList()
|
||||
updateWelcomeScreenVisibility()
|
||||
|
||||
viewBinding!!.swipeRefresh.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
viewBinding!!.swipeRefresh.setOnRefreshListener {
|
||||
viewBinding.swipeRefresh.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
viewBinding.swipeRefresh.setOnRefreshListener {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
}
|
||||
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
private fun populateSectionList() {
|
||||
viewBinding!!.homeContainer.removeAllViews()
|
||||
viewBinding.homeContainer.removeAllViews()
|
||||
|
||||
val prefs: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
if (Build.VERSION.SDK_INT >= 33 && ContextCompat.checkSelfPermission(requireContext(),
|
||||
|
@ -78,8 +78,10 @@ class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
addSection(AllowNotificationsSection())
|
||||
}
|
||||
}
|
||||
if (Calendar.getInstance()[Calendar.YEAR] == EchoActivity.RELEASE_YEAR && Calendar.getInstance()[Calendar.MONTH] == Calendar.DECEMBER && Calendar.getInstance()[Calendar.DAY_OF_MONTH] >= 10 && prefs.getInt(
|
||||
PREF_HIDE_ECHO, 0) != EchoActivity.RELEASE_YEAR) {
|
||||
if (Calendar.getInstance()[Calendar.YEAR] == EchoActivity.RELEASE_YEAR &&
|
||||
Calendar.getInstance()[Calendar.MONTH] == Calendar.DECEMBER &&
|
||||
Calendar.getInstance()[Calendar.DAY_OF_MONTH] >= 10 &&
|
||||
prefs.getInt(PREF_HIDE_ECHO, 0) != EchoActivity.RELEASE_YEAR) {
|
||||
addSection(EchoSection())
|
||||
}
|
||||
|
||||
|
@ -96,8 +98,8 @@ class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
private fun addSection(section: Fragment?) {
|
||||
val containerView = FragmentContainerView(requireContext())
|
||||
containerView.id = View.generateViewId()
|
||||
viewBinding!!.homeContainer.addView(containerView)
|
||||
childFragmentManager.beginTransaction().add(containerView.id, section!!).commit()
|
||||
viewBinding.homeContainer.addView(containerView)
|
||||
if (section != null) childFragmentManager.beginTransaction().add(containerView.id, section).commit()
|
||||
}
|
||||
|
||||
private fun getSection(tag: String): Fragment? {
|
||||
|
@ -113,10 +115,10 @@ class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedUpdateRunningEvent) {
|
||||
viewBinding!!.swipeRefresh.isRefreshing = event.isFeedUpdateRunning
|
||||
viewBinding.swipeRefresh.isRefreshing = event.isFeedUpdateRunning
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.homesettings_items -> {
|
||||
HomeSectionsSettingsDialog.open(requireContext()
|
||||
|
@ -128,7 +130,7 @@ class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
return true
|
||||
}
|
||||
R.id.action_search -> {
|
||||
(activity as MainActivity?)?.loadChildFragment(SearchFragment.newInstance())
|
||||
(activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
|
||||
return true
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
|
@ -156,16 +158,14 @@ class HomeFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
private fun updateWelcomeScreenVisibility() {
|
||||
if (disposable != null) {
|
||||
disposable?.dispose()
|
||||
}
|
||||
disposable =
|
||||
Observable.fromCallable { DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter).items.size }
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable { DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter).items.size }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ numSubscriptions: Int ->
|
||||
viewBinding!!.welcomeContainer.visibility = if (numSubscriptions == 0) View.VISIBLE else View.GONE
|
||||
viewBinding!!.homeContainer.visibility = if (numSubscriptions == 0) View.GONE else View.VISIBLE
|
||||
viewBinding.welcomeContainer.visibility = if (numSubscriptions == 0) View.VISIBLE else View.GONE
|
||||
viewBinding.homeContainer.visibility = if (numSubscriptions == 0) View.GONE else View.VISIBLE
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
|
|
|
@ -24,28 +24,25 @@ import java.util.*
|
|||
* Section on the HomeFragment
|
||||
*/
|
||||
abstract class HomeSection : Fragment(), OnCreateContextMenuListener {
|
||||
@JvmField
|
||||
protected var viewBinding: HomeSectionBinding? = null
|
||||
protected lateinit var viewBinding: HomeSectionBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
viewBinding = HomeSectionBinding.inflate(inflater)
|
||||
viewBinding!!.titleLabel.text = sectionTitle
|
||||
viewBinding.titleLabel.text = sectionTitle
|
||||
if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR) {
|
||||
viewBinding!!.moreButton.text = "$moreLinkTitle\u00A0»"
|
||||
viewBinding.moreButton.text = "$moreLinkTitle\u00A0»"
|
||||
} else {
|
||||
viewBinding!!.moreButton.text = "«\u00A0$moreLinkTitle"
|
||||
viewBinding.moreButton.text = "«\u00A0$moreLinkTitle"
|
||||
}
|
||||
viewBinding!!.moreButton.setOnClickListener { view: View? -> handleMoreClick() }
|
||||
viewBinding.moreButton.setOnClickListener { view: View? -> handleMoreClick() }
|
||||
if (TextUtils.isEmpty(moreLinkTitle)) {
|
||||
viewBinding!!.moreButton.visibility = View.INVISIBLE
|
||||
viewBinding.moreButton.visibility = View.INVISIBLE
|
||||
}
|
||||
// Dummies are necessary to ensure height, but do not animate them
|
||||
viewBinding!!.recyclerView.itemAnimator = null
|
||||
viewBinding!!.recyclerView.postDelayed(
|
||||
{ viewBinding!!.recyclerView.itemAnimator = DefaultItemAnimator() }, 500)
|
||||
return viewBinding!!.root
|
||||
viewBinding.recyclerView.itemAnimator = null
|
||||
viewBinding.recyclerView.postDelayed(
|
||||
{ viewBinding.recyclerView.itemAnimator = DefaultItemAnimator() }, 500)
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -54,19 +51,19 @@ abstract class HomeSection : Fragment(), OnCreateContextMenuListener {
|
|||
// Apparently, none of the visibility check method works reliably on its own, so we just use all.
|
||||
return false
|
||||
}
|
||||
if (viewBinding!!.recyclerView.adapter is HorizontalFeedListAdapter) {
|
||||
val adapter = viewBinding!!.recyclerView.adapter as HorizontalFeedListAdapter?
|
||||
val selectedFeed = adapter!!.longPressedItem
|
||||
return (selectedFeed != null
|
||||
&& FeedMenuHandler.onMenuItemClicked(this, item.itemId, selectedFeed) {})
|
||||
if (viewBinding.recyclerView.adapter is HorizontalFeedListAdapter) {
|
||||
val adapter = viewBinding.recyclerView.adapter as? HorizontalFeedListAdapter
|
||||
val selectedFeed = adapter?.longPressedItem
|
||||
return (selectedFeed != null && FeedMenuHandler.onMenuItemClicked(this, item.itemId, selectedFeed) {})
|
||||
}
|
||||
|
||||
var longPressedItem: FeedItem? = null
|
||||
if (viewBinding!!.recyclerView.adapter is EpisodeItemListAdapter) {
|
||||
val adapter = viewBinding!!.recyclerView.adapter as EpisodeItemListAdapter?
|
||||
if (adapter != null) longPressedItem = adapter.longPressedItem
|
||||
} else if (viewBinding!!.recyclerView.adapter is HorizontalItemListAdapter) {
|
||||
val adapter = viewBinding!!.recyclerView.adapter as HorizontalItemListAdapter?
|
||||
if (adapter != null) longPressedItem = adapter.longPressedItem
|
||||
if (viewBinding.recyclerView.adapter is EpisodeItemListAdapter) {
|
||||
val adapter = viewBinding.recyclerView.adapter as? EpisodeItemListAdapter
|
||||
longPressedItem = adapter?.longPressedItem
|
||||
} else if (viewBinding.recyclerView.adapter is HorizontalItemListAdapter) {
|
||||
val adapter = viewBinding.recyclerView.adapter as HorizontalItemListAdapter?
|
||||
longPressedItem = adapter?.longPressedItem
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
@ -81,13 +78,13 @@ abstract class HomeSection : Fragment(), OnCreateContextMenuListener {
|
|||
override fun onStart() {
|
||||
super.onStart()
|
||||
EventBus.getDefault().register(this)
|
||||
registerForContextMenu(viewBinding!!.recyclerView)
|
||||
registerForContextMenu(viewBinding.recyclerView)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
unregisterForContextMenu(viewBinding!!.recyclerView)
|
||||
unregisterForContextMenu(viewBinding.recyclerView)
|
||||
}
|
||||
|
||||
protected abstract val sectionTitle: String?
|
||||
|
|
|
@ -22,8 +22,7 @@ object HomeSectionsSettingsDialog {
|
|||
|
||||
val builder = MaterialAlertDialogBuilder(context)
|
||||
builder.setTitle(R.string.configure_home)
|
||||
builder.setMultiChoiceItems(sectionLabels,
|
||||
checked) { dialog: DialogInterface?, which: Int, isChecked: Boolean ->
|
||||
builder.setMultiChoiceItems(sectionLabels, checked) { dialog: DialogInterface?, which: Int, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
hiddenSections.remove(sectionTags[which])
|
||||
} else {
|
||||
|
|
|
@ -19,37 +19,39 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.databinding.HomeSectionNotificationBinding
|
||||
import ac.mdiq.podvinci.ui.home.HomeFragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
@UnstableApi
|
||||
class AllowNotificationsSection : Fragment() {
|
||||
var viewBinding: HomeSectionNotificationBinding? = null
|
||||
lateinit var viewBinding: HomeSectionNotificationBinding
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
(activity as MainActivity).loadFragment(HomeFragment.TAG, null)
|
||||
} else {
|
||||
viewBinding!!.openSettingsButton.visibility = View.VISIBLE
|
||||
viewBinding!!.allowButton.visibility = View.GONE
|
||||
viewBinding.openSettingsButton.visibility = View.VISIBLE
|
||||
viewBinding.allowButton.visibility = View.GONE
|
||||
Toast.makeText(context, R.string.notification_permission_denied, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = HomeSectionNotificationBinding.inflate(inflater)
|
||||
viewBinding!!.allowButton.setOnClickListener { v: View? ->
|
||||
viewBinding.allowButton.setOnClickListener { v: View? ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
viewBinding!!.openSettingsButton.setOnClickListener { view: View? ->
|
||||
viewBinding.openSettingsButton.setOnClickListener { view: View? ->
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
val uri = Uri.fromParts("package", requireContext().packageName, null)
|
||||
intent.setData(uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
viewBinding!!.denyButton.setOnClickListener { v: View? ->
|
||||
viewBinding.denyButton.setOnClickListener { v: View? ->
|
||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
builder.setMessage(R.string.notification_permission_deny_warning)
|
||||
builder.setPositiveButton(R.string.deny_label
|
||||
|
@ -61,6 +63,6 @@ class AllowNotificationsSection : Fragment() {
|
|||
builder.setNegativeButton(R.string.cancel_label, null)
|
||||
builder.show()
|
||||
}
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,17 +32,15 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
|
||||
class DownloadsSection : HomeSection() {
|
||||
private var adapter: EpisodeItemListAdapter? = null
|
||||
private var items: List<FeedItem>? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
val view: View = super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding?.recyclerView?.setPadding(0, 0, 0, 0)
|
||||
viewBinding?.recyclerView?.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER)
|
||||
viewBinding?.recyclerView?.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
viewBinding?.recyclerView?.setRecycledViewPool((requireActivity() as MainActivity).recycledViewPool)
|
||||
viewBinding.recyclerView.setPadding(0, 0, 0, 0)
|
||||
viewBinding.recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER)
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
viewBinding.recyclerView.setRecycledViewPool((requireActivity() as MainActivity).recycledViewPool)
|
||||
adapter = object : EpisodeItemListAdapter(requireActivity() as MainActivity) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
|
@ -53,10 +51,10 @@ class DownloadsSection : HomeSection() {
|
|||
}
|
||||
}
|
||||
adapter?.setDummyViews(NUM_EPISODES)
|
||||
if (adapter != null) viewBinding?.recyclerView?.adapter = adapter
|
||||
viewBinding.recyclerView.adapter = adapter
|
||||
|
||||
val swipeActions = SwipeActions(this, CompletedDownloadsFragment.TAG)
|
||||
if (viewBinding != null) swipeActions.attachTo(viewBinding!!.recyclerView)
|
||||
swipeActions.attachTo(viewBinding.recyclerView)
|
||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
||||
return view
|
||||
}
|
||||
|
@ -66,7 +64,7 @@ class DownloadsSection : HomeSection() {
|
|||
loadItems()
|
||||
}
|
||||
|
||||
override fun handleMoreClick() {
|
||||
@UnstableApi override fun handleMoreClick() {
|
||||
(requireActivity() as MainActivity).loadChildFragment(CompletedDownloadsFragment())
|
||||
}
|
||||
|
||||
|
@ -77,11 +75,11 @@ class DownloadsSection : HomeSection() {
|
|||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||
if (viewBinding == null || adapter == null) {
|
||||
if (adapter == null) {
|
||||
return
|
||||
}
|
||||
for (i in 0 until adapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = viewBinding!!.recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
val holder: EpisodeItemViewHolder? = viewBinding.recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
|
@ -119,9 +117,8 @@ class DownloadsSection : HomeSection() {
|
|||
if (downloads.size > NUM_EPISODES) {
|
||||
downloads = downloads.subList(0, NUM_EPISODES)
|
||||
}
|
||||
items = downloads
|
||||
adapter?.setDummyViews(0)
|
||||
if (items != null) adapter?.updateItems(items!!)
|
||||
adapter?.updateItems(downloads)
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
|
|
|
@ -13,28 +13,30 @@ import ac.mdiq.podvinci.core.storage.DBReader
|
|||
import ac.mdiq.podvinci.databinding.HomeSectionEchoBinding
|
||||
import ac.mdiq.podvinci.ui.echo.EchoActivity
|
||||
import ac.mdiq.podvinci.ui.home.HomeFragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.*
|
||||
|
||||
@UnstableApi
|
||||
class EchoSection : Fragment() {
|
||||
private var viewBinding: HomeSectionEchoBinding? = null
|
||||
private lateinit var viewBinding: HomeSectionEchoBinding
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = HomeSectionEchoBinding.inflate(inflater)
|
||||
viewBinding!!.titleLabel.text = getString(R.string.podvinci_echo_year, EchoActivity.RELEASE_YEAR)
|
||||
viewBinding!!.echoButton.setOnClickListener { v: View? ->
|
||||
viewBinding.titleLabel.text = getString(R.string.podvinci_echo_year, EchoActivity.RELEASE_YEAR)
|
||||
viewBinding.echoButton.setOnClickListener { v: View? ->
|
||||
startActivity(Intent(context,
|
||||
EchoActivity::class.java))
|
||||
}
|
||||
viewBinding!!.closeButton.setOnClickListener { v: View? -> hideThisYear() }
|
||||
viewBinding.closeButton.setOnClickListener { v: View? -> hideThisYear() }
|
||||
updateVisibility()
|
||||
return viewBinding!!.root
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
private fun jan1(): Long {
|
||||
|
@ -64,10 +66,9 @@ class EchoSection : Fragment() {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ totalTime: Long ->
|
||||
val shouldShow = (totalTime >= 3600 * 10)
|
||||
viewBinding!!.root.visibility = if (shouldShow) View.VISIBLE else View.GONE
|
||||
if (!shouldShow) {
|
||||
hideThisYear()
|
||||
}
|
||||
viewBinding.root.visibility = if (shouldShow) View.VISIBLE else View.GONE
|
||||
if (!shouldShow) hideThisYear()
|
||||
|
||||
}, { obj: Throwable -> obj.printStackTrace() })
|
||||
}
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
val view: View = super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding?.shuffleButton?.setVisibility(View.VISIBLE)
|
||||
viewBinding?.shuffleButton?.setOnClickListener { v: View? ->
|
||||
viewBinding.shuffleButton.setVisibility(View.VISIBLE)
|
||||
viewBinding.shuffleButton.setOnClickListener { v: View? ->
|
||||
seed = Random().nextInt()
|
||||
viewBinding?.recyclerView?.scrollToPosition(0)
|
||||
viewBinding.recyclerView.scrollToPosition(0)
|
||||
loadItems()
|
||||
}
|
||||
listAdapter = object : HorizontalItemListAdapter(activity as MainActivity) {
|
||||
|
@ -53,13 +53,12 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
}
|
||||
}
|
||||
listAdapter?.setDummyViews(NUM_EPISODES)
|
||||
viewBinding?.recyclerView?.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
viewBinding?.recyclerView?.adapter = listAdapter
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
viewBinding.recyclerView.adapter = listAdapter
|
||||
val paddingHorizontal: Int = (12 * resources.displayMetrics.density).toInt()
|
||||
viewBinding?.recyclerView?.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
if (seed == 0) {
|
||||
seed = Random().nextInt()
|
||||
}
|
||||
viewBinding.recyclerView.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
if (seed == 0) seed = Random().nextInt()
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -68,7 +67,7 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
loadItems()
|
||||
}
|
||||
|
||||
override fun handleMoreClick() {
|
||||
@UnstableApi override fun handleMoreClick() {
|
||||
(requireActivity() as MainActivity).loadChildFragment(AllEpisodesFragment())
|
||||
}
|
||||
|
||||
|
@ -105,9 +104,8 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(episodes, downloadUrl)
|
||||
if (pos >= 0) {
|
||||
listAdapter?.notifyItemChangedCompat(pos)
|
||||
}
|
||||
if (pos >= 0) listAdapter?.notifyItemChangedCompat(pos)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +115,7 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
return
|
||||
}
|
||||
for (i in 0 until listAdapter!!.itemCount) {
|
||||
val holder: HorizontalItemViewHolder? = viewBinding?.recyclerView?.findViewHolderForAdapterPosition(i) as? HorizontalItemViewHolder
|
||||
val holder: HorizontalItemViewHolder? = viewBinding.recyclerView.findViewHolderForAdapterPosition(i) as? HorizontalItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
break
|
||||
|
@ -128,10 +126,7 @@ class EpisodesSurpriseSection : HomeSection() {
|
|||
private fun loadItems() {
|
||||
disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable {
|
||||
DBReader.getRandomEpisodes(
|
||||
NUM_EPISODES, seed)
|
||||
}
|
||||
disposable = Observable.fromCallable { DBReader.getRandomEpisodes(NUM_EPISODES, seed) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ episodes: List<FeedItem> ->
|
||||
|
|
|
@ -40,10 +40,10 @@ class InboxSection : HomeSection() {
|
|||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
val view: View = super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding?.recyclerView?.setPadding(0, 0, 0, 0)
|
||||
viewBinding?.recyclerView?.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER)
|
||||
viewBinding?.recyclerView?.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
viewBinding?.recyclerView?.setRecycledViewPool((requireActivity() as MainActivity).recycledViewPool)
|
||||
viewBinding.recyclerView.setPadding(0, 0, 0, 0)
|
||||
viewBinding.recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER)
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
viewBinding.recyclerView.setRecycledViewPool((requireActivity() as MainActivity).recycledViewPool)
|
||||
adapter = object : EpisodeItemListAdapter(requireActivity() as MainActivity) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
|
@ -52,10 +52,10 @@ class InboxSection : HomeSection() {
|
|||
}
|
||||
}
|
||||
adapter?.setDummyViews(NUM_EPISODES)
|
||||
if (adapter != null) viewBinding?.recyclerView?.adapter = adapter
|
||||
viewBinding.recyclerView.adapter = adapter
|
||||
|
||||
val swipeActions = SwipeActions(this, InboxFragment.TAG)
|
||||
if (viewBinding != null) swipeActions.attachTo(viewBinding!!.recyclerView)
|
||||
swipeActions.attachTo(viewBinding.recyclerView)
|
||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.NEW))
|
||||
return view
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class InboxSection : HomeSection() {
|
|||
loadItems()
|
||||
}
|
||||
|
||||
override fun handleMoreClick() {
|
||||
@UnstableApi override fun handleMoreClick() {
|
||||
(requireActivity() as MainActivity).loadChildFragment(InboxFragment())
|
||||
}
|
||||
|
||||
|
@ -114,11 +114,11 @@ class InboxSection : HomeSection() {
|
|||
items = data.first
|
||||
adapter?.setDummyViews(0)
|
||||
adapter?.updateItems(items)
|
||||
viewBinding?.numNewItemsLabel?.visibility = View.VISIBLE
|
||||
viewBinding.numNewItemsLabel.visibility = View.VISIBLE
|
||||
if (data.second >= 100) {
|
||||
viewBinding?.numNewItemsLabel?.text = String.format(Locale.getDefault(), "%d+", 99)
|
||||
viewBinding.numNewItemsLabel.text = String.format(Locale.getDefault(), "%d+", 99)
|
||||
} else {
|
||||
viewBinding?.numNewItemsLabel?.text = String.format(Locale.getDefault(), "%d", data.second)
|
||||
viewBinding.numNewItemsLabel.text = String.format(Locale.getDefault(), "%d", data.second)
|
||||
}
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
package ac.mdiq.podvinci.ui.home.sections
|
||||
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ac.mdiq.podvinci.R
|
||||
import ac.mdiq.podvinci.activity.MainActivity
|
||||
import ac.mdiq.podvinci.adapter.HorizontalItemListAdapter
|
||||
import ac.mdiq.podvinci.core.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podvinci.core.storage.DBReader
|
||||
|
@ -21,13 +15,18 @@ import ac.mdiq.podvinci.fragment.QueueFragment
|
|||
import ac.mdiq.podvinci.model.feed.FeedItem
|
||||
import ac.mdiq.podvinci.ui.home.HomeSection
|
||||
import ac.mdiq.podvinci.view.viewholder.HorizontalItemViewHolder
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
class QueueSection : HomeSection() {
|
||||
private var listAdapter: HorizontalItemListAdapter? = null
|
||||
|
@ -46,10 +45,10 @@ class QueueSection : HomeSection() {
|
|||
}
|
||||
}
|
||||
listAdapter?.setDummyViews(NUM_EPISODES)
|
||||
viewBinding?.recyclerView?.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
viewBinding?.recyclerView?.adapter = listAdapter
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
viewBinding.recyclerView.adapter = listAdapter
|
||||
val paddingHorizontal: Int = (12 * resources.displayMetrics.density).toInt()
|
||||
viewBinding?.recyclerView?.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
viewBinding.recyclerView.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -58,7 +57,7 @@ class QueueSection : HomeSection() {
|
|||
loadItems()
|
||||
}
|
||||
|
||||
override fun handleMoreClick() {
|
||||
@UnstableApi override fun handleMoreClick() {
|
||||
(requireActivity() as MainActivity).loadChildFragment(QueueFragment())
|
||||
}
|
||||
|
||||
|
@ -112,7 +111,7 @@ class QueueSection : HomeSection() {
|
|||
var currentlyPlayingItemIsFirst = true
|
||||
for (i in 0 until listAdapter!!.itemCount) {
|
||||
val holder: HorizontalItemViewHolder =
|
||||
viewBinding?.recyclerView?.findViewHolderForAdapterPosition(i) as? HorizontalItemViewHolder ?: continue
|
||||
viewBinding.recyclerView.findViewHolderForAdapterPosition(i) as? HorizontalItemViewHolder ?: continue
|
||||
if (holder.isCurrentlyPlayingItem) {
|
||||
holder.notifyPlaybackPositionUpdated(event)
|
||||
foundCurrentlyPlayingItem = true
|
||||
|
|
|
@ -34,7 +34,7 @@ class SubscriptionsSection : HomeSection() {
|
|||
container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
val view: View = super.onCreateView(inflater, container, savedInstanceState)
|
||||
viewBinding?.recyclerView?.layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false)
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false)
|
||||
listAdapter = object : HorizontalFeedListAdapter(activity as MainActivity) {
|
||||
override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo?
|
||||
) {
|
||||
|
@ -46,9 +46,9 @@ class SubscriptionsSection : HomeSection() {
|
|||
}
|
||||
}
|
||||
listAdapter?.setDummyViews(NUM_FEEDS)
|
||||
viewBinding?.recyclerView?.adapter = listAdapter
|
||||
viewBinding.recyclerView.adapter = listAdapter
|
||||
val paddingHorizontal: Int = (12 * resources.displayMetrics.density).toInt()
|
||||
viewBinding?.recyclerView?.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
viewBinding.recyclerView.setPadding(paddingHorizontal, 0, paddingHorizontal, 0)
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -78,11 +78,8 @@ class SubscriptionsSection : HomeSection() {
|
|||
val prefs: SharedPreferences =
|
||||
requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE)
|
||||
val includeMarkedAsPlayed: Boolean = prefs.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false)
|
||||
disposable = Observable.fromCallable<List<StatisticsItem>> {
|
||||
DBReader.getStatistics(includeMarkedAsPlayed,
|
||||
0,
|
||||
Long.MAX_VALUE).feedTime
|
||||
}
|
||||
disposable = Observable.fromCallable<List<StatisticsItem>>
|
||||
{ DBReader.getStatistics(includeMarkedAsPlayed, 0, Long.MAX_VALUE).feedTime }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ statisticsData: List<StatisticsItem> ->
|
||||
|
|
|
@ -9,6 +9,7 @@ class AspectRatioVideoView @JvmOverloads constructor(context: Context?,
|
|||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : VideoView(context, attrs, defStyle) {
|
||||
|
||||
private var mVideoWidth = 0
|
||||
private var mVideoHeight = 0
|
||||
private var mAvailableWidth = -1f
|
||||
|
|
|
@ -53,7 +53,7 @@ class ChapterSeekBar : AppCompatSeekBar {
|
|||
if (dividerPos != null) {
|
||||
this.dividerPos = FloatArray(dividerPos.size + 2)
|
||||
this.dividerPos!![0] = 0f
|
||||
System.arraycopy(dividerPos, 0, this.dividerPos, 1, dividerPos.size)
|
||||
System.arraycopy(dividerPos, 0, this.dividerPos!!, 1, dividerPos.size)
|
||||
this.dividerPos!![this.dividerPos!!.size - 1] = 1f
|
||||
} else {
|
||||
this.dividerPos = null
|
||||
|
@ -104,30 +104,32 @@ class ChapterSeekBar : AppCompatSeekBar {
|
|||
|
||||
canvas.translate(paddingLeft.toFloat(), paddingTop.toFloat())
|
||||
|
||||
for (i in 1 until dividerPos!!.size) {
|
||||
val right = dividerPos!![i] * width - chapterMargin
|
||||
val left = dividerPos!![i - 1] * width
|
||||
val rightCurr = dividerPos!![currChapter] * width - chapterMargin
|
||||
val leftCurr = dividerPos!![currChapter - 1] * width
|
||||
if (dividerPos != null && dividerPos!!.isNotEmpty()) {
|
||||
for (i in 1 until dividerPos!!.size) {
|
||||
val right = dividerPos!![i] * width - chapterMargin
|
||||
val left = dividerPos!![i - 1] * width
|
||||
val rightCurr = dividerPos!![currChapter] * width - chapterMargin
|
||||
val leftCurr = dividerPos!![currChapter - 1] * width
|
||||
|
||||
canvas.drawRect(left, top, right, bottom, paintBackground)
|
||||
canvas.drawRect(left, top, right, bottom, paintBackground)
|
||||
|
||||
if (progressSecondary > 0 && progressSecondary < width) {
|
||||
if (right < progressSecondary) {
|
||||
canvas.drawRect(left, top, right, bottom, paintBackground)
|
||||
} else if (progressSecondary > left) {
|
||||
canvas.drawRect(left, top, progressSecondary, bottom, paintBackground)
|
||||
if (progressSecondary > 0 && progressSecondary < width) {
|
||||
if (right < progressSecondary) {
|
||||
canvas.drawRect(left, top, right, bottom, paintBackground)
|
||||
} else if (progressSecondary > left) {
|
||||
canvas.drawRect(left, top, progressSecondary, bottom, paintBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (right < progressPrimary) {
|
||||
currChapter = i + 1
|
||||
canvas.drawRect(left, top, right, bottom, paintProgressPrimary)
|
||||
} else if (isHighlighted || isPressed) {
|
||||
canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground)
|
||||
canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary)
|
||||
} else {
|
||||
canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary)
|
||||
if (right < progressPrimary) {
|
||||
currChapter = i + 1
|
||||
canvas.drawRect(left, top, right, bottom, paintProgressPrimary)
|
||||
} else if (isHighlighted || isPressed) {
|
||||
canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground)
|
||||
canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary)
|
||||
} else {
|
||||
canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
canvas.restoreToCount(saveCount)
|
||||
|
|
|
@ -58,38 +58,41 @@ class EmptyViewHandler(context: Context?) {
|
|||
}
|
||||
|
||||
private fun addToParentView(view: View) {
|
||||
var parent = (view.parent as ViewGroup)
|
||||
var parent = view.parent as? ViewGroup
|
||||
while (parent != null) {
|
||||
if (parent is RelativeLayout) {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
} else if (parent is FrameLayout) {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as FrameLayout.LayoutParams
|
||||
layoutParams.gravity = Gravity.CENTER
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
} else if (parent is CoordinatorLayout) {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as CoordinatorLayout.LayoutParams
|
||||
layoutParams.gravity = Gravity.CENTER
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
when (parent) {
|
||||
is RelativeLayout -> {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
}
|
||||
is FrameLayout -> {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as FrameLayout.LayoutParams
|
||||
layoutParams.gravity = Gravity.CENTER
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
}
|
||||
is CoordinatorLayout -> {
|
||||
parent.addView(emptyView)
|
||||
val layoutParams =
|
||||
emptyView.layoutParams as CoordinatorLayout.LayoutParams
|
||||
layoutParams.gravity = Gravity.CENTER
|
||||
emptyView.layoutParams = layoutParams
|
||||
break
|
||||
}
|
||||
}
|
||||
parent = parent.parent as ViewGroup
|
||||
parent = parent.parent as? ViewGroup
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAdapter(adapter: RecyclerView.Adapter<*>?) {
|
||||
if (this.recyclerAdapter != null) {
|
||||
recyclerAdapter!!.unregisterAdapterDataObserver(adapterObserver)
|
||||
}
|
||||
recyclerAdapter?.unregisterAdapterDataObserver(adapterObserver)
|
||||
|
||||
this.recyclerAdapter = adapter
|
||||
adapter?.registerAdapterDataObserver(adapterObserver)
|
||||
updateVisibility()
|
||||
|
|
|
@ -10,28 +10,28 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import ac.mdiq.podvinci.R
|
||||
|
||||
class EpisodeItemListRecyclerView : RecyclerView {
|
||||
private var layoutManager: LinearLayoutManager? = null
|
||||
private lateinit var layoutManager: LinearLayoutManager
|
||||
|
||||
constructor(context: Context?) : super(ContextThemeWrapper(context, R.style.FastScrollRecyclerView)) {
|
||||
constructor(context: Context) : super(ContextThemeWrapper(context, R.style.FastScrollRecyclerView)) {
|
||||
setup()
|
||||
}
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(ContextThemeWrapper(context,
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(ContextThemeWrapper(context,
|
||||
R.style.FastScrollRecyclerView), attrs) {
|
||||
setup()
|
||||
}
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(ContextThemeWrapper(context,
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(ContextThemeWrapper(context,
|
||||
R.style.FastScrollRecyclerView), attrs, defStyleAttr) {
|
||||
setup()
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
layoutManager!!.recycleChildrenOnDetach = true
|
||||
layoutManager.recycleChildrenOnDetach = true
|
||||
setLayoutManager(layoutManager)
|
||||
setHasFixedSize(true)
|
||||
addItemDecoration(DividerItemDecoration(context, layoutManager!!.orientation))
|
||||
addItemDecoration(DividerItemDecoration(context, layoutManager.orientation))
|
||||
clipToPadding = false
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ class EpisodeItemListRecyclerView : RecyclerView {
|
|||
}
|
||||
|
||||
fun saveScrollPosition(tag: String) {
|
||||
val firstItem = layoutManager!!.findFirstVisibleItemPosition()
|
||||
val firstItemView = layoutManager!!.findViewByPosition(firstItem)
|
||||
val firstItem = layoutManager.findFirstVisibleItemPosition()
|
||||
val firstItemView = layoutManager.findViewByPosition(firstItem)
|
||||
val topOffset = firstItemView?.top?.toFloat() ?: 0f
|
||||
|
||||
context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit()
|
||||
|
@ -57,15 +57,15 @@ class EpisodeItemListRecyclerView : RecyclerView {
|
|||
val position = prefs.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0)
|
||||
val offset = prefs.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0)
|
||||
if (position > 0 || offset > 0) {
|
||||
layoutManager!!.scrollToPositionWithOffset(position, offset)
|
||||
layoutManager.scrollToPositionWithOffset(position, offset)
|
||||
}
|
||||
}
|
||||
|
||||
val isScrolledToBottom: Boolean
|
||||
get() {
|
||||
val visibleEpisodeCount = childCount
|
||||
val totalEpisodeCount = layoutManager!!.itemCount
|
||||
val firstVisibleEpisode = layoutManager!!.findFirstVisibleItemPosition()
|
||||
val totalEpisodeCount = layoutManager.itemCount
|
||||
val firstVisibleEpisode = layoutManager.findFirstVisibleItemPosition()
|
||||
return (totalEpisodeCount - visibleEpisodeCount) <= (firstVisibleEpisode + 3)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,14 +22,13 @@ class LiftOnScrollListener(appBar: View) : RecyclerView.OnScrollListener(), Nest
|
|||
}
|
||||
|
||||
private fun isScrolled(recyclerView: RecyclerView): Boolean {
|
||||
val firstItem =
|
||||
(recyclerView.layoutManager as LinearLayoutManager?)!!.findFirstVisibleItemPosition()
|
||||
val firstItem = (recyclerView.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition()?:-1
|
||||
if (firstItem < 0) {
|
||||
return false
|
||||
} else if (firstItem > 0) {
|
||||
return true
|
||||
}
|
||||
val firstItemView = recyclerView.layoutManager!!.findViewByPosition(firstItem)
|
||||
val firstItemView = recyclerView.layoutManager?.findViewByPosition(firstItem)
|
||||
return if (firstItemView == null) {
|
||||
false
|
||||
} else {
|
||||
|
|
|
@ -7,9 +7,8 @@ import ac.mdiq.podvinci.model.feed.FeedItem
|
|||
import ac.mdiq.podvinci.ui.i18n.R
|
||||
|
||||
object LocalDeleteModal {
|
||||
fun showLocalFeedDeleteWarningIfNecessary(context: Context?, items: Iterable<FeedItem>,
|
||||
deleteCommand: Runnable
|
||||
) {
|
||||
fun showLocalFeedDeleteWarningIfNecessary(context: Context, items: Iterable<FeedItem>,
|
||||
deleteCommand: Runnable) {
|
||||
var anyLocalFeed = false
|
||||
for (item in items) {
|
||||
if (item.feed?.isLocalFeed == true) {
|
||||
|
@ -23,7 +22,7 @@ object LocalDeleteModal {
|
|||
return
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(context!!)
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.delete_episode_label)
|
||||
.setMessage(R.string.delete_local_feed_warning_body)
|
||||
.setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int -> deleteCommand.run() }
|
||||
|
|
|
@ -69,10 +69,7 @@ class NestedScrollableHost : FrameLayout {
|
|||
}
|
||||
|
||||
private fun setAttributes(context: Context, attrs: AttributeSet?) {
|
||||
val a = context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.NestedScrollableHost,
|
||||
0, 0)
|
||||
val a = context.theme.obtainStyledAttributes(attrs, R.styleable.NestedScrollableHost, 0, 0)
|
||||
|
||||
try {
|
||||
preferHorizontal = a.getInteger(R.styleable.NestedScrollableHost_preferHorizontal, 1)
|
||||
|
@ -89,11 +86,11 @@ class NestedScrollableHost : FrameLayout {
|
|||
|
||||
viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean {
|
||||
var v = parent as View
|
||||
var v = parent as? View
|
||||
while (v != null && v !is ViewPager2 || isntSameDirection(v)) {
|
||||
v = v.parent as View
|
||||
v = v!!.parent as? View
|
||||
}
|
||||
parentViewPager = v as ViewPager2
|
||||
parentViewPager = v as? ViewPager2
|
||||
|
||||
viewTreeObserver.removeOnPreDrawListener(this)
|
||||
return false
|
||||
|
@ -146,7 +143,6 @@ class NestedScrollableHost : FrameLayout {
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
if (e.action == MotionEvent.ACTION_DOWN) {
|
||||
initialX = e.x
|
||||
initialY = e.y
|
||||
|
|
|
@ -24,20 +24,23 @@ class PlayButton : AppCompatImageButton {
|
|||
if (this.isShowPlay != showPlay) {
|
||||
this.isShowPlay = showPlay
|
||||
contentDescription = context.getString(if (showPlay) R.string.play_label else R.string.pause_label)
|
||||
if (isVideoScreen) {
|
||||
setImageResource(if (showPlay) R.drawable.ic_play_video_white else R.drawable.ic_pause_video_white)
|
||||
} else if (!isShown) {
|
||||
setImageResource(if (showPlay) R.drawable.ic_play_48dp else R.drawable.ic_pause)
|
||||
} else if (showPlay) {
|
||||
val drawable = AnimatedVectorDrawableCompat.create(
|
||||
context, R.drawable.ic_animate_pause_play)
|
||||
setImageDrawable(drawable)
|
||||
drawable!!.start()
|
||||
} else {
|
||||
val drawable = AnimatedVectorDrawableCompat.create(
|
||||
context, R.drawable.ic_animate_play_pause)
|
||||
setImageDrawable(drawable)
|
||||
drawable!!.start()
|
||||
when {
|
||||
isVideoScreen -> {
|
||||
setImageResource(if (showPlay) R.drawable.ic_play_video_white else R.drawable.ic_pause_video_white)
|
||||
}
|
||||
!isShown -> {
|
||||
setImageResource(if (showPlay) R.drawable.ic_play_48dp else R.drawable.ic_pause)
|
||||
}
|
||||
showPlay -> {
|
||||
val drawable = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_animate_pause_play)
|
||||
setImageDrawable(drawable)
|
||||
drawable?.start()
|
||||
}
|
||||
else -> {
|
||||
val drawable = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_animate_play_pause)
|
||||
setImageDrawable(drawable)
|
||||
drawable?.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import androidx.core.util.Consumer
|
|||
import ac.mdiq.podvinci.R
|
||||
|
||||
class PlaybackSpeedSeekBar : FrameLayout {
|
||||
private var seekBar: SeekBar? = null
|
||||
private lateinit var seekBar: SeekBar
|
||||
private var progressChangedListener: Consumer<Float>? = null
|
||||
|
||||
constructor(context: Context) : super(context) {
|
||||
|
@ -28,10 +28,10 @@ class PlaybackSpeedSeekBar : FrameLayout {
|
|||
private fun setup() {
|
||||
inflate(context, R.layout.playback_speed_seek_bar, this)
|
||||
seekBar = findViewById(R.id.playback_speed)
|
||||
findViewById<View>(R.id.butDecSpeed).setOnClickListener { v: View? -> seekBar?.progress = (seekBar?.progress ?: 0) - 2 }
|
||||
findViewById<View>(R.id.butIncSpeed).setOnClickListener { v: View? -> seekBar?.progress = (seekBar?.progress ?: 0) + 2 }
|
||||
findViewById<View>(R.id.butDecSpeed).setOnClickListener { v: View? -> seekBar.progress -= 2 }
|
||||
findViewById<View>(R.id.butIncSpeed).setOnClickListener { v: View? -> seekBar.progress += 2 }
|
||||
|
||||
seekBar?.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
val playbackSpeed = (progress + 10) / 20.0f
|
||||
if (progressChangedListener != null) {
|
||||
|
@ -48,7 +48,7 @@ class PlaybackSpeedSeekBar : FrameLayout {
|
|||
}
|
||||
|
||||
fun updateSpeed(speedMultiplier: Float) {
|
||||
seekBar!!.progress = Math.round((20 * speedMultiplier) - 10)
|
||||
seekBar.progress = Math.round((20 * speedMultiplier) - 10)
|
||||
}
|
||||
|
||||
fun setProgressChangedListener(progressChangedListener: Consumer<Float>?) {
|
||||
|
@ -56,11 +56,11 @@ class PlaybackSpeedSeekBar : FrameLayout {
|
|||
}
|
||||
|
||||
val currentSpeed: Float
|
||||
get() = (seekBar!!.progress + 10) / 20.0f
|
||||
get() = (seekBar.progress + 10) / 20.0f
|
||||
|
||||
override fun setEnabled(enabled: Boolean) {
|
||||
super.setEnabled(enabled)
|
||||
seekBar!!.isEnabled = enabled
|
||||
seekBar.isEnabled = enabled
|
||||
findViewById<View>(R.id.butDecSpeed).isEnabled = enabled
|
||||
findViewById<View>(R.id.butIncSpeed).isEnabled = enabled
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import ac.mdiq.podvinci.core.util.IntentUtils
|
|||
import ac.mdiq.podvinci.core.util.NetworkUtils
|
||||
import ac.mdiq.podvinci.core.util.ShareUtils
|
||||
import ac.mdiq.podvinci.core.util.gui.ShownotesCleaner
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import kotlin.math.max
|
||||
|
||||
class ShownotesWebView : WebView, View.OnLongClickListener {
|
||||
|
@ -80,27 +81,28 @@ class ShownotesWebView : WebView, View.OnLongClickListener {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
@UnstableApi override fun onLongClick(v: View): Boolean {
|
||||
val r: HitTestResult = getHitTestResult()
|
||||
if (r.type == HitTestResult.SRC_ANCHOR_TYPE) {
|
||||
Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.extra)
|
||||
selectedUrl = r.extra
|
||||
showContextMenu()
|
||||
return true
|
||||
} else if (r.type == HitTestResult.EMAIL_TYPE) {
|
||||
Log.d(TAG, "E-Mail of webview was long-pressed. Extra: " + r.extra)
|
||||
ContextCompat.getSystemService(
|
||||
context,
|
||||
ClipboardManager::class.java)?.setPrimaryClip(ClipData.newPlainText("PodVinci", r.extra))
|
||||
if (Build.VERSION.SDK_INT <= 32 && this.context is MainActivity) {
|
||||
(this.context as MainActivity).showSnackbarAbovePlayer(
|
||||
resources.getString(R.string.copied_to_clipboard),
|
||||
Snackbar.LENGTH_SHORT)
|
||||
when (r.type) {
|
||||
HitTestResult.SRC_ANCHOR_TYPE -> {
|
||||
Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.extra)
|
||||
selectedUrl = r.extra
|
||||
showContextMenu()
|
||||
return true
|
||||
}
|
||||
HitTestResult.EMAIL_TYPE -> {
|
||||
Log.d(TAG, "E-Mail of webview was long-pressed. Extra: " + r.extra)
|
||||
ContextCompat.getSystemService(context, ClipboardManager::class.java)?.setPrimaryClip(ClipData.newPlainText("PodVinci", r.extra))
|
||||
if (Build.VERSION.SDK_INT <= 32 && this.context is MainActivity) {
|
||||
(this.context as MainActivity).showSnackbarAbovePlayer(resources.getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
selectedUrl = null
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
selectedUrl = null
|
||||
return false
|
||||
}
|
||||
|
||||
fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -109,29 +111,34 @@ class ShownotesWebView : WebView, View.OnLongClickListener {
|
|||
}
|
||||
|
||||
val itemId = item.itemId
|
||||
if (itemId == R.id.open_in_browser_item) {
|
||||
if (selectedUrl != null) IntentUtils.openInBrowser(context, selectedUrl!!)
|
||||
} else if (itemId == R.id.share_url_item) {
|
||||
if (selectedUrl != null) ShareUtils.shareLink(context, selectedUrl!!)
|
||||
} else if (itemId == R.id.copy_url_item) {
|
||||
val clipData: ClipData = ClipData.newPlainText(selectedUrl, selectedUrl)
|
||||
val cm = context
|
||||
.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cm.setPrimaryClip(clipData)
|
||||
if (Build.VERSION.SDK_INT < 32) {
|
||||
val s: Snackbar = Snackbar.make(this, R.string.copied_to_clipboard, Snackbar.LENGTH_LONG)
|
||||
s.view.elevation = 100f
|
||||
s.show()
|
||||
when (itemId) {
|
||||
R.id.open_in_browser_item -> {
|
||||
if (selectedUrl != null) IntentUtils.openInBrowser(context, selectedUrl!!)
|
||||
}
|
||||
} else if (itemId == R.id.go_to_position_item) {
|
||||
if (ShownotesCleaner.isTimecodeLink(selectedUrl) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener!!.accept(ShownotesCleaner.getTimecodeLinkTime(selectedUrl))
|
||||
} else {
|
||||
Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: $selectedUrl")
|
||||
R.id.share_url_item -> {
|
||||
if (selectedUrl != null) ShareUtils.shareLink(context, selectedUrl!!)
|
||||
}
|
||||
R.id.copy_url_item -> {
|
||||
val clipData: ClipData = ClipData.newPlainText(selectedUrl, selectedUrl)
|
||||
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cm.setPrimaryClip(clipData)
|
||||
if (Build.VERSION.SDK_INT < 32) {
|
||||
val s: Snackbar = Snackbar.make(this, R.string.copied_to_clipboard, Snackbar.LENGTH_LONG)
|
||||
s.view.elevation = 100f
|
||||
s.show()
|
||||
}
|
||||
}
|
||||
R.id.go_to_position_item -> {
|
||||
if (ShownotesCleaner.isTimecodeLink(selectedUrl) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener!!.accept(ShownotesCleaner.getTimecodeLinkTime(selectedUrl))
|
||||
} else {
|
||||
Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: $selectedUrl")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
selectedUrl = null
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
selectedUrl = null
|
||||
return false
|
||||
}
|
||||
selectedUrl = null
|
||||
return true
|
||||
|
|
|
@ -40,9 +40,7 @@ abstract class ToolbarIconTintManager(private val context: Context,
|
|||
}
|
||||
|
||||
private fun safeSetColorFilter(icon: Drawable?, filter: PorterDuffColorFilter?) {
|
||||
if (icon != null) {
|
||||
icon.colorFilter = filter
|
||||
}
|
||||
icon?.colorFilter = filter
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ import ac.mdiq.podvinci.ui.common.CircularProgressBar
|
|||
|
||||
class DownloadLogItemViewHolder(context: Context?, parent: ViewGroup?) :
|
||||
RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false)) {
|
||||
|
||||
@JvmField
|
||||
val secondaryActionButton: View = itemView.findViewById(R.id.secondaryActionButton)
|
||||
@JvmField
|
||||
|
|
|
@ -41,8 +41,9 @@ import kotlin.math.max
|
|||
* Holds the view which shows FeedItems.
|
||||
*/
|
||||
@UnstableApi
|
||||
class EpisodeItemViewHolder(activity: MainActivity, parent: ViewGroup?) :
|
||||
class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGroup?) :
|
||||
RecyclerView.ViewHolder(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false)) {
|
||||
|
||||
private val container: View = itemView.findViewById(R.id.container)
|
||||
@JvmField
|
||||
val dragHandle: ImageView = itemView.findViewById(R.id.drag_handle)
|
||||
|
@ -69,7 +70,6 @@ class EpisodeItemViewHolder(activity: MainActivity, parent: ViewGroup?) :
|
|||
@JvmField
|
||||
val coverHolder: CardView
|
||||
|
||||
private val activity: MainActivity = activity
|
||||
private var item: FeedItem? = null
|
||||
|
||||
init {
|
||||
|
@ -269,8 +269,11 @@ class EpisodeItemViewHolder(activity: MainActivity, parent: ViewGroup?) :
|
|||
* Hides the separator dot between icons and text if there are no icons.
|
||||
*/
|
||||
fun hideSeparatorIfNecessary() {
|
||||
val hasIcons =
|
||||
isInbox.visibility == View.VISIBLE || isInQueue.visibility == View.VISIBLE || isVideo.visibility == View.VISIBLE || isFavorite.visibility == View.VISIBLE || isInbox.visibility == View.VISIBLE
|
||||
val hasIcons = isInbox.visibility == View.VISIBLE ||
|
||||
isInQueue.visibility == View.VISIBLE ||
|
||||
isVideo.visibility == View.VISIBLE ||
|
||||
isFavorite.visibility == View.VISIBLE ||
|
||||
isInbox.visibility == View.VISIBLE
|
||||
separatorIcons.visibility = if (hasIcons) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import kotlin.math.max
|
|||
|
||||
class HorizontalItemViewHolder(private val activity: MainActivity, parent: ViewGroup?) :
|
||||
RecyclerView.ViewHolder(LayoutInflater.from(activity).inflate(R.layout.horizontal_itemlist_item, parent, false)) {
|
||||
|
||||
@JvmField
|
||||
val card: CardView = itemView.findViewById(R.id.card)
|
||||
|
||||
|
@ -110,7 +111,7 @@ class HorizontalItemViewHolder(private val activity: MainActivity, parent: ViewG
|
|||
}
|
||||
|
||||
val isCurrentlyPlayingItem: Boolean
|
||||
@UnstableApi get() = item != null && item!!.media != null && PlaybackStatus.isCurrentlyPlaying(item!!.media)
|
||||
@UnstableApi get() = item?.media != null && PlaybackStatus.isCurrentlyPlaying(item!!.media)
|
||||
|
||||
fun notifyPlaybackPositionUpdated(event: PlaybackPositionEvent) {
|
||||
setProgressBar(true, 100.0f * event.position / event.duration)
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
|
@ -18,10 +20,11 @@
|
|||
android:supportsRtl="true">
|
||||
|
||||
<service android:name=".service.playback.PlaybackService"
|
||||
android:label="@string/app_name"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedService">
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:label="@string/app_name"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
tools:ignore="ExportedService">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
|
|
|
@ -223,6 +223,7 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
}
|
||||
|
||||
fun setPlaybackParams(speed: Float, skipSilence: Boolean) {
|
||||
Log.d(TAG, "setPlaybackParams speed=$speed pitch=${playbackParameters.pitch} skipSilence=$skipSilence")
|
||||
playbackParameters = PlaybackParameters(speed, playbackParameters.pitch)
|
||||
exoPlayer.skipSilenceEnabled = skipSilence
|
||||
exoPlayer.playbackParameters = playbackParameters
|
||||
|
|
|
@ -166,16 +166,16 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
notificationBuilder = PlaybackServiceNotificationBuilder(this)
|
||||
|
||||
// TODO: this shit doesn't work
|
||||
// if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
|
||||
// registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"), RECEIVER_EXPORTED)
|
||||
// registerReceiver(shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE), RECEIVER_EXPORTED)
|
||||
// } else {
|
||||
// ContextCompat.registerReceiver(applicationContext, autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"), ContextCompat.RECEIVER_EXPORTED)
|
||||
// ContextCompat.registerReceiver(applicationContext, shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE), ContextCompat.RECEIVER_EXPORTED)
|
||||
// }
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
|
||||
registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"), RECEIVER_NOT_EXPORTED)
|
||||
registerReceiver(shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE), RECEIVER_NOT_EXPORTED)
|
||||
} else {
|
||||
registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"))
|
||||
registerReceiver(shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE))
|
||||
}
|
||||
|
||||
registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"))
|
||||
registerReceiver(shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE))
|
||||
// registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"))
|
||||
// registerReceiver(shutdownReceiver, IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE))
|
||||
|
||||
registerReceiver(headsetDisconnected, IntentFilter(Intent.ACTION_HEADSET_PLUG))
|
||||
registerReceiver(bluetoothStateUpdated, IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED))
|
||||
|
@ -272,13 +272,13 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
cancelPositionObserver()
|
||||
mediaSession?.release()
|
||||
mediaSession = null
|
||||
mediaPlayer?.shutdown()
|
||||
|
||||
unregisterReceiver(autoStateUpdated)
|
||||
unregisterReceiver(headsetDisconnected)
|
||||
unregisterReceiver(shutdownReceiver)
|
||||
unregisterReceiver(bluetoothStateUpdated)
|
||||
unregisterReceiver(audioBecomingNoisy)
|
||||
mediaPlayer?.shutdown()
|
||||
taskManager.shutdown()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
@ -699,7 +699,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Unhandled key code: $keycode")
|
||||
if (info?.playable != null && info?.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
|
||||
if (info?.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
|
||||
val message = String.format(resources.getString(R.string.unknown_media_key), keycode)
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package ac.mdiq.podvinci.core.service.playback
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.app.ServiceCompat
|
||||
import kotlin.concurrent.Volatile
|
||||
|
@ -13,11 +15,13 @@ internal class PlaybackServiceStateManager(private val playbackService: Playback
|
|||
@Volatile
|
||||
private var hasReceivedValidStartCommand = false
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
fun startForeground(notificationId: Int, notification: Notification?) {
|
||||
fun startForeground(notificationId: Int, notification: Notification) {
|
||||
Log.d(TAG, "startForeground")
|
||||
// TODO: need to add declaration in manifest
|
||||
playbackService.startForeground(notificationId, notification)
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
playbackService.startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
|
||||
} else {
|
||||
playbackService.startForeground(notificationId, notification)
|
||||
}
|
||||
isInForeground = true
|
||||
}
|
||||
|
||||
|
|
|
@ -242,10 +242,8 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
cancelWidgetUpdater()
|
||||
disableSleepTimer()
|
||||
|
||||
if (chapterLoaderFuture != null) {
|
||||
chapterLoaderFuture!!.dispose()
|
||||
chapterLoaderFuture = null
|
||||
}
|
||||
chapterLoaderFuture?.dispose()
|
||||
chapterLoaderFuture = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -646,7 +646,11 @@ object DBReader {
|
|||
@JvmStatic
|
||||
fun loadChaptersOfFeedItem(item: FeedItem): List<Chapter>? {
|
||||
Log.d(TAG, "loadChaptersOfFeedItem() called with: item = [$item]")
|
||||
|
||||
// TODO: need to find out who are often calling this
|
||||
// val stackTraceElements = Thread.currentThread().stackTrace
|
||||
// stackTraceElements.forEach { element ->
|
||||
// println(element)
|
||||
// }
|
||||
val adapter = getInstance()
|
||||
adapter!!.open()
|
||||
try {
|
||||
|
|
|
@ -205,7 +205,7 @@ import java.util.concurrent.TimeUnit
|
|||
removedFromQueue.add(item)
|
||||
}
|
||||
if (item.media != null) {
|
||||
if (item.media!!.id == currentlyPlayingFeedMediaId) {
|
||||
if (item.media?.id == currentlyPlayingFeedMediaId) {
|
||||
// Applies to both downloaded and streamed media
|
||||
writeNoMediaPlaying()
|
||||
sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE)
|
||||
|
|
|
@ -10,7 +10,7 @@ object FeedItemUtil {
|
|||
fun indexOfItemWithId(items: List<FeedItem?>, id: Long): Int {
|
||||
for (i in items.indices) {
|
||||
val item = items[i]
|
||||
if (item != null && item.id == id) {
|
||||
if (item?.id == id) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ object FeedItemUtil {
|
|||
fun indexOfItemWithDownloadUrl(items: List<FeedItem?>, downloadUrl: String): Int {
|
||||
for (i in items.indices) {
|
||||
val item = items[i]
|
||||
if (item?.media != null && item.media!!.download_url == downloadUrl) {
|
||||
if (item?.media?.download_url == downloadUrl) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ object FeedItemUtil {
|
|||
|
||||
@JvmStatic
|
||||
fun getIds(items: List<FeedItem>?): LongArray {
|
||||
if (items == null || items.isEmpty()) {
|
||||
if (items.isNullOrEmpty()) {
|
||||
return LongArray(0)
|
||||
}
|
||||
val result = LongArray(items.size)
|
||||
|
|
|
@ -74,19 +74,17 @@ abstract class PlaybackController(private val activity: FragmentActivity?) {
|
|||
initialized = true
|
||||
|
||||
// TODO: this shit doesn't work
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// activity?.registerReceiver(statusUpdate, IntentFilter(
|
||||
// PlaybackService.ACTION_PLAYER_STATUS_CHANGED), Context.RECEIVER_NOT_EXPORTED)
|
||||
// activity?.registerReceiver(notificationReceiver, IntentFilter(
|
||||
// PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION), Context.RECEIVER_NOT_EXPORTED)
|
||||
// } else {
|
||||
// ContextCompat.registerReceiver(activity!!, statusUpdate, IntentFilter(
|
||||
// PlaybackService.ACTION_PLAYER_STATUS_CHANGED), ContextCompat.RECEIVER_EXPORTED)
|
||||
// ContextCompat.registerReceiver(activity, notificationReceiver, IntentFilter(
|
||||
// PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION), ContextCompat.RECEIVER_EXPORTED)
|
||||
// }
|
||||
activity?.registerReceiver(statusUpdate, IntentFilter(PlaybackService.ACTION_PLAYER_STATUS_CHANGED))
|
||||
activity?.registerReceiver(notificationReceiver, IntentFilter(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
activity?.registerReceiver(statusUpdate, IntentFilter(
|
||||
PlaybackService.ACTION_PLAYER_STATUS_CHANGED), Context.RECEIVER_NOT_EXPORTED)
|
||||
activity?.registerReceiver(notificationReceiver, IntentFilter(
|
||||
PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION), Context.RECEIVER_NOT_EXPORTED)
|
||||
} else {
|
||||
activity?.registerReceiver(statusUpdate, IntentFilter(PlaybackService.ACTION_PLAYER_STATUS_CHANGED))
|
||||
activity?.registerReceiver(notificationReceiver, IntentFilter(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION))
|
||||
}
|
||||
// activity?.registerReceiver(statusUpdate, IntentFilter(PlaybackService.ACTION_PLAYER_STATUS_CHANGED))
|
||||
// activity?.registerReceiver(notificationReceiver, IntentFilter(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION))
|
||||
|
||||
if (!released) {
|
||||
bindToService()
|
||||
|
@ -390,7 +388,7 @@ abstract class PlaybackController(private val activity: FragmentActivity?) {
|
|||
if (playbackService == null || playbackService!!.audioTracks.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
return playbackService!!.audioTracks!!.filterNotNull().map { it }
|
||||
return playbackService!!.audioTracks.filterNotNull().map { it }
|
||||
}
|
||||
|
||||
val selectedAudioTrack: Int
|
||||
|
|
|
@ -31,6 +31,6 @@ class Chapter : FeedComponent {
|
|||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ID3Chapter [title=" + title + ", start=" + start + ", url=" + link + "]"
|
||||
return "ID3Chapter [title=$title, start=$start, url=$link]"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue