initial shortcut support

This commit is contained in:
Adrian Ulrich 2018-10-26 20:20:15 +02:00
parent e37225bb91
commit 62e498a1a9
6 changed files with 137 additions and 2 deletions

View File

@ -31,6 +31,8 @@ THE SOFTWARE.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- This is needed for isWiredHeadsetOn() to work in some cases. (bug?) --> <!-- This is needed for isWiredHeadsetOn() to work in some cases. (bug?) -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Playlist shortcuts -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:resizeableActivity="true" android:resizeableActivity="true"

View File

@ -616,6 +616,7 @@ public class LibraryActivity
private static final int CTX_MENU_OPEN_EXTERNAL = 10; private static final int CTX_MENU_OPEN_EXTERNAL = 10;
private static final int CTX_MENU_PLUGINS = 11; private static final int CTX_MENU_PLUGINS = 11;
private static final int CTX_MENU_SHOW_DETAILS = 12; private static final int CTX_MENU_SHOW_DETAILS = 12;
private static final int CTX_MENU_ADD_TO_HOMESCREEN = 13;
/** /**
* Creates a context menu for an adapter row. * Creates a context menu for an adapter row.
@ -651,6 +652,8 @@ public class LibraryActivity
if (type == MediaUtils.TYPE_SONG) { if (type == MediaUtils.TYPE_SONG) {
menu.add(0, CTX_MENU_SHOW_DETAILS, 0, R.string.details).setIntent(rowData); menu.add(0, CTX_MENU_SHOW_DETAILS, 0, R.string.details).setIntent(rowData);
} }
if (type >= MediaUtils.TYPE_ARTIST && type <= MediaUtils.TYPE_COMPOSER && type != MediaUtils.TYPE_SONG)
menu.add(0, CTX_MENU_ADD_TO_HOMESCREEN, 0, R.string.add_to_homescreen).setIntent(rowData);
if (type == MediaUtils.TYPE_ALBUM || type == MediaUtils.TYPE_SONG) if (type == MediaUtils.TYPE_ALBUM || type == MediaUtils.TYPE_SONG)
menu.add(0, CTX_MENU_MORE_FROM_ARTIST, 0, R.string.more_from_artist).setIntent(rowData); menu.add(0, CTX_MENU_MORE_FROM_ARTIST, 0, R.string.more_from_artist).setIntent(rowData);
if (type == MediaUtils.TYPE_SONG) { if (type == MediaUtils.TYPE_SONG) {
@ -762,11 +765,19 @@ public class LibraryActivity
setLimiter(MediaUtils.TYPE_ALBUM, "_id=" + intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID)); setLimiter(MediaUtils.TYPE_ALBUM, "_id=" + intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID));
updateLimiterViews(); updateLimiterViews();
break; break;
case CTX_MENU_ADD_TO_PLAYLIST: case CTX_MENU_ADD_TO_PLAYLIST: {
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID); long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
PlaylistDialog plDialog = PlaylistDialog.newInstance(this, intent, (id == LibraryAdapter.HEADER_ID ? mCurrentAdapter : null)); PlaylistDialog plDialog = PlaylistDialog.newInstance(this, intent, (id == LibraryAdapter.HEADER_ID ? mCurrentAdapter : null));
plDialog.show(getFragmentManager(), "PlaylistDialog"); plDialog.show(getFragmentManager(), "PlaylistDialog");
break; break;
}
case CTX_MENU_ADD_TO_HOMESCREEN: {
int type = intent.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
long id = intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID);
String label = intent.getStringExtra(LibraryAdapter.DATA_TITLE);
SystemUtils.installLauncherShortcut(this, label, type, id);
break;
}
default: default:
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
} }

View File

@ -167,6 +167,10 @@ public final class PlaybackService extends Service
* Flushes the queue, switches to random mode and starts playing. * Flushes the queue, switches to random mode and starts playing.
*/ */
public static final String ACTION_RANDOM_MIX_AUTOPLAY = "ch.blinkenlights.android.vanilla.action.RANDOM_MIX_AUTOPLAY"; public static final String ACTION_RANDOM_MIX_AUTOPLAY = "ch.blinkenlights.android.vanilla.action.RANDOM_MIX_AUTOPLAY";
/**
* Flushes the queue and plays everything of the passed type/id combination.
*/
public static final String ACTION_FROM_TYPE_ID_AUTOPLAY = "ch.blinkenlights.android.vanilla.action.FROM_TYPE_ID_AUTOPLAY";
/** /**
* Change the shuffle mode. * Change the shuffle mode.
*/ */
@ -600,6 +604,13 @@ public final class PlaybackService extends Service
// We therefore send a GO message to the same queue, so it will get handled as // We therefore send a GO message to the same queue, so it will get handled as
// soon as the queue is ready. // soon as the queue is ready.
mHandler.sendEmptyMessage(MSG_CALL_GO); mHandler.sendEmptyMessage(MSG_CALL_GO);
} else if (ACTION_FROM_TYPE_ID_AUTOPLAY.equals(action)) {
int type = intent.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
long id = intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID);
QueryTask query = MediaUtils.buildQuery(type, id, Song.FILLED_PROJECTION, null);
// Flush the queue and start playing:
query.mode = SongTimeline.MODE_PLAY;
addSongs(query);
} else if (ACTION_CLOSE_NOTIFICATION.equals(action)) { } else if (ACTION_CLOSE_NOTIFICATION.equals(action)) {
mForceNotificationVisible = false; mForceNotificationVisible = false;
pause(); pause();

View File

@ -57,11 +57,22 @@ public class ShortcutPseudoActivity extends Activity {
case PlaybackService.ACTION_PREVIOUS_SONG: case PlaybackService.ACTION_PREVIOUS_SONG:
case PlaybackService.ACTION_PREVIOUS_SONG_AUTOPLAY: case PlaybackService.ACTION_PREVIOUS_SONG_AUTOPLAY:
case PlaybackService.ACTION_CYCLE_SHUFFLE: case PlaybackService.ACTION_CYCLE_SHUFFLE:
case PlaybackService.ACTION_CYCLE_REPEAT: case PlaybackService.ACTION_CYCLE_REPEAT: {
Intent intent = new Intent(this, PlaybackService.class); Intent intent = new Intent(this, PlaybackService.class);
intent.setAction(action); intent.setAction(action);
startService(intent); startService(intent);
break; break;
}
case PlaybackService.ACTION_FROM_TYPE_ID_AUTOPLAY: {
// From pinned shortcuts: Same as other actions, but this
// includes some extras.
Intent intent = new Intent(this, PlaybackService.class);
intent.setAction(action);
intent.putExtra(LibraryAdapter.DATA_TYPE, getIntent().getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID));
intent.putExtra(LibraryAdapter.DATA_ID, getIntent().getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID));
startService(intent);
break;
}
default: default:
throw new IllegalArgumentException("No such action: " + action); throw new IllegalArgumentException("No such action: " + action);
} }

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2018 Adrian Ulrich <adrian@blinkenlights.ch>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package ch.blinkenlights.android.vanilla;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
/**
* Provides some static System-related utility functions.
*/
public class SystemUtils {
/**
* Installs (or prompts user to install) a shortcut launcher pointing to the given type and id
* combination. Launching the shortcut will cause vanilla to play the type / id combination.
*
* @param context the context to use.
* @param label the label of the shortcut.
* @param type the media type of the passed in id.
* @param id an id of a media type
*/
public static void installLauncherShortcut(Context context, String label, int type, long id) {
Intent shortcut = new Intent(context, ShortcutPseudoActivity.class);
shortcut.setAction(PlaybackService.ACTION_FROM_TYPE_ID_AUTOPLAY);
shortcut.putExtra(LibraryAdapter.DATA_TYPE, type);
shortcut.putExtra(LibraryAdapter.DATA_ID, id);
Bitmap cover = null;
Song song = MediaUtils.getSongByTypeId(context, type, id);
if (song != null) {
cover = song.getSmallCover(context);
}
if (cover == null) {
cover = BitmapFactory.decodeResource(context.getResources(), R.drawable.fallback_cover);
}
// TODO: Support pre api 26.
installShortcut(context, label, cover, shortcut);
}
/**
* Prompts the user to install a shortcut. This is only available on API >= 26.
*
* @param context the context to use.
* @param label the label to use for this shortcut.
* @param cover the icon to use for this shortcut.
* @param intent intent launched by the shortcut.
*
* @return true if the shortcut MAY have been created.
*/
private static boolean installShortcut(Context context, String label, Bitmap cover, Intent intent) {
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
if (manager == null || !manager.isRequestPinShortcutSupported())
return false;
String uniqueId = "vanilla:shortcut:" + System.currentTimeMillis();
ShortcutInfo pin = new ShortcutInfo.Builder(context, uniqueId)
.setIntent(intent)
.setShortLabel(label)
.setIcon(Icon.createWithBitmap(cover))
.build();
manager.requestPinShortcut(pin, null);
return true;
}
// TODO: Test this on kitkat.
private static boolean installShortcutLegacy(Context context, String label, Intent intent) {
Intent add = new Intent();
add.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
add.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
add.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(context, R.drawable.repeat_active));
add.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
context.sendBroadcast(add);
return true;
}
}

View File

@ -68,6 +68,7 @@ THE SOFTWARE.
<string name="rename">Rename</string> <string name="rename">Rename</string>
<string name="save_as_playlist">Save as playlist…</string> <string name="save_as_playlist">Save as playlist…</string>
<string name="add_to_playlist">Add to playlist…</string> <string name="add_to_playlist">Add to playlist…</string>
<string name="add_to_homescreen">Add to homescreen…</string>
<string name="add_to_favorites">Add to favorites</string> <string name="add_to_favorites">Add to favorites</string>
<string name="remove_from_favorites">Remove from favorites</string> <string name="remove_from_favorites">Remove from favorites</string>
<string name="playlist_favorites">Favorites</string> <string name="playlist_favorites">Favorites</string>