Compare commits

...

8 Commits

27 changed files with 375 additions and 36 deletions
@@ -0,0 +1,94 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "ffba56c2f572c25080ce8596e8bb8945",
"entities": [
{
"tableName": "history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `url` TEXT NOT NULL, `position` INTEGER NOT NULL, `datetime` INTEGER NOT NULL, `name` TEXT NOT NULL, `serialized` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "position",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "datetime",
"columnName": "datetime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serialized",
"columnName": "serialized",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_history_url",
"unique": false,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_history_url` ON `${TABLE_NAME}` (`url`)"
},
{
"name": "index_history_name",
"unique": false,
"columnNames": [
"name"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_history_name` ON `${TABLE_NAME}` (`name`)"
},
{
"name": "index_history_datetime",
"unique": false,
"columnNames": [
"datetime"
],
"orders": [
"DESC"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_history_datetime` ON `${TABLE_NAME}` (`datetime` DESC)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ffba56c2f572c25080ce8596e8bb8945')"
]
}
}
@@ -0,0 +1,88 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "eb813d54b9c44d29f1d7bb198a16d4d1",
"entities": [
{
"tableName": "subscription_cache",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `url` TEXT, `channelUrl` TEXT, `datetime` INTEGER, `serialized` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "channelUrl",
"columnName": "channelUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "datetime",
"columnName": "datetime",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "serialized",
"columnName": "serialized",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_subscription_cache_url",
"unique": false,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_subscription_cache_url` ON `${TABLE_NAME}` (`url`)"
},
{
"name": "index_subscription_cache_channelUrl",
"unique": false,
"columnNames": [
"channelUrl"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_subscription_cache_channelUrl` ON `${TABLE_NAME}` (`channelUrl`)"
},
{
"name": "index_subscription_cache_datetime",
"unique": false,
"columnNames": [
"datetime"
],
"orders": [
"DESC"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_subscription_cache_datetime` ON `${TABLE_NAME}` (`datetime` DESC)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eb813d54b9c44d29f1d7bb198a16d4d1')"
]
}
}
@@ -0,0 +1,52 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "6e3b2d286325c4ea8a7a4c94c290daec",
"entities": [
{
"tableName": "testing",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`someString` TEXT NOT NULL, `someNum` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `serialized` BLOB)",
"fields": [
{
"fieldPath": "someString",
"columnName": "someString",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "someNum",
"columnName": "someNum",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "serialized",
"columnName": "serialized",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6e3b2d286325c4ea8a7a4c94c290daec')"
]
}
}
+6 -2
View File
@@ -11,6 +11,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<application
android:allowBackup="true"
@@ -35,9 +37,11 @@
android:enabled="true"
android:foregroundServiceType="mediaPlayback" />
<service android:name=".services.DownloadService"
android:enabled="true" />
android:enabled="true"
android:foregroundServiceType="dataSync" />
<service android:name=".services.ExportingService"
android:enabled="true" />
android:enabled="true"
android:foregroundServiceType="dataSync" />
<receiver android:name=".receivers.MediaControlReceiver" />
<receiver android:name=".receivers.AudioNoisyReceiver" />
@@ -555,7 +555,7 @@ class Settings : FragmentedStorageFileJson() {
val cookieManager: CookieManager = CookieManager.getInstance();
cookieManager.removeAllCookies(null);
}
@FormField(R.string.reinstall_embedded_plugins, FieldForm.BUTTON, R.string.also_removes_any_data_related_plugin_like_login_or_settings, 1)
/*@FormField(R.string.reinstall_embedded_plugins, FieldForm.BUTTON, R.string.also_removes_any_data_related_plugin_like_login_or_settings, 1)
fun reinstallEmbedded() {
StateApp.instance.scopeOrNull!!.launch(Dispatchers.IO) {
try {
@@ -574,7 +574,7 @@ class Settings : FragmentedStorageFileJson() {
}
}
}
}
}*/
}
@@ -2,6 +2,8 @@ package com.futo.platformplayer.api.media.structures
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.assume
import kotlin.streams.asSequence
import kotlin.streams.toList
/**
@@ -28,6 +30,7 @@ class PlatformContentPager : IPager<IPlatformContent> {
_page++;
_currentItems = _items.stream()
.skip((_page * _pageSize).toLong())
.asSequence()
.toList()
.take(_pageSize)
.toList();
@@ -7,6 +7,7 @@ import android.view.LayoutInflater
import android.widget.Button
import com.futo.platformplayer.R
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.fragment.mainactivity.main.SourcesFragment
import com.futo.platformplayer.readBytes
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateBackup
@@ -19,6 +20,7 @@ class ImportOptionsDialog: AlertDialog {
private lateinit var _button_import_ezip: BigButton;
private lateinit var _button_import_txt: BigButton;
private lateinit var _button_import_newpipe_subs: BigButton;
private lateinit var _button_import_platform: BigButton;
private lateinit var _button_close: Button;
@@ -33,6 +35,7 @@ class ImportOptionsDialog: AlertDialog {
_button_import_ezip = findViewById(R.id.button_import_ezip);
_button_import_txt = findViewById(R.id.button_import_txt);
_button_import_newpipe_subs = findViewById(R.id.button_import_newpipe_subs);
_button_import_platform = findViewById(R.id.button_import_platform);
_button_close = findViewById(R.id.button_cancel);
_button_import_zip.onClick.subscribe {
@@ -61,6 +64,10 @@ class ImportOptionsDialog: AlertDialog {
StateBackup.importNewPipeSubs(_context, json);
};
};
_button_import_platform.onClick.subscribe {
dismiss();
_context.navigate(_context.getFragment<SourcesFragment>());
};
_button_close.setOnClickListener {
dismiss();
}
@@ -264,6 +264,31 @@ class SourceDetailFragment : MainFragment() {
}
)
);
val migrationButtons = mutableListOf<BigButton>();
if (isEnabled && source.capabilities.hasGetUserSubscriptions) {
migrationButtons.add(
BigButton(c, context.getString(R.string.import_subscriptions), context.getString(R.string.login_required), R.drawable.ic_subscriptions) {
}.apply { this.alpha = 0.5f }
);
}
if (isEnabled && source.capabilities.hasGetUserPlaylists && source.capabilities.hasGetPlaylist) {
val bigButton = BigButton(c, context.getString(R.string.import_playlists), context.getString(R.string.login_required), R.drawable.ic_playlist) {
}.apply { this.alpha = 0.5f };
bigButton.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);
};
migrationButtons.add(bigButton);
}
if (migrationButtons.size > 0) {
groups.add(BigButtonGroup(c, context.getString(R.string.migration), *migrationButtons.toTypedArray()));
}
}
}
@@ -538,6 +538,7 @@ class VideoDetailView : ConstraintLayout {
if(!_destroyed) {
updateQueueState();
StatePlayer.instance.updateMediaSession(null);
_cast.setLoopVisible(!StatePlayer.instance.hasQueue);
}
};
StatePlayer.instance.onVideoChanging.subscribe(this) {
@@ -565,9 +566,7 @@ class VideoDetailView : ConstraintLayout {
};
_upNext.onNextItem.subscribe {
val item = StatePlayer.instance.nextQueueItem();
if(item != null)
setVideoOverview(item, true);
nextVideo(true, true, true);
};
_upNext.onOpenQueueClick.subscribe {
_container_content_queue.updateQueue();
@@ -17,6 +17,7 @@ import kotlinx.coroutines.*
import kotlinx.serialization.Serializable
import java.nio.ByteBuffer
import java.time.OffsetDateTime
import kotlin.system.measureTimeMillis
class PolycentricCache {
data class CachedOwnedClaims(val ownedClaims: List<OwnedClaim>?, val creationTime: OffsetDateTime = OffsetDateTime.now()) {
@@ -29,8 +30,15 @@ class PolycentricCache {
private val _cache = hashMapOf<PlatformID, CachedOwnedClaims>()
private val _profileCache = hashMapOf<PublicKey, CachedPolycentricProfile>()
private val _profileUrlCache = FragmentedStorage.get<CachedPolycentricProfileStorage>("profileUrlCache")
private val _profileUrlCache: CachedPolycentricProfileStorage;
private val _scope = CoroutineScope(Dispatchers.IO);
init {
Logger.i(TAG, "Initializing Polycentric cache");
val time = measureTimeMillis {
_profileUrlCache = FragmentedStorage.get<CachedPolycentricProfileStorage>("profileUrlCache")
}
Logger.i(TAG, "Initialized Polycentric cache (${_profileUrlCache.map.size}, ${time}ms)");
}
private val _taskGetProfile = BatchedTaskHandler<PublicKey, CachedPolycentricProfile>(_scope,
{ system ->
@@ -222,7 +230,7 @@ class PolycentricCache {
}
}
suspend fun getProfileAsync(id: PlatformID, url: String? = null): CachedPolycentricProfile? {
suspend fun getProfileAsync(id: PlatformID, urlNullCache: String? = null): CachedPolycentricProfile? {
if (!StatePolycentric.instance.enabled || id.claimType <= 0) {
return CachedPolycentricProfile(null);
}
@@ -243,8 +251,8 @@ class PolycentricCache {
Logger.v(TAG, "getProfileAsync (id: $id) != null (with retrieved valid claims)")
return getProfileAsync(claims.ownedClaims.first().system).await()
} else {
if(url != null)
_profileUrlCache.setAndSave(url, PolycentricCache.CachedPolycentricProfile(null));
if(urlNullCache != null)
_profileUrlCache.setAndSave(urlNullCache, PolycentricCache.CachedPolycentricProfile(null));
return null;
}
}
@@ -6,6 +6,9 @@ import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.futo.platformplayer.*
@@ -250,7 +253,11 @@ class DownloadService : Service() {
val notif = builder.build();
notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
startForeground(DOWNLOAD_NOTIF_ID, notif);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(DOWNLOAD_NOTIF_ID, notif, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
startForeground(DOWNLOAD_NOTIF_ID, notif);
}
}
fun closeDownloadSession() {
@@ -7,6 +7,8 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.FileProvider
@@ -173,7 +175,11 @@ class ExportingService : Service() {
val notif = builder.build();
notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
startForeground(EXPORT_NOTIF_ID, notif);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(EXPORT_NOTIF_ID, notif, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
startForeground(EXPORT_NOTIF_ID, notif);
}
}
fun closeExportSession() {
@@ -1,6 +1,7 @@
package com.futo.platformplayer.states
import android.content.Context
import kotlin.streams.asSequence
import kotlin.streams.toList
/***
@@ -26,7 +27,7 @@ class StateAssets {
else
break;
}
return (parts1 + parts2.stream().skip(toSkip.toLong()).toList()).joinToString("/");
return (parts1 + parts2.stream().skip(toSkip.toLong()).asSequence().toList()).joinToString("/");
}
/**
@@ -99,7 +99,7 @@ class StateCache {
if(existing != null && doUpdate) {
_subscriptionCache.update(existing.id!!, serialized);
return true;
return false;
}
else if(existing == null) {
_subscriptionCache.insert(serialized);
@@ -57,12 +57,16 @@ class StatePlayer {
var queueShuffle: Boolean = false
private set;
val hasQueue: Boolean get() {
val queueSize: Int get() {
synchronized(_queue) {
return _queue.isNotEmpty()
return _queue.size
}
}
val hasQueue: Boolean get() {
return queueSize > 1
}
val queueName: String get() = _queueName ?: _queueType;
//Events
@@ -169,10 +169,10 @@ class StatePolycentric {
}
}
fun getChannelUrls(url: String, channelId: PlatformID? = null, cacheOnly: Boolean = false): List<String> {
return getChannelUrlsWithUpdateResult(url, channelId, cacheOnly).second;
fun getChannelUrls(url: String, channelId: PlatformID? = null, cacheOnly: Boolean = false, doCacheNull: Boolean = false): List<String> {
return getChannelUrlsWithUpdateResult(url, channelId, cacheOnly, doCacheNull).second;
}
fun getChannelUrlsWithUpdateResult(url: String, channelId: PlatformID? = null, cacheOnly: Boolean = false): Pair<Boolean, List<String>> {
fun getChannelUrlsWithUpdateResult(url: String, channelId: PlatformID? = null, cacheOnly: Boolean = false, doCacheNull: Boolean = false): Pair<Boolean, List<String>> {
var didUpdate = false;
if (!enabled) {
return Pair(false, listOf(url));
@@ -184,7 +184,7 @@ class StatePolycentric {
if (polycentricCached == null && channelId != null) {
Logger.i("StateSubscriptions", "Get polycentric profile not cached");
if(!cacheOnly) {
polycentricProfile = runBlocking { PolycentricCache.instance.getProfileAsync(channelId, url) }?.profile;
polycentricProfile = runBlocking { PolycentricCache.instance.getProfileAsync(channelId, if(doCacheNull) url else null) }?.profile;
didUpdate = true;
}
} else {
@@ -258,11 +258,12 @@ class StateSubscriptions {
var polycentricBudget: Int = 10;
val subUrls = getSubscriptions().parallelStream().map {
if(usePolycentric) {
val result = StatePolycentric.instance.getChannelUrlsWithUpdateResult(it.channel.url, it.channel.id, polycentricBudget <= 0);
if(result.first)
val result = StatePolycentric.instance.getChannelUrlsWithUpdateResult(it.channel.url, it.channel.id, polycentricBudget <= 0, true);
if(result.first) {
synchronized(lock) {
polycentricBudget--;
}
}
Pair(it, result.second);
}
else
@@ -106,6 +106,10 @@ class CastView : ConstraintLayout {
_gestureControlView.stopAllGestures();
}
fun setLoopVisible(visible: Boolean) {
_buttonLoop.visibility = if (visible) View.VISIBLE else View.GONE;
}
fun setIsPlaying(isPlaying: Boolean) {
_updateTimeJob?.cancel();
@@ -22,7 +22,10 @@ class QueueEditorOverlay : LinearLayout {
_topbar.onClose.subscribe(this, onClose::emit);
_editor.onVideoOrderChanged.subscribe { StatePlayer.instance.setQueueWithExisting(it) }
_editor.onVideoRemoved.subscribe { v -> StatePlayer.instance.removeFromQueue(v) }
_editor.onVideoRemoved.subscribe { v ->
StatePlayer.instance.removeFromQueue(v);
_topbar.setInfo(context.getString(R.string.queue), "${StatePlayer.instance.queueSize} " + context.getString(R.string.videos));
}
_editor.onVideoClicked.subscribe { v -> StatePlayer.instance.setQueuePosition(v) }
_topbar.setInfo(context.getString(R.string.queue), "");
@@ -305,6 +305,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
StatePlayer.instance.onQueueChanged.subscribe(this) {
CoroutineScope(Dispatchers.Main).launch(Dispatchers.Main) {
setLoopVisible(!StatePlayer.instance.hasQueue)
updateNextPrevious();
}
}
@@ -361,6 +362,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_currentChapterLoopActive = false;
}
fun setLoopVisible(visible: Boolean) {
_control_loop.visibility = if (visible) View.VISIBLE else View.GONE;
_control_loop_fullscreen.visibility = if (visible) View.VISIBLE else View.GONE;
}
fun stopAllGestures() {
gestureControl.stopAllGestures();
}
@@ -391,11 +397,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
fun setArtwork(drawable: Drawable?) {
if (drawable != null) {
_videoView.defaultArtwork = drawable;
_videoView.useArtwork = true;
_videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_FILL;
fitOrFill(isFullScreen);
} else {
_videoView.defaultArtwork = null;
_videoView.useArtwork = false;
_videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_OFF;
}
}
@@ -71,6 +71,16 @@
android:paddingTop="10dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp">
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_import_platform"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleY="0.9"
android:scaleX="0.9"
app:buttonIcon="@drawable/ic_sources_filled"
app:buttonText="Import from Platform"
app:buttonBackground="@drawable/background_big_button_black"
app:buttonSubText="Import your data from a specific source" />
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_import_zip"
android:layout_width="match_parent"
+4 -3
View File
@@ -70,10 +70,11 @@
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
app:layout_constraintLeft_toLeftOf="@id/text_body"
app:layout_constraintTop_toBottomOf="@id/text_body"
android:layout_marginLeft="-10dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical">
@@ -83,7 +84,7 @@
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="10dp" />
android:layout_marginStart="9dp" />
<LinearLayout
android:id="@+id/layout_rating"
@@ -29,7 +29,7 @@
android:textColor="@color/white"
android:textSize="13dp"
android:gravity="center_vertical"
tools:text="500K" />
tools:text="50K" />
<com.futo.platformplayer.views.LoaderView
android:id="@+id/loader_likes"
@@ -42,6 +42,8 @@
android:id="@+id/pill_seperator"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
android:background="#808080"/>
@@ -68,7 +70,7 @@
android:textColor="@color/white"
android:gravity="center_vertical"
android:textSize="13dp"
tools:text="500K" />
tools:text="50K" />
<com.futo.platformplayer.views.LoaderView
android:id="@+id/loader_dislikes"
android:layout_width="14dp"
+1
View File
@@ -577,6 +577,7 @@
<string name="import_your_subscriptions_from_this_source">Import your subscriptions from this source</string>
<string name="import_your_playlists_from_this_source">Import your playlists from this source</string>
<string name="login">Login</string>
<string name="login_required">Login Required</string>
<string name="sign_into_the_platform_of_this_source">Sign into the platform of this source</string>
<string name="management">Management</string>
<string name="uninstall">Uninstall</string>
+16 -3
View File
@@ -24,6 +24,12 @@
<data android:host="www.youtube.com" />
<data android:host="m.youtube.com" />
<data android:host="rumble.com" />
<data android:host="kick.com" />
<data android:host="nebula.tv" />
<data android:host="odysee.com" />
<data android:host="patreon.com" />
<data android:host="soundcloud.com" />
<data android:host="twitch.tv" />
<data android:pathPrefix="/" />
</intent-filter>
<intent-filter android:autoVerify="true">
@@ -33,11 +39,18 @@
<data android:mimeType="text/plain" />
<data android:host="youtube.com" />
<data android:host="m.youtube.com" />
<data android:host="you.be" />
<data android:host="youtu.be" />
<data android:host="www.you.be" />
<data android:host="youtube.com" />
<data android:host="www.youtube.com" />
<data android:host="m.youtube.com" />
<data android:host="rumble.com" />
<data android:host="kick.com" />
<data android:host="nebula.tv" />
<data android:host="odysee.com" />
<data android:host="patreon.com" />
<data android:host="soundcloud.com" />
<data android:host="twitch.tv" />
</intent-filter>
</activity>
</application>