Skip to content
Merged

Dev #443

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ android {
applicationId = "com.github.livingwithhippos.unchained"
minSdk = 27
targetSdk = 36
versionCode = 55
versionName = "1.5.0"
versionCode = 56
versionName = "1.5.1"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber

const val MAX_SERVICE_DURATION = 5 * 60 * 60 * 1000
const val MIN_SERVICE_DURATION = 20 * 60 * 1000

@AndroidEntryPoint
@SuppressLint("MissingPermission")
class ForegroundTorrentService : LifecycleService() {
Expand Down Expand Up @@ -157,21 +160,38 @@ class ForegroundTorrentService : LifecycleService() {
// right now on api >= 35 after 6 hours the service will crash
// because of system imposed limits
if (
Build.VERSION.SDK_INT >= 35 &&
System.currentTimeMillis() - serviceStart > 5 * 60 * 60 * 1000
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM &&
System.currentTimeMillis() - serviceStart > MAX_SERVICE_DURATION
) {
Timber.w("Service has been running for too long, stopping it.")
break
}
try {
torrentsLiveData.postValue(getTorrentList())
val torrentList = getTorrentList()
torrentsLiveData.postValue(torrentList)

if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM &&
System.currentTimeMillis() - serviceStart > MIN_SERVICE_DURATION
) {
// if there are no active torrents and the services has been started
// for at least some minutes, stop the service
val unfinishedTorrents =
torrentList.count { loadingStatusList.contains(it.status) }
if (unfinishedTorrents == 0) {
Timber.i(
"Service has been running and no torrents are active, stopping it."
)
break
}
}
} catch (ex: IllegalArgumentException) {
// no valid token ready, retry later
}
// update notifications every 5 seconds
delay(updateTiming)
}
stopSelf()
stopTorrentService()
}
}

Expand Down Expand Up @@ -271,10 +291,14 @@ class ForegroundTorrentService : LifecycleService() {
}

private fun stopTorrentService() {
torrentsLiveData.value?.let {
it.forEach { torrent -> notificationManager.cancel(torrent.id.hashCode()) }
lifecycleScope.launch {
// delay used to let the notification finish
delay(1000)
notificationManager.cancel(SUMMARY_ID)
// this will avoid removing the notifications, so the user can see what happened in the
// meanwhile
stopForeground(STOP_FOREGROUND_DETACH)
}
stopSelf()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,14 +512,18 @@ class DownloadsListFragment : UnchainedFragment(), DownloadListListener {
// removes the loading icon from the swipe layout
val downloadObserver =
Observer<PagingData<DownloadItem>> {
lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launch {
val b =
_binding
?: return@launch // capture binding and bail out if view was destroyed

downloadAdapter.submitData(it)
// stop the refresh animation if playing
if (binding.srLayout.isRefreshing) {
binding.srLayout.isRefreshing = false
if (b.srLayout.isRefreshing) {
b.srLayout.isRefreshing = false
// scroll to top if we were refreshing
lifecycleScope.launch {
binding.rvDownloadList.delayedScrolling(requireContext())
b.rvDownloadList.delayedScrolling(requireContext())
}
}
// delay for notifying the list that the items have changed, otherwise stuff
Expand Down Expand Up @@ -699,19 +703,19 @@ class TorrentsListFragment : UnchainedFragment(), TorrentListListener {
} else context?.showToast(R.string.select_one_item)
}
binding.bDetailsSelected.setOnClickListener {
if (torrentTracker.selection.toList().size == 1) {
val item: TorrentItem = torrentTracker.selection.toList().first()
val action =
if (beforeSelectionStatusList.contains(item.status))
ListsTabFragmentDirections.actionListTabsDestToTorrentProcessingFragment(
torrentID = item.id
)
else ListsTabFragmentDirections.actionListsTabToTorrentDetails(item)
findNavController().navigate(action)
} else
Timber.e(
"Somehow user triggered openSelectedDetails with a selection size of ${torrentTracker.selection.toList().size}"
)
if (torrentTracker.selection.size() != 1) {
// we can only open a single torrent at a time
return@setOnClickListener
}

val item: TorrentItem = torrentTracker.selection.toList().first()
val action =
if (beforeSelectionStatusList.contains(item.status))
ListsTabFragmentDirections.actionListTabsDestToTorrentProcessingFragment(
torrentID = item.id
)
else ListsTabFragmentDirections.actionListsTabToTorrentDetails(item)
findNavController().navigate(action)
}
binding.bAddNew?.setOnClickListener {
// landscape only
Expand All @@ -736,13 +740,13 @@ class TorrentsListFragment : UnchainedFragment(), TorrentListListener {

val torrentObserver =
Observer<PagingData<TorrentItem>> {
lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launch {
val b = _binding ?: return@launch

torrentAdapter.submitData(it)
if (binding.srLayout.isRefreshing) {
binding.srLayout.isRefreshing = false
lifecycleScope.launch {
binding.rvTorrentList.delayedScrolling(requireContext())
}
if (b.srLayout.isRefreshing) {
b.srLayout.isRefreshing = false
lifecycleScope.launch { b.rvTorrentList.delayedScrolling(requireContext()) }
}
delay(300)
torrentAdapter.notifyDataSetChanged()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.NavDirections
import androidx.navigation.fragment.findNavController
import com.github.livingwithhippos.unchained.R
import com.github.livingwithhippos.unchained.base.UnchainedFragment
Expand Down Expand Up @@ -40,19 +41,19 @@ class StartFragment : UnchainedFragment() {
FSMAuthenticationState.StartNewLogin -> {
val action =
StartFragmentDirections.actionStartFragmentToAuthenticationFragment()
findNavController().navigate(action)
safeNavigate(action)
}
FSMAuthenticationState.AuthenticatedOpenToken -> {
val action =
StartFragmentDirections.actionStartFragmentToUserProfileFragment()
findNavController().navigate(action)
activityViewModel.goToStartUpScreen()
val navigated = safeNavigate(action)
if (navigated) activityViewModel.goToStartUpScreen()
}
FSMAuthenticationState.AuthenticatedPrivateToken -> {
val action =
StartFragmentDirections.actionStartFragmentToUserProfileFragment()
findNavController().navigate(action)
activityViewModel.goToStartUpScreen()
val navigated = safeNavigate(action)
if (navigated) activityViewModel.goToStartUpScreen()
}
is FSMAuthenticationState.WaitingUserAction -> {
// todo: show action needed
Expand Down Expand Up @@ -103,6 +104,25 @@ class StartFragment : UnchainedFragment() {
return binding.root
}

private fun safeNavigate(action: NavDirections): Boolean {
val nav = findNavController()
val current = nav.currentDestination
if (current != null && current.getAction(action.actionId) != null) {
try {
nav.navigate(action)
return true
} catch (e: IllegalArgumentException) {
Timber.w(e, "Safe navigate failed for actionId=${action.actionId}")
return false
}
} else {
Timber.w(
"Navigation action not found from destination ${current?.id} for actionId=${action.actionId}"
)
return false
}
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class TorrentDetailsFragment : UnchainedFragment(), TorrentContentListener {
binding.tvStatus.text = statusTranslation[args.item.status] ?: args.item.status
binding.fabShareMagnet.setOnClickListener { onShareMagnetClick() }
binding.fabCopyMagnet.setOnClickListener { onCopyMagnetClick() }
binding.bDownload.setOnClickListener { onDownloadClick(args.item) }
binding.bDownload.setOnClickListener { onDownloadClick() }

val adapter = TorrentContentFilesAdapter()
binding.rvFileList.adapter = adapter
Expand All @@ -133,6 +133,8 @@ class TorrentDetailsFragment : UnchainedFragment(), TorrentContentListener {
torrent.files?.count { file -> file.selected == 1 } ?: 0
binding.tvSelectedFilesNumber.text = selectedFiles.toString()

binding.tvStatus.text = statusTranslation[torrent.status] ?: torrent.status

binding.tvTotalFiles.text = (torrent.files?.count() ?: 0).toString()
binding.tvName.text = torrent.filename
binding.tvProgressPercent.text =
Expand Down Expand Up @@ -278,7 +280,8 @@ class TorrentDetailsFragment : UnchainedFragment(), TorrentContentListener {
_binding = null
}

fun onDownloadClick(item: TorrentItem) {
fun onDownloadClick() {
val item: TorrentItem = viewModel.torrentLiveData.value?.peekContent() ?: args.item
if (item.links.size > 1) {
val action =
TorrentDetailsFragmentDirections.actionTorrentDetailsToTorrentFolder(
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@
<string name="new_service">Nuevo servicio</string>
<string name="remote_device">Dispositivo remoto</string>
<string name="service_deleted">Servicio suprimido</string>
<string name="service_stopped">El servicio se ha detenido</string>
<string name="service">Servicio</string>
<string name="services">Servicios</string>
<plurals name="services_format">
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@
<string name="new_service">Nouveau service</string>
<string name="remote_device">Dispositif à distance</string>
<string name="service_deleted">Service supprimé</string>
<string name="service_stopped">Le service a été interrompu</string>
<string name="service">Service</string>
<string name="services">Services</string>
<plurals name="services_format">
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@
<string name="new_service">Nuovo servizio</string>
<string name="remote_device">Dispositivo remoto</string>
<string name="service_deleted">Servizio eliminato</string>
<string name="service_stopped">Il servizio è stato fermato</string>
<string name="service">Servizio</string>
<plurals name="services_format">
<item quantity="zero">%d servizi</item>
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@
<string name="new_service">새 서비스</string>
<string name="remote_device">원격 기기</string>
<string name="service_deleted">서비스 삭제됨</string>
<string name="service_stopped">서비스가 중지되었습니다</string>
<string name="service">서비스</string>
<string name="services">서비스</string>
<plurals name="services_format">
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values-tr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@
<string name="new_service">Yeni Hizmet</string>
<string name="remote_device">Uzak Cihaz</string>
<string name="service_deleted">Hizmet silindi</string>
<string name="service_stopped">Hizmet durduruldu</string>
<string name="service">Hizmet</string>
<string name="services">Hizmetler</string>
<plurals name="services_format">
Expand Down
1 change: 1 addition & 0 deletions app/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,7 @@
<string name="remote_device">Remote Device</string>
<string name="jackett" translatable="false">Jackett</string>
<string name="service_deleted">Service deleted</string>
<string name="service_stopped">Service has been stopped</string>
<string name="service">Service</string>
<string name="services">Services</string>
<plurals name="services_format">
Expand Down
2 changes: 1 addition & 1 deletion fastlane/metadata/android/en-US/changelogs/55.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
What's new 1.3.8:
What's new in 1.5.0:
- added Korean translation thanks to poihoii
- torrent file can be selected by pressing on their whole row
- show user code needed for new authentication
Expand Down
5 changes: 5 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/56.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
What's new in 1.5.1:
- fixed missing torrent status update
- fixed torrent to download conversion not working when reselecting files
- stop torrent monitoring services after 20 minutes if there's no activity
- tried to fix crashes