I am using the Spotify android SDK, and I am trying to get a single song to play, and I would like to potentially play a song after the current one is completed. The issue is that after the song completes Spotify plays a random song afterwards. Is it possible to play the song and not have anything else automatically play after it?
I am simply calling the app remotes player API play function,
mSpotifyAppRemote?.getPlayerApi()?.play(uri)
#Mikee you are doing nothing wrong, Spotify messes around with track replay if you are only using the Free version, if you use Premium it will work correctly. Funny enough if you try playing by an alblum or artist that works with the Free version.
I'm not an expert but from their documentation it looks like you could watch the PlayerState. I'm also not sure when a PlayerState event would trigger but if it's coming back relatively often you could check the track value and see if it's gone to null, or another value and work using that.
Here's a Java example from their website:
// Subscribe to PlayerState
mSpotifyAppRemote.getPlayerApi()
.subscribeToPlayerState()
.setEventCallback(new Subscription.EventCallback<PlayerState>() {
public void onEvent(PlayerState playerState) {
// See what values are in playerState, might be able to determine
// if it's now randomly playing?
final Track track = playerState.track;
if (track != null) {
Log.d("MainActivity", track.name + " by " + track.artist.name);
// If the track is now different, your song has finished, stop it?
}
}
});
I've put a few extra comments in the code above that might yield some results!
Related
My website plays a background song and some audio effects at the same time. Although the song loads fine (I can see its duration via Audio.duration property), it doesn't play, while the audio effects play (I have already clicked on the screen to activate audio playing). On Firefox in my PC it plays without problem. The audio file is a 10 MB mp3 (but my Android is at home wi-fi). Below is part of my code.
var music,clicked=false,downloaded=false;
function play() {
music.play();
}
function canPlay() {
downloaded = true;
if (clicked) {
play();
}
}
function touchStart(e) {
if (!clicked) {
clicked = true;
if (downloaded) {
play();
}
}
}
function load() {
music = new Audio('mp3/music.mp3');
music.addEventListener('canplaythrough',canPlay,false);
window.addEventListener('touchstart',touchStart,false);
}
The load function is called in the onload of body. The clicked and downloaded variables are required because I never know what happens first: the user click or the audio finish downloading.
On Google mobile-friendly test, it says the audio file couldn’t be loaded (Status: Other error). I've read this test doesn't wait for more than 3 seconds. However, reading the time of the song in seconds doesn't prove it has downloaded?
Why won't the song play on mobile?
EDIT
I created a timer interval that tells me the position (music.currentTime) of the song once every minute. On desktop, it gives the right time. On mobile, it always gives 0, meaning that the song, although loaded (?), isn't really being played.
EDIT 2
I investigated the promise returned by music.play(), and noticed it was giving the error: "(NotAllowedError: play() failed because the user didn't interact with the document first)". It seems that touchStart doesn't count as a "user interaction" the way that mouseDown does...
In the end, I removed both touchStart and touchEnd (each with his own preventDefault), leaving the song to be played after the resulting mouseDown. I've kept the touchMove, however, because it's different from the mouseMove, and required for my site.
After reading the documentation on Spotify's Android Media Notifications API, https://beta.developer.spotify.com/documentation/android-sdk/guides/android-media-notifications/, I successfully managed to receive the notifications metadata and it is displayed properly on my app.
However, the notifications metadata is only updated when the queue changes, when the track changes, and when playback is changed, so unless one of these three actions happens, the "positionInMs" intent extra isn't sent.
As of right now as a workaround I am simply starting a timer using the time the intent was sent, the last known playback position, and the track duration to track current playback position.
This seemed to work at first, but after further testing I've realized that the timer I set can go out of sync, if the track the user is listening to freezes because of a slow internet connection.
Any ideas to properly track the playback position, while accounting for a slow internet connection? Or are there any alternatives I should look into?
I understand that this question is rather old, but I am going to answer anyway if anyone else comes across it.
I recommend constantly querying Spotify to get the playback position. One way you can do this is by using a timer and querying Spotify every given time frame. The below example queries Spotify every 100ms. If you want to reduce/increase the numbers of queries, you can simply use stopwatch.setClockDelay() and provide your required time
For instance, you can use this timer library
implementation 'com.yashovardhan99.timeit:timeit:1.2.0'
Then use the following code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify);
Stopwatch stopwatch = new Stopwatch();
stopwatch.setOnTickListener(this);
stopwatch.start();
}
#Override
public void onTick(Stopwatch stopwatch) {
Data.getAndroidSpotifyAppRemote().getPlayerApi().getPlayerState().setResultCallback(new CallResult.ResultCallback<PlayerState>() {
#Override
public void onResult(PlayerState playerState) {
Log.d("TAG", playerState.playbackPosition);
}
});
}
Don't forget to add the following code at the top of your class:
implements Stopwatch.OnTickListener
I want to get all connected players to game. I can get players that are in google+ circles but I want the player to get all users. I can't find what permission do I need to do this.
I am using this code to get players, but it always returns 0.
PendingResult<LoadPlayersResult> players = Games.Players.loadConnectedPlayers(mGoogleApiClient, false);
players.setResultCallback(new ResultCallback<Players.LoadPlayersResult>()
{
#Override
public void onResult(LoadPlayersResult result)
{
PlayerBuffer buf = result.getPlayers();
Toast.makeText(getApplicationContext(), "players"+buf.getCount(), Toast.LENGTH_SHORT).show();
}
});
this had been pre-announced and then announced a while ago already and method .loadConnectedPlayers() had been deprecated... which merely boils down to, that Google+ had been separated from Play Games and the functionality you are looking for is not available anymore.
in order to get a list of connected players, you would have to use your own API now. Just seen the question is old and had been posted before the announce - nevertheless this appears to be the current status.
I use Google Play Games Services and everythink working fine (leaderboards, random opponent etc etc)
But when a player call finishMatch(), the status of match.canRematch() is always "true", beyond the result.
If the player has won the battle may not be able to ask for a rematch!
I use this code to send the result:
Games.TurnBasedMultiplayer.finishMatch(mGoogleApiClient,
mMatch.getMatchId(), mMatch.getData(), myscore, creatorscore).setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(
TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
I would like to disable the possibility of revenge. How can I do?
Ok. This IS an answer.
It is not possible from the API to disallow rematch.
However, I just discovered a workaround that achieves the same effect.
detect whether a match is a rematch by calling getMatchNumber() and getPreviousMatchData(). Cancel/Dismiss the match if the former isn't 1 or the latter isn't null. It ensures a rematch user input will have the same effect as a dismissal.
when a match is completed completely (cancelled, expired, or MATCH_STATUS_COMPLETE && MATCH_TURN_STATUS_COMPLETE), dismiss it right away. It minimizes the chance that the user will even see the "Match complete, rematch" UI. This check can also be checked periodically for matches in the inbox.
I am implementing a Turn Base Multiplayer game using Google Play Services. There seems to be a major feature missing from it though: tracking wins and loses! Seems like a pretty important feature for any sort of competitive game.
Am I missing something? Is this already handled by Google in some way?
If not, I am wondering if anyone has implemented their own w/l tracking system, and how they approached it. I am worried about just saving it locally, as it might get out of sync with reality. I suppose I could use Google Cloud Storage, but I also worry that keeping track of which matches have been accounted for could be a little error prone (eg. counting wins/loses on the same match multiple times). Maybe using the Google Play Leaderboard system would be good, as you could compare with friends/etc.
You might want to try using the Events provided by Google Play Game Services. Here's the steps for using it:
Go to the Android Developer Console
On the sidebar click on Game Services, choose your App, then Events
Then create two new Events, one called 'Games Won' and the other 'Games Lost', then click to publish your changes
Click the Get Resources link to see the resource ids of your new events, and add them to your App in the res/values directory
When a game is won or lost, call the following code, where eventRef is your friendly resource name you added in the last step, either "events_games_won" or "events_games_lost":
int eventInt = activity.getApplicationContext().getResources().getIdentifier(eventRef, "string", "com.yourpackage.android");
String eventID = activity.getString(eventInt);
Games.Events.increment(apiClient, eventID, 1);
If you're using basegameutils, your apiClient instance can be obtained with aHelper.getApiClient(), otherwise follow these instructions to set it up.
To read the data you've saved, you can use a callback like this:
PendingResult<Events.LoadEventsResult> results = Games.Events.load(apiClient, true);
results.setResultCallback(new ResultCallback<Events.LoadEventsResult>() {
#Override
public void onResult(Events.LoadEventsResult result)
{
Events.LoadEventsResult r = (Events.LoadEventsResult)result;
EventBuffer eb = r.getEvents();
for (int i=0; i < eb.getCount(); i++) {
Event event = eb.get(i);
// do something, like cache the results for later
YourGameState.eventStats.put(event.getName(), (int)event.getValue());
}
eb.close();
listener.onResult();
}
});
The benefits of this approach over maintaining and managing counts locally are:
the wins and losses are stored in Google Play's cloud, and they'll be stored across all the user's devices
you can use the Events to create Quests (also described on Google Play), such as "Win 10 games this week", or "Go this week undefeated"
Google Play will give you stats on Events for everyone who plays your game, which can be very useful for analysing player activity and performance
And, of course, you can use the same approach to create Events for anything you want.
It depends on your implementation. I've created a chess application and I just use SharedPreferences to keep track of my wins and losses. If you use it properly it won't be error prone.
Eg.
SharedPreferences prefs = this.getSharedPreferences(
"com.example.app", Context.MODE_PRIVATE);
if (gameOver(Side.White)) {
prefs.edit().putLong(dateTimeKey, prefs.getLong(losses, 0) + 1).commit();
} else if (gameOver(Side.Black)){
prefs.edit().putLong(dateTimeKey, prefs.getLong(wins, 0) + 1).commit();
}