diff --git a/app/src/main/java/org/xbmc/kore/jsonrpc/type/PlayerType.java b/app/src/main/java/org/xbmc/kore/jsonrpc/type/PlayerType.java index 34438cc..6d6796b 100644 --- a/app/src/main/java/org/xbmc/kore/jsonrpc/type/PlayerType.java +++ b/app/src/main/java/org/xbmc/kore/jsonrpc/type/PlayerType.java @@ -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); diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/remote/NowPlayingFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/remote/NowPlayingFragment.java index b83ebd7..275010d 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/remote/NowPlayingFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/remote/NowPlayingFragment.java @@ -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() { + @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() { - @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); - } - }; } diff --git a/app/src/main/java/org/xbmc/kore/ui/widgets/MediaProgressIndicator.java b/app/src/main/java/org/xbmc/kore/ui/widgets/MediaProgressIndicator.java new file mode 100644 index 0000000..0cc5dbc --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/widgets/MediaProgressIndicator.java @@ -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); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_now_playing.xml b/app/src/main/res/layout/fragment_now_playing.xml index e2bbacc..7586ce3 100644 --- a/app/src/main/res/layout/fragment_now_playing.xml +++ b/app/src/main/res/layout/fragment_now_playing.xml @@ -81,7 +81,7 @@ android:scaleType="centerCrop"/> - - - - - + android:background="?attr/contentBackgroundColor"/> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ca5beb1..23ced5c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -416,5 +416,6 @@ Refreshing not implemented for this item No songs found\\n\\nSwipe down to refresh Not implemented + 0:00