40 changed files with 304 additions and 2792 deletions
@ -1,104 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
xmlns:vlc="http://schemas.android.com/apk/res-auto"> |
|||
|
|||
<data> |
|||
|
|||
<import type="android.view.View" /> |
|||
|
|||
<import type="android.text.TextUtils" /> |
|||
|
|||
<variable |
|||
name="holder" |
|||
type="org.videolan.vlc.gui.browser.ExtensionAdapter.ViewHolder" /> |
|||
|
|||
<variable |
|||
name="item" |
|||
type="org.videolan.vlc.extensions.api.VLCExtensionItem" /> |
|||
|
|||
<variable |
|||
name="cover" |
|||
type="android.graphics.drawable.BitmapDrawable" /> |
|||
</data> |
|||
|
|||
<androidx.constraintlayout.widget.ConstraintLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="60dp" |
|||
android:layout_margin="5dp" |
|||
android:clickable="true" |
|||
android:onLongClick="@{holder::onLongClick}" |
|||
android:onClick="@{holder::onClick}"> |
|||
|
|||
<ImageView |
|||
android:id="@+id/extension_image" |
|||
android:layout_width="64dp" |
|||
android:layout_height="64dp" |
|||
vlc:imageUri="@{item.imageUri}" |
|||
vlc:layout_constraintEnd_toStartOf="@id/title" |
|||
vlc:layout_constraintBottom_toBottomOf="parent" |
|||
vlc:layout_constraintStart_toStartOf="parent" |
|||
vlc:layout_constraintTop_toTopOf="parent" |
|||
android:layout_marginEnd="8dp" |
|||
android:layout_marginRight="8dp" |
|||
android:visibility="@{item.imageUri == null || TextUtils.isEmpty(item.imageUri.toString()) ? View.GONE : View.VISIBLE}" |
|||
android:src="@{cover}" |
|||
tools:src="@drawable/icon" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/title" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:gravity="bottom" |
|||
android:text="@{item.title}" |
|||
android:fontFamily="sans-serif" |
|||
android:textColor="?attr/list_title" |
|||
android:textSize="16sp" |
|||
android:ellipsize="marquee" |
|||
android:marqueeRepeatLimit="marquee_forever" |
|||
android:singleLine="true" |
|||
android:maxLines="1" |
|||
vlc:layout_constraintEnd_toStartOf="@+id/item_more" |
|||
vlc:layout_constraintStart_toEndOf="@id/extension_image" |
|||
vlc:layout_constraintTop_toTopOf="parent" |
|||
vlc:layout_constraintBottom_toTopOf="@+id/author" |
|||
vlc:layout_constraintVertical_chainStyle="packed" |
|||
tools:text="Title" |
|||
tools:targetApi="jelly_bean" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/author" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:gravity="top" |
|||
android:text="@{item.subTitle}" |
|||
android:fontFamily="sans-serif" |
|||
android:textColor="?attr/list_subtitle" |
|||
android:ellipsize="marquee" |
|||
android:singleLine="true" |
|||
android:maxLines="1" |
|||
android:layout_marginTop="2dp" |
|||
vlc:layout_constraintEnd_toStartOf="@id/item_more" |
|||
vlc:layout_constraintTop_toBottomOf="@+id/title" |
|||
vlc:layout_constraintStart_toStartOf="@id/title" |
|||
vlc:layout_constraintBottom_toBottomOf="parent" |
|||
android:visibility="@{TextUtils.isEmpty(item.subTitle) ? View.GONE : View.VISIBLE}" |
|||
tools:text="Author" |
|||
tools:targetApi="jelly_bean" /> |
|||
|
|||
<ImageView |
|||
android:id="@+id/item_more" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
vlc:srcCompat="@drawable/ic_more" |
|||
android:background="?attr/selectableItemBackgroundBorderless" |
|||
android:visibility="@{item.type != 0 ? View.VISIBLE : View.GONE}" |
|||
android:layout_marginEnd="1dp" |
|||
vlc:layout_constraintRight_toRightOf="parent" |
|||
vlc:layout_constraintTop_toTopOf="parent" |
|||
vlc:layout_constraintBottom_toBottomOf="parent" |
|||
android:layout_marginRight="1dp" |
|||
tools:src="@drawable/ic_more" |
|||
android:onClick="@{holder::onMoreClick}" /> |
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
</layout> |
|||
@ -1,56 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
|
|||
<group android:id="@+id/scrollable_group" |
|||
android:checkableBehavior="none" |
|||
android:orderInCategory="1"> |
|||
|
|||
<item |
|||
android:id="@+id/nav_video" |
|||
android:title="@string/video" |
|||
android:icon="@drawable/ic_menu_video" /> |
|||
<item |
|||
android:id="@+id/nav_audio" |
|||
android:title="@string/audio" |
|||
android:icon="@drawable/ic_menu_audio" /> |
|||
<item |
|||
android:id="@+id/nav_playlists" |
|||
android:title="@string/playlists" |
|||
android:icon="@drawable/ic_menu_playlist" /> |
|||
<item |
|||
android:id="@+id/nav_directories" |
|||
android:title="@string/directories" |
|||
android:icon="@drawable/ic_menu_folder" /> |
|||
<item |
|||
android:id="@+id/nav_network" |
|||
android:title="@string/network_browsing" |
|||
android:icon="@drawable/ic_menu_network" /> |
|||
<item |
|||
android:id="@+id/nav_mrl" |
|||
android:title="@string/streams" |
|||
android:icon="@drawable/ic_menu_stream" /> |
|||
<item |
|||
android:id="@+id/nav_history" |
|||
android:title="@string/history" |
|||
android:icon="@drawable/ic_menu_history" /> |
|||
</group> |
|||
|
|||
<item |
|||
android:id="@+id/extensions_group" |
|||
android:title="@string/plugins" |
|||
android:visible="false" |
|||
android:orderInCategory="2"> |
|||
<menu> |
|||
</menu> |
|||
</item> |
|||
|
|||
<group android:id="@+id/fixed_group" |
|||
android:orderInCategory="3"> |
|||
<item |
|||
android:id="@+id/nav_settings" |
|||
android:title="@string/preferences" |
|||
android:icon="@drawable/ic_menu_preferences" /> |
|||
</group> |
|||
|
|||
</menu> |
|||
@ -1,7 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.preference.PreferenceScreen |
|||
xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:key="extensions_category" |
|||
android:title="@string/extensions_prefs_category">> |
|||
|
|||
</androidx.preference.PreferenceScreen> |
|||
@ -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<ExtensionListing> CREATOR |
|||
= new Creator<ExtensionListing>() { |
|||
@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); |
|||
} |
|||
} |
|||
} |
|||
@ -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<VLCExtensionItem> 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<VLCExtensionItem> 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<Pair<Object, Operation>> deferredOps
|
|||
// = new LinkedList<Pair<Object, Operation>>();
|
|||
} |
|||
|
|||
private final Handler mHandler = new Handler(); |
|||
|
|||
public List<ExtensionListing> getExtensions() { |
|||
return ExtensionsManager.getInstance().getExtensions(getApplication(), false); |
|||
} |
|||
} |
|||
@ -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<ExtensionListing> mExtensions = new ArrayList<>(); |
|||
|
|||
public static ExtensionsManager getInstance() { |
|||
if (sExtensionsManager == null) |
|||
sExtensionsManager = new ExtensionsManager(); |
|||
return sExtensionsManager; |
|||
} |
|||
|
|||
private List<ExtensionListing> updateAvailableExtensions(Context context) { |
|||
androidAutoInstalled = isAndroidAutoInstalled(context); |
|||
PackageManager pm = context.getPackageManager(); |
|||
List<ResolveInfo> resolveInfos = pm.queryIntentServices( |
|||
new Intent(ACTION_EXTENSION), PackageManager.GET_META_DATA); |
|||
|
|||
List<ExtensionListing> 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<ExtensionListing> 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<ExtensionListing> list, SharedPreferences settings) { |
|||
boolean extensionMissing = false; |
|||
List<String> extensionNames = new ArrayList<>(); |
|||
for (ExtensionListing extension : list) |
|||
extensionNames.add(extension.componentName().getPackageName()); |
|||
for (Map.Entry<String, ?> 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<CharSequence> extraTitles = new ArrayList<>(); |
|||
final List<String> 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<extraTitles.size(); i++) |
|||
Settings.INSTANCE.getInstance(activity.getApplication()).edit().putBoolean(extraKeys.get(i), extraCheckedStates[i]).apply(); |
|||
displayPlugin(activity, id, extension, true); |
|||
activity.findViewById(R.id.navigation).postInvalidate(); |
|||
} |
|||
}) |
|||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { |
|||
@Override |
|||
public void onClick(DialogInterface dialogInterface, int i) { |
|||
dialogInterface.cancel(); |
|||
} |
|||
}) |
|||
.setOnCancelListener(new DialogInterface.OnCancelListener() { |
|||
@Override |
|||
public void onCancel(DialogInterface dialogInterface) { |
|||
Settings.INSTANCE.getInstance(activity.getApplication()).edit().putBoolean(key, false).apply(); |
|||
for (int i=0; i<extraTitles.size(); i++) |
|||
Settings.INSTANCE.getInstance(activity.getApplication()).edit().putBoolean(extraKeys.get(i), false).apply(); |
|||
} |
|||
}) |
|||
.show(); |
|||
} |
|||
|
|||
public int getExtensionId(String packageName) { |
|||
if (mExtensions == null || mExtensions.isEmpty()) |
|||
return 0; |
|||
for (int i=0; i<mExtensions.size(); i++) |
|||
if (mExtensions.get(i).componentName().getPackageName().equals(packageName)) |
|||
return i; |
|||
return 0; |
|||
} |
|||
|
|||
private boolean isAndroidAutoInstalled(Context context) { |
|||
final PackageManager packageManager = context.getPackageManager(); |
|||
try { |
|||
packageManager.getPackageInfo("com.google.android.projection.gearhead", 0); |
|||
return true; |
|||
} catch (PackageManager.NameNotFoundException e) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
package org.videolan.vlc.extensions; |
|||
|
|||
import android.net.Uri; |
|||
|
|||
import org.videolan.medialibrary.MLServiceLocator; |
|||
import org.videolan.medialibrary.interfaces.media.MediaWrapper; |
|||
import org.videolan.vlc.extensions.api.VLCExtensionItem; |
|||
|
|||
public class Utils { |
|||
|
|||
public static MediaWrapper mediawrapperFromExtension(VLCExtensionItem vlcItem) { |
|||
MediaWrapper media = MLServiceLocator.getAbstractMediaWrapper(Uri.parse(vlcItem.link)); |
|||
media.setDisplayTitle(vlcItem.title); |
|||
if (vlcItem.type != VLCExtensionItem.TYPE_OTHER_FILE) |
|||
media.setType(vlcItem.type); |
|||
return media; |
|||
|
|||
} |
|||
} |
|||
@ -1,111 +0,0 @@ |
|||
package org.videolan.vlc.gui.browser |
|||
|
|||
import android.view.LayoutInflater |
|||
import android.view.View |
|||
import android.view.ViewGroup |
|||
import androidx.core.net.toUri |
|||
import androidx.databinding.DataBindingUtil |
|||
import androidx.databinding.ViewDataBinding |
|||
import androidx.recyclerview.widget.RecyclerView |
|||
import org.videolan.medialibrary.MLServiceLocator |
|||
import org.videolan.medialibrary.interfaces.media.MediaWrapper |
|||
import org.videolan.vlc.R |
|||
import org.videolan.vlc.databinding.ExtensionItemViewBinding |
|||
import org.videolan.vlc.extensions.api.VLCExtensionItem |
|||
import org.videolan.vlc.media.MediaUtils |
|||
import java.util.* |
|||
|
|||
class ExtensionAdapter(internal var fragment: ExtensionBrowser?) : RecyclerView.Adapter<ExtensionAdapter.ViewHolder>() { |
|||
private var itemsList: MutableList<VLCExtensionItem> = ArrayList() |
|||
|
|||
val all: List<VLCExtensionItem> |
|||
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<ViewDataBinding>(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<VLCExtensionItem>) { |
|||
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" |
|||
} |
|||
} |
|||
@ -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<VLCExtensionItem>(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<VLCExtensionItem>) { |
|||
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<MediaWrapper>(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<ExtensionBrowser>(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 |
|||
} |
|||
} |
|||
@ -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<Preference>() |
|||
|
|||
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<View>(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) |
|||
} |
|||
} |
|||
@ -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<ExtensionListing> = 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<View>(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) |
|||
} |
|||
} |
|||
@ -1 +0,0 @@ |
|||
/build |
|||
@ -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' |
|||
@ -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 *; |
|||
#} |
|||
@ -1,33 +0,0 @@ |
|||
<!-- |
|||
~ ************************************************************************** |
|||
~ AndroidManifest.xml.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. |
|||
~ *************************************************************************** |
|||
--> |
|||
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
|
|||
<application |
|||
android:supportsRtl="true"> |
|||
|
|||
<activity android:name="org.videolan.vlc.extensions.api.WarningActivity" |
|||
android:theme="@style/TransparentTheme" /> |
|||
</application> |
|||
|
|||
</manifest> |
|||
@ -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<VLCExtensionItem> items, boolean showParams, boolean isRefresh); |
|||
oneway void playUri(in Uri uri, String title); |
|||
oneway void unBind(int index); |
|||
} |
|||
@ -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(); |
|||
} |
|||
@ -1,3 +0,0 @@ |
|||
package org.videolan.vlc.extensions.api; |
|||
|
|||
parcelable VLCExtensionItem; |
|||
@ -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 |
|||
* |
|||
* <p> |
|||
* This class follows the <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent |
|||
* interface</a> 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: |
|||
* |
|||
* <pre class="prettyprint"> |
|||
* VLCExtensionItem data = new VLCExtensionItem(id, null) |
|||
* .setTitle("My Video") |
|||
* .setLink("http://mysite.net/myvideo.ogv"); |
|||
* </pre> |
|||
* |
|||
*/ |
|||
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<VLCExtensionItem> CREATOR = new |
|||
Parcelable.Creator<VLCExtensionItem>() { |
|||
@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(); |
|||
} |
|||
} |
|||
@ -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<VLCExtensionItem> 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(); |
|||
} |
|||
}); |
|||
} |
|||
}; |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +0,0 @@ |
|||
<resources> |
|||
<string name="network_error_title">No Internet connection</string> |
|||
<string name="network_error_message">This extension needs your device to be connected to internet</string> |
|||
<string name="vlc_error_title">VLC is not installed</string> |
|||
<string name="vlc_error_message">This feature needs VLC application to be installed on your device</string> |
|||
</resources> |
|||
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
<style name="TransparentTheme" parent="Theme.AppCompat.Light.NoActionBar"> |
|||
|
|||
<item name="android:background">@android:color/transparent</item> |
|||
<item name="background">@android:color/transparent</item> |
|||
|
|||
<item name="android:windowBackground">@android:color/transparent</item> |
|||
<item name="android:colorBackgroundCacheHint">@android:color/transparent</item> |
|||
<item name="android:windowContentOverlay">@android:color/transparent</item> |
|||
<item name="android:windowIsTranslucent">true</item> |
|||
<item name="android:windowAnimationStyle">@android:color/transparent</item> |
|||
</style> |
|||
</resources> |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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' |
|||
|
|||
Loading…
Reference in new issue