Skip to content

Commit

Permalink
Merge pull request #19 from LivingWithHippos/magnet-intent
Browse files Browse the repository at this point in the history
Magnet and Torrent support
  • Loading branch information
LivingWithHippos authored Sep 28, 2020
2 parents be15be9 + 52de9c5 commit b441a07
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 76 deletions.
127 changes: 127 additions & 0 deletions app/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,133 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Shamelessly copied from LibreTorrent-->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="content" />
</intent-filter>
<!-- accept all the incoming torrent and magnet links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="content" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.torrent"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.torrent"
android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="magnet" />
</intent-filter>
</activity>

<meta-data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ class AuthenticationFragment : UnchainedFragment(), ButtonListener {

override fun onCopyClick(text: String) {
copyToClipboard("real-debrid authorization code", text)
showToast(R.string.code_copied)
context?.showToast(R.string.code_copied)
}

override fun onInsertTokenClick(etToken: EditText) {
//todo: rename all these references to privateKey or something like that to avoid confusion with token from open source client id
val token = etToken.text.toString().trim()
// mine is 52 characters
if (token.length < 40)
showToast(R.string.invalid_token)
context?.showToast(R.string.invalid_token)
else
// pass the value to be checked and eventually saved
viewModel.checkAndSaveToken(privateKey = token)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package com.github.livingwithhippos.unchained.base


import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.ContentResolver.SCHEME_CONTENT
import android.content.ContentResolver.SCHEME_FILE
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -13,9 +21,17 @@ import com.github.livingwithhippos.unchained.databinding.ActivityMainBinding
import com.github.livingwithhippos.unchained.settings.SettingsActivity
import com.github.livingwithhippos.unchained.start.viewmodel.MainActivityViewModel
import com.github.livingwithhippos.unchained.utilities.BottomNavManager
import com.github.livingwithhippos.unchained.utilities.SCHEME_HTTP
import com.github.livingwithhippos.unchained.utilities.SCHEME_HTTPS
import com.github.livingwithhippos.unchained.utilities.SCHEME_MAGNET
import com.github.livingwithhippos.unchained.utilities.extension.isMagnet
import com.github.livingwithhippos.unchained.utilities.extension.isTorrent
import com.github.livingwithhippos.unchained.utilities.extension.observeOnce
import com.github.livingwithhippos.unchained.utilities.extension.showToast
import com.google.android.material.bottomnavigation.BottomNavigationView
import dagger.hilt.android.AndroidEntryPoint


/**
* A [AppCompatActivity] subclass.
* Shared between all the fragments except for the preferences.
Expand All @@ -27,6 +43,8 @@ class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

val viewModel: MainActivityViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -47,7 +65,7 @@ class MainActivity : AppCompatActivity() {
}
}

val viewModel: MainActivityViewModel by viewModels()
// manage the authentication state
viewModel.authenticationState.observe(this, Observer { state ->
when (state.peekContent()) {
// go to login fragment
Expand All @@ -71,8 +89,113 @@ class MainActivity : AppCompatActivity() {
}
}
})

// check if the app has been opened by clicking on torrents/magnet on sharing links
getIntentData()

// observe for torrents downloaded
registerReceiver(getDownloadCompleteReceiver(), IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}

private fun getDownloadCompleteReceiver(): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
viewModel.checkDownload(it.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1))
}
}
}
}

private fun getIntentData() {

when (intent?.action) {
Intent.ACTION_SEND -> {
if (intent.type == "text/plain")
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { text ->
when {
text.isMagnet() -> {
// check auth state before loading it
viewModel.authenticationState.observeOnce(this, { auth ->
when (auth.peekContent()) {
AuthenticationState.AUTHENTICATED -> processLinkIntent(text)
AuthenticationState.AUTHENTICATED_NO_PREMIUM -> baseContext.showToast(
R.string.premium_needed_torrent
)
else -> showToast(R.string.please_login)
}
})
}
text.isTorrent() -> {
viewModel.authenticationState.observeOnce(this, { auth ->
when (auth.peekContent()) {
AuthenticationState.AUTHENTICATED -> processLinkIntent(text)
AuthenticationState.AUTHENTICATED_NO_PREMIUM -> baseContext.showToast(
R.string.premium_needed_torrent
)
else -> showToast(R.string.please_login)
}
})
}
else -> {
// we do not have other cases
}
}
}

}
Intent.ACTION_VIEW -> {
/* Implicit intent with path to torrent file or magnet link */

val data = intent.data
// check uri content
if (data != null) {

when (data.scheme) {
//clicked on a torrent file or a magnet link
SCHEME_MAGNET, SCHEME_CONTENT, SCHEME_FILE -> {
// check auth state before loading it
viewModel.authenticationState.observeOnce(this, { auth ->
when (auth.peekContent()) {
AuthenticationState.AUTHENTICATED -> processLinkIntent(data)
AuthenticationState.AUTHENTICATED_NO_PREMIUM -> baseContext.showToast(
R.string.premium_needed_torrent
)
else -> showToast(R.string.please_login)
}
})
}
SCHEME_HTTP, SCHEME_HTTPS -> {
showToast("You activated the http/s scheme somehow")
}
}
}
}
null -> { // app opened directly by the user. Do nothing.
}
else -> {

}
}
}

override fun onDestroy() {
super.onDestroy()
//todo: test if this works (probably not)
unregisterReceiver(getDownloadCompleteReceiver())
}

private fun processLinkIntent(uri: Uri) {
// simulate click on new download tab
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
if (bottomNav.selectedItemId != R.id.navigation_download) {
bottomNav.selectedItemId = R.id.navigation_download
}
viewModel.addLink(uri)
}

private fun processLinkIntent(text: String) = processLinkIntent(Uri.parse(text))

private fun openSettings() {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DownloadDetailsFragment : UnchainedFragment(), DownloadDetailsListener {

override fun onCopyClick(text: String) {
copyToClipboard("Real-Debrid Download Link", text)
showToast(R.string.link_copied)
context?.showToast(R.string.link_copied)
}

override fun onOpenClick(url: String) {
Expand All @@ -62,7 +62,7 @@ class DownloadDetailsFragment : UnchainedFragment(), DownloadDetailsListener {
if (activityViewModel.isTokenPrivate()) {
viewModel.fetchStreamingInfo(id)
} else
showToast(R.string.api_needs_private_token)
context?.showToast(R.string.api_needs_private_token)

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class ListsTabFragment : UnchainedFragment(), DownloadListListener, TorrentListL
val action = ListsTabFragmentDirections.actionListsTabToDownloadDetails(item)
findNavController().navigate(action)
} else
showToast(R.string.premium_needed)
context?.showToast(R.string.premium_needed)
}

override fun onClick(item: TorrentItem) {
Expand All @@ -176,13 +176,13 @@ class ListsTabFragment : UnchainedFragment(), DownloadListListener, TorrentListL
if (item.status == "downloaded") {
// if the item has many links to download, show a toast
if (item.links.size>2)
showToast(R.string.downloading_torrent)
context?.showToast(R.string.downloading_torrent)
viewModel.downloadTorrent(item)
}
else
showToast(R.string.torrent_not_downloaded)
context?.showToast(R.string.torrent_not_downloaded)
} else
showToast(R.string.premium_needed)
context?.showToast(R.string.premium_needed)
}

companion object {
Expand Down
Loading

0 comments on commit b441a07

Please sign in to comment.