I have a 2 Insole (left and right insole), then 2 data generated from the left and right insole will be sent to firebase every 1 second. To send data per second I use 2 timers namely left_timer.scheduleAtFixedRate and right_timer.scheduleAtFixedRate but the data generated from these 2 insoles is still out of sync. because the resulting data is messy. the reason the resulting data is messy is that if we walk only one foot will step on the insole. but with this code, two feet will be on the ground when we walk. I suspect this delay is due to the process of sending data. is there any solution for this problem
Here My code:
connectLeftBtn:
connectLeftBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if(isChecked)
{
connectDevice(L_insole_mac);
}
else
{
if (is_L_insole_connected)
if(is_L_insole_started)
left_insole_device_interface.stopInsole();
startLeftBtn.setText("Start Left");
is_L_insole_started = false;
bluetoothManager.closeDevice(left_insole_device_interface);
is_L_insole_connected = false;
Toast.makeText(Visualization.this, "Left Insole Disconnected.", Toast.LENGTH_SHORT).show();
}
}
private void connectDevice(String mac) {
bluetoothManager.openSerialDevice(mac)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onConnected, this::onError);
}
#RequiresApi(api = Build.VERSION_CODES.N)
private void onConnected(BluetoothSerialDevice connectedDevice) {
// You are now connected to this device!
// Here you may want to retain an instance to your device:
left_insole_device_interface = connectedDevice.toSimpleDeviceInterface();
// Listen to bluetooth events
left_insole_device_interface.setListeners(message -> onMessageReceived(message), this::onMessageSent, this::onError);
left_insole_device_interface.stopInsole();
is_L_insole_connected = true;
Toast.makeText(getApplication(), "Connected to Left Insole.", Toast.LENGTH_SHORT).show();
}
#RequiresApi(api = Build.VERSION_CODES.N)
private void onMessageReceived(String message) {
//store incoming bytes temporarily
if(!is_L_insole_started){
left_temp_bytes+=message+" ";
}
//check whether the start_bytes exits in the temporary buffer
if(!is_L_insole_started && left_temp_bytes.contains(start_bytes)){
is_L_insole_started = true;
left_temp_bytes ="";
}
//if the start_bytes are found in the temporary buffer, start storing the incoming messages in the actual buffer
if(is_L_insole_started){
left_data_len++;
if(left_data_len>15) {
left_sensor_data_count++;
if (!non_sensor_indeces.contains(left_sensor_data_count)) {
l_data_double_arr[left_data_index] = Double.parseDouble(message);
// System.out.println("NON SENSOR INDEX:" + left_data_index + " "+ message);
left_data_index++;
}
}
Date date = new Date();
leftDataDict.put(String.valueOf(formatter.format(date)), Arrays.toString(l_data_double_arr));
//if the data length reach the max_data_length, release the buffer and invert the start flag
if(left_data_len>=max_data_len+15){
heatMapLeft.clearData();
for(int i=0; i<x_L.length; i++) {
HeatMap.DataPoint point = new HeatMap.DataPoint(x_L[i], y[i], l_data_double_arr[i]);
heatMapLeft.addData(point);
heatMapLeft.forceRefresh();
}
left_package_count++;
left_data_index= 0;
left_sensor_data_count = 0;
left_data_len=0;
is_L_insole_started=false;
}
}
}
private void onMessageSent(String message) {
// We sent a message! Handle it here.
// Toast.makeText(getApplication(), "Sent a message! Message was: " + message, Toast.LENGTH_LONG).show(); // Replace context with your context instance.
}
private void onError(Throwable error) {
// Handle the error
}
});
connectRightBtn:
connectRightBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (isChecked) {
connectDevice(R_insole_mac);
} else {
if (is_R_insole_connected) {
if (is_R_insole_started) {
right_insole_device_interface.stopInsole();
startRightBtn.setText("Start Right");
is_R_insole_started = false;
}
bluetoothManager.closeDevice(right_insole_device_interface);
is_R_insole_connected = false;
Toast.makeText(Visualization.this, "Right Insole Disconnected.", Toast.LENGTH_SHORT).show();
}
}
}
private void connectDevice (String mac){
bluetoothManager.openSerialDevice(mac)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onConnected, this::onError);
}
#RequiresApi(api = Build.VERSION_CODES.O)
private void onConnected (BluetoothSerialDevice connectedDevice){
// You are now connected to this device!
// Here you may want to retain an instance to your device:
right_insole_device_interface = connectedDevice.toSimpleDeviceInterface();
// Listen to bluetooth events
right_insole_device_interface.setListeners(message -> onMessageReceived(message), this::onMessageSent, this::onError);
right_insole_device_interface.stopInsole();
is_R_insole_connected = true;
Toast.makeText(getApplication(), "Connected to Right Insole.", Toast.LENGTH_SHORT).show();
}
private void onMessageSent (String message){
// We sent a message! Handle it here.
}
#RequiresApi(api = Build.VERSION_CODES.N)
private void onMessageReceived (String message){
//store incoming bytes temporarily
if(!is_R_insole_started){
right_temp_bytes+=message+" ";
}
//check whether the start_bytes exits in the temporary buffer
if(!is_R_insole_started && right_temp_bytes.contains(start_bytes)){
is_R_insole_started = true;
right_temp_bytes ="";
}
//if the start_bytes are found in the temporary buffer, start storing the incoming messages in the actual buffer
if(is_R_insole_started){
right_data_len++;
if(right_data_len>15) {
right_sensor_data_count++;
if (!non_sensor_indeces.contains(right_sensor_data_count)) {
r_data_double_arr[right_data_index] = Double.parseDouble(message);
// System.out.println("NON SENSOR INDEX:" + right_data_index + " "+ message);
right_data_index++;
}
}
Date date = new Date();
rightDataDict.put(String.valueOf(formatter.format(date)),Arrays.toString(r_data_double_arr));
// if the data length reach the max_data_length, release the buffer and invert the start flag
if(right_data_len>=max_data_len+15){
heatMapRight.clearData();
for(int i=0; i<x_R.length; i++) {
HeatMap.DataPoint point = new HeatMap.DataPoint(x_R[i], y[i], r_data_double_arr[i]);
heatMapRight.addData(point);
heatMapRight.forceRefresh();
}
right_data_index= 0;
right_sensor_data_count = 0;
right_data_len=0;
is_R_insole_started=false;
}
}
}
private void onError(Throwable error) {
// Handle the error
}
});
startLeftBtn:
startLeftBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (is_L_insole_connected) {
if (is_L_insole_started) {
left_insole_device_interface.stopInsole();
is_L_insole_started = false;
startLeftBtn.setText("Start Left");
left_timer.cancel();
} else {
left_insole_device_interface.startInsole();
is_L_insole_started = true;
startLeftBtn.setText("Stop Left");
left_timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#RequiresApi(api = Build.VERSION_CODES.N)
public void run() {
// Log.d("TAG", "Hashmap: "+ leftDataDict);
sendToFirebase(leftDataDict, "Left_Insole");
}
});
}
},1000, 1000);
Toast.makeText(Visualization.this, "Left Insole Started.", Toast.LENGTH_SHORT).show();
}
}else
{
Toast.makeText(Visualization.this, "Left Insole Not Connected!", Toast.LENGTH_SHORT).show();
}
}
});
startRightBtn:
startRightBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(is_R_insole_connected) {
if (is_R_insole_started) {
right_insole_device_interface.stopInsole();
is_R_insole_started = false;
startRightBtn.setText("Start Right");
right_timer.cancel();
} else {
right_insole_device_interface.startInsole();
is_R_insole_started = true;
startRightBtn.setText("Stop Right");
right_timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
sendToFirebase(rightDataDict, "Right_Insole");
}
});
}
},1000, 1000);
Toast.makeText(Visualization.this, "Right Insole Started.", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(Visualization.this, "Right Insole Not Connected!", Toast.LENGTH_SHORT).show();
}
}
});
SendToFirebase:
public void sendToFirebase(Map<String,Object> data, String of_insole){
Date date = new Date();
db.collection("Experiments").document(PATIENT_NAME).set(patientRecord);
db.collection("Experiments")
.document(PATIENT_NAME)
.collection(of_insole)
.document(String.valueOf(formatter2.format(date)))
.set(data)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void unused) {
data.clear();
Toast.makeText(Visualization.this, "Succesfully saved to Firebase", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(Visualization.this, "Failed to saved to Firebase", Toast.LENGTH_SHORT).show();
}
});
}
Related
I write in Java music player for Android. It have a special function to trim a fragment of audio with range seekbar. After replay button is turned ON this fragment is looped. It works and selected fragment with range seekbar is played but not always as it would be necessary. Sometimes it gets out of max selected duration and goes to end of song.I can't find the place why this is happening...
Preview
Many thanks for any help and suggestions.
Here is the logic responsible for playing the song, that is activated when PLAY button is pressed:
playButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(mMediaPlayer != null){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();
playButton.setText("PLAY");
timer.shutdown();
}else{
mMediaPlayer.start();
playButton.setText("PAUSE");
timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
playProgress();
if (mMediaPlayer != null) {
if (!mSeekBar.isPressed()) {
mSeekBar.setProgress(mMediaPlayer.getCurrentPosition());
}
}
}
},10,10, TimeUnit.MILLISECONDS);
}
}
}
});
A function that regulates the playback of a fragment in rangeSeekbar
private void playProgress () {
if (mMediaPlayer.getCurrentPosition() == max) {
if(isRepeat == true) {
mMediaPlayer.seekTo(min);
}else{
mMediaPlayer.seekTo(min);
mMediaPlayer.pause();
}
}
if (mMediaPlayer.isPlaying()) {
mRunnable = new Runnable() {
#Override
public void run() {
playProgress();
}
};
mHandler.postDelayed(mRunnable, 0);
}
}
The logic responsible for "creating" the player that will form when the song is opened:
public void createMediaPlayer(Uri uri){
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
try {
mMediaPlayer.setDataSource(getApplicationContext(), uri);
mMediaPlayer.prepare();
title.setText(getNameFromUri(uri));
playButton.setEnabled(true);
mRangeSeekBar.setNotifyWhileDragging(true);
max = mMediaPlayer.getDuration();
mRangeSeekBar.setRangeValues(0, mMediaPlayer.getDuration());
mSeekBar.setMax(mMediaPlayer.getDuration());
long total_secs = TimeUnit.SECONDS.convert(max, TimeUnit.MILLISECONDS);
long mins = TimeUnit.MINUTES.convert(total_secs, TimeUnit.SECONDS);
long secs = total_secs - (mins*60);
duration = mins + ":" + secs;
elapse.setText("00:00 / " + duration);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
releaseMediaPlayer();
}
});
} catch (IOException e){
title.setText(e.toString());
}
}
Replay function activated by the REPLAY button
replayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//repeat = false
if(isRepeat){
isRepeat = false;
mMediaPlayer.setLooping(false);
replayButton.setText("Powtórka wyłączona");
Toast.makeText(PlayerActivity.this, "Tryb powtórki jest wyłączony", Toast.LENGTH_SHORT).show();
}else{
isRepeat = true;
mMediaPlayer.setLooping(true);
replayButton.setText("Powtórka włączona");
Toast.makeText(PlayerActivity.this, "Tryb powtórki jest włączony", Toast.LENGTH_SHORT).show();
}
//mediaPlayer.setLooping(true);
// Toast.makeText(PlayerActivity.this, "Repeat if ON", Toast.LENGTH_SHORT).show();
}
});
Here is code for this double slider that is supposed to set the beginning and end:
mRangeSeekBar.setOnRangeSeekBarChangeListener(new RangeSeekBar.OnRangeSeekBarChangeListener<Integer>() {
#Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, Integer minValue, Integer maxValue) {
//mRangeSeekBar.setNotifyWhileDragging(true);
mMediaPlayer.seekTo(minValue);
max = maxValue;
min = minValue;
String infoMax = String.valueOf(max);
Log.i("MAX", infoMax);
}
});
And here for this green bar with one point representing the duration of the song:
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (mMediaPlayer != null){
int millis = mMediaPlayer.getCurrentPosition();
long total_secs = TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS);
long mins = TimeUnit.MINUTES.convert(total_secs, TimeUnit.SECONDS);
long secs = total_secs - (mins*60);
elapse.setText(mins + ":" + secs + " / " + duration);
I've got a bug from google play console with the following stack trace
android.view.WindowManager$BadTokenException:
at android.view.ViewRootImpl.setView (ViewRootImpl.java:1078)
at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:381)
at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:93)
at android.app.Dialog.show (Dialog.java:470)
at android.app.AlertDialog$Builder.show (AlertDialog.java:1151)
at android.widget.VideoView$5.onError (VideoView.java:600)
at android.media.MediaPlayer$EventHandler.handleMessage (MediaPlayer.java:4417)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:214)
at android.app.ActivityThread.main (ActivityThread.java:7156)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:975)
I can't reproduce it locally and find what causes this problem. I found out that it could happen when application context is used in mediaController instead of activity, but I set it by getActivity(), so it seemed to be caused by something different. If there are any ideas of what could cause this crash and how to fix, I would be very grateful for your help!
#EFragment(resName = "dvd_player")
public class DvdPlayerFragment extends BaseFragment implements OnPreparedListener, OnErrorListener, OnCompletionListener {
private static final String TAG = "DvdPlayerFragment";
#FragmentArg
Dvd dvd;
#ViewById(resName = "root")
View rootView;
#ViewById
VideoView videoView;
#ViewById
ProgressBar progressIndicator;
private MediaController mediaController;
private Uri uri;
private int savedPosition;
private boolean paused;
private MediaPlayer mediaPlayer;
private boolean showedUserMessage = false;
private Timer watchStatsTimer;
private final String watchKey = UUID.randomUUID().toString();
private long accumulatedPlayTime = 0;
private long lastPushedAccumulatedPlayTime = 0;
private Long lastServerUpdate = null;
#Override
public void onActivityCreated(Bundle bundle) {
Log.d(TAG, "onActivityCreated: BEGIN");
initView();
Log.d(TAG, "onActivityCreated: END");
super.onActivityCreated(bundle);
}
private void play() {
Log.d(TAG, "play: ");
if (uri != null) {
int seekToPosition = videoView.getCurrentPosition() == 0 ? savedPosition : videoView.getCurrentPosition();
playVideo(seekToPosition, !paused);
} else {
showProgressIndicator(true);
getUrl();
}
showUserMessageIfNeeded();
}
#Background
void getUrl() {
final Url url = app.getDvdWatchUrl(dvd);
if (url != null) {
onVideoUrl(url.getLink());
} else {
showMessage("Could not load dvd.");
showProgressIndicator(false);
}
}
#UiThread
void onVideoUrl(final String url) {
uri = Uri.parse(url);
playVideo(0, true);
}
#UiThread
void playVideo(final int seekTo, final boolean start) {
Log.d(TAG, "playVideo: ");
initMediaController();
videoView.seekTo(seekTo);
if (start) {
videoView.start();
}
}
private void initMediaController() {
Log.d(TAG, "initMediaController: BEGIN");
if (isAdded()) {
Log.d(TAG, "initMediaController: isAdded");
if (mediaController == null || mediaController.getContext() != getActivity()) {
mediaController = new MediaController(getActivity());
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);
videoView.setOnErrorListener(this);
setVideoUri(uri);
}
}
}
private void setVideoUri(final Uri uri) {
try {
Log.d(TAG, "setVideoUri: ");
Method setVideoURIMethod = videoView.getClass().getMethod("setVideoURI", Uri.class, Map.class);
Map<String, String> params = new HashMap<>(1);
final String cred = app.getLoginUsername() + ":" + app.getLoginPassword();
final String auth = "Basic " + Base64.encodeBytes(cred.getBytes(StandardCharsets.UTF_8));
params.put("Authorization", auth);
setVideoURIMethod.invoke(videoView, uri, params);
} catch (Exception e) {
e.printStackTrace();
}
}
void initView() {
Log.d(TAG, "initView: BEGIN");
rootView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
Log.d(TAG, "onTouch: ");
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mediaController != null && getActivity() != null &&
mediaController.getContext() == getActivity()) {
if (!getActivity().isFinishing() && !mediaController.isAttachedToWindow()) {
try {
mediaController.show();
} catch (IllegalStateException | NullPointerException e) {
e.printStackTrace();
}
}
} else {
initMediaController();
}
}
return true;
}
});
setNavigationBarTitle(dvd.getLabel());
alignTitleToRight(true);
showBackButton(backTitle);
}
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(TAG, "onPrepared: ");
this.mediaPlayer = mp;
showProgressIndicator(false);
createWatchStatsTimer();
}
#Override
public boolean onError(MediaPlayer mp, int i, int j) {
Log.d(TAG, "onError: BEGIN");
showProgressIndicator(false);
destroyWatchStatsTimer();
updateServer();
Log.d(TAG, "onError: END");
return false;
}
#Override
public void onCompletion(MediaPlayer mp) {
}
private void createWatchStatsTimer() {
Log.d(TAG, "createWatchStatsTimer: ");
if (watchStatsTimer == null) {
DvdWatchStatsTask task = new DvdWatchStatsTask();
watchStatsTimer = new Timer();
watchStatsTimer.schedule(task, 1000, 1000);
}
}
private void destroyWatchStatsTimer() {
Log.d(TAG, "destroyWatchStatsTimer: ");
if (watchStatsTimer != null) {
watchStatsTimer.cancel();
watchStatsTimer.purge();
watchStatsTimer = null;
}
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause: ");
savedPosition = videoView.getCurrentPosition();
paused = !videoView.isPlaying();
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
play();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
destroyWatchStatsTimer();
if (accumulatedPlayTime > lastPushedAccumulatedPlayTime) {
updateServer();
}
}
#UiThread
#IgnoreWhen(IgnoreWhen.State.VIEW_DESTROYED)
void showProgressIndicator(final boolean show) {
if (show) {
progressIndicator.setVisibility(View.VISIBLE);
} else {
progressIndicator.setVisibility(View.GONE);
}
}
#UiThread
void showUserMessageIfNeeded() {
Log.d(TAG, "showUserMessageIfNeeded: ");
if (!showedUserMessage) {
showedUserMessage = true;
final String version = android.os.Build.VERSION.RELEASE;
if (version.equals("4.4.3") || version.equals("4.4.4")) {
final String message = "Video has known issues in Android " + version;
showMessage(message);
}
}
}
#UiThread
void showMessage(final String message) {
Log.d(TAG, "showMessage: ");
final Toast toast = Toast.makeText(getActivity(), message, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, -150);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "run: toast show in UI thread");
toast.show();
}
}, 2500);
}
private void updateServer() {
Log.d(TAG, "updateServer: ");
try {
Long percentage = 10000 * accumulatedPlayTime / mediaPlayer.getDuration();
app.updateDvdWatchStats(dvd, watchKey, accumulatedPlayTime / 1000, percentage);
Log.d("", "accumulatedPlayTime: " + accumulatedPlayTime + ", percentage: " + (float) percentage / 100);
} catch (Exception ex) {
Log.e(TAG, "updateServer: ", ex);
ex.printStackTrace();
}
lastPushedAccumulatedPlayTime = accumulatedPlayTime;
lastServerUpdate = new Date().getTime();
}
class DvdWatchStatsTask extends TimerTask {
#Override
public void run() {
if (mediaPlayer != null) {
try {
if (mediaPlayer.isPlaying()) {
// add a second to counter
accumulatedPlayTime += 1000;
Log.d(TAG, "run: accumulatedPlayTime " + accumulatedPlayTime);
}
} catch (Exception e) {
Log.e(TAG, "run: ", e);
e.printStackTrace();
}
if (shouldUpdateServer()) {
updateServer();
}
}
}
private boolean shouldUpdateServer() {
final long now = new Date().getTime();
Log.d(TAG, "shouldUpdateServer: " + now);
boolean playingAndShouldUpdate = false;
try {
playingAndShouldUpdate = mediaPlayer.isPlaying() && (lastServerUpdate == null || (now - lastServerUpdate > 29300));
} catch (Exception e) {
Log.e(TAG, "shouldUpdateServer: ", e);
e.printStackTrace();
}
// paused or completed
boolean notPlayingAndShouldUpdate = lastServerUpdate != null && (now - lastServerUpdate) > 29300 && accumulatedPlayTime > lastPushedAccumulatedPlayTime;
return playingAndShouldUpdate || notPlayingAndShouldUpdate;
}
}
}
When the audio player app is resumed while playing audio, the SeekBar resets to 0. During audio playback, the SeekBar updates progress. However, when the screen is resumed, the SeekBar starts from the beginning without indicating the player's current position. When you press the pause button and then press the play button, it plays at the current position again. In updateProgress() method, there is long currentPosition = mLastPlaybackState.getPosition(); I think this code doesn't indicate the current position when resumed.
I implemented SeekBar update progress based on the UAMP FullScreenActivity
This is my NowPlayingAcitivy.java:
private PlaybackStateCompat mLastPlaybackState;
private final Handler mHandler = new Handler();
private final Runnable mUpdateProgressTask = new Runnable() {
#Override
public void run() {
updateProgress();
}
};
private final ScheduledExecutorService mExecutorService =
Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> mScheduledFuture;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNowPlayingBinding = DataBindingUtil.setContentView(
this, R.layout.activity_now_playing);
createMediaBrowserCompat();
mNowPlayingBinding.playingInfo.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mNowPlayingBinding.playingInfo.tvStart.setText(DateUtils.formatElapsedTime(
progress/1000));
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Cancel the future returned by scheduleAtFixedRate() to stop the SeekBar from progressing
stopSeekbarUpdate();
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
MediaControllerCompat.getMediaController(NowPlayingActivity.this)
.getTransportControls().seekTo(seekBar.getProgress());
// Create and execute a periodic action to update the SeekBar progress
scheduleSeekbarUpdate();
}
});
}
private void createMediaBrowserCompat() {
mMediaBrowser = new MediaBrowserCompat(this,
new ComponentName(this, PodcastService.class),
mConnectionCallbacks,
null);
}
#Override
protected void onStart() {
super.onStart();
mMediaBrowser.connect();
}
#Override
protected void onResume() {
super.onResume();
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
#Override
protected void onStop() {
super.onStop();
if (MediaControllerCompat.getMediaController(this) != null) {
MediaControllerCompat.getMediaController(this).unregisterCallback(controllerCallback);
}
mMediaBrowser.disconnect();
}
#Override
protected void onDestroy() {
super.onDestroy();
stopSeekbarUpdate();
mExecutorService.shutdown();
}
private final MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
MediaSessionCompat.Token token = mMediaBrowser.getSessionToken();
MediaControllerCompat mediaController = null;
try {
mediaController = new MediaControllerCompat(NowPlayingActivity.this, token);
} catch (RemoteException e) {
Timber.e("Error creating media controller");
}
MediaControllerCompat.setMediaController(NowPlayingActivity.this,
mediaController);
buildTransportControls();
}
#Override
public void onConnectionSuspended() {
super.onConnectionSuspended();
}
#Override
public void onConnectionFailed() {
super.onConnectionFailed();
}
};
void buildTransportControls() {
mNowPlayingBinding.playingInfo.ibPlayPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PlaybackStateCompat pbState =
MediaControllerCompat.getMediaController(NowPlayingActivity.this).getPlaybackState();
if (pbState != null) {
MediaControllerCompat.TransportControls controls =
MediaControllerCompat.getMediaController(NowPlayingActivity.this).getTransportControls();
switch (pbState.getState()) {
case PlaybackStateCompat.STATE_PLAYING: // fall through
case PlaybackStateCompat.STATE_BUFFERING:
controls.pause();
stopSeekbarUpdate();
break;
case PlaybackStateCompat.STATE_PAUSED:
case PlaybackStateCompat.STATE_STOPPED:
controls.play();
scheduleSeekbarUpdate();
break;
default:
Timber.d("onClick with state " + pbState);
}
}
}
});
MediaControllerCompat mediaController =
MediaControllerCompat.getMediaController(NowPlayingActivity.this);
MediaMetadataCompat metadata = mediaController.getMetadata();
PlaybackStateCompat pbState = mediaController.getPlaybackState();
updatePlaybackState(pbState);
if (metadata != null) {
// Get the episode duration from the metadata and sets the end time to the textView
updateDuration(metadata);
}
// Set the current progress to the current position
updateProgress();
if (pbState != null && (pbState.getState() == PlaybackStateCompat.STATE_PLAYING ||
pbState.getState() == PlaybackStateCompat.STATE_BUFFERING)) {
scheduleSeekbarUpdate();
}
mediaController.registerCallback(controllerCallback);
}
MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() {
#Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
super.onMetadataChanged(metadata);
if (metadata != null) {
updateDuration(metadata);
}
}
#Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
super.onPlaybackStateChanged(state);
// Update the playback state
updatePlaybackState(state);
}
};
/**
* Creates and executes a periodic action that becomes enabled first after the given initial delay,
* and subsequently with the given period;that is executions will commence after initialDelay
* then initialDelay + period, then initialDelay + 2 * period, and so on.
*/
private void scheduleSeekbarUpdate() {
stopSeekbarUpdate();
if (!mExecutorService.isShutdown()) {
mScheduleFuture = mExecutorService.scheduleAtFixedRate(
new Runnable() {
#Override
public void run() {
mHandler.post(mUpdateProgressTask);
}
}, 100,
1000, TimeUnit.MILLISECONDS);
}
}
/**
* Cancels the future returned by scheduleAtFixedRate() to stop the SeekBar from progressing.
*/
private void stopSeekbarUpdate() {
if (mScheduledFuture != null) {
mScheduledFuture.cancel(false);
}
}
/**
* Gets the episode duration from the metadata and sets the end time to be displayed in the TextView.
*/
private void updateDuration(MediaMetadataCompat metadata) {
if (metadata == null) {
return;
}
int duration = (int) metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
* 1000;
mNowPlayingBinding.playingInfo.seekBar.setMax(duration);
mNowPlayingBinding.playingInfo.tvEnd.setText(
DateUtils.formatElapsedTime(duration / 1000));
}
/**
* Calculates the current position (distance = timeDelta * velocity) and sets the current progress
* to the current position.
*/
private void updateProgress() {
if (mLastPlaybackState == null) {
return;
}
long currentPosition = mLastPlaybackState.getPosition();
if (mLastPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING) {
// Calculate the elapsed time between the last position update and now and unless
// paused, we can assume (delta * speed) + current position is approximately the
// latest position. This ensure that we do not repeatedly call the getPlaybackState()
// on MediaControllerCompat.
long timeDelta = SystemClock.elapsedRealtime() -
mLastPlaybackState.getLastPositionUpdateTime();
currentPosition += (int) timeDelta * mLastPlaybackState.getPlaybackSpeed();
}
mNowPlayingBinding.playingInfo.seekBar.setProgress((int) currentPosition);
}
private void updatePlaybackState(PlaybackStateCompat state) {
if (state == null) {
return;
}
mLastPlaybackState = state;
switch (state.getState()) {
case PlaybackStateCompat.STATE_PLAYING:
hideLoading();
mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_pause);
scheduleSeekbarUpdate();
break;
case PlaybackStateCompat.STATE_PAUSED:
hideLoading();
mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
stopSeekbarUpdate();
break;
case PlaybackStateCompat.STATE_NONE:
case PlaybackStateCompat.STATE_STOPPED:
hideLoading();
mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
stopSeekbarUpdate();
break;
case PlaybackStateCompat.STATE_BUFFERING:
showLoading();
mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
stopSeekbarUpdate();
break;
default:
Timber.d("Unhandled state " + state.getState());
}
}
This is my PodcastService.java:
public class PodcastService extends MediaBrowserServiceCompat implements Player.EventListener {
#Override
public void onCreate() {
super.onCreate();
initializeMediaSession();
}
private void initializeMediaSession() {
mMediaSession = new MediaSessionCompat(PodcastService.this, TAG);
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_REWIND |
PlaybackStateCompat.ACTION_FAST_FORWARD |
PlaybackStateCompat.ACTION_PLAY_PAUSE);
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setCallback(new MySessionCallback());
setSessionToken(mMediaSession.getSessionToken());
mMediaSession.setSessionActivity(PendingIntent.getActivity(this,
11,
new Intent(this, NowPlayingActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT));
}
private void initializePlayer() {
if (mExoPlayer == null) {
DefaultRenderersFactory defaultRenderersFactory = new DefaultRenderersFactory(this);
TrackSelector trackSelector = new DefaultTrackSelector();
LoadControl loadControl = new DefaultLoadControl();
mExoPlayer = ExoPlayerFactory.newSimpleInstance(this, defaultRenderersFactory,
trackSelector, loadControl);
mExoPlayer.addListener(this);
// Prepare the MediaSource
Uri mediaUri = Uri.parse(mUrl);
MediaSource mediaSource = buildMediaSource(mediaUri);
mExoPlayer.prepare(mediaSource);
mExoPlayer.setPlayWhenReady(true);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || intent.getAction() == null) {
Timber.e("intent in onStartCommand is null");
return START_STICKY;
}
// Check if the old player should be released
if (intent.getAction() != null && intent.getAction().equals(ACTION_RELEASE_OLD_PLAYER)) {
if (mExoPlayer != null) {
mExoPlayer.stop();
releasePlayer();
}
}
Bundle b = intent.getBundleExtra(EXTRA_ITEM);
if (b != null) {
mItem = b.getParcelable(EXTRA_ITEM);
mUrl = mItem.getEnclosures().get(0).getUrl();
}
initializePlayer();
// Convert hh:mm:ss string to seconds to put it into the metadata
long duration = PodUtils.getDurationInMilliSeconds(mItem);
MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration).build();
mMediaSession.setMetadata(metadata);
return START_STICKY;
}
private void releasePlayer() {
mExoPlayer.release();
mExoPlayer = null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
if (mExoPlayer != null) {
mExoPlayer.stop(true);
}
stopSelf();
}
#Override
public void onDestroy() {
mMediaSession.release();
releasePlayer();
super.onDestroy();
}
private MediaSource buildMediaSource(Uri mediaUri) {
String userAgent = Util.getUserAgent(this, getString(R.string.app_name));
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(
this, userAgent);
CacheDataSourceFactory cacheDataSourceFactory =
new CacheDataSourceFactory(
DownloadUtil.getCache(this),
dataSourceFactory,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
return new ExtractorMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaUri);
}
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid,
#Nullable Bundle rootHints) {
return new BrowserRoot("pod_root_id", null);
}
#Override
public void onLoadChildren(#NonNull String parentMediaId,
#NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
// Browsing not allowed
if (TextUtils.equals("empty_root_id", parentMediaId)) {
result.sendResult(null);
return;
}
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
// Check if this is the root menu:
if ("pod_root_id".equals(parentMediaId)) {
// Build the MediaItem objects for the top level,
// and put them in the mediaItems list...
} else {
// Examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list...
}
result.sendResult(mediaItems);
}
private class MySessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
startService(new Intent(getApplicationContext(), PodcastService.class));
mMediaSession.setActive(true);
// Start the player
if (mExoPlayer != null) {
mExoPlayer.setPlayWhenReady(true);
}
}
#Override
public void onPause() {
mExoPlayer.setPlayWhenReady(false);
stopForeground(false);
}
#Override
public void onRewind() {
mExoPlayer.seekTo(Math.max(mExoPlayer.getCurrentPosition() - 10000, 0));
}
#Override
public void onFastForward() {
long duration = mExoPlayer.getDuration();
mExoPlayer.seekTo(Math.min(mExoPlayer.getCurrentPosition() + 30000, duration));
}
#Override
public void onStop() {
stopSelf();
mMediaSession.setActive(false);
mExoPlayer.stop();
stopForeground(true);
}
#Override
public void onSeekTo(long pos) {
super.onSeekTo(pos);
if (mExoPlayer != null) {
mExoPlayer.seekTo((int) pos);
}
}
}
// Player Event Listeners
#Override
public void onTimelineChanged(Timeline timeline, #Nullable Object manifest, int reason) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_IDLE) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
mExoPlayer.getCurrentPosition(), 1f);
} else if (playbackState == Player.STATE_BUFFERING) {
mStateBuilder.setState(PlaybackStateCompat.STATE_BUFFERING,
mExoPlayer.getCurrentPosition(), 1f);
} else if (playbackState == Player.STATE_READY && playWhenReady) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
mExoPlayer.getCurrentPosition(), 1f);
Timber.d("onPlayerStateChanged: we are playing");
} else if (playbackState == Player.STATE_READY) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
mExoPlayer.getCurrentPosition(), 1f);
Timber.d("onPlayerStateChanged: we are paused");
} else if (playbackState == Player.STATE_ENDED) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
mExoPlayer.getCurrentPosition(), 1f);
} else {
mStateBuilder.setState(PlaybackStateCompat.STATE_NONE,
mExoPlayer.getCurrentPosition(), 1f);
}
mMediaSession.setPlaybackState(mStateBuilder.build());
}
}
Edit: The full source code is available here.
To set the state progress based on value should use setProgress(value) method.
when paused the music save value from seekBar as an Integer, then when resume it get that value and put it as a parameter in setProgress() method.
when you pause music to save the value:
#Override
protected void onPause() {
super.onPause();
mSeekBarRate.setOnSeekBarChangeListener(
new SeekBar.OnSeekBarChangeListener() {
int progress = 0;
#Override
public void onProgressChanged(SeekBar mSeekBarRate, int progressValue, boolean fromUser) {
progress = progressValue;
}
#Override
public void onStartTrackingTouch(SeekBar mSeekBarRate) {
}
#Override
public void onStopTrackingTouch(SeekBar mSeekBarRate) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("value", progress);
}
});
}
when you resume music retrieve that value:
#Override
protected void onStart() {
super.onStart();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int value = prefs.getInt("value", 0);
mSeekBarRate.setProgress(value);
}
Hope it Helps you.
I'm making an app that plays videos in a loop. It is supposed to play forever as long as the device is on. I have a list of videos in a variable and I use index to loop through every video to play it. The app works fine but when it runs for many hours (more than 20hrs) at a certain point the videoView freezes on a single frame and it won't call onCompletion or onError listeners. I run this on devices with API 17 and API 19. They both get the same error.
Here is my code (two methods that do the play loop):
public void playNextVideo(){
if(playIndex < 0) { playIndex = 0; }
if(allPromos.size() > 0) {
try {
Promo p = allPromos.get(playIndex);
playVideo(p);
} catch (IndexOutOfBoundsException e) {
Log.w("EXCEPTION", e);
isPlaying = false;
} finally {
playIndex++;
if (playIndex >= allPromos.size()) {
playIndex = 0;
}
}
} else {
Log.d("PlayNextVideo", "No videos in the playlist");
}
}
public void playVideo(Promo promo) {
File videoFile = new File(promo.getLocal_link());
if (!videoFile.exists()) {
Log.d("VIDEOPLAYER", "Video " + playIndex + " Has been skipped. File not found");
playNextVideo();
} else {
if (videoView.isPlaying()) {
videoView.stopPlayback();
}
videoView.setVideoPath(promo.getLocal_link());
videoView.requestFocus();
videoView.start();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(false);
}
});
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
videoView.stopPlayback();
playNextVideo();
}
});
videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d("MEDIA PLAYER ERROR", what + ", " + extra);
videoView.stopPlayback();
try{
mp.release();
} catch (Exception e) {
Log.w("RELEASE EXCEPTION", e);
}
playNextVideo();
return true;
}
});
}
}
I am trying to find when and where CH34xAndroidDriver.isConnected() value becomes true.
I have tried to find out and display its value in a toast. can anybody explain it clearly.
public class UartLoopBackActivity extends Activity {
public static final String TAG = "com.wch.wchusbdriver";
private static final String ACTION_USB_PERMISSION = "com.wch.wchusbdriver.USB_PERMISSION";
/* thread to read the data */
public readThread handlerThread;
protected final Object ThreadLock = new Object();
/* declare UART interface variable */
public CH34xAndroidDriver uartInterface;
// byte timeout; // time out
public Context global_context;
public boolean isConfiged = false;
public boolean READ_ENABLE = false;
public SharedPreferences sharePrefSettings;
Drawable originalDrawable;
public String act_string;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* create editable text objects */
readText = (EditText) findViewById(R.id.ReadValues);
// writeText = (EditText) findViewById(R.id.WriteValues);
global_context = this;
configButton = (Button) findViewById(R.id.configButton);
originalDrawable = configButton.getBackground();
readBuffer = new char[512];
baudRate = 9600;
stopBit = 1;
dataBit = 8;
parity = 0;
flowControl = 0;
configButton.setOnClickListener(new OpenDeviceListener());
// writeButton.setOnClickListener(new OnClickedWriteButton());
// writeButton.setEnabled(false);
//
uartInterface = new CH34xAndroidDriver(
(UsbManager) getSystemService(Context.USB_SERVICE), this,
ACTION_USB_PERMISSION);
act_string = getIntent().getAction();
if (-1 != act_string.indexOf("android.intent.action.MAIN")) {
Log.d(TAG, "android.intent.action.MAIN");
} else if (-1 != act_string
.indexOf("android.hardware.usb.action.USB_DEVICE_ATTACHED")) {
Log.d(TAG, "android.hardware.usb.action.USB_DEVICE_ATTACHED");
}
if (!uartInterface.UsbFeatureSupported()) {
Toast.makeText(this, "No Support USB host API", Toast.LENGTH_SHORT)
.show();
readText.setText("No Support USB host API");
uartInterface = null;
Toast.makeText(global_context,
"148k" + ((Boolean) uartInterface.isConnected()),
Toast.LENGTH_SHORT).show();
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
if (READ_ENABLE == false) {
READ_ENABLE = true;
handlerThread = new readThread(handler);
handlerThread.start();
Toast.makeText(global_context,"155k" + ((Boolean) uartInterface.isConnected()),Toast.LENGTH_SHORT).show();
}
}
public class OpenDeviceListener implements View.OnClickListener {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
boolean flags;
Toast.makeText(global_context,"170" + ((Boolean) uartInterface.isConnected()),Toast.LENGTH_SHORT).show();
Log.d("onClick", "12");
if (false == isConfiged) {
Log.d("onClick", "58");
isConfiged = true;
Log.d("onClick", "98");
// writeButton.setEnabled(true);
if (uartInterface.isConnected()) {
Log.d("onClick", "100");
flags = uartInterface.UartInit();
if (!flags) {
Log.d(TAG, "Init Uart Error");
Toast.makeText(global_context, "Init Uart Error",
Toast.LENGTH_SHORT).show();
} else {
if (uartInterface.SetConfig(baudRate, dataBit, stopBit,
parity, flowControl)) {
Log.d(TAG, "Configed");
}
}
}
if (isConfiged == true) {
Toast.makeText(global_context,"193" + ((Boolean) uartInterface.isConnected()),Toast.LENGTH_SHORT).show();
Log.d("onClick", "200");
configButton.setEnabled(false);
}
}
}
}
public void onHomePressed() {
onBackPressed();
}
public void onBackPressed() {
super.onBackPressed();
}
protected void onResume() {
super.onResume();
if (2 == uartInterface.ResumeUsbList()) {
uartInterface.CloseDevice();
Log.d(TAG, "Enter onResume Error");
}
}
protected void onPause() {
super.onPause();
}
protected void onStop() {
if (READ_ENABLE == true) {
READ_ENABLE = false;
}
super.onStop();
}
protected void onDestroy() {
if (uartInterface != null) {
if (uartInterface.isConnected()) {
uartInterface.CloseDevice();
}
uartInterface = null;
}
super.onDestroy();
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (actualNumBytes != 0x00) {
readText.append(String.copyValueOf(readBuffer, 0,
actualNumBytes));
Toast.makeText(global_context,"269k" + ((Boolean) uartInterface.isConnected()),Toast.LENGTH_SHORT).show();
actualNumBytes = 0;
}
}
};
/* usb input data handler */
private class readThread extends Thread {
Handler mHandler;
/* constructor */
Handler mhandler;
readThread(Handler h) {
mhandler = h;
this.setPriority(Thread.MIN_PRIORITY);
}
public void run() {
while (READ_ENABLE) {
Message msg = mhandler.obtainMessage();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
// Log.d(TAG, "Thread");
synchronized (ThreadLock) {
if (uartInterface != null) {
actualNumBytes = uartInterface.ReadData(readBuffer, 64);
if (actualNumBytes > 0) {
mhandler.sendMessage(msg);
}
}
}
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.uart_loop_back, menu);
return true;
}
}
upto line 74 (Toast.makeText(global_context,"155k" + ((Boolean) uartInterface.isConnected()),Toast.LENGTH_SHORT).show();) i found it returns false but when the onClick() is called it return true. why if any body has answer pls check it.
Thanks
the method ResumeUsbList() enables usb connection and changes isConnected() to true. if ResumeUsbList() fails it returns 2
Check your Activity's onResume()