From c858cd1badc00255e4dd366cd6e4a89619f9f152 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Tue, 11 Oct 2022 13:18:13 +0200 Subject: [PATCH] Completely remove the obsolete extensions --- .../java/org/videolan/resources/Constants.kt | 1 - .../ui/preferences/PreferencesFragment.kt | 1 - application/vlc-android/build.gradle | 1 - .../res/layout/extension_item_view.xml | 104 ---- .../vlc-android/res/menu/navigation.xml | 56 -- .../vlc-android/res/xml/preferences.xml | 3 - .../res/xml/preferences_extensions.xml | 7 - application/vlc-android/res/xml/shortcuts.xml | 24 - .../org/videolan/vlc/MediaSessionCallback.kt | 144 +++-- .../src/org/videolan/vlc/PlaybackService.kt | 2 - .../src/org/videolan/vlc/StartActivity.kt | 2 - .../vlc/extensions/ExtensionListing.java | 240 -------- .../extensions/ExtensionManagerService.java | 243 -------- .../vlc/extensions/ExtensionsManager.java | 228 -------- .../org/videolan/vlc/extensions/Utils.java | 19 - .../org/videolan/vlc/gui/ContentActivity.kt | 5 - .../src/org/videolan/vlc/gui/MainActivity.kt | 9 - .../vlc/gui/browser/ExtensionAdapter.kt | 111 ---- .../vlc/gui/browser/ExtensionBrowser.kt | 200 ------- .../org/videolan/vlc/gui/helpers/Navigator.kt | 129 ++--- .../PreferencesExtensionFragment.kt | 122 ---- .../gui/preferences/PreferencesExtensions.kt | 111 ---- .../gui/preferences/PreferencesFragment.kt | 5 - .../videolan/vlc/media/MediaSessionBrowser.kt | 529 +++++++----------- extension-api/.gitignore | 1 - extension-api/build.gradle | 56 -- extension-api/proguard-rules.pro | 17 - extension-api/src/main/AndroidManifest.xml | 33 -- .../vlc/extensions/api/IExtensionHost.aidl | 11 - .../vlc/extensions/api/IExtensionService.aidl | 11 - .../vlc/extensions/api/VLCExtensionItem.aidl | 3 - .../vlc/extensions/api/VLCExtensionItem.java | 222 -------- .../extensions/api/VLCExtensionService.java | 166 ------ .../vlc/extensions/api/WarningActivity.java | 37 -- .../vlc/extensions/api/tools/Dialogs.java | 134 ----- .../vlc/extensions/api/tools/Helpers.java | 49 -- extension-api/src/main/res/values/strings.xml | 6 - extension-api/src/main/res/values/styles.xml | 14 - .../vlc/extensions/api/ExampleUnitTest.java | 38 -- settings.gradle | 2 +- 40 files changed, 304 insertions(+), 2792 deletions(-) delete mode 100644 application/vlc-android/res/layout/extension_item_view.xml delete mode 100644 application/vlc-android/res/menu/navigation.xml delete mode 100644 application/vlc-android/res/xml/preferences_extensions.xml delete mode 100644 application/vlc-android/src/org/videolan/vlc/extensions/ExtensionListing.java delete mode 100644 application/vlc-android/src/org/videolan/vlc/extensions/ExtensionManagerService.java delete mode 100644 application/vlc-android/src/org/videolan/vlc/extensions/ExtensionsManager.java delete mode 100644 application/vlc-android/src/org/videolan/vlc/extensions/Utils.java delete mode 100644 application/vlc-android/src/org/videolan/vlc/gui/browser/ExtensionAdapter.kt delete mode 100644 application/vlc-android/src/org/videolan/vlc/gui/browser/ExtensionBrowser.kt delete mode 100644 application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensionFragment.kt delete mode 100644 application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensions.kt delete mode 100644 extension-api/.gitignore delete mode 100644 extension-api/build.gradle delete mode 100644 extension-api/proguard-rules.pro delete mode 100644 extension-api/src/main/AndroidManifest.xml delete mode 100644 extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionHost.aidl delete mode 100644 extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionService.aidl delete mode 100644 extension-api/src/main/aidl/org/videolan/vlc/extensions/api/VLCExtensionItem.aidl delete mode 100644 extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionItem.java delete mode 100644 extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionService.java delete mode 100644 extension-api/src/main/java/org/videolan/vlc/extensions/api/WarningActivity.java delete mode 100644 extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Dialogs.java delete mode 100644 extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Helpers.java delete mode 100644 extension-api/src/main/res/values/strings.xml delete mode 100644 extension-api/src/main/res/values/styles.xml delete mode 100644 extension-api/src/test/java/org/videolan/vlc/extensions/api/ExampleUnitTest.java diff --git a/application/resources/src/main/java/org/videolan/resources/Constants.kt b/application/resources/src/main/java/org/videolan/resources/Constants.kt index aeabf3324..1d82d5c12 100644 --- a/application/resources/src/main/java/org/videolan/resources/Constants.kt +++ b/application/resources/src/main/java/org/videolan/resources/Constants.kt @@ -67,7 +67,6 @@ val ACTION_REMOTE_GENERIC = "remote.".buildPkgString() @JvmField val CUSTOM_ACTION_REWIND = "rewind".buildPkgString() @JvmField val EXTRA_CUSTOM_ACTION_ID = "EXTRA_CUSTOM_ACTION_ID".buildPkgString() @JvmField val EXTRA_SEEK_DELAY = "EXTRA_CUSTOM_ACTION_ID".buildPkgString() -const val ACTION_CAR_MODE_EXIT = "android.app.action.EXIT_CAR_MODE" const val PLAYLIST_TYPE_AUDIO = 0 const val PLAYLIST_TYPE_VIDEO = 1 const val PLAYLIST_TYPE_VIDEO_RESUME = 2 diff --git a/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesFragment.kt b/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesFragment.kt index 5ed84ec49..7f36182ba 100644 --- a/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesFragment.kt +++ b/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesFragment.kt @@ -49,7 +49,6 @@ class PreferencesFragment : BasePreferenceFragment(), SharedPreferences.OnShared override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) findPreference(SCREEN_ORIENTATION)?.isVisible = false - findPreference("extensions_category")?.isVisible = false findPreference("casting_category")?.isVisible = false findPreference(KEY_VIDEO_APP_SWITCH)?.isVisible = AndroidDevices.hasPiP } diff --git a/application/vlc-android/build.gradle b/application/vlc-android/build.gradle index 003ede2ed..acc4d0366 100644 --- a/application/vlc-android/build.gradle +++ b/application/vlc-android/build.gradle @@ -147,7 +147,6 @@ dependencies { releaseApi "org.videolan.android:medialibrary-all:$rootProject.ext.medialibraryVersion" signedReleaseApi "org.videolan.android:libvlc-all:$rootProject.ext.libvlcVersion" signedReleaseApi "org.videolan.android:medialibrary-all:$rootProject.ext.medialibraryVersion" - api project(':extension-api') api project(':application:tools') api project(':application:resources') api project(':application:mediadb') diff --git a/application/vlc-android/res/layout/extension_item_view.xml b/application/vlc-android/res/layout/extension_item_view.xml deleted file mode 100644 index 7b185214c..000000000 --- a/application/vlc-android/res/layout/extension_item_view.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/application/vlc-android/res/menu/navigation.xml b/application/vlc-android/res/menu/navigation.xml deleted file mode 100644 index 8172aab73..000000000 --- a/application/vlc-android/res/menu/navigation.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/application/vlc-android/res/xml/preferences.xml b/application/vlc-android/res/xml/preferences.xml index dd31ecf36..f2e9f67c6 100644 --- a/application/vlc-android/res/xml/preferences.xml +++ b/application/vlc-android/res/xml/preferences.xml @@ -86,9 +86,6 @@ - -> - - \ No newline at end of file diff --git a/application/vlc-android/res/xml/shortcuts.xml b/application/vlc-android/res/xml/shortcuts.xml index 1d512b359..a1a46e147 100644 --- a/application/vlc-android/res/xml/shortcuts.xml +++ b/application/vlc-android/res/xml/shortcuts.xml @@ -26,18 +26,6 @@ android:targetClass="org.videolan.vlc.StartActivity" /> - - - - - - - - diff --git a/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt b/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt index cc82472b7..1a4a7e764 100644 --- a/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt +++ b/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt @@ -44,7 +44,6 @@ import org.videolan.resources.util.getFromMl import org.videolan.tools.Settings import org.videolan.tools.removeQuery import org.videolan.tools.retrieveParent -import org.videolan.vlc.extensions.ExtensionsManager import org.videolan.vlc.gui.helpers.MediaComparators import org.videolan.vlc.media.MediaSessionBrowser import org.videolan.vlc.util.VoiceSearchParams @@ -161,88 +160,83 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService playbackService.lifecycleScope.launch { val context = playbackService.applicationContext try { - if (mediaId.startsWith(ExtensionsManager.EXTENSION_PREFIX)) { - val id = mediaId.replace(ExtensionsManager.EXTENSION_PREFIX + "_" + mediaId.split("_".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1] + "_", "") - onPlayFromUri(id.toUri(), null) - } else { - val mediaIdUri = Uri.parse(mediaId) - val position = mediaIdUri.getQueryParameter("i")?.toInt() ?: 0 - val page = mediaIdUri.getQueryParameter("p") - val pageOffset = page?.toInt()?.times(MediaSessionBrowser.MAX_RESULT_SIZE) ?: 0 - when (mediaIdUri.removeQuery().toString()) { - MediaSessionBrowser.ID_NO_MEDIA -> playbackService.displayPlaybackError(R.string.search_no_result) - MediaSessionBrowser.ID_NO_PLAYLIST -> playbackService.displayPlaybackError(R.string.noplaylist) - MediaSessionBrowser.ID_SHUFFLE_ALL -> { - val tracks = context.getFromMl { audio } - if (tracks.isNotEmpty() && isActive) { - tracks.sortWith(MediaComparators.ANDROID_AUTO) - loadMedia(tracks.toList(), SecureRandom().nextInt(min(tracks.size, MEDIALIBRARY_PAGE_SIZE))) - if (!playbackService.isShuffling) playbackService.shuffle() - } else { - playbackService.displayPlaybackError(R.string.search_no_result) - } + val mediaIdUri = Uri.parse(mediaId) + val position = mediaIdUri.getQueryParameter("i")?.toInt() ?: 0 + val page = mediaIdUri.getQueryParameter("p") + val pageOffset = page?.toInt()?.times(MediaSessionBrowser.MAX_RESULT_SIZE) ?: 0 + when (mediaIdUri.removeQuery().toString()) { + MediaSessionBrowser.ID_NO_MEDIA -> playbackService.displayPlaybackError(R.string.search_no_result) + MediaSessionBrowser.ID_NO_PLAYLIST -> playbackService.displayPlaybackError(R.string.noplaylist) + MediaSessionBrowser.ID_SHUFFLE_ALL -> { + val tracks = context.getFromMl { audio } + if (tracks.isNotEmpty() && isActive) { + tracks.sortWith(MediaComparators.ANDROID_AUTO) + loadMedia(tracks.toList(), SecureRandom().nextInt(min(tracks.size, MEDIALIBRARY_PAGE_SIZE))) + if (!playbackService.isShuffling) playbackService.shuffle() + } else { + playbackService.displayPlaybackError(R.string.search_no_result) } - MediaSessionBrowser.ID_LAST_ADDED -> { - val tracks = context.getFromMl { getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MediaSessionBrowser.MAX_HISTORY_SIZE, 0) } - if (tracks.isNotEmpty() && isActive) { - loadMedia(tracks.toList(), position) - } + } + MediaSessionBrowser.ID_LAST_ADDED -> { + val tracks = context.getFromMl { getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MediaSessionBrowser.MAX_HISTORY_SIZE, 0) } + if (tracks.isNotEmpty() && isActive) { + loadMedia(tracks.toList(), position) } - MediaSessionBrowser.ID_HISTORY -> { - val tracks = context.getFromMl { lastMediaPlayed()?.toList()?.filter { MediaSessionBrowser.isMediaAudio(it) } } - if (!tracks.isNullOrEmpty() && isActive) { - val mediaList = tracks.subList(0, tracks.size.coerceAtMost(MediaSessionBrowser.MAX_HISTORY_SIZE)) - loadMedia(mediaList, position) - } + } + MediaSessionBrowser.ID_HISTORY -> { + val tracks = context.getFromMl { lastMediaPlayed()?.toList()?.filter { MediaSessionBrowser.isMediaAudio(it) } } + if (!tracks.isNullOrEmpty() && isActive) { + val mediaList = tracks.subList(0, tracks.size.coerceAtMost(MediaSessionBrowser.MAX_HISTORY_SIZE)) + loadMedia(mediaList, position) } - MediaSessionBrowser.ID_STREAM -> { - val tracks = context.getFromMl { lastStreamsPlayed() } - if (tracks.isNotEmpty() && isActive) { - tracks.sortWith(MediaComparators.ANDROID_AUTO) - loadMedia(tracks.toList(), position) - } + } + MediaSessionBrowser.ID_STREAM -> { + val tracks = context.getFromMl { lastStreamsPlayed() } + if (tracks.isNotEmpty() && isActive) { + tracks.sortWith(MediaComparators.ANDROID_AUTO) + loadMedia(tracks.toList(), position) } - MediaSessionBrowser.ID_TRACK -> { - val tracks = context.getFromMl { audio } - if (tracks.isNotEmpty() && isActive) { - tracks.sortWith(MediaComparators.ANDROID_AUTO) - loadMedia(tracks.toList(), pageOffset + position) - } + } + MediaSessionBrowser.ID_TRACK -> { + val tracks = context.getFromMl { audio } + if (tracks.isNotEmpty() && isActive) { + tracks.sortWith(MediaComparators.ANDROID_AUTO) + loadMedia(tracks.toList(), pageOffset + position) + } + } + MediaSessionBrowser.ID_SEARCH -> { + val query = mediaIdUri.getQueryParameter("query") ?: "" + val tracks = context.getFromMl { + search(query, false)?.tracks?.toList() ?: emptyList() + } + if (tracks.isNotEmpty() && isActive) { + loadMedia(tracks, position) } - MediaSessionBrowser.ID_SEARCH -> { - val query = mediaIdUri.getQueryParameter("query") ?: "" - val tracks = context.getFromMl { - search(query, false)?.tracks?.toList() ?: emptyList() + } + else -> { + val id = ContentUris.parseId(mediaIdUri) + when (mediaIdUri.retrieveParent().toString()) { + MediaSessionBrowser.ID_ALBUM -> { + val tracks = context.getFromMl { getAlbum(id)?.tracks } + if (isActive) tracks?.let { loadMedia(it.toList(), position) } } - if (tracks.isNotEmpty() && isActive) { - loadMedia(tracks, position) + MediaSessionBrowser.ID_ARTIST -> { + val tracks = context.getFromMl { getArtist(id)?.tracks } + if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } } - } - else -> { - val id = ContentUris.parseId(mediaIdUri) - when (mediaIdUri.retrieveParent().toString()) { - MediaSessionBrowser.ID_ALBUM -> { - val tracks = context.getFromMl { getAlbum(id)?.tracks } - if (isActive) tracks?.let { loadMedia(it.toList(), position) } - } - MediaSessionBrowser.ID_ARTIST -> { - val tracks = context.getFromMl { getArtist(id)?.tracks } - if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } - } - MediaSessionBrowser.ID_GENRE -> { - val tracks = context.getFromMl { getGenre(id)?.albums?.flatMap { it.tracks.toList() } } - if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } - } - MediaSessionBrowser.ID_PLAYLIST -> { - val tracks = context.getFromMl { getPlaylist(id, Settings.includeMissing)?.tracks } - if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } - } - MediaSessionBrowser.ID_MEDIA -> { - val tracks = context.getFromMl { getMedia(id)?.tracks } - if (isActive) tracks?.let { loadMedia(it.toList()) } - } - else -> throw IllegalStateException("Failed to load: $mediaId") + MediaSessionBrowser.ID_GENRE -> { + val tracks = context.getFromMl { getGenre(id)?.albums?.flatMap { it.tracks.toList() } } + if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } + } + MediaSessionBrowser.ID_PLAYLIST -> { + val tracks = context.getFromMl { getPlaylist(id, Settings.includeMissing)?.tracks } + if (isActive) tracks?.let { loadMedia(it.toList(), allowRandom = true) } + } + MediaSessionBrowser.ID_MEDIA -> { + val tracks = context.getFromMl { getMedia(id)?.tracks } + if (isActive) tracks?.let { loadMedia(it.toList()) } } + else -> throw IllegalStateException("Failed to load: $mediaId") } } } diff --git a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt index be70a9507..67491f057 100644 --- a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt +++ b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt @@ -172,7 +172,6 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc MiniPlayerAppWidgetProvider.ACTION_WIDGET_INIT -> updateWidget() MiniPlayerAppWidgetProvider.ACTION_WIDGET_ENABLED, VLCAppWidgetProvider.ACTION_WIDGET_DISABLED -> updateHasWidget() MiniPlayerAppWidgetProvider.ACTION_WIDGET_ENABLED, VLCAppWidgetProvider.ACTION_WIDGET_DISABLED -> updateHasWidget() - ACTION_CAR_MODE_EXIT -> MediaSessionBrowser.unbindExtensionConnection() AudioManager.ACTION_AUDIO_BECOMING_NOISY -> if (detectHeadset) { if (BuildConfig.DEBUG) Log.i(TAG, "Becoming noisy") wasPlaying = isPlaying @@ -626,7 +625,6 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc addAction(Intent.ACTION_HEADSET_PLUG) addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY) if (CarConnectionHandler.preferCarConnectionHandler()) addAction(CarConnectionHandler.RECEIVER_ACTION) - addAction(ACTION_CAR_MODE_EXIT) addAction(CUSTOM_ACTION) } registerReceiver(receiver, filter) diff --git a/application/vlc-android/src/org/videolan/vlc/StartActivity.kt b/application/vlc-android/src/org/videolan/vlc/StartActivity.kt index 5ddb6063a..a699f0bcc 100644 --- a/application/vlc-android/src/org/videolan/vlc/StartActivity.kt +++ b/application/vlc-android/src/org/videolan/vlc/StartActivity.kt @@ -72,8 +72,6 @@ class StartActivity : FragmentActivity() { "vlc.shortcut.video" -> R.id.nav_video "vlc.shortcut.audio" -> R.id.nav_audio "vlc.shortcut.browser" -> R.id.nav_directories - "vlc.shortcut.network" -> R.id.nav_network - "vlc.shortcut.playlists" -> R.id.nav_playlists "vlc.shortcut.resume" -> R.id.ml_menu_last_playlist else -> 0 } diff --git a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionListing.java b/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionListing.java deleted file mode 100644 index fe794fa05..000000000 --- a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionListing.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * ************************************************************************* - * pluginListing.java - * ************************************************************************** - * Copyright © 2015 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ - -package org.videolan.vlc.extensions; - -import android.content.ComponentName; -import android.os.Parcel; -import android.os.Parcelable; - -public class ExtensionListing implements Parcelable { - - public static final int PARCELABLE_VERSION = 1; - - private ComponentName mComponentName; - private int mProtocolVersion; - private boolean mCompatible; - private int menuIcon = 0; - private String mTitle; - private String mDescription; - private boolean mAndroidAutoEnabled = false; - private ComponentName mSettingsActivity; - - private ExtensionManagerService.Connection connection; - - public ExtensionListing(){} - - @Override - public int describeContents() { - return 0; - } - - /** - * Returns the full qualified component name of the extension. - */ - public ComponentName componentName() { - return mComponentName; - } - - /** - * Sets the full qualified component name of the extension. - */ - public ExtensionListing componentName(ComponentName componentName) { - mComponentName = componentName; - return this; - } - - /** - * Returns the version of the {@link org.videolan.vlc.extensions.api.VLCExtensionService} - * protocol used by the extension. - */ - public int protocolVersion() { - return mProtocolVersion; - } - - /** - * Sets the resId of the icon displayed in VLC menu for this extension. - */ - public ExtensionListing menuIcon(int menuIcon) { - this.menuIcon = menuIcon; - return this; - } - - /** - * Returns the resId of the icon displayed in VLC menu for this extension. - */ - public int menuIcon() { - return menuIcon; - } - - /** - * Sets the version of the {@link org.videolan.vlc.extensions.api.VLCExtensionService} - * protocol used by the extension. - */ - public ExtensionListing protocolVersion(int protocolVersion) { - mProtocolVersion = protocolVersion; - return this; - } - - /** - * Returns whether this extension is compatible to the host application; that is whether - * the version of the {@link org.videolan.vlc.extensions.api.VLCExtensionService} - * protocol used by the extension matches what is used by the host application. - */ - public boolean compatible() { - return mCompatible; - } - - /** - * Sets whether this extension is considered compatible to the host application. - */ - public ExtensionListing compatible(boolean compatible) { - mCompatible = compatible; - return this; - } - - /** - * Returns the label of the extension. - */ - public String title() { - return mTitle; - } - - /** - * Sets the label of the extension. - */ - public ExtensionListing title(String title) { - mTitle = title; - return this; - } - - /** - * Returns a description of the extension. - */ - public String description() { - return mDescription; - } - - /** - * Sets a description of the extension. - */ - public ExtensionListing description(String description) { - mDescription = description; - return this; - } - - /** - * Returns true if android-auto is supported. - */ - public boolean androidAutoEnabled() { - return mAndroidAutoEnabled; - } - - /** - * Sets the support of android-auto. - */ - public ExtensionListing androidAutoEnabled(boolean androidAutoEnabled) { - mAndroidAutoEnabled = androidAutoEnabled; - return this; - } - - /** - * Returns the full qualified component name of the settings class to configure - * the extension. - */ - public ComponentName settingsActivity() { - return mSettingsActivity; - } - - /** - * Sets the full qualified component name of the settings class to configure - * the extension. - */ - public ExtensionListing settingsActivity(ComponentName settingsActivity) { - this.mSettingsActivity = settingsActivity; - return this; - } - - public ExtensionManagerService.Connection getConnection() { - return connection; - } - - public void setConnection(ExtensionManagerService.Connection connection) { - this.connection = connection; - } - - /** - * @see android.os.Parcelable - */ - public static final Creator CREATOR - = new Creator() { - @Override - public ExtensionListing createFromParcel(Parcel in) { - return new ExtensionListing(in); - } - - @Override - public ExtensionListing[] newArray(int size) { - return new ExtensionListing[size]; - } - }; - - private ExtensionListing(Parcel in) { - int parcelableVersion = in.readInt(); - - // Version 1 below - if (parcelableVersion >= 1) { - mComponentName = ComponentName.readFromParcel(in); - mProtocolVersion = in.readInt(); - mCompatible = in.readInt() == 1; - mTitle = in.readString(); - mDescription = in.readString(); - boolean hasSettings = in.readInt() == 1; - menuIcon = in.readInt(); - if (hasSettings) { - mSettingsActivity = ComponentName.readFromParcel(in); - } - } - } - - @Override - public void writeToParcel(Parcel parcel, int i) { - /** - * NOTE: When adding fields in the process of updating this API, make sure to bump - * {@link #PARCELABLE_VERSION}. - */ - parcel.writeInt(PARCELABLE_VERSION); - - // Version 1 below - mComponentName.writeToParcel(parcel, 0); - parcel.writeInt(mProtocolVersion); - parcel.writeInt(mCompatible ? 1 : 0); - parcel.writeString(mTitle); - parcel.writeString(mDescription); - parcel.writeInt(mSettingsActivity != null ? 1 : 0); - parcel.writeInt(menuIcon); - if (mSettingsActivity != null) { - mSettingsActivity.writeToParcel(parcel, 0); - } - } -} diff --git a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionManagerService.java b/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionManagerService.java deleted file mode 100644 index b4038b47d..000000000 --- a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionManagerService.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * ************************************************************************* - * PluginService.java - * ************************************************************************** - * Copyright © 2015 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ - -package org.videolan.vlc.extensions; - -import android.app.Service; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.net.Uri; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; - -import org.videolan.medialibrary.MLServiceLocator; -import org.videolan.medialibrary.interfaces.media.MediaWrapper; -import org.videolan.vlc.extensions.api.IExtensionHost; -import org.videolan.vlc.extensions.api.IExtensionService; -import org.videolan.vlc.extensions.api.VLCExtensionItem; -import org.videolan.vlc.media.MediaUtils; - -import java.util.List; - -public class ExtensionManagerService extends Service { - - private static final String TAG = "VLC/ExtensionManagerService"; - - private final IBinder mBinder = new LocalBinder(); - private ExtensionManagerActivity mExtensionManagerActivity; - - int mCurrentIndex = -1; - - public interface ExtensionManagerActivity { - void displayExtensionItems(int extensionId, String title, List items, boolean showParams, boolean isRefresh); - } - - public void setExtensionManagerActivity(ExtensionManagerActivity activity) { - mExtensionManagerActivity = activity; - } - - @Override - public void onCreate() { - super.onCreate(); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_NOT_STICKY; - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public boolean onUnbind(Intent intent) { - setExtensionManagerActivity(null); - disconnect(); - return false; - } - - public class LocalBinder extends Binder { - public ExtensionManagerService getService() { - return ExtensionManagerService.this; - } - } - - public void openExtension(int index) { - if (index == mCurrentIndex) - browse(null); - else - connectService(index); - - } - - public ExtensionListing getCurrentExtension() { - return getExtensions().get(mCurrentIndex); - } - - public void connectService(final int index) { - ExtensionListing info = getExtensions().get(index); - - if (mCurrentIndex != -1) { - disconnect(); - } - - final Connection conn = new Connection(); - ComponentName cn = info.componentName(); - conn.componentName = cn; - conn.hostInterface = makeHostInterface(); - conn.serviceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - conn.ready = true; - conn.binder = IExtensionService.Stub.asInterface(service); - try { - conn.binder.onInitialize(index, conn.hostInterface); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mCurrentIndex = -1; - } - }; - - info.setConnection(conn); - - try { - if (!bindService(new Intent().setComponent(cn), conn.serviceConnection, - Context.BIND_AUTO_CREATE)) { - Log.e(TAG, "Error binding to extension " + cn.flattenToShortString()); - info.setConnection(null); - } else - mCurrentIndex = index; - } catch (SecurityException e) { - Log.e(TAG, "Error binding to extension " + cn.flattenToShortString(), e); - info.setConnection(null); - } - } - - public void refresh() { - try { - ExtensionListing extension = getExtensions().get(mCurrentIndex); - if (extension == null) - return; - IExtensionService service = extension.getConnection().binder; - if (service == null) - return; - service.refresh(); - } catch (RemoteException e) {} - } - - public void browse(String stringId) { - try { - ExtensionListing extension = getExtensions().get(mCurrentIndex); - if (extension == null) - return; - IExtensionService service = extension.getConnection().binder; - if (service == null) - return; - service.browse(stringId); - } catch (RemoteException e) {} - } - - public void disconnect() { - if (mCurrentIndex == -1) - return; - ExtensionListing extension = getCurrentExtension(); - Connection conn = extension.getConnection(); - if (conn != null) { - try { - unbindService(conn.serviceConnection); - } catch (Exception e) {} // In case of extension service crashed - } - extension.setConnection(null); - } - - private IExtensionHost makeHostInterface() { - return new IExtensionHost.Stub(){ - - @Override - public void updateList(final String title, final List items, final boolean showParams, final boolean isRefresh) throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mExtensionManagerActivity != null) - mExtensionManagerActivity.displayExtensionItems(mCurrentIndex, title, items, showParams, isRefresh); - } - }); - } - - @Override - public void playUri(Uri uri, String title) throws RemoteException { - final MediaWrapper media = MLServiceLocator.getAbstractMediaWrapper(uri); - if (!TextUtils.isEmpty(title)); - media.setDisplayTitle(title); - mHandler.post(new Runnable() { - @Override - public void run() { - MediaUtils.INSTANCE.openMediaNoUi(ExtensionManagerService.this, media); - } - }); - } - - @Override - public void unBind(int index) throws RemoteException { - if (mCurrentIndex == index) - mCurrentIndex = -1; - } - }; - } - - public static class Connection { - boolean ready = false; - ComponentName componentName; - ServiceConnection serviceConnection; - IExtensionService binder; - IExtensionHost hostInterface; - - /** - * Only access on the async thread. The pair is (collapse token, operation) - */ -// final Queue> deferredOps -// = new LinkedList>(); - } - - private final Handler mHandler = new Handler(); - - public List getExtensions() { - return ExtensionsManager.getInstance().getExtensions(getApplication(), false); - } -} diff --git a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionsManager.java b/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionsManager.java deleted file mode 100644 index 2840f0d68..000000000 --- a/application/vlc-android/src/org/videolan/vlc/extensions/ExtensionsManager.java +++ /dev/null @@ -1,228 +0,0 @@ -package org.videolan.vlc.extensions; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import com.google.android.material.navigation.NavigationView; -import androidx.appcompat.app.AlertDialog; -import android.text.TextUtils; -import android.view.MenuItem; - -import org.videolan.vlc.R; -import org.videolan.vlc.gui.MainActivity; -import org.videolan.tools.Settings; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -public class ExtensionsManager { - - private static final String KEY_PROTOCOL_VERSION = "protocolVersion"; - private static final String KEY_LISTING_TITLE = "title"; - private static final String KEY_DESCRIPTION = "description"; - private static final String KEY_MENU_ICON = "menuicon"; - private static final String KEY_SETTINGS_ACTIVITY = "settingsActivity"; - private static final String KEY_ANDROID_AUTO_ENABLED = "androidAutoEnabled"; - private static final String ACTION_EXTENSION = "org.videolan.vlc.Extension"; - private static final int PROTOCOLE_VERSION = 1; - public final static String EXTENSION_PREFIX = "extension"; - public final static String ANDROID_AUTO_SUFFIX = "androidAuto"; - public static boolean androidAutoInstalled = false; - - private static ExtensionsManager sExtensionsManager; - private final List mExtensions = new ArrayList<>(); - - public static ExtensionsManager getInstance() { - if (sExtensionsManager == null) - sExtensionsManager = new ExtensionsManager(); - return sExtensionsManager; - } - - private List updateAvailableExtensions(Context context) { - androidAutoInstalled = isAndroidAutoInstalled(context); - PackageManager pm = context.getPackageManager(); - List resolveInfos = pm.queryIntentServices( - new Intent(ACTION_EXTENSION), PackageManager.GET_META_DATA); - - List extensions = new ArrayList<>(); - - for (ResolveInfo resolveInfo : resolveInfos) { - ExtensionListing extension = new ExtensionListing(); - extension.componentName(new ComponentName(resolveInfo.serviceInfo.packageName, - resolveInfo.serviceInfo.name)); - Bundle metaData = resolveInfo.serviceInfo.metaData; - if (metaData != null) { - extension.compatible(metaData.getInt(KEY_PROTOCOL_VERSION) == PROTOCOLE_VERSION); - if (!extension.compatible()) - continue; - String title = metaData.getString(KEY_LISTING_TITLE); - extension.title(title != null ? title : resolveInfo.loadLabel(pm).toString()); - extension.description(metaData.getString(KEY_DESCRIPTION)); - String settingsActivity = metaData.getString(KEY_SETTINGS_ACTIVITY); - if (!TextUtils.isEmpty(settingsActivity)) { - extension.settingsActivity(ComponentName.unflattenFromString( - resolveInfo.serviceInfo.packageName + "/" + settingsActivity)); - } - extension.androidAutoEnabled(metaData.getBoolean(KEY_ANDROID_AUTO_ENABLED, false)); - extension.menuIcon(metaData.getInt(KEY_MENU_ICON, 0)); - extensions.add(extension); - } - } - - SharedPreferences settings = Settings.INSTANCE.getInstance(context); - deleteUnusedExtensionPreferences(extensions, settings); - - if (context instanceof MainActivity && ((MainActivity)context).currentIdIsExtension()) { - if (previousExtensionIsEnabled(context)) { - String lastExtensionTitle = settings.getString("current_extension_name", null); - for (int i = 0; i < extensions.size(); ++i) { - if (TextUtils.equals(extensions.get(i).title(), lastExtensionTitle)) { - ((MainActivity)context).setCurrentFragmentId(i); - settings.edit().putInt("fragment_id", i).apply(); - break; - } - } - } else { - ((MainActivity)context).setCurrentFragmentId(-1); - settings.edit().putInt("fragment_id", -1).apply(); - } - } - - synchronized (mExtensions) { - mExtensions.clear(); - mExtensions.addAll(extensions); - } - return extensions; - } - - public List getExtensions(Context context, boolean update) { - if (mExtensions.size() == 0 || update) - return updateAvailableExtensions(context); - else - return mExtensions; - } - - public boolean previousExtensionIsEnabled(Context context) { - SharedPreferences settings = Settings.INSTANCE.getInstance(context); - String key = EXTENSION_PREFIX + "_" + settings.getString("current_extension_name", null); - return settings.contains(key) && settings.getBoolean(key, false); - } - - private boolean deleteUnusedExtensionPreferences(List list, SharedPreferences settings) { - boolean extensionMissing = false; - List extensionNames = new ArrayList<>(); - for (ExtensionListing extension : list) - extensionNames.add(extension.componentName().getPackageName()); - for (Map.Entry entry : settings.getAll().entrySet()) - if (entry.getKey().startsWith(EXTENSION_PREFIX + "_") - && !entry.getKey().endsWith("_" + ANDROID_AUTO_SUFFIX) - && !extensionNames.contains(entry.getKey().replace(EXTENSION_PREFIX + "_", ""))) { - settings.edit().remove(entry.getKey()).apply(); - settings.edit().remove(entry.getKey() + "_" + ANDROID_AUTO_SUFFIX).apply(); - extensionMissing = true; - } - return extensionMissing; - } - - public void displayPlugin(Activity activity, int id, ExtensionListing extension, boolean visible) { - if (visible) { - MenuItem extensionGroup = ((NavigationView)activity.findViewById(R.id.navigation)).getMenu().findItem(R.id.extensions_group); - extensionGroup.setVisible(true); - MenuItem item = extensionGroup.getSubMenu().add(R.id.extensions_group, id, 0, extension.title()); - item.setCheckable(false); - int iconRes = extension.menuIcon(); - Drawable extensionIcon = null; - if (iconRes != 0) { - try { - Resources res = activity.getPackageManager().getResourcesForApplication(extension.componentName().getPackageName()); - extensionIcon = res.getDrawable(extension.menuIcon()); - } catch (PackageManager.NameNotFoundException ignored) {} - } - if (extensionIcon != null) - item.setIcon(extensionIcon); - else - try { - item.setIcon(activity.getPackageManager().getApplicationIcon(extension.componentName().getPackageName())); - } catch (PackageManager.NameNotFoundException e) { - item.setIcon(R.drawable.icon); - } - } - } - - public void showExtensionPermissionDialog(final Activity activity, final int id, final ExtensionListing extension, final String key) { - final List extraTitles = new ArrayList<>(); - final List extraKeys = new ArrayList<>(); - - //Add necessary checkboxes - if (androidAutoInstalled && extension.androidAutoEnabled()) { - extraTitles.add(activity.getString(R.string.extension_permission_checkbox_title, activity.getString(R.string.android_auto))); - extraKeys.add(key + "_" + ANDROID_AUTO_SUFFIX); - } - - final boolean[] extraCheckedStates = new boolean[extraTitles.size()]; - Arrays.fill(extraCheckedStates, Boolean.TRUE); - new AlertDialog.Builder(activity).setTitle(activity.getString(R.string.extension_permission_title, extension.title())) - .setMultiChoiceItems(extraTitles.toArray(new CharSequence[extraTitles.size()]), - extraCheckedStates, - new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int pos, boolean b) { - extraCheckedStates[pos] = b; - } - }) - .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int which) { - Settings.INSTANCE.getInstance(activity.getApplication()).edit().putBoolean(key, true).apply(); - for (int i=0; i() { - private var itemsList: MutableList = ArrayList() - - val all: List - get() = itemsList - - inner class ViewHolder(var binding: ExtensionItemViewBinding) : RecyclerView.ViewHolder(binding.root), View.OnLongClickListener { - - init { - binding.holder = this - } - - fun onClick(v: View) { - val item = getItem(layoutPosition) - if (item.type == VLCExtensionItem.TYPE_DIRECTORY) { - fragment!!.browseItem(item) - } else if (item.type == VLCExtensionItem.TYPE_AUDIO || item.type == VLCExtensionItem.TYPE_VIDEO) { - val mw = MLServiceLocator.getAbstractMediaWrapper(item.link.toUri()) - mw.setDisplayTitle(item.getTitle()) - mw.description = item.getSubTitle() - mw.type = getTypeAccordingToItem(item.type) - MediaUtils.openMedia(v.context, mw) - } - } - - fun onMoreClick(@Suppress("UNUSED_PARAMETER") v: View) { - openContextMenu() - } - - override fun onLongClick(v: View): Boolean { - return openContextMenu() - } - - private fun openContextMenu(): Boolean { - if (fragment == null) - return false - fragment!!.openContextMenu(layoutPosition) - return true - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.extension_item_view, parent, false) as ExtensionItemViewBinding) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = getItem(position) - holder.binding.item = item - holder.binding.executePendingBindings() - } - - private fun getIconResId(item: VLCExtensionItem): Int { - return when (item.type) { - VLCExtensionItem.TYPE_AUDIO -> R.drawable.ic_browser_audio_normal - VLCExtensionItem.TYPE_DIRECTORY -> R.drawable.ic_menu_folder - VLCExtensionItem.TYPE_VIDEO -> R.drawable.ic_browser_video_normal - VLCExtensionItem.TYPE_SUBTITLE -> R.drawable.ic_browser_subtitle_normal - else -> R.drawable.ic_browser_unknown_normal - } - } - - fun getItem(position: Int): VLCExtensionItem { - return itemsList[position] - } - - override fun getItemCount(): Int { - return itemsList.size - } - - fun addAll(list: List) { - itemsList.clear() - itemsList.addAll(list) - notifyDataSetChanged() - } - - fun clear() { - itemsList.clear() - } - - private fun getTypeAccordingToItem(type: Int): Int { - return when (type) { - VLCExtensionItem.TYPE_DIRECTORY -> MediaWrapper.TYPE_DIR - VLCExtensionItem.TYPE_VIDEO -> MediaWrapper.TYPE_VIDEO - VLCExtensionItem.TYPE_AUDIO -> MediaWrapper.TYPE_AUDIO - VLCExtensionItem.TYPE_PLAYLIST -> MediaWrapper.TYPE_PLAYLIST - VLCExtensionItem.TYPE_SUBTITLE -> MediaWrapper.TYPE_SUBTITLE - else -> MediaWrapper.TYPE_ALL - } - } - - companion object { - private const val TAG = "VLC/ExtensionAdapter" - } -} diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/ExtensionBrowser.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/ExtensionBrowser.kt deleted file mode 100644 index 8644e0de7..000000000 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/ExtensionBrowser.kt +++ /dev/null @@ -1,200 +0,0 @@ -package org.videolan.vlc.gui.browser - -import android.content.Intent -import android.os.Bundle -import android.os.Message -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.floatingactionbutton.FloatingActionButton -import org.videolan.medialibrary.interfaces.media.MediaWrapper -import org.videolan.resources.CTX_APPEND -import org.videolan.resources.CTX_ITEM_DL -import org.videolan.resources.CTX_PLAY_ALL -import org.videolan.resources.CTX_PLAY_AS_AUDIO -import org.videolan.tools.WeakHandler -import org.videolan.vlc.R -import org.videolan.vlc.extensions.ExtensionManagerService -import org.videolan.vlc.extensions.Utils -import org.videolan.vlc.extensions.api.VLCExtensionItem -import org.videolan.vlc.gui.dialogs.CtxActionReceiver -import org.videolan.vlc.gui.view.SwipeRefreshLayout -import org.videolan.vlc.media.MediaUtils - -class ExtensionBrowser : Fragment(), View.OnClickListener, androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener, CtxActionReceiver { - - private var mTitle: String? = null - private var mAddDirectoryFAB: FloatingActionButton? = null - private val mAdapter: ExtensionAdapter = ExtensionAdapter(this) - private lateinit var mRecyclerView: RecyclerView - private lateinit var mEmptyView: TextView - private lateinit var mSwipeRefreshLayout: SwipeRefreshLayout - - private var mExtensionManagerService: ExtensionManagerService? = null - private var showSettings = false - private var mustBeTerminated = false - - private val mHandler = ExtensionBrowserHandler(this) - - fun setExtensionService(service: ExtensionManagerService) { - mExtensionManagerService = service - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val bundle = savedInstanceState ?: arguments - if (bundle != null) { - mTitle = bundle.getString(KEY_TITLE) - showSettings = bundle.getBoolean(KEY_SHOW_FAB) - val list = bundle.getParcelableArrayList(KEY_ITEMS_LIST) - if (list != null) mAdapter.addAll(list) - } - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val v = inflater.inflate(R.layout.directory_browser, container, false) - mRecyclerView = v.findViewById(R.id.network_list) - mEmptyView = v.findViewById(android.R.id.empty) - mEmptyView.setText(R.string.extension_empty) - mRecyclerView.layoutManager = LinearLayoutManager(activity) - mRecyclerView.adapter = mAdapter - registerForContextMenu(mRecyclerView) - mSwipeRefreshLayout = v.findViewById(R.id.swipeLayout) - mSwipeRefreshLayout.setOnRefreshListener(this) - return v - } - - override fun onResume() { - super.onResume() - if (mustBeTerminated) - requireActivity().supportFragmentManager.beginTransaction().remove(this).commit() - mustBeTerminated = true - } - - override fun onStart() { - super.onStart() - setTitle(mTitle) - updateDisplay() - if (showSettings) { - if (mAddDirectoryFAB == null) mAddDirectoryFAB = requireActivity().findViewById(R.id.fab) - mAddDirectoryFAB!!.setImageResource(R.drawable.ic_fab_add) - mAddDirectoryFAB!!.show() - mAddDirectoryFAB!!.setOnClickListener(this) - } - } - - override fun onStop() { - super.onStop() - if (showSettings) { - mAddDirectoryFAB!!.hide() - mAddDirectoryFAB!!.setOnClickListener(null) - } - } - - private fun setTitle(title: String?) { - val activity = activity as AppCompatActivity? - if (activity != null && activity.supportActionBar != null) { - activity.supportActionBar!!.title = title - requireActivity().invalidateOptionsMenu() - } - } - - fun goBack() { - val activity = activity - if (activity != null && activity.supportFragmentManager.popBackStackImmediate()) requireActivity().finish() - } - - fun doRefresh(title: String, items: List) { - setTitle(title) - mAdapter.addAll(items) - } - - private fun updateDisplay() { - if (mAdapter.itemCount > 0) { - mEmptyView.visibility = View.GONE - mRecyclerView.visibility = View.VISIBLE - } else { - mEmptyView.visibility = View.VISIBLE - mRecyclerView.visibility = View.GONE - } - } - - fun browseItem(item: VLCExtensionItem) { - mExtensionManagerService!!.browse(item.stringId) - } - - override fun onClick(v: View) { - if (v.id == mAddDirectoryFAB!!.id) { - val extension = mExtensionManagerService!!.currentExtension ?: return - val intent = Intent(Intent.ACTION_VIEW) - intent.component = extension.settingsActivity() - startActivity(intent) - } - } - - override fun onRefresh() { - mExtensionManagerService!!.refresh() - mHandler.sendEmptyMessageDelayed(ACTION_HIDE_REFRESH, REFRESH_TIMEOUT.toLong()) - } - - fun openContextMenu(position: Int) { - //showContext(requireActivity(), this, position, mAdapter.getItem(position), CTX_PLAY_ALL or CTX_APPEND or CTX_PLAY_AS_AUDIO or CTX_ITEM_DL) - } - - override fun onCtxAction(position: Int, option: Long) { - when (option) { - CTX_PLAY_ALL -> { - val items = mAdapter.all - val medias = ArrayList(items.size) - for (vlcItem in items) medias.add(Utils.mediawrapperFromExtension(vlcItem)) - MediaUtils.openList(activity, medias, position) - } - CTX_APPEND -> MediaUtils.appendMedia(activity, Utils.mediawrapperFromExtension(mAdapter.getItem(position))) - CTX_PLAY_AS_AUDIO -> { - val mw = Utils.mediawrapperFromExtension(mAdapter.getItem(position)) - mw.addFlags(MediaWrapper.MEDIA_FORCE_AUDIO) - MediaUtils.openMedia(activity, mw) - } - CTX_ITEM_DL -> { - } - }//TODO - } - - private inner class ExtensionBrowserHandler(owner: ExtensionBrowser) : WeakHandler(owner) { - - override fun handleMessage(msg: Message) { - when (msg.what) { - ACTION_HIDE_REFRESH -> { - removeMessages(ACTION_SHOW_REFRESH) - owner?.mSwipeRefreshLayout?.isRefreshing = false - } - ACTION_SHOW_REFRESH -> { - removeMessages(ACTION_HIDE_REFRESH) - owner?.mSwipeRefreshLayout?.isRefreshing = true - sendEmptyMessageDelayed(ACTION_HIDE_REFRESH, REFRESH_TIMEOUT.toLong()) - } - } - } - } - - companion object { - - const val TAG = "VLC/ExtensionBrowser" - - const val KEY_ITEMS_LIST = "key_items_list" - const val KEY_SHOW_FAB = "key_fab" - const val KEY_TITLE = "key_title" - - - private const val ACTION_HIDE_REFRESH = 42 - private const val ACTION_SHOW_REFRESH = 43 - - private const val REFRESH_TIMEOUT = 5000 - } -} diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/Navigator.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/Navigator.kt index b7d1614bf..8da57cd67 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/Navigator.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/Navigator.kt @@ -28,7 +28,6 @@ import android.content.* import android.os.Bundle import android.view.MenuItem import androidx.core.content.edit -import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.DefaultLifecycleObserver @@ -40,37 +39,27 @@ import org.videolan.resources.* import org.videolan.tools.* import org.videolan.vlc.BuildConfig import org.videolan.vlc.R -import org.videolan.vlc.extensions.ExtensionManagerService -import org.videolan.vlc.extensions.ExtensionsManager -import org.videolan.vlc.extensions.api.VLCExtensionItem import org.videolan.vlc.gui.* import org.videolan.vlc.gui.audio.AudioBrowserFragment import org.videolan.vlc.gui.browser.BaseBrowserFragment -import org.videolan.vlc.gui.browser.ExtensionBrowser import org.videolan.vlc.gui.browser.MainBrowserFragment import org.videolan.vlc.gui.helpers.UiTools.isTablet -import org.videolan.vlc.gui.preferences.PreferencesActivity import org.videolan.vlc.gui.video.VideoGridFragment import org.videolan.vlc.util.getScreenWidth private const val TAG = "Navigator" + class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObserver, INavigator { - private val defaultFragmentId= R.id.nav_video - override var currentFragmentId : Int = 0 + private val defaultFragmentId = R.id.nav_video + override var currentFragmentId: Int = 0 private var currentFragment: Fragment? = null private set private lateinit var activity: MainActivity private lateinit var settings: SharedPreferences - private var extensionsService: ExtensionManagerService? = null override lateinit var navigationView: List override lateinit var appbarLayout: AppBarLayout - override lateinit var extensionsManager: ExtensionsManager - override var extensionServiceConnection: ServiceConnection? = null - override var extensionManagerService: ExtensionManagerService? = null - private val isExtensionServiceBinded: Boolean - get() = extensionServiceConnection != null override fun MainActivity.setupNavigation(state: Bundle?) { activity = this @@ -80,7 +69,7 @@ class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObse currentFragment = supportFragmentManager.getFragment(state, "current_fragment") } lifecycle.addObserver(this@Navigator) - navigationView = listOf(findViewById(R.id.navigation),findViewById(R.id.navigation_rail)) + navigationView = listOf(findViewById(R.id.navigation), findViewById(R.id.navigation_rail)) appbarLayout = findViewById(R.id.appbar) } @@ -91,14 +80,6 @@ class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObse override fun onStop(owner: LifecycleOwner) { navigationView.forEach { it.setOnItemSelectedListener(null) } - if (isExtensionServiceBinded) { - activity.unbindService(extensionServiceConnection!!) - extensionServiceConnection = null - } - if (currentIdIsExtension()) { - val name = extensionsManager.getExtensions(activity.application, false)[currentFragmentId].componentName().packageName - settings.putSingle("current_extension_name", name) - } } private fun getNewFragment(id: Int): Fragment { @@ -153,17 +134,12 @@ class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObse override fun getFragmentWidth(activity: Activity): Int { val screenWidth = activity.getScreenWidth() - return screenWidth - activity.resources.getDimension(R.dimen.navigation_margin).toInt() + return screenWidth - activity.resources.getDimension(R.dimen.navigation_margin).toInt() } private fun getTag(id: Int) = when (id) { - R.id.nav_settings -> ID_PREFERENCES R.id.nav_audio -> ID_AUDIO - R.id.nav_playlists -> ID_PLAYLISTS R.id.nav_directories -> ID_DIRECTORIES - R.id.nav_history -> ID_HISTORY - R.id.nav_mrl -> ID_MRL - R.id.nav_network -> ID_NETWORK else -> ID_VIDEO } @@ -172,75 +148,40 @@ class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObse val current = currentFragment appbarLayout.setExpanded(true, true) - if (item.groupId == R.id.extensions_group) { - if (currentFragmentId == id) { - clearBackstackFromClass(ExtensionBrowser::class.java) - return false - } else - extensionsService?.openExtension(id) - } else { - if (isExtensionServiceBinded) extensionsService?.disconnect() - if (current == null) { + if (current == null) { + return false + } + if (current is BaseFragment && current.actionMode != null) current.stopActionMode() + + if (currentFragmentId == id) { /* Already selected */ + // Go back at root level of current mProvider + if ((current as? BaseBrowserFragment)?.isStarted() == false) { + activity.supportFragmentManager.popBackStackImmediate("root", FragmentManager.POP_BACK_STACK_INCLUSIVE) + } else { return false } - if (current is BaseFragment && current.actionMode != null) current.stopActionMode() - - if (currentFragmentId == id) { /* Already selected */ - // Go back at root level of current mProvider - if ((current as? BaseBrowserFragment)?.isStarted() == false) { - activity.supportFragmentManager.popBackStackImmediate("root", FragmentManager.POP_BACK_STACK_INCLUSIVE) - } else { - return false - } - } else when (id) { - R.id.nav_settings -> activity.startActivityForResult(Intent(activity, PreferencesActivity::class.java), ACTIVITY_RESULT_PREFERENCES) - else -> { - activity.slideDownAudioPlayer() - showFragment(id) - } + } else when (id) { +// R.id.nav_settings -> activity.startActivityForResult(Intent(activity, PreferencesActivity::class.java), ACTIVITY_RESULT_PREFERENCES) + else -> { + activity.slideDownAudioPlayer() + showFragment(id) } } return true } - override fun displayExtensionItems(extensionId: Int, title: String, items: List, showParams: Boolean, refresh: Boolean) { - if (refresh && currentFragment is ExtensionBrowser) { - (currentFragment as ExtensionBrowser).doRefresh(title, items) - } else { - val fragment = ExtensionBrowser() - fragment.arguments = bundleOf(ExtensionBrowser.KEY_ITEMS_LIST to ArrayList(items), - ExtensionBrowser.KEY_SHOW_FAB to showParams, ExtensionBrowser.KEY_TITLE to title) - extensionsService?.let { fragment.setExtensionService(it) } - when { - currentFragment !is ExtensionBrowser -> //case: non-extension to extension root - showFragment(fragment, extensionId, title) - currentFragmentId == extensionId -> //case: extension root to extension sub dir - showFragment(fragment, extensionId, title) - else -> { //case: extension to other extension root - clearBackstackFromClass(ExtensionBrowser::class.java) - showFragment(fragment, extensionId, title) - } - } - } - updateCheckedItem(extensionId) - } private fun updateCheckedItem(id: Int) { - when (id) { - R.id.nav_settings -> return - else -> { - val currentId = currentFragmentId - navigationView.forEach { - val target = it.menu.findItem(id) - if (id != it.selectedItemId && target != null) { - val current = it.menu.findItem(currentId) - if (current != null) current.isChecked = false - target.isChecked = true - /* Save the tab status in pref */ - settings.edit { putInt("fragment_id", id) } - } - } + val currentId = currentFragmentId + navigationView.forEach { + val target = it.menu.findItem(id) + if (id != it.selectedItemId && target != null) { + val current = it.menu.findItem(currentId) + if (current != null) current.isChecked = false + target.isChecked = true + /* Save the tab status in pref */ + settings.edit { putInt("fragment_id", id) } } } } @@ -250,17 +191,11 @@ class Navigator : NavigationBarView.OnItemSelectedListener, DefaultLifecycleObse interface INavigator { var navigationView: List var appbarLayout: AppBarLayout - var currentFragmentId : Int - - - var extensionsManager: ExtensionsManager - var extensionServiceConnection: ServiceConnection? - var extensionManagerService: ExtensionManagerService? + var currentFragmentId: Int fun MainActivity.setupNavigation(state: Bundle?) - fun currentIdIsExtension() : Boolean - fun displayExtensionItems(extensionId: Int, title: String, items: List, showParams: Boolean, refresh: Boolean) + fun currentIdIsExtension(): Boolean fun reloadPreferences() - fun configurationChanged(size:Int) + fun configurationChanged(size: Int) fun getFragmentWidth(activity: Activity): Int } \ No newline at end of file diff --git a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensionFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensionFragment.kt deleted file mode 100644 index 33f327139..000000000 --- a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensionFragment.kt +++ /dev/null @@ -1,122 +0,0 @@ -package org.videolan.vlc.gui.preferences - - -import android.content.SharedPreferences -import android.os.Bundle -import android.view.View -import androidx.preference.CheckBoxPreference -import androidx.preference.Preference -import androidx.preference.SwitchPreferenceCompat -import androidx.preference.TwoStatePreference -import com.google.android.material.appbar.AppBarLayout -import org.videolan.tools.Settings -import org.videolan.tools.putSingle -import org.videolan.vlc.R -import org.videolan.vlc.extensions.ExtensionListing -import org.videolan.vlc.extensions.ExtensionsManager -import java.util.* - -class PreferencesExtensionFragment : BasePreferenceFragment() { - - private var extension: ExtensionListing? = null - private var extensionId: Int = 0 - private var extensionTitle: String? = null - private var extensionKey: String? = null - private var extensionPackageName: String? = null - private lateinit var settings: SharedPreferences - // private var preferenceScreen: PreferenceScreen? = null - private var androidAutoAvailable = false - private val preferences = ArrayList() - - override fun onCreate(bundle: Bundle?) { - var newBundle = bundle - super.onCreate(newBundle) - settings = Settings.getInstance(requireActivity()) - setHasOptionsMenu(true) - if (newBundle == null) - newBundle = arguments - - if (newBundle != null) { - extensionKey = newBundle.getString("extension_key") - extensionPackageName = extensionKey!!.replace(ExtensionsManager.EXTENSION_PREFIX + "_", "") - extensionId = ExtensionsManager.getInstance().getExtensionId(extensionPackageName) - extension = ExtensionsManager.getInstance().getExtensions(requireActivity().application, false)[extensionId] - extensionTitle = extension!!.title() - setTitle(extensionTitle) - androidAutoAvailable = ExtensionsManager.androidAutoInstalled && extension!!.androidAutoEnabled() - createCheckboxes() - } - } - - override fun onSaveInstanceState(outState: Bundle) { - outState.putString("extension_key", extensionKey) - super.onSaveInstanceState(outState) - } - - override fun onStart() { - super.onStart() - ((activity as PreferencesActivity).findViewById(R.id.appbar) as AppBarLayout).setExpanded(true, false) - } - - override fun getXml(): Int { - return R.xml.preferences_extension_page - } - - override fun getTitleId(): Int { - return 0 - } - - private fun setTitle(title: String?) { - val activity = activity as PreferencesActivity? - if (activity != null && activity.supportActionBar != null) - (getActivity() as PreferencesActivity).supportActionBar!!.title = title - } - - private fun createCheckboxes() { - preferenceScreen = this.preferenceScreen - - //Main switch - val switchPreference = SwitchPreferenceCompat(preferenceScreen!!.context) - switchPreference.title = preferenceScreen!!.context.getString(R.string.extension_prefs_activation_title).uppercase(Locale.getDefault()) - switchPreference.key = extensionKey - switchPreference.isChecked = settings.getBoolean(extensionKey, false) - switchPreference.onPreferenceChangeListener = null - preferenceScreen!!.addPreference(switchPreference) - - //Android-auto - if (androidAutoAvailable) { - val checkbox = CheckBoxPreference(preferenceScreen!!.context) - checkbox.setTitle(R.string.android_auto) - val key = extensionKey + "_" + ExtensionsManager.ANDROID_AUTO_SUFFIX - checkbox.key = key - checkbox.isChecked = switchPreference.isChecked && settings.getBoolean(key, false) - checkbox.isEnabled = switchPreference.isChecked - preferences.add(checkbox) - preferenceScreen!!.addPreference(checkbox) - } - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - val key = preference.key - if (key == null || !key.startsWith(extensionKey!!)) - return false - - if (key == extensionKey) { - val switchPreference = preference as CheckBoxPreference - settings.putSingle(key, switchPreference.isChecked) - if (switchPreference.isChecked) { - for (checkbox in preferences) - checkbox.isEnabled = true - } else { - for (checkbox in preferences) { - (checkbox as CheckBoxPreference).isChecked = false - settings.putSingle(checkbox.getKey(), false) - checkbox.setEnabled(false) - } - } - } else if (key.endsWith("_" + ExtensionsManager.ANDROID_AUTO_SUFFIX)) { - settings.putSingle(preference.key, (preference as TwoStatePreference).isChecked) - } - return super.onPreferenceTreeClick(preference) - } -} \ No newline at end of file diff --git a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensions.kt b/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensions.kt deleted file mode 100644 index 602ea1f6e..000000000 --- a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesExtensions.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.videolan.vlc.gui.preferences - -import android.content.SharedPreferences -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.View -import androidx.appcompat.widget.SwitchCompat -import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf -import androidx.preference.Preference -import androidx.preference.PreferenceCategory -import com.google.android.material.appbar.AppBarLayout -import org.videolan.vlc.R -import org.videolan.vlc.extensions.ExtensionListing -import org.videolan.vlc.extensions.ExtensionsManager -import org.videolan.vlc.gui.view.ClickableSwitchPreference -import org.videolan.tools.Settings -import org.videolan.tools.putSingle -import java.util.* - -class PreferencesExtensions : BasePreferenceFragment() { - - private var extensions: List = ArrayList() - private lateinit var settings: SharedPreferences - private var count = 0 - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - settings = Settings.getInstance(requireContext()) - extensions = ExtensionsManager.getInstance().getExtensions(requireActivity().applicationContext, false) - preferenceScreen = this.preferenceScreen - } - - override fun onStart() { - super.onStart() - ((activity as PreferencesActivity).findViewById(R.id.appbar) as AppBarLayout).setExpanded(true, false) - createCheckboxes() - } - - override fun onStop() { - super.onStop() - preferenceScreen!!.removeAll() - } - - override fun getXml(): Int { - return R.xml.preferences_extensions - } - - override fun getTitleId(): Int { - return R.string.extensions_prefs_category - } - - private fun createCheckboxes() { - val pm = requireActivity().applicationContext.packageManager - for (i in extensions.indices) { - val extension = extensions[i] - val switchPreference = ClickableSwitchPreference(preferenceScreen!!.context) - switchPreference.title = extension.title() - switchPreference.summary = extension.description() - val key = ExtensionsManager.EXTENSION_PREFIX + "_" + extension.componentName().packageName - switchPreference.key = key - val iconRes = extension.menuIcon() - var extensionIcon: Drawable? = null - if (iconRes != 0) { - try { - extensionIcon = ContextCompat.getDrawable(requireActivity(), extension.menuIcon()) - } catch (e: PackageManager.NameNotFoundException) { - } - - } - if (extensionIcon != null) - switchPreference.icon = extensionIcon - else - try { - switchPreference.icon = pm.getApplicationIcon(extensions[i].componentName().packageName) - } catch (e: PackageManager.NameNotFoundException) { - switchPreference.setIcon(R.drawable.icon) - } - - val checked = settings.getBoolean(key, false) - switchPreference.isChecked = checked - preferenceScreen!!.addPreference(switchPreference) - switchPreference.setOnSwitchClickListener(View.OnClickListener { - if ((view as SwitchCompat).isChecked) - settings.putSingle(key, true) - else - for ((key1) in settings.all) - if (key1.startsWith(ExtensionsManager.EXTENSION_PREFIX + "_")) - settings.putSingle(key1, false) - }) - count++ - } - - if (count == 0) { - val emptyCategory = PreferenceCategory(preferenceScreen!!.context) - emptyCategory.setTitle(R.string.extensions_empty) - preferenceScreen!!.addPreference(emptyCategory) - } - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - val key = preference.key - if (key == null || !key.startsWith(ExtensionsManager.EXTENSION_PREFIX + "_")) - return false - val fragment = PreferencesExtensionFragment() - fragment.arguments = bundleOf("extension_key" to key) - loadFragment(fragment) - return super.onPreferenceTreeClick(preference) - } -} diff --git a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesFragment.kt index 6f3ac78e1..60a08d09b 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/preferences/PreferencesFragment.kt @@ -62,7 +62,6 @@ class PreferencesFragment : BasePreferenceFragment(), SharedPreferences.OnShared override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - findPreference("extensions_category")?.isVisible = BuildConfig.DEBUG arguments?.getParcelable(EXTRA_PREF_END_POINT)?.let { endPoint -> when (endPoint.parentScreen) { R.xml.preferences_ui -> loadFragment(PreferencesUi().apply { @@ -77,9 +76,6 @@ class PreferencesFragment : BasePreferenceFragment(), SharedPreferences.OnShared R.xml.preferences_audio -> loadFragment(PreferencesAudio().apply { arguments = bundleOf(EXTRA_PREF_END_POINT to endPoint) }) - R.xml.preferences_extensions -> loadFragment(PreferencesExtensions().apply { - arguments = bundleOf(EXTRA_PREF_END_POINT to endPoint) - }) R.xml.preferences_adv -> loadFragment(PreferencesAdvanced().apply { arguments = bundleOf(EXTRA_PREF_END_POINT to endPoint) }) @@ -109,7 +105,6 @@ class PreferencesFragment : BasePreferenceFragment(), SharedPreferences.OnShared "video_category" -> loadFragment(PreferencesVideo()) "subtitles_category" -> loadFragment(PreferencesSubtitles()) "audio_category" -> loadFragment(PreferencesAudio()) - "extensions_category" -> loadFragment(PreferencesExtensions()) "adv_category" -> loadFragment(PreferencesAdvanced()) "casting_category" -> loadFragment(PreferencesCasting()) PLAYBACK_HISTORY -> { diff --git a/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt b/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt index d593d6f2a..eb12f14ce 100644 --- a/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt +++ b/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt @@ -23,15 +23,12 @@ */ package org.videolan.vlc.media -import android.content.* -import android.content.pm.PackageManager -import android.content.res.Resources -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.drawable.BitmapDrawable +import android.content.ContentResolver +import android.content.ContentUris +import android.content.Context +import android.content.ServiceConnection import android.net.Uri import android.os.Bundle -import android.os.IBinder import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaDescriptionCompat import androidx.annotation.StringRes @@ -42,19 +39,12 @@ import org.videolan.medialibrary.interfaces.media.Album import org.videolan.medialibrary.interfaces.media.MediaWrapper import org.videolan.medialibrary.media.MediaLibraryItem import org.videolan.resources.* -import org.videolan.resources.AppContextProvider.appContext import org.videolan.tools.* import org.videolan.vlc.ArtworkProvider import org.videolan.vlc.BuildConfig import org.videolan.vlc.R -import org.videolan.vlc.extensions.ExtensionManagerService -import org.videolan.vlc.extensions.ExtensionManagerService.ExtensionManagerActivity -import org.videolan.vlc.extensions.ExtensionsManager -import org.videolan.vlc.extensions.api.VLCExtensionItem import org.videolan.vlc.gui.helpers.MediaComparators import org.videolan.vlc.gui.helpers.MediaComparators.formatArticles -import org.videolan.vlc.gui.helpers.UiTools.getDefaultAudioDrawable -import org.videolan.vlc.gui.helpers.getBitmapFromDrawable import org.videolan.vlc.isPathValid import org.videolan.vlc.media.MediaUtils.getMediaAlbum import org.videolan.vlc.media.MediaUtils.getMediaArtist @@ -104,34 +94,7 @@ import java.util.concurrent.Semaphore * Search * //org.videolan.vlc/search?query= */ -class MediaSessionBrowser : ExtensionManagerActivity { - override fun displayExtensionItems(extensionId: Int, title: String, items: List, showParams: Boolean, isRefresh: Boolean) { - if (showParams && items.size == 1 && items[0].getType() == VLCExtensionItem.TYPE_DIRECTORY) { - extensionManagerService?.browse(items[0].stringId) - return - } - var mediaItem: MediaDescriptionCompat.Builder - var extensionItem: VLCExtensionItem - for ((i, extensionItem) in items.withIndex()) { - if (extensionItem.getType() != VLCExtensionItem.TYPE_AUDIO && extensionItem.getType() != VLCExtensionItem.TYPE_DIRECTORY) continue - mediaItem = MediaDescriptionCompat.Builder() - val coverUri = extensionItem.getImageUri() - if (coverUri == null) mediaItem.setIconBitmap(getDefaultAudioDrawable(appContext).bitmap) - else mediaItem.setIconUri(coverUri) - mediaItem.setTitle(extensionItem.getTitle()) - mediaItem.setSubtitle(extensionItem.getSubTitle()) - val playable = extensionItem.getType() == VLCExtensionItem.TYPE_AUDIO - if (playable) { - mediaItem.setMediaId("${ExtensionsManager.EXTENSION_PREFIX}_${extensionId}_${extensionItem.getLink()}") - extensionItems.add(MediaBrowserCompat.MediaItem(mediaItem.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) - } else { - mediaItem.setMediaId("${ExtensionsManager.EXTENSION_PREFIX}_${extensionId}_${extensionItem.stringId}") - extensionItems.add(MediaBrowserCompat.MediaItem(mediaItem.build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - } - if (i == MAX_EXTENSION_SIZE - 1) break - } - extensionLock.release() - } +class MediaSessionBrowser { companion object { private const val TAG = "VLC/MediaSessionBrowser" @@ -165,15 +128,10 @@ class MediaSessionBrowser : ExtensionManagerActivity { const val ID_GENRE = "$ID_LIBRARY/g" const val MAX_HISTORY_SIZE = 100 const val MAX_COVER_ART_ITEMS = 50 - private const val MAX_EXTENSION_SIZE = 100 private const val MAX_SUGGESTED_SIZE = 15 const val MAX_RESULT_SIZE = 800 // Extensions management - private var extensionServiceConnection: ServiceConnection? = null - private var extensionManagerService: ExtensionManagerService? = null - private val extensionItems: ArrayList = ArrayList() - private val extensionLock = Semaphore(0) @WorkerThread fun browse(context: Context, parentId: String, isShuffling: Boolean): List? { @@ -182,273 +140,215 @@ class MediaSessionBrowser : ExtensionManagerActivity { var limitSize = false val res = context.resources //Extensions - if (parentId.startsWith(ExtensionsManager.EXTENSION_PREFIX)) { - if (extensionServiceConnection == null) { - createExtensionServiceConnection(context) - try { - extensionLock.acquire() - } catch (e: InterruptedException) { - e.printStackTrace() - } - } - if (extensionServiceConnection == null) return null - val data = parentId.split("_").toTypedArray() - val index = Integer.valueOf(data[1]) - extensionItems.clear() - if (data.size == 2) { - //case extension root - extensionManagerService?.connectService(index) - } else { - //case sub-directory - val stringId = parentId.replace("${ExtensionsManager.EXTENSION_PREFIX}_${index}_", "") - extensionManagerService?.browse(stringId) - } - try { - extensionLock.acquire() - } catch (e: InterruptedException) { - e.printStackTrace() - } - results = extensionItems - } else { - val ml = Medialibrary.getInstance() - val parentIdUri = parentId.toUri() - val page = parentIdUri.getQueryParameter("p") - val pageOffset = page?.toInt()?.times(MAX_RESULT_SIZE) ?: 0 - val flatten = parentIdUri.getBooleanQueryParameter("f", false) - when (parentIdUri.removeQuery().toString()) { - ID_ROOT -> { - //List of Extensions - val extensions = ExtensionsManager.getInstance().getExtensions(context, true) - for ((i, extension) in extensions.withIndex()) { - val item = MediaDescriptionCompat.Builder() - if (extension.androidAutoEnabled() - && Settings.getInstance(context).getBoolean(ExtensionsManager.EXTENSION_PREFIX + "_" + extension.componentName().packageName + "_" + ExtensionsManager.ANDROID_AUTO_SUFFIX, false)) { - item.setMediaId(ExtensionsManager.EXTENSION_PREFIX + "_" + i) - .setTitle(extension.title()) - val iconRes = extension.menuIcon() - var b: Bitmap? = null - var extensionRes: Resources? - if (iconRes != 0) { - try { - extensionRes = context.packageManager - .getResourcesForApplication(extension.componentName().packageName) - b = BitmapFactory.decodeResource(extensionRes, iconRes) - } catch (ignored: PackageManager.NameNotFoundException) { - } - } - if (b != null) item.setIconBitmap(b) else try { - b = (context.packageManager.getApplicationIcon(extension.componentName().packageName) as BitmapDrawable).bitmap - item.setIconBitmap(b) - } catch (e: PackageManager.NameNotFoundException) { - b = context.getBitmapFromDrawable(R.drawable.icon) - item.setIconBitmap(b) - } - results.add(MediaBrowserCompat.MediaItem(item.build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - } - } - //Home - if (flatten) browse(context, ID_HOME, isShuffling)?.let { results.addAll(it) } - else { - val homeMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_HOME) - .setTitle(res.getString(R.string.auto_home)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_home)) - .setExtras(getContentStyle(CONTENT_STYLE_GRID_ITEM_HINT_VALUE, CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) - .build() - results.add(MediaBrowserCompat.MediaItem(homeMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - } - //Playlists - val playlistMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_PLAYLIST) - .setTitle(res.getString(R.string.playlists)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_playlist)) + val ml = Medialibrary.getInstance() + val parentIdUri = parentId.toUri() + val page = parentIdUri.getQueryParameter("p") + val pageOffset = page?.toInt()?.times(MAX_RESULT_SIZE) ?: 0 + val flatten = parentIdUri.getBooleanQueryParameter("f", false) + when (parentIdUri.removeQuery().toString()) { + ID_ROOT -> { + //Home + if (flatten) browse(context, ID_HOME, isShuffling)?.let { results.addAll(it) } + else { + val homeMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_HOME) + .setTitle(res.getString(R.string.auto_home)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_home)) .setExtras(getContentStyle(CONTENT_STYLE_GRID_ITEM_HINT_VALUE, CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) .build() - results.add(MediaBrowserCompat.MediaItem(playlistMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - //My library - if (flatten) browse(context, ID_LIBRARY, isShuffling)?.let { results.addAll(it) } - else { - val libraryMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_LIBRARY) - .setTitle(res.getString(R.string.auto_my_library)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_audio)) - .setExtras(getContentStyle(CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE)) - .build() - results.add(MediaBrowserCompat.MediaItem(libraryMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - } - //Streams - val streamsMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_STREAM) - .setTitle(res.getString(R.string.streams)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_stream)) - .build() - results.add(MediaBrowserCompat.MediaItem(streamsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - return results + results.add(MediaBrowserCompat.MediaItem(homeMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) } - ID_HOME -> { - /* Shuffle All */ - val audioCount = ml.audioCount - val shuffleAllPath = if (audioCount > 0) { - Uri.Builder() - .appendPath(ArtworkProvider.SHUFFLE_ALL) - .appendPath(ArtworkProvider.computeExpiration()) - .appendPath("$audioCount") - .build() - } else null - val shuffleAllMediaDesc = getPlayAllBuilder(context, ID_SHUFFLE_ALL, R.string.shuffle_all_title, audioCount, shuffleAllPath).build() - results.add(MediaBrowserCompat.MediaItem(shuffleAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) - /* Last Added */ - val recentAudio = ml.getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MAX_HISTORY_SIZE, 0) - val recentAudioSize = recentAudio.size - val lastAddedPath = if (recentAudioSize > 0) { - Uri.Builder() - .appendPath(ArtworkProvider.LAST_ADDED) - .appendPath("${ArtworkProvider.computeChecksum(recentAudio.toList())}") - .appendPath("$recentAudioSize") - .build() - } else null - val lastAddedMediaDesc = getPlayAllBuilder(context, ID_LAST_ADDED, R.string.auto_last_added_media, recentAudioSize, lastAddedPath).build() - results.add(MediaBrowserCompat.MediaItem(lastAddedMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - /* History */ - if (Settings.getInstance(context).getBoolean(PLAYBACK_HISTORY, true)) { - val lastMediaPlayed = ml.lastMediaPlayed()?.toList()?.filter { isMediaAudio(it) } - if (!lastMediaPlayed.isNullOrEmpty()) { - val lastMediaSize = lastMediaPlayed.size.coerceAtMost(MAX_HISTORY_SIZE) - val historyPath = Uri.Builder() - .appendPath(ArtworkProvider.HISTORY) - .appendPath("${ArtworkProvider.computeChecksum(lastMediaPlayed)}") - .appendPath("$lastMediaSize") - .build() - val historyMediaDesc = getPlayAllBuilder(context, ID_HISTORY, R.string.history, lastMediaSize, historyPath).build() - results.add(MediaBrowserCompat.MediaItem(historyMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - } - } - return results - } - ID_LIBRARY -> { - //Artists - val artistsShowAll = Settings.getInstance(context).getBoolean(KEY_ARTISTS_SHOW_ALL, false) - val artistsCount = ml.getArtistsCount(artistsShowAll) - val artistsMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_ARTIST) - .setTitle(res.getString(R.string.artists)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_artist)) - .setExtras(if (artistsCount > MAX_RESULT_SIZE) getContentStyle(CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE) else null) - .build() - results.add(MediaBrowserCompat.MediaItem(artistsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - //Albums - val albumsMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_ALBUM) - .setTitle(res.getString(R.string.albums)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_album)) - .setExtras(getContentStyle(if(ml.albumsCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) - .build() - results.add(MediaBrowserCompat.MediaItem(albumsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - //Tracks - val tracksMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_TRACK) - .setTitle(res.getString(R.string.tracks)) + //Playlists + val playlistMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_PLAYLIST) + .setTitle(res.getString(R.string.playlists)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_playlist)) + .setExtras(getContentStyle(CONTENT_STYLE_GRID_ITEM_HINT_VALUE, CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) + .build() + results.add(MediaBrowserCompat.MediaItem(playlistMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + //My library + if (flatten) browse(context, ID_LIBRARY, isShuffling)?.let { results.addAll(it) } + else { + val libraryMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_LIBRARY) + .setTitle(res.getString(R.string.auto_my_library)) .setIconUri(res.getResourceUri(R.drawable.ic_auto_audio)) - .setExtras(getContentStyle(if (ml.audioCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE)) + .setExtras(getContentStyle(CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE)) .build() - results.add(MediaBrowserCompat.MediaItem(tracksMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - //Genres - val genresMediaDesc = MediaDescriptionCompat.Builder() - .setMediaId(ID_GENRE) - .setTitle(res.getString(R.string.genres)) - .setIconUri(res.getResourceUri(R.drawable.ic_auto_genre)) - .setExtras(getContentStyle(if (ml.genresCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE)) - .build() - results.add(MediaBrowserCompat.MediaItem(genresMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) - return results - } - ID_ARTIST -> { - val artistsShowAll = Settings.getInstance(context).getBoolean(KEY_ARTISTS_SHOW_ALL, false) - val artists = ml.getArtists(artistsShowAll, Medialibrary.SORT_ALPHA, false, false) - artists.sortWith(MediaComparators.ANDROID_AUTO) - if (page == null && artists.size > MAX_RESULT_SIZE) - return paginateLibrary(artists, parentIdUri, res.getResourceUri(R.drawable.ic_auto_artist)) - list = artists.copyOfRange(pageOffset.coerceAtMost(artists.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(artists.size)) - } - ID_ALBUM -> { - val albums = ml.getAlbums(Medialibrary.SORT_ALPHA, false, false) - albums.sortWith(MediaComparators.ANDROID_AUTO) - if (page == null && albums.size > MAX_RESULT_SIZE) - return paginateLibrary(albums, parentIdUri, res.getResourceUri(R.drawable.ic_auto_album), getContentStyle(CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) - list = albums.copyOfRange(pageOffset.coerceAtMost(albums.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(albums.size)) - } - ID_TRACK -> { - val tracks = ml.getAudio(Medialibrary.SORT_ALPHA, false, false) - tracks.sortWith(MediaComparators.ANDROID_AUTO) - if (page == null && tracks.size > MAX_RESULT_SIZE) - return paginateLibrary(tracks, parentIdUri, res.getResourceUri(R.drawable.ic_auto_audio)) - list = tracks.copyOfRange(pageOffset.coerceAtMost(tracks.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(tracks.size)) + results.add(MediaBrowserCompat.MediaItem(libraryMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) } - ID_GENRE -> { - val genres = ml.getGenres(Medialibrary.SORT_ALPHA, false, false) - genres.sortWith(MediaComparators.ANDROID_AUTO) - if (page == null && genres.size > MAX_RESULT_SIZE) - return paginateLibrary(genres, parentIdUri, res.getResourceUri(R.drawable.ic_auto_genre)) - list = genres.copyOfRange(pageOffset.coerceAtMost(genres.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(genres.size)) + //Streams + val streamsMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_STREAM) + .setTitle(res.getString(R.string.streams)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_stream)) + .build() + results.add(MediaBrowserCompat.MediaItem(streamsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + return results + } + ID_HOME -> { + /* Shuffle All */ + val audioCount = ml.audioCount + val shuffleAllPath = if (audioCount > 0) { + Uri.Builder() + .appendPath(ArtworkProvider.SHUFFLE_ALL) + .appendPath(ArtworkProvider.computeExpiration()) + .appendPath("$audioCount") + .build() + } else null + val shuffleAllMediaDesc = getPlayAllBuilder(context, ID_SHUFFLE_ALL, R.string.shuffle_all_title, audioCount, shuffleAllPath).build() + results.add(MediaBrowserCompat.MediaItem(shuffleAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) + /* Last Added */ + val recentAudio = ml.getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MAX_HISTORY_SIZE, 0) + val recentAudioSize = recentAudio.size + val lastAddedPath = if (recentAudioSize > 0) { + Uri.Builder() + .appendPath(ArtworkProvider.LAST_ADDED) + .appendPath("${ArtworkProvider.computeChecksum(recentAudio.toList())}") + .appendPath("$recentAudioSize") + .build() + } else null + val lastAddedMediaDesc = getPlayAllBuilder(context, ID_LAST_ADDED, R.string.auto_last_added_media, recentAudioSize, lastAddedPath).build() + results.add(MediaBrowserCompat.MediaItem(lastAddedMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + /* History */ + if (Settings.getInstance(context).getBoolean(PLAYBACK_HISTORY, true)) { + val lastMediaPlayed = ml.lastMediaPlayed()?.toList()?.filter { isMediaAudio(it) } + if (!lastMediaPlayed.isNullOrEmpty()) { + val lastMediaSize = lastMediaPlayed.size.coerceAtMost(MAX_HISTORY_SIZE) + val historyPath = Uri.Builder() + .appendPath(ArtworkProvider.HISTORY) + .appendPath("${ArtworkProvider.computeChecksum(lastMediaPlayed)}") + .appendPath("$lastMediaSize") + .build() + val historyMediaDesc = getPlayAllBuilder(context, ID_HISTORY, R.string.history, lastMediaSize, historyPath).build() + results.add(MediaBrowserCompat.MediaItem(historyMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + } } - ID_PLAYLIST -> { + return results + } + ID_LIBRARY -> { + //Artists + val artistsShowAll = Settings.getInstance(context).getBoolean(KEY_ARTISTS_SHOW_ALL, false) + val artistsCount = ml.getArtistsCount(artistsShowAll) + val artistsMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_ARTIST) + .setTitle(res.getString(R.string.artists)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_artist)) + .setExtras(if (artistsCount > MAX_RESULT_SIZE) getContentStyle(CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE) else null) + .build() + results.add(MediaBrowserCompat.MediaItem(artistsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + //Albums + val albumsMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_ALBUM) + .setTitle(res.getString(R.string.albums)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_album)) + .setExtras(getContentStyle(if (ml.albumsCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) + .build() + results.add(MediaBrowserCompat.MediaItem(albumsMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + //Tracks + val tracksMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_TRACK) + .setTitle(res.getString(R.string.tracks)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_audio)) + .setExtras(getContentStyle(if (ml.audioCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE)) + .build() + results.add(MediaBrowserCompat.MediaItem(tracksMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + //Genres + val genresMediaDesc = MediaDescriptionCompat.Builder() + .setMediaId(ID_GENRE) + .setTitle(res.getString(R.string.genres)) + .setIconUri(res.getResourceUri(R.drawable.ic_auto_genre)) + .setExtras(getContentStyle(if (ml.genresCount > MAX_RESULT_SIZE) CONTENT_STYLE_CATEGORY_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE)) + .build() + results.add(MediaBrowserCompat.MediaItem(genresMediaDesc, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)) + return results + } + ID_ARTIST -> { + val artistsShowAll = Settings.getInstance(context).getBoolean(KEY_ARTISTS_SHOW_ALL, false) + val artists = ml.getArtists(artistsShowAll, Medialibrary.SORT_ALPHA, false, false) + artists.sortWith(MediaComparators.ANDROID_AUTO) + if (page == null && artists.size > MAX_RESULT_SIZE) + return paginateLibrary(artists, parentIdUri, res.getResourceUri(R.drawable.ic_auto_artist)) + list = artists.copyOfRange(pageOffset.coerceAtMost(artists.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(artists.size)) + } + ID_ALBUM -> { + val albums = ml.getAlbums(Medialibrary.SORT_ALPHA, false, false) + albums.sortWith(MediaComparators.ANDROID_AUTO) + if (page == null && albums.size > MAX_RESULT_SIZE) + return paginateLibrary(albums, parentIdUri, res.getResourceUri(R.drawable.ic_auto_album), getContentStyle(CONTENT_STYLE_GRID_ITEM_HINT_VALUE)) + list = albums.copyOfRange(pageOffset.coerceAtMost(albums.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(albums.size)) + } + ID_TRACK -> { + val tracks = ml.getAudio(Medialibrary.SORT_ALPHA, false, false) + tracks.sortWith(MediaComparators.ANDROID_AUTO) + if (page == null && tracks.size > MAX_RESULT_SIZE) + return paginateLibrary(tracks, parentIdUri, res.getResourceUri(R.drawable.ic_auto_audio)) + list = tracks.copyOfRange(pageOffset.coerceAtMost(tracks.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(tracks.size)) + } + ID_GENRE -> { + val genres = ml.getGenres(Medialibrary.SORT_ALPHA, false, false) + genres.sortWith(MediaComparators.ANDROID_AUTO) + if (page == null && genres.size > MAX_RESULT_SIZE) + return paginateLibrary(genres, parentIdUri, res.getResourceUri(R.drawable.ic_auto_genre)) + list = genres.copyOfRange(pageOffset.coerceAtMost(genres.size), (pageOffset + MAX_RESULT_SIZE).coerceAtMost(genres.size)) + } + ID_PLAYLIST -> { list = ml.playlists - list.sortWith(MediaComparators.ANDROID_AUTO) - } - ID_STREAM -> { - list = ml.lastStreamsPlayed() - list.sortWith(MediaComparators.ANDROID_AUTO) - } - ID_LAST_ADDED -> { - limitSize = true - list = ml.getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MAX_HISTORY_SIZE, 0) - } - ID_HISTORY -> { - limitSize = true - list = ml.lastMediaPlayed()?.toList()?.filter { isMediaAudio(it) }?.toTypedArray() - } - ID_SUGGESTED -> return buildSuggestions(context, parentId, ml) - else -> { - val id = ContentUris.parseId(parentIdUri) - when (parentIdUri.retrieveParent().toString()) { - ID_ALBUM -> list = ml.getAlbum(id).tracks - ID_ARTIST -> { - val artist = ml.getArtist(id) - list = artist.albums - if (list != null && list.size > 1) { - val hasArtwork = list.any { !it.artworkMrl.isNullOrEmpty() && isPathValid(it.artworkMrl) } - val shuffleMode = isShuffling && artist.tracksCount > 2 - val playAllPath = if (hasArtwork) { - Uri.Builder() - .appendPath(ArtworkProvider.PLAY_ALL) - .appendPath(ArtworkProvider.ARTIST) - .appendPath("${artist.tracksCount}") - .appendPath("$id") - .appendQueryParameter(ArtworkProvider.SHUFFLE, "${shuffleMode.toInt()}") - .build() - } else null - val title = if (shuffleMode) R.string.shuffle_all_title else R.string.play_all - val playAllMediaDesc = getPlayAllBuilder(context, parentId, title, artist.tracksCount, playAllPath).build() - results.add(MediaBrowserCompat.MediaItem(playAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) - } - } - ID_GENRE -> { - val genre = ml.getGenre(id) - list = genre.albums - val tracksCount = list.sumOf { it.tracksCount } - if (list != null && list.size > 1) { - val shuffleMode = isShuffling && tracksCount > 2 - val playAllPath = Uri.Builder() + list.sortWith(MediaComparators.ANDROID_AUTO) + } + ID_STREAM -> { + list = ml.lastStreamsPlayed() + list.sortWith(MediaComparators.ANDROID_AUTO) + } + ID_LAST_ADDED -> { + limitSize = true + list = ml.getPagedAudio(Medialibrary.SORT_INSERTIONDATE, true, false, MAX_HISTORY_SIZE, 0) + } + ID_HISTORY -> { + limitSize = true + list = ml.lastMediaPlayed()?.toList()?.filter { isMediaAudio(it) }?.toTypedArray() + } + ID_SUGGESTED -> return buildSuggestions(context, parentId, ml) + else -> { + val id = ContentUris.parseId(parentIdUri) + when (parentIdUri.retrieveParent().toString()) { + ID_ALBUM -> list = ml.getAlbum(id).tracks + ID_ARTIST -> { + val artist = ml.getArtist(id) + list = artist.albums + if (list != null && list.size > 1) { + val hasArtwork = list.any { !it.artworkMrl.isNullOrEmpty() && isPathValid(it.artworkMrl) } + val shuffleMode = isShuffling && artist.tracksCount > 2 + val playAllPath = if (hasArtwork) { + Uri.Builder() .appendPath(ArtworkProvider.PLAY_ALL) - .appendPath(ArtworkProvider.GENRE) - .appendPath("$tracksCount") + .appendPath(ArtworkProvider.ARTIST) + .appendPath("${artist.tracksCount}") .appendPath("$id") .appendQueryParameter(ArtworkProvider.SHUFFLE, "${shuffleMode.toInt()}") .build() - val title = if (shuffleMode) R.string.shuffle_all_title else R.string.play_all - val playAllMediaDesc = getPlayAllBuilder(context, parentId, title, tracksCount, playAllPath).build() - results.add(MediaBrowserCompat.MediaItem(playAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) - } + } else null + val title = if (shuffleMode) R.string.shuffle_all_title else R.string.play_all + val playAllMediaDesc = getPlayAllBuilder(context, parentId, title, artist.tracksCount, playAllPath).build() + results.add(MediaBrowserCompat.MediaItem(playAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) + } + } + ID_GENRE -> { + val genre = ml.getGenre(id) + list = genre.albums + val tracksCount = list.sumOf { it.tracksCount } + if (list != null && list.size > 1) { + val shuffleMode = isShuffling && tracksCount > 2 + val playAllPath = Uri.Builder() + .appendPath(ArtworkProvider.PLAY_ALL) + .appendPath(ArtworkProvider.GENRE) + .appendPath("$tracksCount") + .appendPath("$id") + .appendQueryParameter(ArtworkProvider.SHUFFLE, "${shuffleMode.toInt()}") + .build() + val title = if (shuffleMode) R.string.shuffle_all_title else R.string.play_all + val playAllMediaDesc = getPlayAllBuilder(context, parentId, title, tracksCount, playAllPath).build() + results.add(MediaBrowserCompat.MediaItem(playAllMediaDesc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)) } } } @@ -754,30 +654,5 @@ class MediaSessionBrowser : ExtensionManagerActivity { .setSubtitle(res.getString(R.string.track_number, trackCount)) .setIconUri(if (uri != null) ArtworkProvider.buildUri(ctx, uri) else res.getResourceUri(R.drawable.ic_auto_playall)) } - - private fun createExtensionServiceConnection(context: Context) { - extensionServiceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, service: IBinder) { - extensionManagerService = (service as ExtensionManagerService.LocalBinder).service.apply { - setExtensionManagerActivity(instance) - } - extensionLock.release() - } - - override fun onServiceDisconnected(name: ComponentName) { - context.unbindService(extensionServiceConnection!!) - extensionServiceConnection = null - extensionManagerService!!.stopSelf() - } - } - extensionServiceConnection?.let { - val intent = Intent(context, ExtensionManagerService::class.java) - if (!context.bindService(intent, it, Context.BIND_AUTO_CREATE)) extensionServiceConnection = null - } - } - - fun unbindExtensionConnection() { - extensionManagerService?.disconnect() - } } } diff --git a/extension-api/.gitignore b/extension-api/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/extension-api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/extension-api/build.gradle b/extension-api/build.gradle deleted file mode 100644 index 8d1ce8706..000000000 --- a/extension-api/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ************************************************************************* - * build.gradle.java - * ************************************************************************** - * Copyright © 2015 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ - -apply plugin: 'com.android.library' - -ext { - library_version = '0.1' - repoName = 'Android' - libraryName = 'VLC-API' - desc = 'SDK for VLC-Android extensions' - lib_artifact = 'vlc-extensions' - - libraryDescription = 'SDK for VLC-Android extensions' -} - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - namespace 'org.videolan.vlc.plugin.api' -} - -dependencies { - api "androidx.appcompat:appcompat:$rootProject.ext.appCompatVersion" - testImplementation "junit:junit:$rootProject.ext.junitVersion" -} - -apply from: '../buildsystem/publish.gradle' diff --git a/extension-api/proguard-rules.pro b/extension-api/proguard-rules.pro deleted file mode 100644 index ae5f54db3..000000000 --- a/extension-api/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /home/dekans/SDK/android-sdk-linux/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/extension-api/src/main/AndroidManifest.xml b/extension-api/src/main/AndroidManifest.xml deleted file mode 100644 index 0603ac48f..000000000 --- a/extension-api/src/main/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - diff --git a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionHost.aidl b/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionHost.aidl deleted file mode 100644 index 490fb35b1..000000000 --- a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionHost.aidl +++ /dev/null @@ -1,11 +0,0 @@ -package org.videolan.vlc.extensions.api; - -import org.videolan.vlc.extensions.api.VLCExtensionItem; -import android.net.Uri; - -interface IExtensionHost { - // Protocol version 1 - oneway void updateList(in String title, in List items, boolean showParams, boolean isRefresh); - oneway void playUri(in Uri uri, String title); - oneway void unBind(int index); -} diff --git a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionService.aidl b/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionService.aidl deleted file mode 100644 index 5b7e14b94..000000000 --- a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/IExtensionService.aidl +++ /dev/null @@ -1,11 +0,0 @@ -package org.videolan.vlc.extensions.api; - -import org.videolan.vlc.extensions.api.IExtensionHost; -import org.videolan.vlc.extensions.api.VLCExtensionItem; - -interface IExtensionService { - // Protocol version 1 - oneway void onInitialize(int index, in IExtensionHost host); - oneway void browse(String stringId); - oneway void refresh(); -} diff --git a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/VLCExtensionItem.aidl b/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/VLCExtensionItem.aidl deleted file mode 100644 index 286e920fb..000000000 --- a/extension-api/src/main/aidl/org/videolan/vlc/extensions/api/VLCExtensionItem.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package org.videolan.vlc.extensions.api; - -parcelable VLCExtensionItem; diff --git a/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionItem.java b/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionItem.java deleted file mode 100644 index 177984db9..000000000 --- a/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionItem.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * ************************************************************************* - * VLCExtensionItem.java - * ************************************************************************** - * Copyright © 2015 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ - -package org.videolan.vlc.extensions.api; - -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A parcelable, serializable object containing information about medium to be sent to VLC - * for browsing of playback - * - *

- * This class follows the fluent - * interface style, using method chaining to provide for more readable code. For example, to set - * the title and link of this data, use {@link #setTitle(String)} and {@link #setLink(String)} - * methods like so: - * - *

- * VLCExtensionItem data = new VLCExtensionItem(id, null)
- *     .setTitle("My Video")
- *     .setLink("http://mysite.net/myvideo.ogv");
- * 
- * - */ -public class VLCExtensionItem implements Parcelable { - - /** - * Item type to show it as a directory in VLC browser - */ - public static final int TYPE_DIRECTORY = 0; - /** - * Item type to show it as a video medium in VLC browser - */ - public static final int TYPE_VIDEO = 1; - /** - * Item type to show it as an audio medium in VLC browser - */ - public static final int TYPE_AUDIO = 2; - /** - * Item type to show it as a playlist item in VLC browser - */ - public static final int TYPE_PLAYLIST = 3; - /** - * Item type to show it as a subtitle file in VLC browser - */ - public static final int TYPE_SUBTITLE = 4; - /** - * Unknown type, VLC will guess from its {#link link} or title - */ - public static final int TYPE_OTHER_FILE = 5; - - public String stringId; - - public String link; - public String title; - public String subTitle; - - public Uri imageUri; // for content provider - public int type; // Using VLC icons. maybe with iconRes? - - /** - * Simple constructor, with only ids. - * You have to provide a String or int id for browsable elements (with type #TYPE_DIRECTORY) - * - * @param stringId The String to use as an ID, set to #null if you prefer to use the #intId - */ - public VLCExtensionItem(String stringId) { - this.stringId = stringId; - } - - public VLCExtensionItem() {} - - private VLCExtensionItem(Parcel in) { - readFromParcel(in); - } - - /** - * Returns the subTitle of this item. e.g. media artist or album - */ - public String getSubTitle() { - return subTitle; - } - - /** - * Sets the subTitles string of this item. - * - * @param subTitle The subTitle string to set. - */ - public VLCExtensionItem setSubTitle(String subTitle) { - this.subTitle = subTitle; - return this; - } - - /** - * Returns the uri string of the {#link VLCExtensionItem} for playback or download - */ - public String getLink() { - return link; - } - - /** - * Sets the uri String of the {#link VLCExtensionItem} - * - * @param link The medium link. - */ - public VLCExtensionItem setLink(String link) { - this.link = link; - return this; - } - - /** - * returns the {#link VLCExtensionItem} title - */ - public String getTitle() { - return title; - } - - /** - * Sets the {#link VLCExtensionItem} title - * - * @param title The string to set as title. - */ - public VLCExtensionItem setTitle(String title) { - this.title = title; - return this; - } - - /** - * Returns the {#link VLCExtensionItem} icon image link - */ - public Uri getImageUri() { - return imageUri; - } - - /** - * Sets the uri string of the {#link VLCExtensionItem} icon image. - * - * @param imageUri The uri string to set. - */ - public VLCExtensionItem setImageUri(Uri imageUri) { - this.imageUri = imageUri; - return this; - } - - /** - * Returns the {#link VLCExtensionItem} type - * @see {#link setType}. - */ - public int getType() { - return type; - } - - /** - * Sets the type of the {#link VLCExtensionItem} - * - * @param type The type among {#link TYPE_DIRECTORY}, {#link TYPE_VIDEO}, - * {#link TYPE_AUDIO} or {#link TYPE_OTHER_FILE}. - */ - public VLCExtensionItem setType(int type) { - this.type = type; - return this; - } - - public static final Parcelable.Creator CREATOR = new - Parcelable.Creator() { - @Override - public VLCExtensionItem createFromParcel(Parcel in) { - return new VLCExtensionItem(in); - } - - @Override - public VLCExtensionItem[] newArray(int size) { - return new VLCExtensionItem[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(stringId); - dest.writeString(link); - dest.writeString(title); - dest.writeString(subTitle); - dest.writeParcelable(imageUri, 0); - dest.writeInt(type); - } - - public void readFromParcel(Parcel in) { - stringId = in.readString(); - link = in.readString(); - title = in.readString(); - subTitle = in.readString(); - imageUri = in.readParcelable(null); - type = in.readInt(); - } -} diff --git a/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionService.java b/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionService.java deleted file mode 100644 index 152e5a963..000000000 --- a/extension-api/src/main/java/org/videolan/vlc/extensions/api/VLCExtensionService.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * ************************************************************************** - * VLCExtensionService.java - * **************************************************************************** - * Copyright © 2016 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ -package org.videolan.vlc.extensions.api; - -import android.app.Service; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.List; - -public abstract class VLCExtensionService extends Service{ - - private static final String TAG = "VLC/ExtensionService"; - private int mIndex = -1; - - private static final ComponentName VLC_HOST_SERVICE = - new ComponentName("org.videolan.vlc", - "org.videolan.vlc.plugin.PluginService"); - - IExtensionHost mHost; - Context mContext = this; - - private volatile Looper mServiceLooper; - protected volatile Handler mServiceHandler; - - /** - * Called by VLC when users wants to browse one of your {#link VLCExtensionItem.TYPE_DIRECTORY} - * VLC provides {#intId} and {#stringId} from chosen item - * - * @param stringId String id of the item to browse - */ - protected abstract void browse(@Nullable String stringId); - - /** - * Called by VLC when user wants to refresh the current list displayed by the extension. - */ - protected abstract void refresh(); - - @Override - public void onCreate() { - super.onCreate(); - HandlerThread thread = new HandlerThread( - "VLCExtension:" + getClass().getSimpleName()); - thread.start(); - - mServiceLooper = thread.getLooper(); - mServiceHandler = new Handler(mServiceLooper); - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public void onDestroy() { - super.onDestroy(); - try { - mHost.unBind(mIndex); - } catch (RemoteException e) {} - mServiceHandler.removeCallbacksAndMessages(null); // remove all callbacks - mServiceLooper.quit(); - } - - /** - * Starts playback of the given uri by VLC - * - * @param uri The uri to play - * @param title Optional - Set the media title to be displayed. - * Otherwise, it will be guessed from uri. - */ - public void playUri(@NonNull Uri uri, @Nullable String title) { - try { - mHost.playUri(uri, title); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - /** - * Displays given items in VLC browser. - * - * @param title The title shown in VLC action bar for this list display. - * @param items The items to show. - * @param showParams Wether you want to show the FAB to launch your extension settings activity. - */ - protected void updateList(String title, List items, boolean showParams, boolean isrefresh){ - try { - mHost.updateList(title, items, showParams, isrefresh); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - /** - * Called once VLC is binded to your service. - * Use it to call {@link #updateList(String, List, boolean, boolean)} with root level elements - * if you want VLC to handle your extension browsing. - */ - protected void onInitialize() { - } - - private final IExtensionService.Stub mBinder = new IExtensionService.Stub() { - @Override - public void onInitialize(int index, IExtensionHost host) throws RemoteException { - mIndex = index; - mHost = host; - mServiceHandler.post(new Runnable() { - @Override - public void run() { - VLCExtensionService.this.onInitialize(); - } - }); - } - - @Override - public void browse(final String text) throws RemoteException { - mServiceHandler.post(new Runnable() { - @Override - public void run() { - VLCExtensionService.this.browse(text); - } - }); - } - - @Override - public void refresh() { - mServiceHandler.post(new Runnable() { - @Override - public void run() { - VLCExtensionService.this.refresh(); - } - }); - } - }; -} diff --git a/extension-api/src/main/java/org/videolan/vlc/extensions/api/WarningActivity.java b/extension-api/src/main/java/org/videolan/vlc/extensions/api/WarningActivity.java deleted file mode 100644 index a6b887319..000000000 --- a/extension-api/src/main/java/org/videolan/vlc/extensions/api/WarningActivity.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * ************************************************************************** - * WarningActivity.java - * **************************************************************************** - * Copyright © 2016 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ -package org.videolan.vlc.extensions.api; - -import android.app.Activity; -import android.os.Bundle; - -import org.videolan.vlc.extensions.api.tools.Dialogs; - -public class WarningActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Dialogs.showInstallVlc(this); - } -} diff --git a/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Dialogs.java b/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Dialogs.java deleted file mode 100644 index 53ac31f19..000000000 --- a/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Dialogs.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * ************************************************************************** - * Dialogs.java - * **************************************************************************** - * Copyright © 2016 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ -package org.videolan.vlc.extensions.api.tools; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.provider.Settings; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; - -import org.videolan.vlc.plugin.api.R; - -public class Dialogs { - - /** - * Constant value to disable Dialog message. - */ - public static final int NO_MESSAGE = 0; - - - /** - * Facility function to show an AlertDialog - * @param context The context to use for Dialog display - * @param title Title for the AlertDialog - * @param message Message to display (set to NO_MESSAGE if you don't want to show any message) - * @param cancelListener The listener wich will be triggered after dialog cancelation. - * If null, the dialog is not cancelable - * @return the AlertDialog instance - */ - public static AlertDialog showAlertDialog(@NonNull Context context, @NonNull int title, - @Nullable int message, - @Nullable DialogInterface.OnCancelListener cancelListener) { - return showAlertDialog(context, title, message, cancelListener, null, null); - } - - /** - * Facility function to show an AlertDialog - * @param context The context to use for Dialog display - * @param title Title for the AlertDialog - * @param message Message to display (set to NO_MESSAGE if you don't want to show any message) - * @param cancelListener The listener wich will be triggered after dialog cancelation. - * If null, the dialog is not cancelable - * @param positiveAction DialogInterface.OnClickListener wich will be triggered on 'OK' button click. - * If null, the AlertDialog doesn't have positive button. - * @param negativeAction ialogInterface.OnClickListener wich will be triggered on 'Cancel' button click. - * If null, the AlertDialog doesn't have negative button. - * @return the AlertDialog instance - */ - public static AlertDialog showAlertDialog(@NonNull Context context, @NonNull int title, - @NonNull int message, - @Nullable DialogInterface.OnCancelListener cancelListener, - @Nullable DialogInterface.OnClickListener positiveAction, - @Nullable DialogInterface.OnClickListener negativeAction) { - - AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setTitle(title); - if (message > 0) - builder.setMessage(message); - if (cancelListener != null) { - builder.setCancelable(true) - .setOnCancelListener(cancelListener); - } - if (positiveAction != null) { - builder.setPositiveButton(android.R.string.ok, positiveAction); - } - if (negativeAction != null) { - builder.setPositiveButton(android.R.string.cancel, negativeAction); - } - AlertDialog alert = builder.create(); - alert.show(); - return alert; - } - - /** - * Show an AlertDialog warning user the extension needs internet access, - * and offers him to activate wifi connection - * @param context The context to use for Dialog display - * @param cancelListener The listener wich will be triggered after dialog cancelation or positive action - * @return the AlertDialog instance - */ - public static AlertDialog showNetworkNeeded(@NonNull final Context context, - @NonNull final DialogInterface.OnCancelListener cancelListener) { - return showAlertDialog(context, R.string.network_error_title, R.string.network_error_message, - cancelListener, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - context.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); - dialog.cancel(); - } - }, null); - } - - public static AlertDialog showInstallVlc(@NonNull final Activity activity) { - return showAlertDialog(activity, R.string.vlc_error_title, R.string.vlc_error_message, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - dialog.dismiss(); - activity.finish(); - } - }, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=org.videolan.vlc"))); - dialog.dismiss(); - activity.finish(); - } - }, null); - } -} diff --git a/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Helpers.java b/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Helpers.java deleted file mode 100644 index 4a370fff2..000000000 --- a/extension-api/src/main/java/org/videolan/vlc/extensions/api/tools/Helpers.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * ************************************************************************** - * Helpers.java - * **************************************************************************** - * Copyright © 2016 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ -package org.videolan.vlc.extensions.api.tools; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; - -import org.videolan.vlc.extensions.api.WarningActivity; - -public class Helpers { - - /** - * Helper method to check if VLC is installed on device. If not, shows an AlertDialog and offers - * the user to install it from the Play Store. - * @param context A simple context reference - * @return true is VLC is installed, false if not. - */ - public static boolean checkVlc(Context context) { - PackageManager pm = context.getPackageManager(); - try { - pm.getPackageInfo("org.videolan.vlc", 0); - return true; - } catch (PackageManager.NameNotFoundException e) { - context.startActivity(new Intent(context, WarningActivity.class)); - return false; - } - } -} diff --git a/extension-api/src/main/res/values/strings.xml b/extension-api/src/main/res/values/strings.xml deleted file mode 100644 index 36b8c0c8e..000000000 --- a/extension-api/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - No Internet connection - This extension needs your device to be connected to internet - VLC is not installed - This feature needs VLC application to be installed on your device - diff --git a/extension-api/src/main/res/values/styles.xml b/extension-api/src/main/res/values/styles.xml deleted file mode 100644 index f5fe60f32..000000000 --- a/extension-api/src/main/res/values/styles.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - \ No newline at end of file diff --git a/extension-api/src/test/java/org/videolan/vlc/extensions/api/ExampleUnitTest.java b/extension-api/src/test/java/org/videolan/vlc/extensions/api/ExampleUnitTest.java deleted file mode 100644 index 00a00b37b..000000000 --- a/extension-api/src/test/java/org/videolan/vlc/extensions/api/ExampleUnitTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * ************************************************************************* - * ExampleUnitTest.java - * ************************************************************************** - * Copyright © 2015 VLC authors and VideoLAN - * Author: Geoffrey Métais - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - * *************************************************************************** - */ - -package org.videolan.vlc.extensions.api; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * To work on unit tests, switch the Test Artifact in the Build Variants view. - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index dc41e90ec..effd7f515 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':libvlcjni:libvlc', ':extension-api', ':medialibrary' +include ':libvlcjni:libvlc', ':medialibrary' include ':application:tools', ':application:resources', ':application:mediadb', ':application:app', ':application:live-plot-graph', ':application:television', ':application:donations' include ':application:vlc-android' include ':application:moviepedia'