Implemented MediaProgressIndicator widget to allow for code sharing (#405)
* Refactored NowPlayingFragment to use the new MediaProgressIndicator
This commit is contained in:
parent
7ce2c014e4
commit
134ec550e8
|
@ -283,6 +283,19 @@ public class PlayerType {
|
|||
this.milliseconds = milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts second duration into a hour, minutes, seconds duration
|
||||
* @param seconds
|
||||
*/
|
||||
public PositionTime(int seconds) {
|
||||
this.hours = seconds / 3600;
|
||||
int remainder = seconds - (this.hours * 3600);
|
||||
this.minutes = remainder / 60;
|
||||
remainder = remainder - (this.minutes * 60);
|
||||
this.seconds = remainder;
|
||||
this.milliseconds = 0;
|
||||
}
|
||||
|
||||
public JsonNode toJsonNode() {
|
||||
final ObjectNode node = objectMapper.createObjectNode();
|
||||
node.put(HOURS, hours);
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.xbmc.kore.jsonrpc.type.PlayerType;
|
|||
import org.xbmc.kore.jsonrpc.type.VideoType;
|
||||
import org.xbmc.kore.ui.generic.GenericSelectDialog;
|
||||
import org.xbmc.kore.ui.sections.video.AllCastActivity;
|
||||
import org.xbmc.kore.ui.widgets.MediaProgressIndicator;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
import org.xbmc.kore.utils.Utils;
|
||||
|
@ -73,7 +74,8 @@ import butterknife.OnClick;
|
|||
public class NowPlayingFragment extends Fragment
|
||||
implements HostConnectionObserver.PlayerEventsObserver,
|
||||
HostConnectionObserver.ApplicationEventsObserver,
|
||||
GenericSelectDialog.GenericSelectDialogListener {
|
||||
GenericSelectDialog.GenericSelectDialogListener,
|
||||
MediaProgressIndicator.OnProgressChangeListener {
|
||||
private static final String TAG = LogUtils.makeLogTag(NowPlayingFragment.class);
|
||||
|
||||
/**
|
||||
|
@ -149,9 +151,7 @@ public class NowPlayingFragment extends Fragment
|
|||
|
||||
@InjectView(R.id.media_title) TextView mediaTitle;
|
||||
@InjectView(R.id.media_undertitle) TextView mediaUndertitle;
|
||||
@InjectView(R.id.media_duration) TextView mediaDuration;
|
||||
@InjectView(R.id.media_progress) TextView mediaProgress;
|
||||
@InjectView(R.id.seek_bar) SeekBar mediaSeekbar;
|
||||
@InjectView(R.id.progress_info) MediaProgressIndicator mediaProgressIndicator;
|
||||
|
||||
@InjectView(R.id.volume_bar) SeekBar volumeSeekBar;
|
||||
@InjectView(R.id.volume_text) TextView volumeTextView;
|
||||
|
@ -538,6 +538,23 @@ public class NowPlayingFragment extends Fragment
|
|||
nowPlayingListener.SwitchToRemotePanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(int progress) {
|
||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(progress);
|
||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
||||
seekAction.execute(hostManager.getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
||||
@Override
|
||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
LogUtils.LOGD(TAG, "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
||||
}
|
||||
}, callbackHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
@ -716,8 +733,15 @@ public class NowPlayingFragment extends Fragment
|
|||
mediaTitle.setText(title);
|
||||
mediaUndertitle.setText(underTitle);
|
||||
|
||||
setDurationInfo(getItemResult.type, getPropertiesResult.time, getPropertiesResult.totaltime, getPropertiesResult.speed);
|
||||
mediaSeekbar.setOnSeekBarChangeListener(seekbarChangeListener);
|
||||
mediaProgressIndicator.setOnProgressChangeListener(this);
|
||||
mediaProgressIndicator.setMaxProgress(getPropertiesResult.totaltime.ToSeconds());
|
||||
mediaProgressIndicator.setProgress(getPropertiesResult.time.ToSeconds());
|
||||
|
||||
int speed = getPropertiesResult.speed;
|
||||
//TODO: check if following is still necessary for PVR playback
|
||||
if (getItemResult.type.equals(ListType.ItemsAll.TYPE_CHANNEL))
|
||||
speed = 1;
|
||||
mediaProgressIndicator.setSpeed(speed);
|
||||
|
||||
if (!TextUtils.isEmpty(year) || !TextUtils.isEmpty(genreSeason)) {
|
||||
mediaYear.setVisibility(View.VISIBLE);
|
||||
|
@ -855,7 +879,8 @@ public class NowPlayingFragment extends Fragment
|
|||
*/
|
||||
private void stopNowPlayingInfo() {
|
||||
// Just stop the seek bar handler callbacks
|
||||
mediaSeekbar.removeCallbacks(seekBarUpdater);
|
||||
mediaProgressIndicator.setSpeed(0);
|
||||
|
||||
availableSubtitles = null;
|
||||
availableAudioStreams = null;
|
||||
currentSubtitleIndex = -1;
|
||||
|
@ -896,56 +921,6 @@ public class NowPlayingFragment extends Fragment
|
|||
mediaCurrentTime = 0; // s
|
||||
private static final int SEEK_BAR_UPDATE_INTERVAL = 1000; // ms
|
||||
|
||||
/**
|
||||
* Seek bar runnable updater. Runs once a second
|
||||
*/
|
||||
private Runnable seekBarUpdater = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ((mediaTotalTime == 0) || (mediaCurrentTime >= mediaTotalTime)) {
|
||||
mediaSeekbar.removeCallbacks(this);
|
||||
return;
|
||||
}
|
||||
|
||||
mediaCurrentTime += 1;
|
||||
mediaSeekbar.setProgress(mediaCurrentTime);
|
||||
|
||||
int hours = mediaCurrentTime / 3600;
|
||||
int minutes = (mediaCurrentTime % 3600) / 60;
|
||||
int seconds = (mediaCurrentTime % 3600) % 60;
|
||||
|
||||
mediaProgress.setText(UIUtils.formatTime(hours, minutes, seconds));
|
||||
mediaSeekbar.postDelayed(this, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the information about current media duration and sets seekbar
|
||||
* @param type What is playing
|
||||
* @param time Current time
|
||||
* @param totalTime Total time
|
||||
* @param speed Media speed
|
||||
*/
|
||||
private void setDurationInfo(String type, GlobalType.Time time, GlobalType.Time totalTime, int speed) {
|
||||
mediaTotalTime = totalTime.hours * 3600 +
|
||||
totalTime.minutes * 60 +
|
||||
totalTime.seconds;
|
||||
mediaSeekbar.setMax(mediaTotalTime);
|
||||
mediaDuration.setText(UIUtils.formatTime(totalTime));
|
||||
|
||||
mediaCurrentTime = time.hours * 3600 +
|
||||
time.minutes * 60 +
|
||||
time.seconds;
|
||||
mediaSeekbar.setProgress(mediaCurrentTime);
|
||||
mediaProgress.setText(UIUtils.formatTime(time));
|
||||
|
||||
// Only update when its playing
|
||||
mediaSeekbar.removeCallbacks(seekBarUpdater);
|
||||
if ((speed == 1) || (type.equals(ListType.ItemsAll.TYPE_CHANNEL))) {
|
||||
mediaSeekbar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets UI volume state
|
||||
* @param muted whether volume is muted
|
||||
|
@ -993,51 +968,4 @@ public class NowPlayingFragment extends Fragment
|
|||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Seekbar change listener. Sends seek commands to XBMC based on the seekbar position
|
||||
*/
|
||||
private SeekBar.OnSeekBarChangeListener seekbarChangeListener = new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
mediaCurrentTime = progress;
|
||||
int hours = mediaCurrentTime / 3600;
|
||||
int minutes = (mediaCurrentTime % 3600) / 60;
|
||||
int seconds = (mediaCurrentTime % 3600) % 60;
|
||||
|
||||
mediaProgress.setText(UIUtils.formatTime(hours, minutes, seconds));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// Stop the seekbar updating
|
||||
seekBar.removeCallbacks(seekBarUpdater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
int hours = mediaCurrentTime / 3600;
|
||||
int minutes = (mediaCurrentTime % 3600) / 60;
|
||||
int seconds = (mediaCurrentTime % 3600) % 60;
|
||||
|
||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(hours, minutes, seconds, 0);
|
||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
||||
seekAction.execute(hostManager.getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
||||
@Override
|
||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
LogUtils.LOGD(TAG, "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
||||
}
|
||||
}, callbackHandler);
|
||||
|
||||
// Reset the updating
|
||||
seekBar.postDelayed(seekBarUpdater, 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2017 Martijn Brekhof. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.xbmc.kore.ui.widgets;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
public class MediaProgressIndicator extends LinearLayout {
|
||||
|
||||
private SeekBar seekBar;
|
||||
private TextView durationTextView;
|
||||
private TextView progressTextView;
|
||||
private int speed = 0;
|
||||
private int maxProgress;
|
||||
private int progress;
|
||||
private static final int SEEK_BAR_UPDATE_INTERVAL = 1000; // ms
|
||||
private int progressIncrement;
|
||||
|
||||
private OnProgressChangeListener onProgressChangeListener;
|
||||
|
||||
public interface OnProgressChangeListener {
|
||||
void onProgressChanged(int progress);
|
||||
}
|
||||
|
||||
public MediaProgressIndicator(Context context) {
|
||||
super(context);
|
||||
initializeView(context);
|
||||
}
|
||||
|
||||
public MediaProgressIndicator(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
initializeView(context);
|
||||
}
|
||||
|
||||
private void initializeView(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.media_progress_indicator, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
seekBar = (SeekBar) findViewById(R.id.mpi_seek_bar);
|
||||
progressTextView = (TextView) findViewById(R.id.mpi_progress);
|
||||
durationTextView = (TextView) findViewById(R.id.mpi_duration);
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
MediaProgressIndicator.this.progress = progress;
|
||||
|
||||
progressTextView.setText(UIUtils.formatTime(MediaProgressIndicator.this.progress));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// Stop the seekbar from updating
|
||||
seekBar.removeCallbacks(seekBarUpdater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
if (onProgressChangeListener != null)
|
||||
onProgressChangeListener.onProgressChanged(seekBar.getProgress());
|
||||
|
||||
if (speed > 0)
|
||||
seekBar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Runnable seekBarUpdater = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ((maxProgress == 0) || (progress >= maxProgress)) {
|
||||
seekBar.removeCallbacks(this);
|
||||
return;
|
||||
}
|
||||
|
||||
progress += progressIncrement;
|
||||
setProgress(progress);
|
||||
|
||||
seekBar.postDelayed(this, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
|
||||
this.onProgressChangeListener = onProgressChangeListener;
|
||||
}
|
||||
|
||||
public void setProgress(int progress) {
|
||||
this.progress = progress;
|
||||
seekBar.setProgress(progress);
|
||||
progressTextView.setText(UIUtils.formatTime(progress));
|
||||
}
|
||||
|
||||
public void setMaxProgress(int max) {
|
||||
maxProgress = max;
|
||||
seekBar.setMax(max);
|
||||
durationTextView.setText(UIUtils.formatTime(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the play speed for the progress indicator
|
||||
* @param speed set to 0 to stop updating the progress indicator.
|
||||
*/
|
||||
public void setSpeed(int speed) {
|
||||
if( speed == this.speed )
|
||||
return;
|
||||
|
||||
this.speed = speed;
|
||||
this.progressIncrement = speed * (SEEK_BAR_UPDATE_INTERVAL/1000);
|
||||
|
||||
seekBar.removeCallbacks(seekBarUpdater);
|
||||
if (speed > 0)
|
||||
seekBar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@
|
|||
android:scaleType="centerCrop"/>
|
||||
|
||||
<!-- Progress bar -->
|
||||
<LinearLayout
|
||||
<org.xbmc.kore.ui.widgets.MediaProgressIndicator
|
||||
android:id="@+id/progress_info"
|
||||
android:layout_below="@id/media_undertitle"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -90,26 +90,7 @@
|
|||
android:paddingRight="@dimen/small_padding"
|
||||
android:paddingLeft="@dimen/small_padding"
|
||||
android:orientation="horizontal"
|
||||
android:background="?attr/contentBackgroundColor">
|
||||
<TextView
|
||||
android:id="@+id/media_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
style="@style/TextAppearance.Media.Progress"/>
|
||||
<SeekBar
|
||||
android:id="@+id/seek_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"/>
|
||||
<TextView
|
||||
android:id="@+id/media_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
style="@style/TextAppearance.Media.Progress"/>
|
||||
</LinearLayout>
|
||||
android:background="?attr/contentBackgroundColor"/>
|
||||
|
||||
<!-- Media control buttons -->
|
||||
<LinearLayout
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Martijn Brekhof. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView
|
||||
android:id="@+id/mpi_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/zeroprogress"/>
|
||||
<SeekBar
|
||||
android:id="@+id/mpi_seek_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"/>
|
||||
<TextView
|
||||
android:id="@+id/mpi_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"/>
|
||||
</merge>
|
|
@ -416,5 +416,6 @@
|
|||
<string name="Refreshing_not_implemented_for_this_item">Refreshing not implemented for this item</string>
|
||||
<string name="no_songs_found_refresh">No songs found\\n\\nSwipe down to refresh</string>
|
||||
<string name="not_implemented">Not implemented</string>
|
||||
<string name="zeroprogress">0:00</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue