Commit 8b758ea4 authored by David Papazian's avatar David Papazian Committed by Geoffrey Métais

Add extension activation dialog and preferences

Signed-off-by: default avatarGeoffrey Métais <geoffrey.metais@gmail.com>
parent 291d5a68
......@@ -31,6 +31,15 @@
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
......
......@@ -357,6 +357,12 @@
<string name="aout_audiotrack" translatable="false">AudioTrack</string>
<string name="aout_opensles" translatable="false">OpenSL ES</string>
<string name="extensions_prefs_category">Extensions</string>
<string name="extensions_enable_category">Chose extensions to enable</string>
<string name="extensions_empty">No extension detected</string>
<string name="extension_permission_title">New extension \"%1$s\" detected</string>
<string name="extension_permission_subtitle">Activate extension\?</string>
<string name="controls_prefs_category">Controls</string>
<string name="performance_prefs_category">Performance</string>
......
......@@ -53,6 +53,9 @@
<Preference
android:title="@string/audio_prefs_category"
android:key="audio_category" />
<Preference
android:title="@string/extensions_prefs_category"
android:key="extensions_category" />
<Preference
android:title="@string/advanced_prefs_category"
android:key="adv_category" />
......
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="extensions_category"
android:title="@string/extensions_prefs_category">>
</PreferenceScreen>
\ No newline at end of file
......@@ -62,7 +62,6 @@ public class VLCApplication extends Application {
public final static String SLEEP_INTENT = Strings.buildPkgString("SleepIntent");
public static Calendar sPlayerSleepTime = null;
private static boolean sTV;
private static SharedPreferences mSettings;
......
......@@ -251,6 +251,6 @@ public class ExtensionManagerService extends Service {
private final Handler mHandler = new Handler();
public List<ExtensionListing> getExtensions() {
return ExtensionsManager.getInstance().getExtensions(getApplication());
return ExtensionsManager.getInstance().getExtensions(getApplication(), false);
}
}
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 android.preference.PreferenceManager;
import android.support.design.widget.NavigationView;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.MenuItem;
import org.videolan.vlc.R;
import org.videolan.vlc.gui.MainActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ExtensionsManager {
private static ExtensionsManager sExtensionsManager;
private List<ExtensionListing> mExtensions = new ArrayList<>();
private static final String KEY_PROTOCOL_VERSION = "protocolVersion";
private static final String KEY_LISTING_TITLE = "title";
private static final String KEY_DESCRIPTION = "description";
......@@ -24,8 +34,8 @@ public class ExtensionsManager {
private static final String ACTION_EXTENSION = "org.videolan.vlc.Extension";
private static final int PROTOCOLE_VERSION = 1;
public ExtensionsManager() {
}
private static ExtensionsManager sExtensionsManager;
private final List<ExtensionListing> mExtensions = new ArrayList<>();
public static ExtensionsManager getInstance() {
if (sExtensionsManager == null)
......@@ -33,7 +43,7 @@ public class ExtensionsManager {
return sExtensionsManager;
}
public List<ExtensionListing> updateAvailableExtensions(Context context) {
private List<ExtensionListing> updateAvailableExtensions(Context context) {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentServices(
new Intent(ACTION_EXTENSION), PackageManager.GET_META_DATA);
......@@ -61,6 +71,34 @@ public class ExtensionsManager {
extensions.add(extension);
}
}
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
if (context instanceof MainActivity) {
MainActivity activity = (MainActivity) context;
if (deleteUnusedExtensionPreferences(extensions, settings)) {
if (activity.currentIdIsExtension()) {
//case: an extension is missing and current was an extension
activity.setCurrentFragmentId(-1);
settings.edit().putInt("fragment_id", -1).apply();
}
} else {
if (activity.currentIdIsExtension()) {
int currentExtensionId = activity.getCurrentFragmentId();
if (extensionIsEnabled(settings, currentExtensionId)) {
String currentExtensionTitle = mExtensions.get(currentExtensionId).title();
for (int i = 0; i < extensions.size(); ++i) {
if (TextUtils.equals(extensions.get(i).title(), currentExtensionTitle)) {
activity.setCurrentFragmentId(i);
settings.edit().putInt("fragment_id", i).apply();
break;
}
}
}
}
}
}
synchronized (mExtensions) {
mExtensions.clear();
mExtensions.addAll(extensions);
......@@ -68,14 +106,83 @@ public class ExtensionsManager {
return extensions;
}
public List<ExtensionListing> getExtensions(Context context) {
if (mExtensions.size() == 0)
public List<ExtensionListing> getExtensions(Context context, boolean update) {
if (mExtensions.size() == 0 || update)
return updateAvailableExtensions(context);
else
return mExtensions;
}
public boolean extensionIsEnabled(SharedPreferences settings, int id) {
return settings.getBoolean("extension_" + mExtensions.get(id).componentName().getPackageName(), true);
if (id == -1) {
return false;
} else {
String key = "extension_" + mExtensions.get(id).componentName().getPackageName();
return settings.getBoolean(key, false);
}
}
private boolean deleteUnusedExtensionPreferences(List<ExtensionListing> list, SharedPreferences settings) {
boolean extensionMissing = false;
ArrayList<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_") && !extensionNames.contains(entry.getKey().replace("extension_", ""))) {
settings.edit().remove(entry.getKey()).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(true);
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) {
new AlertDialog.Builder(activity).setTitle(activity.getString(R.string.extension_permission_title, extension.title()))
.setMessage(R.string.extension_permission_subtitle)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
PreferenceManager.getDefaultSharedPreferences(activity.getApplication()).edit().putBoolean(key, true).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) {
PreferenceManager.getDefaultSharedPreferences(activity.getApplication()).edit().putBoolean(key, false).apply();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
PreferenceManager.getDefaultSharedPreferences(activity.getApplication()).edit().putBoolean(key, false).apply();
}
})
.show();
}
}
......@@ -25,11 +25,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
......@@ -38,7 +35,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.internal.NavigationMenuView;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
......@@ -50,9 +46,7 @@ import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.view.ActionMode;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FilterQueryProvider;
......@@ -114,7 +108,6 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
// Extensions management
private ServiceConnection mExtensionServiceConnection;
private ExtensionManagerService mExtensionManagerService;
private static final int PLUGIN_NAVIGATION_GROUP = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -209,7 +202,6 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
mScanNeeded = savedInstanceState == null && mSettings.getBoolean("auto_rescan", true);
mExtensionsManager = ExtensionsManager.getInstance();
mMediaLibrary = VLCApplication.getMLInstance();
}
private void setupNavigationView() {
......@@ -273,59 +265,36 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
}
private void loadPlugins() {
Menu navMenu = mNavigationView.getMenu();
navMenu.removeGroup(PLUGIN_NAVIGATION_GROUP);
List<ExtensionListing> plugins = mExtensionsManager.getExtensions(getApplication());
List<ExtensionListing> plugins = mExtensionsManager.getExtensions(this, true);
if (plugins.isEmpty()) {
unbindService(mExtensionServiceConnection);
mExtensionServiceConnection = null;
mExtensionManagerService.stopSelf();
return;
}
PackageManager pm = getPackageManager();
SubMenu subMenu = navMenu.addSubMenu(PLUGIN_NAVIGATION_GROUP, PLUGIN_NAVIGATION_GROUP,
PLUGIN_NAVIGATION_GROUP, R.string.plugins);
for (int i = 0; i < plugins.size(); ++i) {
ExtensionListing extension = plugins.get(i);
if (mSettings.getBoolean("extension_" + extension.componentName().getPackageName(), true)) {
MenuItem item = subMenu.add(PLUGIN_NAVIGATION_GROUP, i, 0, extension.title());
item.setCheckable(true);
int iconRes = extension.menuIcon();
Drawable extensionIcon = null;
if (iconRes != 0) {
try {
Resources res = pm.getResourcesForApplication(extension.componentName().getPackageName());
extensionIcon = res.getDrawable(extension.menuIcon());
} catch (PackageManager.NameNotFoundException e) {}
}
if (extensionIcon != null)
item.setIcon(extensionIcon);
else
try {
item.setIcon(pm.getApplicationIcon(plugins.get(i).componentName().getPackageName()));
} catch (PackageManager.NameNotFoundException e) {
item.setIcon(R.drawable.icon);
}
MenuItem extensionGroup = mNavigationView.getMenu().findItem(R.id.extensions_group);
extensionGroup.getSubMenu().clear();
for (int id = 0; id < plugins.size(); ++id) {
final ExtensionListing extension = plugins.get(id);
String key = "extension_" + extension.componentName().getPackageName();
if (mSettings.contains(key)) {
mExtensionsManager.displayPlugin(this, id, extension, mSettings.getBoolean(key, false));
} else {
mExtensionsManager.showExtensionPermissionDialog(this, id, extension, key);
}
}
if (subMenu.size() == 0)
navMenu.setGroupVisible(PLUGIN_NAVIGATION_GROUP, false);
mNavigationView.invalidate();
if (extensionGroup.getSubMenu().size() == 0)
extensionGroup.setVisible(false);
onPluginsLoaded();
mNavigationView.invalidate();
}
private void onPluginsLoaded() {
if (currentIdIsExtension())
if (mExtensionsManager.extensionIsEnabled(mSettings, mCurrentFragmentId)) {
updateCheckedItem(mCurrentFragmentId);
if (mExtensionsManager.extensionIsEnabled(mSettings, mCurrentFragmentId))
mExtensionManagerService.openExtension(mCurrentFragmentId);
}
else {
updateCheckedItem(R.id.nav_video);
else
showFragment(R.id.nav_video);
}
else
updateCheckedItem(mCurrentFragmentId);
}
private void createExtensionServiceConnection() {
......@@ -350,15 +319,16 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
@Override
protected void onResume() {
super.onResume();
mCurrentFragmentId = mSettings.getInt("fragment_id", R.id.nav_video);
if (mMediaLibrary.isInitiated()) {
/* Load media items from database and storage */
if (mScanNeeded && Permissions.canReadStorage())
startService(new Intent(MediaParsingService.ACTION_RELOAD, null,this, MediaParsingService.class));
else
restoreCurrentList();
if (!currentIdIsExtension())
restoreCurrentList();
}
mNavigationView.setNavigationItemSelectedListener(this);
mCurrentFragmentId = mSettings.getInt("fragment_id", R.id.nav_video);
}
@Override
......@@ -387,8 +357,10 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
protected void onSaveInstanceState(Bundle outState) {
if (mCurrentFragment instanceof ExtensionBrowser) {
while (getSupportFragmentManager().popBackStackImmediate()) {}
if (mCurrentFragment != null)
if (mCurrentFragment != null) {
getSupportFragmentManager().beginTransaction().remove(mCurrentFragment).commit();
mCurrentFragment = null;
}
}
super.onSaveInstanceState(outState);
outState.putInt("current", mCurrentFragmentId);
......@@ -458,20 +430,28 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (!(mCurrentFragment instanceof ExtensionBrowser)) {
//case: non-extension to extension root
if (mCurrentFragment != null)
ft.hide(mCurrentFragment);
ft.add(R.id.fragment_placeholder, fragment, title);
ft.commit();
mCurrentFragment = fragment;
} else {
ft.setCustomAnimations(R.anim.anim_enter_right, 0, R.anim.anim_enter_left, 0);
ft.replace(R.id.fragment_placeholder, fragment, title);
} else if (mCurrentFragmentId == extensionId) {
//case: extension root to extension sub dir
ft.hide(mCurrentFragment);
ft.add(R.id.fragment_placeholder, fragment, title);
ft.addToBackStack(getTag(mCurrentFragmentId));
ft.commit();
} else {
//case: extension to other extension root
clearBackstackFromClass(ExtensionBrowser.class);
while (getSupportFragmentManager().popBackStackImmediate());
ft.remove(mCurrentFragment);
ft.add(R.id.fragment_placeholder, fragment, title);
mCurrentFragment = fragment;
}
ft.commit();
updateCheckedItem(extensionId);
mCurrentFragmentId = extensionId;
}
updateCheckedItem(extensionId);
mCurrentFragmentId = extensionId;
}
/**
......@@ -629,15 +609,14 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
if(item == null)
return false;
getSupportActionBar().setTitle(null); //clear title
getSupportActionBar().setSubtitle(null); //clear subtitle
int id = item.getItemId();
Fragment current = getCurrentFragment();
if (item.getGroupId() == PLUGIN_NAVIGATION_GROUP) {
if(mCurrentFragmentId == id)
if (item.getGroupId() == R.id.extensions_group) {
if(mCurrentFragmentId == id) {
clearBackstackFromClass(ExtensionBrowser.class);
mDrawerLayout.closeDrawer(mNavigationView);
return false;
}
else
mExtensionManagerService.openExtension(id);
} else {
......@@ -681,7 +660,6 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
/* Slide down the audio player */
slideDownAudioPlayer();
/* Switch the fragment */
updateCheckedItem(id);
showFragment(id);
}
}
......@@ -690,8 +668,9 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
}
public void updateCheckedItem(int id) {
if (mNavigationView.getMenu().findItem(mCurrentFragmentId) != null) {
mNavigationView.getMenu().findItem(mCurrentFragmentId).setChecked(false);
if (mNavigationView.getMenu().findItem(id) != null) {
if (mNavigationView.getMenu().findItem(mCurrentFragmentId) != null)
mNavigationView.getMenu().findItem(mCurrentFragmentId).setChecked(false);
mNavigationView.getMenu().findItem(id).setChecked(true);
}
}
......@@ -723,6 +702,7 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
else
ft.show(fragment);
ft.commit();
updateCheckedItem(id);
mCurrentFragment = fragment;
mCurrentFragmentId = id;
}
......@@ -765,8 +745,19 @@ public class MainActivity extends ContentActivity implements FilterQueryProvider
}
public boolean currentIdIsExtension() {
return mCurrentFragmentId <= 100;
return idIsExtension(mCurrentFragmentId);
}
public boolean idIsExtension(int id) {
return id <= 100;
}
public int getCurrentFragmentId() {
return mCurrentFragmentId;
}
public void setCurrentFragmentId(int id) {
mCurrentFragmentId = id;
}
}
......@@ -118,6 +118,14 @@ public class ExtensionBrowser extends Fragment implements View.OnClickListener,
}
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
setTitle(mTitle);
if (mAddDirectoryFAB != null)
mAddDirectoryFAB.setVisibility((!isHidden() && showSettings) ? View.VISIBLE : View.GONE);
}
private void setTitle(String title) {
final AppCompatActivity activity = (AppCompatActivity)getActivity();
if (activity != null && activity.getSupportActionBar() != null) {
......
package org.videolan.vlc.gui.preferences;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import android.util.Log;
import org.videolan.vlc.R;
import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.extensions.ExtensionListing;
import org.videolan.vlc.extensions.ExtensionsManager;
import java.util.ArrayList;
import java.util.List;
public class PreferencesExtensions extends BasePreferenceFragment {
private List<ExtensionListing> mExtensions = new ArrayList<>();
private SharedPreferences mSettings;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSettings = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplication());
mExtensions = ExtensionsManager.getInstance().getExtensions(getActivity().getApplication(), false);
if (mExtensions.isEmpty())
createEmptyPref();
else
createCheckboxes();
}
@Override
protected int getXml() {
return R.xml.preferences_extensions;
}
@Override
protected int getTitleId() {
return R.string.extensions_prefs_category;
}
private void createCheckboxes() {
PreferenceScreen preferenceScreen = this.getPreferenceScreen();
PreferenceCategory preferenceCategory = new PreferenceCategory(preferenceScreen.getContext());
preferenceCategory.setTitle(R.string.extensions_enable_category);
preferenceScreen.addPreference(preferenceCategory);
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
for (int i = 0 ; i < mExtensions.size() ; ++i) {
ExtensionListing extension = mExtensions.get(i);
CheckBoxPreference checkbox = new CheckBoxPreference(preferenceScreen.getContext());
checkbox.setTitle(extension.title());
checkbox.setSummary(extension.description());
checkbox.setKey("extension_" + extension.componentName().getPackageName());
int iconRes = extension.menuIcon();
Drawable extensionIcon = null;
if (iconRes != 0) {
try {
Resources res = pm.getResourcesForApplication(extension.componentName().getPackageName());
extensionIcon = res.getDrawable(extension.menuIcon());
} catch (PackageManager.NameNotFoundException e) {}
}
if (extensionIcon != null)
checkbox.setIcon(extensionIcon);
else
try {
checkbox.setIcon(pm.getApplicationIcon(mExtensions.get(i).componentName().getPackageName()));
} catch (PackageManager.NameNotFoundException e) {
checkbox.setIcon(R.drawable.icon);
}
preferenceCategory.addPreference(checkbox);
checkbox.setChecked(mSettings.getBoolean("extension_" + extension.componentName().getPackageName(), false));
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference.getKey() == null)
return false;
if (preference.getKey().startsWith("extension_")) {
mSettings.edit()
.putBoolean(preference.getKey(), ((TwoStatePreference) preference).isChecked())
.apply();
}
return super.onPreferenceTreeClick(preference);
}
private void createEmptyPref() {
PreferenceScreen preferenceScreen = this.getPreferenceScreen();
PreferenceCategory preferenceCategory = new PreferenceCategory(preferenceScreen.getContext());
preferenceCategory.setTitle(R.string.extensions_empty);
preferenceScreen.addPreference(preferenceCategory);
}
}
......@@ -25,8 +25,11 @@ package org.videolan.vlc.gui.preferences;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.preference.Preference;
import android.view.View;
import org.videolan.vlc.BuildConfig;
import org.videolan.vlc.R;
import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.gui.SecondaryActivity;
......@@ -57,6 +60,13 @@ public class PreferencesFragment extends BasePreferenceFragment {
super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!BuildConfig.DEBUG)
findPreference("extensions_category").setVisible(false);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
switch (preference.getKey()){
......@@ -78,6 +88,9 @@ public class PreferencesFragment extends BasePreferenceFragment {
case "audio_category":
loadFragment(new PreferencesAudio());
break;
case "extensions_category":
loadFragment(new PreferencesExtensions());
break;
case "adv_category":
loadFragment(new PreferencesAdvanced());
break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment