android auto

main
h4h13 2019-08-04 20:45:38 +05:30
parent 6d84ad1028
commit e8cb0f5274
11 changed files with 196 additions and 190 deletions

View File

@ -13,7 +13,7 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic" applicationId "code.name.monkey.retromusic"
versionCode 352 versionCode 353
versionName '3.3.100' versionName '3.3.100'
multiDexEnabled true multiDexEnabled true

View File

@ -2,6 +2,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="code.name.monkey.retromusic"> package="code.name.monkey.retromusic">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@ -125,25 +137,7 @@
android:name=".activities.saf.SAFGuideActivity" android:name=".activities.saf.SAFGuideActivity"
android:theme="@style/Theme.Intro" /> android:theme="@style/Theme.Intro" />
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
<meta-data
android:name="com.lge.support.SPLIT_WINDOW"
android:value="true" />
<meta-data
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
android:value="GlideModule" />
<meta-data
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
android:value="GlideModule" />
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="code.name.monkey.retromusic.cast.CastOptionsProvider" />
<provider <provider
android:name=".misc.GenericFileProvider" android:name=".misc.GenericFileProvider"
@ -171,6 +165,7 @@
<action android:name="android.intent.action.MEDIA_BUTTON" /> <action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".appwidgets.BootReceiver"> <receiver android:name=".appwidgets.BootReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
@ -260,22 +255,22 @@
android:name="com.google.android.gms.car.notification.SmallIcon" android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification"/> android:resource="@drawable/ic_notification"/>
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
<meta-data
android:name="com.lge.support.SPLIT_WINDOW"
android:value="true" />
<meta-data
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
android:value="GlideModule" />
<meta-data
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
android:value="GlideModule" />
</application> </application>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature
android:name="android.hardware.type.automotive"
android:required="true" />
</manifest> </manifest>

File diff suppressed because one or more lines are too long

View File

@ -30,6 +30,7 @@ public class AutoMediaIDHelper {
public static final String MEDIA_ID_MUSICS_BY_PLAYLIST = "__BY_PLAYLIST__"; public static final String MEDIA_ID_MUSICS_BY_PLAYLIST = "__BY_PLAYLIST__";
public static final String MEDIA_ID_MUSICS_BY_ALBUM = "__BY_ALBUM__"; public static final String MEDIA_ID_MUSICS_BY_ALBUM = "__BY_ALBUM__";
public static final String MEDIA_ID_MUSICS_BY_ARTIST = "__BY_ARTIST__"; public static final String MEDIA_ID_MUSICS_BY_ARTIST = "__BY_ARTIST__";
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__"; public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__";
public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__"; public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";

View File

@ -32,19 +32,19 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListMap;
import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.AlbumLoader; import code.name.monkey.retromusic.loaders.AlbumLoader;
import code.name.monkey.retromusic.loaders.ArtistLoader; import code.name.monkey.retromusic.loaders.ArtistLoader;
import code.name.monkey.retromusic.loaders.GenreLoader;
import code.name.monkey.retromusic.loaders.PlaylistLoader; import code.name.monkey.retromusic.loaders.PlaylistLoader;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Album; import code.name.monkey.retromusic.model.Album;
import code.name.monkey.retromusic.model.Artist; import code.name.monkey.retromusic.model.Artist;
import code.name.monkey.retromusic.model.Genre;
import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore; import code.name.monkey.retromusic.providers.MusicPlaybackQueueStore;
import code.name.monkey.retromusic.service.MusicService; import code.name.monkey.retromusic.service.MusicService;
import code.name.monkey.retromusic.util.ImageUtil;
/** /**
* Created by Beesham Sarendranauth (Beesham) * Created by Beesham Sarendranauth (Beesham)
@ -57,7 +57,10 @@ public class AutoMusicProvider {
private static final int PATH_SEGMENT_ID = 0; private static final int PATH_SEGMENT_ID = 0;
private static final int PATH_SEGMENT_TITLE = 1; private static final int PATH_SEGMENT_TITLE = 1;
private static final int PATH_SEGMENT_ARTIST = 2; private static final int PATH_SEGMENT_ARTIST = 2;
private static final int PATH_SEGMENT_GENRE = 4;
private static final int PATH_SEGMENT_ALBUM_ID = 5;
private WeakReference<MusicService> mMusicService;
// Categorized caches for music data // Categorized caches for music data
private ConcurrentMap<Integer, Uri> mMusicListByHistory; private ConcurrentMap<Integer, Uri> mMusicListByHistory;
@ -65,6 +68,7 @@ public class AutoMusicProvider {
private ConcurrentMap<Integer, Uri> mMusicListByPlaylist; private ConcurrentMap<Integer, Uri> mMusicListByPlaylist;
private ConcurrentMap<Integer, Uri> mMusicListByAlbum; private ConcurrentMap<Integer, Uri> mMusicListByAlbum;
private ConcurrentMap<Integer, Uri> mMusicListByArtist; private ConcurrentMap<Integer, Uri> mMusicListByArtist;
private ConcurrentMap<Integer, Uri> mMusicListByGenre;
private Uri defaultAlbumArtUri; private Uri defaultAlbumArtUri;
@ -79,12 +83,17 @@ public class AutoMusicProvider {
mMusicListByPlaylist = new ConcurrentSkipListMap<>(); mMusicListByPlaylist = new ConcurrentSkipListMap<>();
mMusicListByAlbum = new ConcurrentSkipListMap<>(); mMusicListByAlbum = new ConcurrentSkipListMap<>();
mMusicListByArtist = new ConcurrentSkipListMap<>(); mMusicListByArtist = new ConcurrentSkipListMap<>();
mMusicListByGenre = new ConcurrentSkipListMap<>();
defaultAlbumArtUri = Uri.parse("android.resource://" + defaultAlbumArtUri = Uri.parse("android.resource://" +
mContext.getPackageName() + "/drawable/" + mContext.getPackageName() + "/drawable/" +
mContext.getResources().getResourceEntryName(R.drawable.default_album_art)); mContext.getResources().getResourceEntryName(R.drawable.default_album_art));
} }
public void setMusicService(MusicService service) {
mMusicService = new WeakReference<>(service);
}
public Iterable<Uri> getHistory() { public Iterable<Uri> getHistory() {
if (mCurrentState != State.INITIALIZED) { if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList(); return Collections.emptyList();
@ -120,6 +129,13 @@ public class AutoMusicProvider {
return mMusicListByArtist.values(); return mMusicListByArtist.values();
} }
public Iterable<Uri> getGenres() {
if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList();
}
return mMusicListByGenre.values();
}
public Iterable<Uri> getQueue() { public Iterable<Uri> getQueue() {
if (mCurrentState != State.INITIALIZED) { if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList(); return Collections.emptyList();
@ -128,8 +144,9 @@ public class AutoMusicProvider {
// Re-built every time since the queue updates often // Re-built every time since the queue updates often
ConcurrentMap<Integer, Uri> queueList = new ConcurrentSkipListMap<>(); ConcurrentMap<Integer, Uri> queueList = new ConcurrentSkipListMap<>();
if (mContext != null) { final MusicService service = mMusicService.get();
final List<Song> songs = MusicPlaybackQueueStore.getInstance(mContext).getSavedOriginalPlayingQueue(); if (service != null) {
final List<Song> songs = MusicPlaybackQueueStore.getInstance(service).getSavedOriginalPlayingQueue();
for (int i = 0; i < songs.size(); i++) { for (int i = 0; i < songs.size(); i++) {
final Song s = songs.get(i); final Song s = songs.get(i);
Uri.Builder topTracksData = Uri.parse(BASE_URI).buildUpon(); Uri.Builder topTracksData = Uri.parse(BASE_URI).buildUpon();
@ -260,6 +277,21 @@ public class AutoMusicProvider {
mMusicListByArtist = newMusicListByArtist; mMusicListByArtist = newMusicListByArtist;
} }
private synchronized void buildListsByGenre() {
ConcurrentMap<Integer, Uri> newMusicListByGenre = new ConcurrentSkipListMap<>();
final List<Genre> genres = GenreLoader.INSTANCE.getAllGenres(mContext);
for (int i = 0; i < genres.size(); i++) {
final Genre p = genres.get(i);
Uri.Builder playlistData = Uri.parse(BASE_URI).buildUpon();
playlistData.appendPath(String.valueOf(p.getId()))
.appendPath(p.getName());
newMusicListByGenre.putIfAbsent(i, playlistData.build());
}
mMusicListByGenre = newMusicListByGenre;
}
private synchronized void retrieveMedia() { private synchronized void retrieveMedia() {
try { try {
if (mCurrentState == State.NON_INITIALIZED) { if (mCurrentState == State.NON_INITIALIZED) {
@ -270,6 +302,7 @@ public class AutoMusicProvider {
buildListsByPlaylist(); buildListsByPlaylist();
buildListsByAlbum(); buildListsByAlbum();
buildListsByArtist(); buildListsByArtist();
buildListsByGenre();
mCurrentState = State.INITIALIZED; mCurrentState = State.INITIALIZED;
} }
} finally { } finally {
@ -290,13 +323,31 @@ public class AutoMusicProvider {
switch (mediaId) { switch (mediaId) {
case AutoMediaIDHelper.MEDIA_ID_ROOT: case AutoMediaIDHelper.MEDIA_ID_ROOT:
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY, resources.getString(R.string.history_label), R.drawable.ic_access_time_white_24dp)); //mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY, resources.getString(R.string.history_label), R.drawable.ic_access_time_white_24dp));
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS, resources.getString(R.string.top_tracks_label), R.drawable.ic_trending_up_white_24dp)); //mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS, resources.getString(R.string.top_tracks_label), R.drawable.ic_trending_up_white_24dp));
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST, resources.getString(R.string.playlists_label), R.drawable.ic_queue_music_white_24dp)); //mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST, resources.getString(R.string.playlists_label), R.drawable.ic_queue_music_white_24dp));
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM, resources.getString(R.string.albums_label), R.drawable.ic_album_white_24dp)); mediaItems.add(
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST, resources.getString(R.string.artists_label), R.drawable.ic_artist_white_24dp)); createBrowsableMediaItem(
mediaItems.add(createPlayableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE, resources.getString(R.string.action_shuffle_all), R.drawable.ic_shuffle_white_24dp, null)); AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM,
mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE, resources.getString(R.string.queue_label), R.drawable.ic_playlist_play_white_24dp)); resources.getString(R.string.albums_label),
R.drawable.ic_album_white_24dp));
mediaItems.add(
createBrowsableMediaItem(
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST,
resources.getString(R.string.artists_label),
R.drawable.ic_artist_white_24dp));
mediaItems.add(
createBrowsableMediaItem(
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE,
resources.getString(R.string.genres_label),
R.drawable.ic_guitar_acoustic_white_24dp));
mediaItems.add(
createPlayableMediaItem(
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE,
resources.getString(R.string.action_shuffle_all),
R.drawable.ic_shuffle_white_24dp,
""));
//mediaItems.add(createBrowsableMediaItem(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE, resources.getString(R.string.queue_label), R.drawable.ic_playlist_play_white_24dp));
break; break;
case AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY: case AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY:
@ -328,6 +379,11 @@ public class AutoMusicProvider {
mediaItems.add(createPlayableMediaItem(mediaId, uri, uri.getPathSegments().get(PATH_SEGMENT_ARTIST), null)); mediaItems.add(createPlayableMediaItem(mediaId, uri, uri.getPathSegments().get(PATH_SEGMENT_ARTIST), null));
} }
break; break;
case AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE:
for (final Uri uri : getGenres()) {
mediaItems.add(createPlayableMediaItem(mediaId, uri, uri.getPathSegments().get(PATH_SEGMENT_TITLE), null));
}
break;
case AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE: case AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE:
// TODO: auto scroll to current track, indicate that it's playing // TODO: auto scroll to current track, indicate that it's playing
@ -343,21 +399,25 @@ public class AutoMusicProvider {
private MediaBrowserCompat.MediaItem createBrowsableMediaItem(String mediaId, String title, int iconDrawableId) { private MediaBrowserCompat.MediaItem createBrowsableMediaItem(String mediaId, String title, int iconDrawableId) {
MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder(); MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(mediaId) builder.setMediaId(mediaId)
.setTitle(title) .setTitle(title);
.setIconBitmap(ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(mContext, iconDrawableId, ThemeStore.Companion.textColorSecondary(mContext))));
return new MediaBrowserCompat.MediaItem(builder.build(), return new MediaBrowserCompat.MediaItem(builder.build(),
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
} }
private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId, Uri musicSelection, private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId,
String title, @Nullable String subtitle) { Uri musicSelection,
String title,
@Nullable String subtitle) {
return createPlayableMediaItem(mediaId, musicSelection, title, subtitle, null, null); return createPlayableMediaItem(mediaId, musicSelection, title, subtitle, null, null);
} }
private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId, Uri musicSelection, private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId,
String title, @Nullable String subtitle, Uri musicSelection,
@Nullable Bitmap albumArt, @Nullable Resources resources) { String title,
@Nullable String subtitle,
@Nullable Bitmap albumArt,
@Nullable Resources resources) {
MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder(); MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(AutoMediaIDHelper.createMediaID(musicSelection.getPathSegments().get(PATH_SEGMENT_ID), mediaId)) builder.setMediaId(AutoMediaIDHelper.createMediaID(musicSelection.getPathSegments().get(PATH_SEGMENT_ID), mediaId))
.setTitle(title); .setTitle(title);
@ -378,12 +438,13 @@ public class AutoMusicProvider {
MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
} }
private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId, String title, int iconDrawableId, private MediaBrowserCompat.MediaItem createPlayableMediaItem(String mediaId,
String title,
int iconDrawableId,
@Nullable String subtitle) { @Nullable String subtitle) {
MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder() MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder()
.setMediaId(mediaId) .setMediaId(mediaId)
.setTitle(title) .setTitle(title);
.setIconBitmap(ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(mContext, iconDrawableId, ThemeStore.Companion.textColorSecondary(mContext))));
if (subtitle != null) { if (subtitle != null) {
builder.setSubtitle(subtitle); builder.setSubtitle(subtitle);

View File

@ -1,28 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.auto;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
public class CarHelper {
private static final String TAG = "CarHelper";
public static boolean isCarUiMode(Context c) {
UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
}
}

View File

@ -92,6 +92,10 @@ class MediaSessionCallback(private val context: Context,
songs.addAll(playlist.getSongs(context)) songs.addAll(playlist.getSongs(context))
openQueue(songs, 0, true) openQueue(songs, 0, true)
} }
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE -> {
songs.addAll(GenreLoader.getSongs(context, itemId))
openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY, AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY,
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS, AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS,
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE -> { AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE -> {

View File

@ -337,8 +337,10 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this); PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this);
restoreState(); restoreState();
mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers); mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers);
mMusicProvider = new AutoMusicProvider(this); mMusicProvider = new AutoMusicProvider(this);
sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED")); sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED"));
registerHeadsetEvents(); registerHeadsetEvents();
@ -352,19 +354,26 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
} }
private void setupMediaSession() { private void setupMediaSession() {
ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class); ComponentName mediaButtonReceiverComponentName = new ComponentName(
getApplicationContext(),
MediaButtonIntentReceiver.class);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName); mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(
getApplicationContext(),
0,
mediaButtonIntent,
0);
mediaSession = new MediaSessionCompat(this, mediaSession = new MediaSessionCompat(this,
"RetroMusicPlayer", "RetroMusicPlayer",
mediaButtonReceiverComponentName, mediaButtonReceiverComponentName,
mediaButtonReceiverPendingIntent); mediaButtonReceiverPendingIntent);
MediaSessionCallback mediasessionCallback = new MediaSessionCallback(getApplicationContext(), this); MediaSessionCallback mediasessionCallback = new MediaSessionCallback(
getApplicationContext(), this);
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
); );
@ -418,7 +427,7 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
} }
} }
return START_STICKY; return START_NOT_STICKY;
} }
private void playFromPlaylist(Intent intent) { private void playFromPlaylist(Intent intent) {
@ -445,21 +454,6 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
} }
} }
private void playSongs(int shuffleMode, ArrayList<Song> playlistSongs) {
if (!playlistSongs.isEmpty()) {
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
int startPosition;
startPosition = new Random().nextInt(playlistSongs.size());
openQueue(playlistSongs, startPosition, true);
setShuffleMode(shuffleMode);
} else {
openQueue(playlistSongs, 0, true);
}
} else {
Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show();
}
}
@Override @Override
public void onDestroy() { public void onDestroy() {
unregisterReceiver(widgetIntentReceiver); unregisterReceiver(widgetIntentReceiver);
@ -478,20 +472,28 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this); PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this);
wakeLock.release(); wakeLock.release();
sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_MUSIC_SERVICE_DESTROYED")); sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_DESTROYED"));
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
// For Android auto, need to call super, or onGetRoot won't be called.
if (intent != null && "android.media.browse.MediaBrowserService".equals(intent.getAction())) {
return super.onBind(intent);
}
return musicBind; return musicBind;
} }
@Nullable @Nullable
@Override @Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
// Check origin to ensure we're not allowing any arbitrary app to browse app contents
if (!mPackageValidator.isKnownCaller(clientPackageName, clientUid)) { if (!mPackageValidator.isKnownCaller(clientPackageName, clientUid)) {
// Request from an untrusted package: return an empty browser root
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null); return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
} }
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_ROOT, null); return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_ROOT, null);
} }
@ -686,10 +688,33 @@ public class MusicService extends MediaBrowserServiceCompat implements SharedPre
.setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, .setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
getSongProgressMillis(), 1); getSongProgressMillis(), 1);
setCustomAction(stateBuilder);
mediaSession.setPlaybackState(stateBuilder.build()); mediaSession.setPlaybackState(stateBuilder.build());
} }
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {
int repeatIcon = R.drawable.ic_repeat_white_24dp; // REPEAT_MODE_NONE
if (getRepeatMode() == REPEAT_MODE_THIS) {
repeatIcon = R.drawable.ic_repeat_one_white_24dp;
} else if (getRepeatMode() == REPEAT_MODE_ALL) {
repeatIcon = R.drawable.ic_repeat_white_24dp;
}
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
CYCLE_REPEAT, getString(R.string.action_cycle_repeat), repeatIcon)
.build());
final int shuffleIcon = getShuffleMode() == SHUFFLE_MODE_NONE ? R.drawable.ic_shuffle_white_24dp : R.drawable.ic_shuffle_white_24dp;
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon)
.build());
final int favoriteIcon = MusicUtil.isFavorite(getApplicationContext(), getCurrentSong()) ? R.drawable.ic_favorite_white_24dp : R.drawable.ic_favorite_border_white_24dp;
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon)
.build());
}
private void updateMediaSessionMetaData() { private void updateMediaSessionMetaData() {
final Song song = getCurrentSong(); final Song song = getCurrentSong();

View File

@ -11,8 +11,10 @@
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
*/ */
package code.name.monkey.retromusic.util package code.name.monkey.retromusic.util
import android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE import android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE
import android.Manifest.permission.MEDIA_CONTENT_CONTROL import android.Manifest.permission.MEDIA_CONTENT_CONTROL
import android.annotation.SuppressLint import android.annotation.SuppressLint
@ -45,7 +47,10 @@ import java.security.NoSuchAlgorithmException
* *
* For more information, see res/xml/allowed_media_browser_callers.xml. * For more information, see res/xml/allowed_media_browser_callers.xml.
*/ */
class PackageValidator(context: Context, @XmlRes xmlResId: Int) { class PackageValidator(
context: Context,
@XmlRes xmlResId: Int
) {
private val context: Context private val context: Context
private val packageManager: PackageManager private val packageManager: PackageManager

View File

@ -191,6 +191,7 @@
<string name="genre">Genre</string> <string name="genre">Genre</string>
<string name="genres">Genres</string> <string name="genres">Genres</string>
<string name="genres_label">Genres</string>
<string name="git_hub_summary">Fork the project on GitHub</string> <string name="git_hub_summary">Fork the project on GitHub</string>

View File

@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?><!-- <?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2014 The Android Open Source Project Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,7 +15,6 @@ limitations under the License.
PackageValidator by default will print to logcat (INFO level) a message with the proper base64 PackageValidator by default will print to logcat (INFO level) a message with the proper base64
version of the caller's certificate that has not been validated. You can copy from logcat and version of the caller's certificate that has not been validated. You can copy from logcat and
paste it here. paste it here.
Spaces and newlines are ignored. Spaces and newlines are ignored.
--> -->
<allowed_callers> <allowed_callers>
@ -164,32 +160,6 @@ limitations under the License.
BKZCvlzboOEAZfx8aBKc7SezqATXpM3ZDNPsywWoyIpgmtBWoE60ih4Flf05XB+n BKZCvlzboOEAZfx8aBKc7SezqATXpM3ZDNPsywWoyIpgmtBWoE60ih4Flf05XB+n
e7MdpSQ0Xgq9TgG1BoJP6rpC0y3Ukmc+z8AXnYYdJunNXEbv0A== e7MdpSQ0Xgq9TgG1BoJP6rpC0y3Ukmc+z8AXnYYdJunNXEbv0A==
</signing_certificate> </signing_certificate>
<signing_certificate
name="Media Browser Service Simulator"
package="com.google.android.mediasimulator"
release="true">
MIIDvTCCAqWgAwIBAgIJAMePnkuTQTAGMA0GCSqGSIb3DQEBBQUAMHUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
aWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDERMA8G
A1UEAwwIZ2VhcmhlYWQwHhcNMTQwNTI3MjMwNTM0WhcNNDExMDEyMjMwNTM0WjB1
MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
bnRhaW4gVmlldzEUMBIGA1UECgwLR29vZ2xlIEluYy4xEDAOBgNVBAsMB0FuZHJv
aWQxETAPBgNVBAMMCGdlYXJoZWFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA050XDkNIsVRMX2wTvVplpCu4OtnyNK2v5B7PS+DggmH2yuZiwpTurdKD
Q9R9UzxH9U4lsC+mIxXkiBYKIWNVgMtiTgxkEy7cgWvdYHgNYpFu8IxZKYDyXes+
02pfvpu63MIBD/PnvVFipo1oUrbfetj+mroEpjnA71gUS0Ok+H6XWWsmb8xFHQVM
oZWEIzsUJ2nhm8EcnPkAPfNZAG++XLPROoRQCaswyYsd42JuYAP3CwZuhDcUbMWm
k7rBi9BVQ8gmkrbwqo94A7qStLUp3NyCmlKSWHaZ05SspEPwsfctka0oXG5bhgT6
67EMCzQ+YsFN1oJRL7Qq+mMQjFJs3wIDAQABo1AwTjAdBgNVHQ4EFgQUGvBfYNeu
6JSJUnJZCiaBGsnXztswHwYDVR0jBBgwFoAUGvBfYNeu6JSJUnJZCiaBGsnXztsw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlGsDY0EPu3NBSH5k6iw/
wJh9e3xMwS17ErKGlhyWogxJMzLjAN6g0aCPHxB40IQC+8qAl+RL7VQx6oxttf0m
31yUGQPcNYbt2CxBTCAr885oLK5t2TAi5tQzhd6ZEYihWSUWUd/X8BQRouxboss9
QbBA/iIx0OpDaxiAcq7Cb67TheXZDxGuQ8fmHYbLx84pEvm3DQOB/LIMkkpQSfEC
1f+oP1zB3urPU/dSvED/LCgOdrpxZ5di7SwSyue+Vq/TZQy34tPygEzD2d8hFlh/
yfhWkMizOeIXcayVAQdNn5zpBkuay1skGOjQQ5kTbDcDzigO2R2rqn6HCd9l5Z0W
IQ==
</signing_certificate>
<signing_certificate <signing_certificate
name="Android Auto Simulator" name="Android Auto Simulator"
package="com.google.android.autosimulator" package="com.google.android.autosimulator"
@ -220,36 +190,6 @@ limitations under the License.
ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW
Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</signing_certificate> </signing_certificate>
<signing_certificate
name="Media Browser Simulator"
package="com.google.android.mediasimulator"
release="true">
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
Fw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
hvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR
24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVy
xW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8X
W8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC
69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexA
cKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkw
HQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0c
xb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1Ud
EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrP
zgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXcla
XjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05a
IskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+a
ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW
Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</signing_certificate>
<signing_certificate <signing_certificate
name="Google" name="Google"
package="com.google.android.googlequicksearchbox" package="com.google.android.googlequicksearchbox"
@ -284,26 +224,28 @@ limitations under the License.
name="Google" name="Google"
package="com.google.android.googlequicksearchbox" package="com.google.android.googlequicksearchbox"
release="true"> release="true">
MIIDvTCCAqWgAwIBAgIJAMePnkuTQTAGMA0GCSqGSIb3DQEBBQUAMHUxCzAJBgNV MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBW
aWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDERMA8G aWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4G
A1UEAwwIZ2VhcmhlYWQwHhcNMTQwNTI3MjMwNTM0WhcNNDExMDEyMjMwNTM0WjB1 A1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQx
MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3Vu
bnRhaW4gVmlldzEUMBIGA1UECgwLR29vZ2xlIEluYy4xEDAOBgNVBAsMB0FuZHJv dGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9p
aWQxETAPBgNVBAMMCGdlYXJoZWFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB ZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC
CgKCAQEA050XDkNIsVRMX2wTvVplpCu4OtnyNK2v5B7PS+DggmH2yuZiwpTurdKD ggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JO
Q9R9UzxH9U4lsC+mIxXkiBYKIWNVgMtiTgxkEy7cgWvdYHgNYpFu8IxZKYDyXes+ Rland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/
02pfvpu63MIBD/PnvVFipo1oUrbfetj+mroEpjnA71gUS0Ok+H6XWWsmb8xFHQVM 8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfY
oZWEIzsUJ2nhm8EcnPkAPfNZAG++XLPROoRQCaswyYsd42JuYAP3CwZuhDcUbMWm wXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LW
k7rBi9BVQ8gmkrbwqo94A7qStLUp3NyCmlKSWHaZ05SspEPwsfctka0oXG5bhgT6 uT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0z
67EMCzQ+YsFN1oJRL7Qq+mMQjFJs3wIDAQABo1AwTjAdBgNVHQ4EFgQUGvBfYNeu OHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Yl
6JSJUnJZCiaBGsnXztswHwYDVR0jBBgwFoAUGvBfYNeu6JSJUnJZCiaBGsnXztsw mn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14al
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlGsDY0EPu3NBSH5k6iw/ oXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
wJh9e3xMwS17ErKGlhyWogxJMzLjAN6g0aCPHxB40IQC+8qAl+RL7VQx6oxttf0m BxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsT
31yUGQPcNYbt2CxBTCAr885oLK5t2TAi5tQzhd6ZEYihWSUWUd/X8BQRouxboss9 B0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTAD
QbBA/iIx0OpDaxiAcq7Cb67TheXZDxGuQ8fmHYbLx84pEvm3DQOB/LIMkkpQSfEC AQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4
1f+oP1zB3urPU/dSvED/LCgOdrpxZ5di7SwSyue+Vq/TZQy34tPygEzD2d8hFlh/ rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCE
yfhWkMizOeIXcayVAQdNn5zpBkuay1skGOjQQ5kTbDcDzigO2R2rqn6HCd9l5Z0W yj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh
IQ== 5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTb
Qe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZM
cUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
</signing_certificate> </signing_certificate>
</allowed_callers> </allowed_callers>