Add "plugins" menu option in full playback view (#559)

- refactor plugin querying in a way that now any
   library activity can utilize it
- Make doc more thorough
- Hopefully fix empty plugin list bug
   (flag was added to wrong intent)
This commit is contained in:
Antic1tizen One 2017-03-06 23:59:15 +03:00 committed by Adrian Ulrich
parent 2b07abdc8b
commit 2bc17ec2dd
3 changed files with 117 additions and 102 deletions

View File

@ -29,8 +29,6 @@ import java.util.ArrayList;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
@ -40,13 +38,10 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import android.app.AlertDialog;
import android.content.DialogInterface;
@ -308,6 +303,7 @@ public class FullPlaybackActivity extends SlidingPlaybackActivity
menu.add(0, MENU_ENQUEUE_GENRE, 30, R.string.enqueue_current_genre);
menu.add(0, MENU_ADD_TO_PLAYLIST, 30, R.string.add_to_playlist);
menu.add(0, MENU_SHARE, 30, R.string.share);
menu.add(0, MENU_PLUGINS, 30, R.string.plugins);
mFavorites = menu.add(0, MENU_SONG_FAVORITE, 0, R.string.add_to_favorites).setIcon(R.drawable.btn_rating_star_off_mtrl_alpha).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
// ensure that mFavorites is updated
@ -383,6 +379,11 @@ public class FullPlaybackActivity extends SlidingPlaybackActivity
dialog.create().show();
}
break;
case MENU_PLUGINS:
Intent songIntent = new Intent();
songIntent.putExtra("id", song.id);
queryPluginsForIntent(songIntent);
break;
default:
return super.onOptionsItemSelected(item);
}

View File

@ -26,20 +26,14 @@ package ch.blinkenlights.android.vanilla;
import ch.blinkenlights.android.medialibrary.MediaLibrary;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@ -60,10 +54,6 @@ import android.widget.TextView;
import android.widget.SearchView;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import junit.framework.Assert;
@ -143,19 +133,6 @@ public class LibraryActivity
* The last used action from the menu. Used with ACTION_LAST_USED.
*/
private int mLastAction = ACTION_PLAY;
/**
* Holds last intent that was passed to the context menu
*/
private Intent mLastRequestedCtx;
/**
* Plugin descriptions and packages.
* Keys are plugin names, values are their packages
*/
private Map<String, ApplicationInfo> mPlugins = new HashMap<>();
/**
* Broadcast receiver for plugin collecting
*/
private BroadcastReceiver mPluginInfoReceiver;
/**
* The pager adapter that manages each media ListView.
*/
@ -189,7 +166,6 @@ public class LibraryActivity
mViewPager = pager;
SharedPreferences settings = PlaybackService.getSettings(this);
mPluginInfoReceiver = new PluginBroadcastReceiver();
mBottomBarControls = (BottomBarControls)findViewById(R.id.bottombar_controls);
mBottomBarControls.setOnClickListener(this);
@ -213,43 +189,6 @@ public class LibraryActivity
bindControlButtons();
}
/**
* Shows plugin selection dialog. Be sure that {@link #mLastRequestedCtx} is initialized
* and {@link #mPlugins} are populated before calling this.
*
* @see PluginBroadcastReceiver
*/
private void showPluginMenu() {
Set<String> pluginNames = mPlugins.keySet();
final String[] pNamesArr = pluginNames.toArray(new String[pluginNames.size()]);
new AlertDialog.Builder(this)
.setItems(pNamesArr, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
long id = mLastRequestedCtx.getLongExtra("id", LibraryAdapter.INVALID_ID);
Song resolved = MediaUtils.getSongByTypeId(LibraryActivity.this, MediaUtils.TYPE_SONG, id);
if (resolved != null) {
ApplicationInfo selected = mPlugins.get(pNamesArr[which]);
Intent request = new Intent(PluginUtils.ACTION_LAUNCH_PLUGIN);
request.setPackage(selected.packageName);
request.putExtra(PluginUtils.EXTRA_PARAM_URI, Uri.fromFile(new File(resolved.path)));
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_TITLE, resolved.title);
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_ARTIST, resolved.artist);
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_ALBUM, resolved.album);
startService(request);
}
mLastRequestedCtx = null;
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mLastRequestedCtx = null;
}
}).create().show();
}
@Override
public void onRestart()
{
@ -267,18 +206,6 @@ public class LibraryActivity
updateHeaders();
}
@Override
public void onResume() {
super.onResume();
registerReceiver(mPluginInfoReceiver, new IntentFilter(PluginUtils.ACTION_HANDLE_PLUGIN_PARAMS));
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(mPluginInfoReceiver);
}
/**
* Load the tab order and update the tab bars if needed.
*/
@ -776,12 +703,7 @@ public class LibraryActivity
break;
}
case CTX_MENU_PLUGINS: {
// obtain list of plugins anew - some plugins may be installed/deleted
mPlugins.clear();
mLastRequestedCtx = intent;
Intent requestPlugins = new Intent(PluginUtils.ACTION_REQUEST_PLUGIN_PARAMS);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(requestPlugins);
queryPluginsForIntent(intent);
break;
}
case CTX_MENU_MORE_FROM_ARTIST: {
@ -1004,21 +926,4 @@ public class LibraryActivity
handler.sendMessage(mHandler.obtainMessage(MSG_SAVE_PAGE, position, 0));
}
}
private class PluginBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
List<ResolveInfo> resolved = getPackageManager().queryBroadcastReceivers(new Intent(PluginUtils.ACTION_REQUEST_PLUGIN_PARAMS), 0);
if (PluginUtils.ACTION_HANDLE_PLUGIN_PARAMS.equals(intent.getAction())) {
// plugin answered, store it in the map
String pluginName = intent.getStringExtra(PluginUtils.EXTRA_PARAM_PLUGIN_NAME);
ApplicationInfo info = intent.getParcelableExtra(PluginUtils.EXTRA_PARAM_PLUGIN_APP);
mPlugins.put(pluginName, info);
if (mPlugins.size() == resolved.size()) { // got all plugins
showPluginMenu();
}
}
}
}
}

View File

@ -25,13 +25,24 @@ package ch.blinkenlights.android.vanilla;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@ -75,6 +86,19 @@ public abstract class PlaybackActivity extends Activity
* The looper for the worker thread.
*/
protected Looper mLooper;
/**
* Broadcast receiver for plugin collecting
*/
private BroadcastReceiver mPluginInfoReceiver;
/**
* Holds last intent that was passed to the context menu
*/
private Intent mLastRequestedCtx;
/**
* Plugin descriptions and packages.
* Keys are plugin names, values are their packages
*/
private Map<String, ApplicationInfo> mPlugins = new HashMap<>();
protected CoverView mCoverView;
protected ImageButton mPlayPauseButton;
@ -92,6 +116,8 @@ public abstract class PlaybackActivity extends Activity
PlaybackService.addTimelineCallback(this);
mPluginInfoReceiver = new PluginBroadcastReceiver();
setVolumeControlStream(AudioManager.STREAM_MUSIC);
HandlerThread thread = new HandlerThread(getClass().getName(), Process.THREAD_PRIORITY_LOWEST);
@ -138,6 +164,7 @@ public abstract class PlaybackActivity extends Activity
public void onResume()
{
super.onResume();
registerReceiver(mPluginInfoReceiver, new IntentFilter(PluginUtils.ACTION_HANDLE_PLUGIN_PARAMS));
if (PlaybackService.hasInstance()) {
PlaybackService service = PlaybackService.get(this);
service.userActionTriggered();
@ -145,6 +172,11 @@ public abstract class PlaybackActivity extends Activity
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mPluginInfoReceiver);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
@ -375,7 +407,8 @@ public abstract class PlaybackActivity extends Activity
static final int MENU_EMPTY_QUEUE = 16;
static final int MENU_ADD_TO_PLAYLIST = 17;
static final int MENU_SHARE = 18;
static final int MENU_GO_HOME= 19;
static final int MENU_GO_HOME = 19;
static final int MENU_PLUGINS = 20; // used in FullPlaybackActivity
@Override
public boolean onCreateOptionsMenu(Menu menu)
@ -661,4 +694,80 @@ public abstract class PlaybackActivity extends Activity
return true;
}
/**
* Sends broadcast query that wakes plugins are queries them for info.
* Answers are later processed in {@link PluginBroadcastReceiver}
*
* @param songIntent intent containing song id as {@link Long} in its "id" extra.
*/
protected void queryPluginsForIntent(Intent songIntent) {
// obtain list of plugins anew - some plugins may be installed/deleted
mPlugins.clear();
mLastRequestedCtx = songIntent;
Intent requestPlugins = new Intent(PluginUtils.ACTION_REQUEST_PLUGIN_PARAMS);
requestPlugins.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(requestPlugins);
}
/**
* Broadcast receiver that handles return intents from queried plugins.
* Answered ones are stored in {@link #mPlugins}.
* Shows plugin menu once all queried plugins have answered.
*
* @see #showPluginMenu()
*/
private class PluginBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
List<ResolveInfo> resolved = getPackageManager().queryBroadcastReceivers(new Intent(PluginUtils.ACTION_REQUEST_PLUGIN_PARAMS), 0);
if (PluginUtils.ACTION_HANDLE_PLUGIN_PARAMS.equals(intent.getAction())) {
// plugin answered, store it in the map
String pluginName = intent.getStringExtra(PluginUtils.EXTRA_PARAM_PLUGIN_NAME);
ApplicationInfo info = intent.getParcelableExtra(PluginUtils.EXTRA_PARAM_PLUGIN_APP);
mPlugins.put(pluginName, info);
if (mPlugins.size() == resolved.size()) { // got all plugins
showPluginMenu();
}
}
}
}
/**
* Shows plugin selection dialog. Be sure that {@link #mLastRequestedCtx} is initialized
* and {@link #mPlugins} are populated before calling this.
*
* @see PluginBroadcastReceiver
*/
private void showPluginMenu() {
Set<String> pluginNames = mPlugins.keySet();
final String[] pNamesArr = pluginNames.toArray(new String[pluginNames.size()]);
new AlertDialog.Builder(this)
.setItems(pNamesArr, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
long id = mLastRequestedCtx.getLongExtra("id", LibraryAdapter.INVALID_ID);
Song resolved = MediaUtils.getSongByTypeId(PlaybackActivity.this, MediaUtils.TYPE_SONG, id);
if (resolved != null) {
ApplicationInfo selected = mPlugins.get(pNamesArr[which]);
Intent request = new Intent(PluginUtils.ACTION_LAUNCH_PLUGIN);
request.setPackage(selected.packageName);
request.putExtra(PluginUtils.EXTRA_PARAM_URI, Uri.fromFile(new File(resolved.path)));
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_TITLE, resolved.title);
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_ARTIST, resolved.artist);
request.putExtra(PluginUtils.EXTRA_PARAM_SONG_ALBUM, resolved.album);
startService(request);
}
mLastRequestedCtx = null;
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mLastRequestedCtx = null;
}
}).create().show();
}
}