Implemented unit/integration tests for the now playing panel (#420)
* Fixed issue with opening drawer and Espresso not waiting for when drawer was actually opened. * Implemented new handlers for MockTcpServer to test media control functions. * Decreased delay for sending reponses in MockTcpServer from 1 sec to 100 ms. This was needed to prevent race conditions with the progress bar that increases automatically once per second.
This commit is contained in:
parent
02a1d445f3
commit
dbd08c07aa
|
@ -27,8 +27,11 @@ import android.view.View;
|
|||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.testhelpers.action.ViewActions;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onData;
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
|
@ -43,6 +46,7 @@ import static android.support.test.espresso.assertion.ViewAssertions.doesNotExis
|
|||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||
|
@ -60,8 +64,8 @@ public class EspressoTestUtils {
|
|||
= activity.getResources().getConfiguration().orientation;
|
||||
activity.setRequestedOrientation(
|
||||
(orientation == Configuration.ORIENTATION_PORTRAIT) ?
|
||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
|
||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
|
||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,11 +145,11 @@ public class EspressoTestUtils {
|
|||
/**
|
||||
* Performs a click on an item in an adapter view, such as GridView or ListView
|
||||
* @param position
|
||||
* @param resourceId
|
||||
* @param resourceId of adapter view holding the item that should be clicked
|
||||
*/
|
||||
public static void clickAdapterViewItem(int position, int resourceId) {
|
||||
onData(anything()).inAdapterView(allOf(withId(resourceId), isDisplayed()))
|
||||
.atPosition(position).perform(click());
|
||||
.atPosition(position).perform(click());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +157,7 @@ public class EspressoTestUtils {
|
|||
* @param query text that SearchView should contain
|
||||
*/
|
||||
public static void checkTextInSearchQuery(String query) {
|
||||
onView(isAssignableFrom(AutoCompleteTextView.class)).check(matches(withText(query)));
|
||||
onView(isAssignableFrom(AutoCompleteTextView.class)).check(matches(withText(query)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -280,4 +284,18 @@ public class EspressoTestUtils {
|
|||
onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.default_toolbar))))
|
||||
.check(matches(withText(actionbarTitle)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for 10 seconds till panel has given state.
|
||||
*
|
||||
* @param panelState desired state of panel
|
||||
*/
|
||||
public static void waitForPanelState(final SlidingUpPanelLayout.PanelState panelState) {
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.now_playing_panel, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((SlidingUpPanelLayout) v).getPanelState() == panelState;
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,16 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.xbmc.kore.ui.widgets.RepeatModeButton;
|
||||
import org.xbmc.kore.ui.widgets.HighlightButton;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
public class Matchers {
|
||||
public static MenuItemTitleMatcher withMenuTitle(String title) {
|
||||
|
@ -128,4 +133,88 @@ public class Matchers {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withProgress(final int progress) {
|
||||
return new BoundedMatcher<View, SeekBar>(SeekBar.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(SeekBar item) {
|
||||
return item.getProgress() == progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected: " + progress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withProgress(final String progress) {
|
||||
return new BoundedMatcher<View, TextView>(TextView.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(TextView item) {
|
||||
return progress.contentEquals(item.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected: " + progress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withProgressGreaterThanOrEqual(final String time) {
|
||||
return new BoundedMatcher<View, SeekBar>(SeekBar.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(SeekBar item) {
|
||||
return item.getProgress() >= UIUtils.timeToSeconds(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected progress greater than " + time);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withProgressGreaterThan(final int progress) {
|
||||
return new BoundedMatcher<View, SeekBar>(SeekBar.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(SeekBar item) {
|
||||
return item.getProgress() > progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected progress greater than " + progress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withHighlightState(final boolean highlight) {
|
||||
return new BoundedMatcher<View, HighlightButton>(HighlightButton.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(HighlightButton item) {
|
||||
return item.isHighlighted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected: " + highlight);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withRepeatMode(final RepeatModeButton.MODE mode) {
|
||||
return new BoundedMatcher<View, RepeatModeButton>(RepeatModeButton.class) {
|
||||
@Override
|
||||
protected boolean matchesSafely(RepeatModeButton item) {
|
||||
return item.getMode() == mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("expected: " + mode.name());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@ package org.xbmc.kore.testhelpers;
|
|||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
|
||||
import org.junit.internal.runners.statements.RunAfters;
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
|
@ -63,6 +66,11 @@ public class Utils {
|
|||
drawerLayout.openDrawer(Gravity.LEFT);
|
||||
}
|
||||
});
|
||||
DrawerLayout drawerLayout = (DrawerLayout) activityTestRule.getActivity().findViewById(R.id.drawer_layout);
|
||||
while(true) {
|
||||
if (drawerLayout.isDrawerOpen(Gravity.LEFT))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize(ActivityTestRule<?> activityTestRule, HostInfo info) throws Throwable {
|
||||
|
|
|
@ -17,10 +17,27 @@
|
|||
package org.xbmc.kore.testhelpers.action;
|
||||
|
||||
|
||||
import android.support.test.espresso.PerformException;
|
||||
import android.support.test.espresso.UiController;
|
||||
import android.support.test.espresso.ViewAction;
|
||||
import android.support.test.espresso.action.MotionEvents;
|
||||
import android.support.test.espresso.action.Press;
|
||||
import android.support.test.espresso.util.HumanReadables;
|
||||
import android.support.test.espresso.util.TreeIterables;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
|
||||
public final class ViewActions {
|
||||
|
||||
/**
|
||||
* Returns an action that clears the focus on the view.
|
||||
* <br/>
|
||||
|
@ -33,4 +50,97 @@ public final class ViewActions {
|
|||
return actionWithAssertions(new ClearFocus());
|
||||
}
|
||||
|
||||
public interface CheckStatus {
|
||||
boolean check(View v);
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewAction that waits until view with viewId becomes visible
|
||||
* @param viewId Resource identifier of view item that must be checked
|
||||
* @param checkStatus called when viewId has been found to check its status. If return value
|
||||
* is true waitForView will stop, false it will continue until timeout is exceeded
|
||||
* @param millis amount of time to wait for view to become visible
|
||||
* @return
|
||||
*/
|
||||
public static ViewAction waitForView(final int viewId, final CheckStatus checkStatus, final long millis) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Searches for view with id: " + viewId + " and tests its status using CheckStatus, using timeout " + millis + " ms.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, View view) {
|
||||
uiController.loopMainThreadUntilIdle();
|
||||
final long endTime = System.currentTimeMillis() + millis;
|
||||
do {
|
||||
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
|
||||
if (child.getId() == viewId) {
|
||||
if (checkStatus.check(child)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiController.loopMainThreadForAtLeast(50);
|
||||
} while (System.currentTimeMillis() < endTime);
|
||||
|
||||
throw new PerformException.Builder()
|
||||
.withActionDescription(this.getDescription())
|
||||
.withViewDescription(HumanReadables.describe(view))
|
||||
.withCause(new TimeoutException())
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction slideSeekBar(final int progress) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return new TypeSafeMatcher<View>() {
|
||||
@Override
|
||||
protected boolean matchesSafely(View item) {
|
||||
return item instanceof SeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("is a SeekBar.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Slides seekbar to progress position " + progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, View view) {
|
||||
SeekBar seekBar = (SeekBar) view;
|
||||
|
||||
int[] seekBarPos = {0,0};
|
||||
view.getLocationOnScreen(seekBarPos);
|
||||
float[] startPos = {seekBarPos[0], seekBarPos[1]};
|
||||
|
||||
MotionEvents.DownResultHolder downResultHolder =
|
||||
MotionEvents.sendDown(uiController, startPos,
|
||||
Press.PINPOINT.describePrecision());
|
||||
|
||||
while(seekBar.getProgress() < progress) {
|
||||
startPos[0]++;
|
||||
MotionEvents.sendMovement(uiController, downResultHolder.down, startPos);
|
||||
uiController.loopMainThreadForAtLeast(10);
|
||||
}
|
||||
|
||||
MotionEvents.sendUp(uiController, downResultHolder.down, startPos);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
package org.xbmc.kore.tests.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.test.espresso.Espresso;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -35,7 +37,10 @@ import org.xbmc.kore.testhelpers.Utils;
|
|||
import org.xbmc.kore.testutils.Database;
|
||||
import org.xbmc.kore.testutils.tcpserver.MockTcpServer;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.AddonsHandler;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.ApplicationHandler;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.JSONConnectionHandlerManager;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.JSONRPCHandler;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.PlayerHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -50,10 +55,17 @@ abstract public class AbstractTestClass<T extends AppCompatActivity> {
|
|||
private static MockTcpServer server;
|
||||
private static JSONConnectionHandlerManager manager;
|
||||
private AddonsHandler addonsHandler;
|
||||
private static PlayerHandler playerHandler;
|
||||
private static ApplicationHandler applicationHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupMockTCPServer() throws Throwable {
|
||||
playerHandler = new PlayerHandler();
|
||||
applicationHandler = new ApplicationHandler();
|
||||
manager = new JSONConnectionHandlerManager();
|
||||
manager.addHandler(playerHandler);
|
||||
manager.addHandler(applicationHandler);
|
||||
manager.addHandler(new JSONRPCHandler());
|
||||
server = new MockTcpServer(manager);
|
||||
server.start();
|
||||
}
|
||||
|
@ -79,6 +91,12 @@ abstract public class AbstractTestClass<T extends AppCompatActivity> {
|
|||
loaderIdlingResource = new LoaderIdlingResource(activityTestRule.getActivity().getSupportLoaderManager());
|
||||
Espresso.registerIdlingResources(loaderIdlingResource);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activityTestRule.getActivity());
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
|
||||
//Relaunch the activity for the changes (Host selections, preference reset) to take effect
|
||||
activityTestRule.launchActivity(new Intent());
|
||||
|
||||
Utils.closeDrawer(activityTestRule);
|
||||
|
@ -89,6 +107,9 @@ abstract public class AbstractTestClass<T extends AppCompatActivity> {
|
|||
if ( loaderIdlingResource != null )
|
||||
Espresso.unregisterIdlingResources(loaderIdlingResource);
|
||||
|
||||
applicationHandler.reset();
|
||||
playerHandler.reset();
|
||||
|
||||
Utils.cleanup();
|
||||
}
|
||||
|
||||
|
@ -103,4 +124,12 @@ abstract public class AbstractTestClass<T extends AppCompatActivity> {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PlayerHandler getPlayerHandler() {
|
||||
return playerHandler;
|
||||
}
|
||||
|
||||
public static ApplicationHandler getApplicationHandler() {
|
||||
return applicationHandler;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,18 @@
|
|||
package org.xbmc.kore.tests.ui;
|
||||
|
||||
import android.support.test.espresso.Espresso;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.testhelpers.EspressoTestUtils;
|
||||
import org.xbmc.kore.testhelpers.Utils;
|
||||
import org.xbmc.kore.ui.BaseMediaActivity;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.xbmc.kore.testhelpers.EspressoTestUtils.clickAdapterViewItem;
|
||||
import static org.xbmc.kore.testhelpers.EspressoTestUtils.rotateDevice;
|
||||
|
||||
/**
|
||||
* Contains generic tests for all activities extending BaseMediaActivity
|
||||
|
@ -53,7 +54,7 @@ abstract public class BaseMediaActivityTests<T extends BaseMediaActivity> extend
|
|||
*/
|
||||
@Test
|
||||
public void showArrowWhenSelectingListItem() {
|
||||
EspressoTestUtils.clickAdapterViewItem(0, R.id.list);
|
||||
clickAdapterViewItem(0, R.id.list);
|
||||
|
||||
assertTrue(((T) EspressoTestUtils.getActivity()).getDrawerIndicatorIsArrow());
|
||||
}
|
||||
|
@ -68,7 +69,7 @@ abstract public class BaseMediaActivityTests<T extends BaseMediaActivity> extend
|
|||
*/
|
||||
@Test
|
||||
public void showHamburgerWhenSelectingListItemAndReturn() {
|
||||
EspressoTestUtils.clickAdapterViewItem(0, R.id.list);
|
||||
clickAdapterViewItem(0, R.id.list);
|
||||
Espresso.pressBack();
|
||||
|
||||
assertFalse(((T) EspressoTestUtils.getActivity()).getDrawerIndicatorIsArrow());
|
||||
|
@ -85,8 +86,8 @@ abstract public class BaseMediaActivityTests<T extends BaseMediaActivity> extend
|
|||
*/
|
||||
@Test
|
||||
public void restoreArrowOnConfigurationChange() {
|
||||
EspressoTestUtils.clickAdapterViewItem(0, R.id.list);
|
||||
EspressoTestUtils.rotateDevice(getActivity());
|
||||
clickAdapterViewItem(0, R.id.list);
|
||||
rotateDevice(getActivity());
|
||||
|
||||
assertTrue(((T) EspressoTestUtils.getActivity()).getDrawerIndicatorIsArrow());
|
||||
}
|
||||
|
@ -103,8 +104,8 @@ abstract public class BaseMediaActivityTests<T extends BaseMediaActivity> extend
|
|||
*/
|
||||
@Test
|
||||
public void restoreHamburgerOnConfigurationChangeOnReturn() {
|
||||
EspressoTestUtils.clickAdapterViewItem(0, R.id.list);
|
||||
EspressoTestUtils.rotateDevice(getActivity());
|
||||
clickAdapterViewItem(0, R.id.list);
|
||||
rotateDevice(getActivity());
|
||||
Espresso.pressBack();
|
||||
|
||||
assertFalse(((T) EspressoTestUtils.getActivity()).getDrawerIndicatorIsArrow());
|
||||
|
|
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* 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.tests.ui.music;
|
||||
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.Settings;
|
||||
import org.xbmc.kore.testhelpers.Utils;
|
||||
import org.xbmc.kore.testhelpers.action.ViewActions;
|
||||
import org.xbmc.kore.tests.ui.AbstractTestClass;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.PlayerHandler;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.Player;
|
||||
import org.xbmc.kore.ui.sections.audio.MusicActivity;
|
||||
import org.xbmc.kore.ui.widgets.HighlightButton;
|
||||
import org.xbmc.kore.ui.widgets.RepeatModeButton;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.xbmc.kore.testhelpers.EspressoTestUtils.clickAdapterViewItem;
|
||||
import static org.xbmc.kore.testhelpers.EspressoTestUtils.rotateDevice;
|
||||
import static org.xbmc.kore.testhelpers.EspressoTestUtils.waitForPanelState;
|
||||
import static org.xbmc.kore.testhelpers.Matchers.withHighlightState;
|
||||
import static org.xbmc.kore.testhelpers.Matchers.withProgress;
|
||||
|
||||
public class SlideUpPanelTests extends AbstractTestClass<MusicActivity> {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<MusicActivity> musicActivityActivityTestRule =
|
||||
new ActivityTestRule<>(MusicActivity.class);
|
||||
|
||||
@Override
|
||||
protected ActivityTestRule<MusicActivity> getActivityTestRule() {
|
||||
return musicActivityActivityTestRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Throwable {
|
||||
super.setUp();
|
||||
|
||||
getPlayerHandler().reset();
|
||||
getPlayerHandler().startPlay();
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel title is correctly set
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Result: panel title should show current playing media item
|
||||
*/
|
||||
@Test
|
||||
public void panelTitleTest() {
|
||||
Player.GetItem item = getPlayerHandler().getMediaItem();
|
||||
onView(withId(R.id.npp_title)).check(matches(withText(item.getTitle())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel buttons are correctly set for music items
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Result: panel should show next, play, and previous buttons
|
||||
*/
|
||||
@Test
|
||||
public void panelButtonsMusicTest() {
|
||||
onView(withId(R.id.npp_next)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.npp_previous)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel buttons are correctly set for movie items
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a movie item
|
||||
* 2. Result: panel should show play button
|
||||
*/
|
||||
@Test
|
||||
public void panelButtonsMoviesTest() {
|
||||
getPlayerHandler().setMediaType(PlayerHandler.TYPE.MOVIE);
|
||||
getPlayerHandler().startPlay();
|
||||
Player.GetItem item = getPlayerHandler().getMediaItem();
|
||||
final String title = item.getTitle();
|
||||
onView(isRoot()).perform(ViewActions.waitForView(
|
||||
R.id.npp_title, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return title.contentEquals(((TextView) v).getText());
|
||||
}
|
||||
}, 10000));
|
||||
|
||||
onView(withId(R.id.npp_next)).check(matches(not(isDisplayed())));
|
||||
onView(withId(R.id.npp_previous)).check(matches(not(isDisplayed())));
|
||||
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel buttons are correctly set for music video items
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music video item
|
||||
* 2. Result: panel should show next, play, and previous buttons
|
||||
*/
|
||||
@Test
|
||||
public void panelButtonsMusicVideoTest() {
|
||||
getPlayerHandler().setMediaType(PlayerHandler.TYPE.MUSICVIDEO);
|
||||
getPlayerHandler().startPlay();
|
||||
Player.GetItem item = getPlayerHandler().getMediaItem();
|
||||
final String title = item.getTitle();
|
||||
onView(isRoot()).perform(ViewActions.waitForView(
|
||||
R.id.npp_title, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return title.contentEquals(((TextView) v).getText());
|
||||
}
|
||||
}, 10000));
|
||||
|
||||
onView(withId(R.id.npp_next)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.npp_previous)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if shuffle button state is correctly set
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Click on shuffle button
|
||||
* 4. Result: shuffle button should be highlighted
|
||||
*/
|
||||
@Test
|
||||
public void panelButtonsShuffleTest() {
|
||||
expandPanel();
|
||||
|
||||
onView(withId(R.id.npp_shuffle)).perform(click());
|
||||
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_shuffle, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((HighlightButton) v).isHighlighted();
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if repeat button state is correctly set
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Click on repeat button
|
||||
* 4. Result: repeat button should be highlighted and show single item repeat mode
|
||||
* 5. Click on repeat button
|
||||
* 6. Result: repeat button should be highlighted and show repeat playlist mode
|
||||
* 7. Click on repeat button
|
||||
* 8. Result: repeat button should not be highlighted
|
||||
*/
|
||||
@Test
|
||||
public void panelButtonsRepeatModes() {
|
||||
expandPanel();
|
||||
|
||||
//Initial state should be OFF
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.OFF;
|
||||
}
|
||||
}, 10000));
|
||||
|
||||
// Test if repeat mode is set to ONE after first click
|
||||
onView(withId(R.id.npp_repeat)).perform(click());
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ONE;
|
||||
}
|
||||
}, 10000));
|
||||
|
||||
// Test if repeat mode is set to ALL after second click
|
||||
onView(withId(R.id.npp_repeat)).perform(click());
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ALL;
|
||||
}
|
||||
}, 10000));
|
||||
|
||||
|
||||
// Test if repeat mode is set to OFF after third click
|
||||
onView(withId(R.id.npp_repeat)).perform(click());
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.OFF;
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel collapsed state is restored on configuration changes
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Rotate device
|
||||
* 3. Result: panel state should be collapsed
|
||||
*/
|
||||
@Test
|
||||
public void keepCollapsedOnRotate() {
|
||||
rotateDevice(getActivity());
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel expanded state is restored on configuration changes
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Rotate device
|
||||
* 4. Result: panel state should be expanded
|
||||
*/
|
||||
@Test
|
||||
public void keepExpandedOnRotate() {
|
||||
expandPanel();
|
||||
|
||||
rotateDevice(getActivity());
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if repeat button state is restored on configuration changes
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Click on repeat button
|
||||
* 4. Rotate device
|
||||
* 5. Result: repeat button state should be restored to state in step 2
|
||||
*/
|
||||
@Test
|
||||
public void restoreRepeatButtonStateOnRotate() {
|
||||
expandPanel();
|
||||
onView(withId(R.id.npp_repeat)).perform(click());
|
||||
|
||||
rotateDevice(getActivity());
|
||||
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ONE;
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if shuffle button state is correctly set
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Click on shuffle button
|
||||
* 4. Result: shuffle button state should be set to shuffle
|
||||
*/
|
||||
@Test
|
||||
public void setShuffleButtonState() {
|
||||
expandPanel();
|
||||
|
||||
onView(withId(R.id.npp_shuffle)).perform(click()); //Set state to shuffled
|
||||
|
||||
onView(withId(R.id.npp_shuffle)).check(matches(withHighlightState(true)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if shuffle button state is restored on configuration changes
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Click on shuffle button
|
||||
* 4. Rotate device
|
||||
* 5. Result: shuffle button state should be restored to state in step 2
|
||||
*/
|
||||
@Test
|
||||
public void restoreShuffleButtonStateOnRotate() {
|
||||
expandPanel();
|
||||
onView(withId(R.id.npp_shuffle)).perform(click()); //Set state to shuffled
|
||||
|
||||
rotateDevice(getActivityTestRule().getActivity());
|
||||
|
||||
//Using waitForView as we need to wait for the rotate to finish
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_shuffle, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((HighlightButton) v).isHighlighted();
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if volume is correctly set at start
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Set volume at server
|
||||
* 3. Expand panel
|
||||
* 4. Result: Volume indicator should show the same volume level as set at the server
|
||||
*/
|
||||
@Test
|
||||
public void setVolume() {
|
||||
final int volume = 16;
|
||||
|
||||
getApplicationHandler().setVolume(volume, true);
|
||||
|
||||
assertTrue(getApplicationHandler().getVolume() == volume);
|
||||
expandPanel();
|
||||
onView(withId(R.id.vli_seek_bar)).check(matches(withProgress(volume)));
|
||||
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if changing volume through the volume slider, updates the volume indicator correctly
|
||||
* and sends the volume change to the server
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Set volume using slider
|
||||
* 4. Result: Volume indicator should show volume level and server should be set to new volume level
|
||||
*/
|
||||
@Test
|
||||
public void changeVolume() {
|
||||
final int volume = 16;
|
||||
expandPanel();
|
||||
|
||||
onView(withId(R.id.vli_seek_bar)).perform(ViewActions.slideSeekBar(volume));
|
||||
|
||||
onView(withId(R.id.vli_seek_bar)).check(matches(withProgress(volume)));
|
||||
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
|
||||
assertTrue(getApplicationHandler().getVolume() == volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if changing volume through the volume slider, updates the volume indicator correctly
|
||||
* and sends the volume change to the server
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Set volume using slider
|
||||
* 4. Result: Volume indicator should show volume level and server should be set to new volume level
|
||||
*/
|
||||
@Test
|
||||
public void restoreVolumeIndicatorOnRotate() {
|
||||
final int volume = 16;
|
||||
expandPanel();
|
||||
onView(withId(R.id.vli_seek_bar)).perform(ViewActions.slideSeekBar(volume));
|
||||
|
||||
rotateDevice(getActivity());
|
||||
|
||||
onView(withId(R.id.vli_seek_bar)).check(matches(withProgress(volume)));
|
||||
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
|
||||
assertTrue(getApplicationHandler().getVolume() == volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if setting progression correctly updates the media progress indicator
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Pause playback
|
||||
* 3. Expand panel
|
||||
* 4. Set progression
|
||||
* 5. Result: Media progression indicator should be correctly updated and progression change
|
||||
* should be sent to the server.
|
||||
*/
|
||||
@Test
|
||||
public void setProgression() {
|
||||
final int progress = 16;
|
||||
final String progressText = "0:16";
|
||||
expandPanel();
|
||||
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
|
||||
|
||||
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
|
||||
|
||||
onView(withId(R.id.mpi_progress)).check(matches(withText(progressText)));
|
||||
assertTrue(getPlayerHandler().getPosition() == progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if progression is correctly restored after device configuration change
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Pause playback
|
||||
* 3. Expand panel
|
||||
* 4. Set progression
|
||||
* 5. Rotate device
|
||||
* 6. Result: Progression should be correctly same as before rotating the device.
|
||||
*/
|
||||
@Test
|
||||
public void restoreProgressOnRotate() {
|
||||
final int progress = 16;
|
||||
final String progressText = "0:16";
|
||||
expandPanel();
|
||||
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
|
||||
|
||||
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
|
||||
rotateDevice(getActivity());
|
||||
|
||||
assertTrue(getPlayerHandler().getPosition() == progress);
|
||||
onView(withId(R.id.mpi_progress)).check(matches(withProgress(progressText)));
|
||||
onView(withId(R.id.mpi_seek_bar)).check(matches(withProgress(progress)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Kodi resumes playback when progression changes.
|
||||
* Test if changing progression when player is paused caused
|
||||
* progression to start updating again
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Pause playback
|
||||
* 4. Set progression
|
||||
* 5. Start playback at server (that's what Kodi does)
|
||||
* 6. Result: Playback should start at paused position
|
||||
*/
|
||||
@Test
|
||||
public void pauseSetProgressionPlay() {
|
||||
expandPanel();
|
||||
|
||||
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
|
||||
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(16));
|
||||
getPlayerHandler().startPlay();
|
||||
|
||||
SeekBar seekBar = (SeekBar) getActivity().findViewById(R.id.mpi_seek_bar);
|
||||
int progress = seekBar.getProgress();
|
||||
SystemClock.sleep(1000); //wait one second to check if progression has indeed progressed
|
||||
assertTrue(seekBar.getProgress() > progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel's progressionbar progresses when playing media
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Result: Progression should be progressing
|
||||
*/
|
||||
@Test
|
||||
public void progressionUpdaterStartedAfterPlay() {
|
||||
expandPanel();
|
||||
SeekBar seekBar = (SeekBar) getActivity().findViewById(R.id.mpi_seek_bar);
|
||||
int progress = seekBar.getProgress();
|
||||
|
||||
SystemClock.sleep(1000); //wait one second to check if progression has indeed progressed
|
||||
|
||||
assertTrue(seekBar.getProgress() > progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel's progression is maintained when starting a new activity
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Expand panel
|
||||
* 3. Set progression
|
||||
* 4. Switch to movies (new activity)
|
||||
* 5. Result: Progression should continue from step 3
|
||||
*/
|
||||
@Test
|
||||
public void continueProgressionAfterSwitchingActivity() throws Throwable {
|
||||
final int progress = 24;
|
||||
expandPanel();
|
||||
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
|
||||
|
||||
Utils.openDrawer(getActivityTestRule());
|
||||
clickAdapterViewItem(2, R.id.navigation_drawer); //select movie activity
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
expandPanel();
|
||||
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
int seekBarProgress = ((SeekBar) v).getProgress();
|
||||
return (seekBarProgress > progress) && (seekBarProgress < (progress + 4));
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if pause button pauses playback
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Pause playback
|
||||
* 3. Result: Server should stop playing and progressbar should pause
|
||||
*/
|
||||
@Test
|
||||
public void pausePlayback() {
|
||||
onView(withId(R.id.npp_play)).perform(click());
|
||||
|
||||
assertFalse(getPlayerHandler().isPlaying());
|
||||
|
||||
expandPanel();
|
||||
final int progress = ((SeekBar) getActivity().findViewById(R.id.mpi_seek_bar)).getProgress();
|
||||
SystemClock.sleep(1000); //wait one second to check if progression has indeed paused
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
int seekBarProgress = ((SeekBar) v).getProgress();
|
||||
return seekBarProgress == progress;
|
||||
}
|
||||
}, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel is not displayed when user disables the panel
|
||||
* through the preference screen
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Disable showing panel in settings
|
||||
* 3. Result: Panel should not show
|
||||
*/
|
||||
@Test
|
||||
public void disableShowingPanelInPreferences() throws Throwable {
|
||||
Utils.openDrawer(getActivityTestRule());
|
||||
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
|
||||
|
||||
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
|
||||
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, false);
|
||||
edit.apply();
|
||||
pressBack();
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if panel is displayed when user enables the panel
|
||||
* through the preference screen
|
||||
*
|
||||
* UI interaction flow tested:
|
||||
* 1. Start playing a music item
|
||||
* 2. Disable showing panel in settings
|
||||
* 3. Show Music screen
|
||||
* 4. Enable showing panel in settings
|
||||
* 4. Return to Music screen
|
||||
* 5. Result: Panel should show
|
||||
*/
|
||||
@Test
|
||||
public void showPanelWhenUserEnablesPanel() throws Throwable {
|
||||
Utils.openDrawer(getActivityTestRule());
|
||||
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
|
||||
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
|
||||
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, false);
|
||||
edit.apply();
|
||||
pressBack();
|
||||
|
||||
Utils.openDrawer(getActivityTestRule());
|
||||
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
|
||||
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, true);
|
||||
edit.apply();
|
||||
pressBack();
|
||||
|
||||
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
}
|
||||
|
||||
private void expandPanel() {
|
||||
int tries = 10;
|
||||
while (tries-- > 0) {
|
||||
try {
|
||||
onView(withId(R.id.npp_title)).perform(click());
|
||||
|
||||
onView(isRoot()).perform(ViewActions.waitForView(R.id.now_playing_panel, new ViewActions.CheckStatus() {
|
||||
@Override
|
||||
public boolean check(View v) {
|
||||
return ((SlidingUpPanelLayout) v).getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED;
|
||||
}
|
||||
}, 1000));
|
||||
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
//Either the click event did not work or the panel did not expand.
|
||||
//Let's try again.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -203,7 +203,7 @@ public class MockTcpServer {
|
|||
try {
|
||||
while (true) {
|
||||
sendResponse();
|
||||
Thread.sleep(1000);
|
||||
Thread.sleep(100);
|
||||
if ( serverSocket.isClosed() )
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class AddonsHandler implements JSONConnectionHandlerManager.ConnectionHan
|
|||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getNotification() {
|
||||
public ArrayList<JsonResponse> getNotifications() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,12 @@ public class ApplicationHandler implements JSONConnectionHandlerManager.Connecti
|
|||
jsonNotifications.add(new OnVolumeChanged(muted, volume));
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getNotification() {
|
||||
public ArrayList<JsonResponse> getNotifications() {
|
||||
ArrayList<JsonResponse> jsonResponses = new ArrayList<>(jsonNotifications);
|
||||
jsonNotifications.clear();
|
||||
return jsonResponses;
|
||||
|
|
|
@ -67,7 +67,7 @@ public class JSONConnectionHandlerManager implements MockTcpServer.TcpServerConn
|
|||
* Used to get any notifications from the handler.
|
||||
* @return {@link JsonResponse} that should be sent to the client
|
||||
*/
|
||||
ArrayList<JsonResponse> getNotification();
|
||||
ArrayList<JsonResponse> getNotifications();
|
||||
|
||||
/**
|
||||
* Should set the state of the handler to its initial state
|
||||
|
@ -132,7 +132,7 @@ public class JSONConnectionHandlerManager implements MockTcpServer.TcpServerConn
|
|||
|
||||
//Handle notifications
|
||||
for(ConnectionHandler handler : handlers) {
|
||||
ArrayList<JsonResponse> jsonNotifications = handler.getNotification();
|
||||
ArrayList<JsonResponse> jsonNotifications = handler.getNotifications();
|
||||
if (jsonNotifications != null) {
|
||||
for (JsonResponse jsonResponse : jsonNotifications) {
|
||||
stringBuffer.append(jsonResponse.toJsonString() +"\n");
|
||||
|
@ -140,7 +140,11 @@ public class JSONConnectionHandlerManager implements MockTcpServer.TcpServerConn
|
|||
}
|
||||
}
|
||||
responseCount++;
|
||||
return stringBuffer.toString();
|
||||
if (stringBuffer.length() > 0) {
|
||||
return stringBuffer.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.jsonrpc.method.JSONRPC;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.JSONRPC.Ping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Simulates JSON RPC JSON-RPC API
|
||||
*/
|
||||
public class JSONRPCHandler implements JSONConnectionHandlerManager.ConnectionHandler {
|
||||
|
||||
@Override
|
||||
public String[] getType() {
|
||||
return new String[] {JSONRPC.Ping.METHOD_NAME};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getResponse(String method, ObjectNode jsonRequest) {
|
||||
ArrayList<JsonResponse> jsonResponses = new ArrayList<>();
|
||||
jsonResponses.add(new Ping(jsonRequest.get("id").asInt()));
|
||||
return jsonResponses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getNotifications() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.Player;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.notifications.Player.OnPause;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.notifications.Player.OnPlay;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.notifications.Player.OnPropertyChanged;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.notifications.Player.OnSeek;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.notifications.Player.OnSpeedChanged;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.xbmc.kore.testutils.tcpserver.handlers.PlayerHandler.TYPE.MUSIC;
|
||||
|
||||
/**
|
||||
* Simulates Player JSON-RPC API
|
||||
*/
|
||||
public class PlayerHandler implements JSONConnectionHandlerManager.ConnectionHandler {
|
||||
private static final String TAG = LogUtils.makeLogTag(PlayerHandler.class);
|
||||
|
||||
public enum TYPE {
|
||||
MUSIC,
|
||||
MOVIE,
|
||||
EPISODE,
|
||||
MUSICVIDEO,
|
||||
UNKNOWN,
|
||||
PICTURE,
|
||||
CHANNEL
|
||||
}
|
||||
|
||||
public static String[] repeatModes = {
|
||||
"off",
|
||||
"one",
|
||||
"all"
|
||||
};
|
||||
|
||||
private int currentRepeatMode;
|
||||
private boolean shuffled;
|
||||
private boolean playing;
|
||||
private int position;
|
||||
private long totalTimeSec = 240; // default value
|
||||
|
||||
private TYPE mediaType = MUSIC;
|
||||
|
||||
private Player.GetItem mediaItem = createSongItem();
|
||||
private String playerType = PlayerType.GetActivePlayersReturnType.AUDIO;
|
||||
|
||||
private ArrayList<JsonResponse> notifications = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getNotifications() {
|
||||
ArrayList<JsonResponse> list = new ArrayList<>(notifications);
|
||||
notifications.clear();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.shuffled = false;
|
||||
this.currentRepeatMode = 0;
|
||||
this.position = 0;
|
||||
this.playing = false;
|
||||
setMediaType(MUSIC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getType() {
|
||||
return new String[] {Player.GetActivePlayers.METHOD_NAME,
|
||||
Player.GetProperties.METHOD_NAME,
|
||||
Player.GetItem.METHOD_NAME,
|
||||
Player.SetRepeat.METHOD_NAME,
|
||||
Player.SetShuffle.METHOD_NAME,
|
||||
Player.Seek.METHOD_NAME,
|
||||
Player.PlayPause.METHOD_NAME};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<JsonResponse> getResponse(String method, ObjectNode jsonRequest) {
|
||||
LogUtils.LOGD(TAG, "getResponse: method="+method);
|
||||
|
||||
ArrayList<JsonResponse> jsonResponses = new ArrayList<>();
|
||||
JsonNode node = jsonRequest.get("id");
|
||||
JsonResponse response = null;
|
||||
int playerId;
|
||||
switch (method) {
|
||||
case Player.GetActivePlayers.METHOD_NAME:
|
||||
response = new Player.GetActivePlayers(node.asInt(), 0, playerType);
|
||||
break;
|
||||
case Player.GetProperties.METHOD_NAME:
|
||||
response = updatePlayerProperties(createPlayerProperties(node.asInt()));
|
||||
break;
|
||||
case Player.GetItem.METHOD_NAME:
|
||||
mediaItem.setMethodId(node.asInt());
|
||||
response = mediaItem;
|
||||
break;
|
||||
case Player.SetRepeat.METHOD_NAME:
|
||||
response = new Player.SetRepeat(node.asInt(), "OK");
|
||||
playerId = jsonRequest.get("params").get("playerid").asInt();
|
||||
currentRepeatMode = ++currentRepeatMode % 3;
|
||||
notifications.add(new OnPropertyChanged(repeatModes[currentRepeatMode], null, playerId));
|
||||
break;
|
||||
case Player.SetShuffle.METHOD_NAME:
|
||||
response = new Player.SetShuffle(node.asInt(), "OK");
|
||||
playerId = jsonRequest.get("params").get("playerid").asInt();
|
||||
shuffled = !shuffled;
|
||||
notifications.add(new OnPropertyChanged(null, shuffled, playerId));
|
||||
break;
|
||||
case Player.PlayPause.METHOD_NAME:
|
||||
playing = !playing;
|
||||
int speed = playing ? 1 : 0;
|
||||
response = new Player.PlayPause(node.asInt(), speed);
|
||||
playerId = jsonRequest.get("params").get("playerid").asInt();
|
||||
if (playing)
|
||||
notifications.add(new OnPlay(1580, getMediaItemType(), playerId, speed));
|
||||
else
|
||||
notifications.add(new OnPause(1580, getMediaItemType(), playerId, speed));
|
||||
notifications.add(new OnSpeedChanged(1580, getMediaItemType(), playerId, speed));
|
||||
break;
|
||||
case Player.Seek.METHOD_NAME:
|
||||
position = new GlobalType.Time(jsonRequest.get("params").get("value")).ToSeconds();
|
||||
response = new Player.Seek(node.asInt(), (100 * position) / (double) totalTimeSec, position,
|
||||
totalTimeSec);
|
||||
playerId = jsonRequest.get("params").get("playerid").asInt();
|
||||
|
||||
notifications.add(new OnSeek(node.asInt(), getMediaItemType(), playerId,
|
||||
playing ? 1 : 0, 0, position));
|
||||
break;
|
||||
}
|
||||
|
||||
jsonResponses.add(response);
|
||||
|
||||
return jsonResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the returned media type
|
||||
* @param mediaType
|
||||
*/
|
||||
public void setMediaType(TYPE mediaType) {
|
||||
switch (mediaType) {
|
||||
case MOVIE:
|
||||
mediaItem = createMovieItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.VIDEO;
|
||||
break;
|
||||
case MUSIC:
|
||||
mediaItem = createSongItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.AUDIO;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
mediaItem = createUnknownItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.AUDIO;
|
||||
break;
|
||||
case MUSICVIDEO:
|
||||
mediaItem = createMusicVideoItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.VIDEO;
|
||||
break;
|
||||
case PICTURE:
|
||||
mediaItem = createPictureItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.PICTURE;
|
||||
break;
|
||||
case CHANNEL:
|
||||
mediaItem = createChannelItem();
|
||||
playerType = PlayerType.GetActivePlayersReturnType.VIDEO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void startPlay() {
|
||||
OnPlay onPlay = new OnPlay(1580, getMediaItemType(), 0, 1);
|
||||
notifications.add(onPlay);
|
||||
playing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current media item for the media type set through {@link #setMediaType(TYPE)}
|
||||
* @return
|
||||
*/
|
||||
public Player.GetItem getMediaItem() {
|
||||
return mediaItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the play position of the current media item
|
||||
* @return the time elapsed in seconds
|
||||
*/
|
||||
public long getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return playing;
|
||||
}
|
||||
|
||||
public void setTotalTimeSec(long totalTimeSec) {
|
||||
this.totalTimeSec = totalTimeSec;
|
||||
}
|
||||
|
||||
private String getMediaItemType() {
|
||||
switch (mediaType) {
|
||||
case MOVIE:
|
||||
return OnPlay.TYPE_MOVIE;
|
||||
case MUSIC:
|
||||
return OnPlay.TYPE_SONG;
|
||||
case UNKNOWN:
|
||||
return OnPlay.TYPE_UNKNOWN;
|
||||
case MUSICVIDEO:
|
||||
return OnPlay.TYPE_MUSICVIDEO;
|
||||
case PICTURE:
|
||||
return OnPlay.TYPE_PICTURE;
|
||||
case CHANNEL:
|
||||
return OnPlay.TYPE_MOVIE;
|
||||
default:
|
||||
return OnPlay.TYPE_SONG;
|
||||
}
|
||||
}
|
||||
|
||||
private Player.GetProperties updatePlayerProperties(Player.GetProperties playerProperties) {
|
||||
if (playing)
|
||||
position++;
|
||||
|
||||
if ( ( position > totalTimeSec ) && currentRepeatMode != 0 )
|
||||
position = 0;
|
||||
|
||||
playerProperties.addPosition(position);
|
||||
playerProperties.addPercentage((int) ((position * 100 ) / totalTimeSec));
|
||||
playerProperties.addTime(0, 0, position, 767);
|
||||
|
||||
playerProperties.addShuffled(shuffled);
|
||||
playerProperties.addRepeat(repeatModes[currentRepeatMode]);
|
||||
|
||||
return playerProperties;
|
||||
}
|
||||
|
||||
private Player.GetProperties createPlayerProperties(int id) {
|
||||
Player.GetProperties properties = new Player.GetProperties(id);
|
||||
properties.addPlaylistId(0);
|
||||
properties.addRepeat(repeatModes[currentRepeatMode]);
|
||||
properties.addShuffled(false);
|
||||
properties.addSpeed(playing ? 1 : 0);
|
||||
properties.addTotaltime(0,0,240,41);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private Player.GetItem createSongItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addAlbum("My Time Is The Right Time");
|
||||
item.addAlbumArtist("Alton Ellis");
|
||||
item.addArtist("Alton Ellis");
|
||||
item.addDisplayartist("Alton Ellis");
|
||||
item.addDuration(240);
|
||||
item.addFile("/Users/martijn/Projects/dummymediafiles/media/music/Alton Ellis/My Time Is The Right Time/11-I Can't Stand It.mp3");
|
||||
item.addGenre("Reggae");
|
||||
item.addLabel("I Can't Stand It");
|
||||
item.addRating(0);
|
||||
item.addTitle("I Can't Stand It");
|
||||
item.addTrack(11);
|
||||
item.addType(Player.GetItem.TYPE.SONG);
|
||||
item.addYear(2000);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createMovieItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addTitle("Elephants Dream");
|
||||
item.addCast("", "Cas Jansen", "Emo");
|
||||
item.addCast("", "Tygo Gernandt", "Proog");
|
||||
item.addDuration(660);
|
||||
item.addFile("/Users/martijn/Projects/dummymediafiles/media/movies/Elephants Dream (2006).mp4");
|
||||
item.addGenre("Animation");
|
||||
item.addRating(0);
|
||||
item.addType(Player.GetItem.TYPE.MOVIE);
|
||||
item.addYear(2006);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createEpisodeItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addShowtitle("According to Jim");
|
||||
item.addCast("image://http%3a%2f%2fthetvdb.com%2fbanners%2factors%2f41995.jpg/", "James Belushi", "Jim");
|
||||
item.addCast("image://http%3a%2f%2fthetvdb.com%2fbanners%2factors%2f41994.jpg/", "Courtney Thorne-Smith", "Cheryl");
|
||||
item.addDuration(1800);
|
||||
item.addFile("/Users/martijn/Projects/dummymediafiles/media/movies/Elephants Dream (2006).mp4");
|
||||
item.addGenre("Comedy");
|
||||
item.addRating(7);
|
||||
item.addType(Player.GetItem.TYPE.EPISODE);
|
||||
item.addFirstaired("2001-10-03");
|
||||
item.addEpisode(1);
|
||||
item.addSeason(1);
|
||||
item.addDirector("Andy Cadiff");
|
||||
item.addTitle("Pilot");
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createMusicVideoItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addType(Player.GetItem.TYPE.MUSICVIDEO);
|
||||
item.addAlbum("...Baby One More Time");
|
||||
item.addDirector("Nigel Dick");
|
||||
item.addThumbnail("image://http%3a%2f%2fwww.theaudiodb.com%2fimages%2fmedia%2falbum%2fthumb%2fbaby-one-more-time-4dcff7453745a.jpg/");
|
||||
item.addYear(1999);
|
||||
item.addTitle("(You Drive Me) Crazy");
|
||||
item.addLabel("(You Drive Me) Crazy");
|
||||
item.addRuntime(12);
|
||||
item.addGenre("Pop");
|
||||
item.addPremiered("1999-01-01");
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createChannelItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addShowtitle("According to Jim");
|
||||
item.addCast("image://http%3a%2f%2fthetvdb.com%2fbanners%2factors%2f41995.jpg/", "James Belushi", "Jim");
|
||||
item.addCast("image://http%3a%2f%2fthetvdb.com%2fbanners%2factors%2f41994.jpg/", "Courtney Thorne-Smith", "Cheryl");
|
||||
item.addDuration(1800);
|
||||
item.addFile("/Users/martijn/Projects/dummymediafiles/media/movies/Elephants Dream (2006).mp4");
|
||||
item.addGenre("Comedy");
|
||||
item.addRating(7);
|
||||
item.addType(Player.GetItem.TYPE.EPISODE);
|
||||
item.addFirstaired("2001-10-03");
|
||||
item.addEpisode(1);
|
||||
item.addSeason(1);
|
||||
item.addDirector("Andy Cadiff");
|
||||
item.addTitle("Pilot");
|
||||
item.addType(Player.GetItem.TYPE.CHANNEL);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createUnknownItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addTitle("Dumpert");
|
||||
item.addCast("", "Martijn Kaiser", "himself");
|
||||
item.addCast("", "", "Skipmode A1");
|
||||
item.addCast("", "", "Sparkline");
|
||||
item.addGenre("Addon");
|
||||
item.addType(Player.GetItem.TYPE.UNKNOWN);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private Player.GetItem createPictureItem() {
|
||||
Player.GetItem item = new Player.GetItem();
|
||||
item.addTitle("Kore Artwork");
|
||||
item.addFile("/Users/martijn/Projects/Kore/art/screenshots/Kore_Artwork_Concept_2.png");
|
||||
item.addType(Player.GetItem.TYPE.PICTURE);
|
||||
return item;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public abstract class JsonResponse {
|
|||
private static final String PARAMS_NODE = "params";
|
||||
private static final String METHOD_NODE = "method";
|
||||
private static final String DATA_NODE = "data";
|
||||
private static final String ID_NODE = "id";
|
||||
protected static final String ID_NODE = "id";
|
||||
private static final String JSONRPC_NODE = "jsonrpc";
|
||||
|
||||
public enum TYPE {
|
||||
|
@ -167,6 +167,14 @@ public abstract class JsonResponse {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the value to the array in node with the given key.
|
||||
* If the array does not exist it will be created
|
||||
* and added.
|
||||
* @param node ObjectNode that should contain an entry with key with an array as value
|
||||
* @param key the key of the item in ObjectNode that should hold the array
|
||||
* @param value the value to be added to the array
|
||||
*/
|
||||
protected void addToArrayNode(ObjectNode node, String key, ObjectNode value) {
|
||||
JsonNode jsonNode = node.get(key);
|
||||
if (jsonNode == null) {
|
||||
|
@ -189,6 +197,10 @@ public abstract class JsonResponse {
|
|||
getDataNode().put(parameter, value);
|
||||
}
|
||||
|
||||
protected void addDataToResponse(String parameter, ObjectNode node) {
|
||||
getDataNode().set(parameter, node);
|
||||
}
|
||||
|
||||
protected void addParameterToResponse(String parameter, String value) {
|
||||
getParametersNode().put(parameter, value);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
|
||||
public class JsonUtils {
|
||||
/**
|
||||
* Fills objectNode with time values
|
||||
* @param objectNode
|
||||
* @param timeSec
|
||||
* @return objectNode for chaining
|
||||
*/
|
||||
public static ObjectNode createTimeNode(ObjectNode objectNode, long timeSec) {
|
||||
int hours = (int) timeSec / 3600;
|
||||
int minutes = (int) ( timeSec / 60 ) % 60;
|
||||
int seconds = (int) timeSec % 60 ;
|
||||
return createTimeNode(objectNode, hours, minutes, seconds, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills objectNode with time values
|
||||
* @param objectNode
|
||||
* @param hours
|
||||
* @param minutes
|
||||
* @param seconds
|
||||
* @param milliseconds
|
||||
* @return objectNode for chaining
|
||||
*/
|
||||
public static ObjectNode createTimeNode(ObjectNode objectNode, int hours, int minutes, int seconds, int milliseconds) {
|
||||
objectNode.put(GlobalType.Time.HOURS, hours);
|
||||
objectNode.put(GlobalType.Time.MINUTES, minutes);
|
||||
objectNode.put(GlobalType.Time.SECONDS, seconds);
|
||||
objectNode.put(GlobalType.Time.MILLISECONDS, milliseconds);
|
||||
return objectNode;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.nodes;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
|
||||
public class AudioDetailsNode extends JsonResponse {
|
||||
|
||||
private AudioDetailsNode() {};
|
||||
|
||||
public AudioDetailsNode(int channels, String codec, String language) {
|
||||
ObjectNode node = (ObjectNode) getResultNode(TYPE.OBJECT);
|
||||
node.put("channels", channels);
|
||||
node.put("codec", codec);
|
||||
node.put("language", language);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.nodes;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
|
||||
public class SubtitleDetailsNode extends JsonResponse {
|
||||
|
||||
private SubtitleDetailsNode() {};
|
||||
|
||||
public SubtitleDetailsNode(String language) {
|
||||
ObjectNode node = (ObjectNode) getResultNode(TYPE.OBJECT);
|
||||
node.put("language", language);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.nodes;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
|
||||
public class VideoDetailsNode extends JsonResponse {
|
||||
|
||||
private VideoDetailsNode() {};
|
||||
|
||||
public VideoDetailsNode(int width, int height, float aspect, String code, int duration) {
|
||||
ObjectNode node = (ObjectNode) getResultNode(TYPE.OBJECT);
|
||||
node.put("width", width);
|
||||
node.put("height", height);
|
||||
node.put("aspect", aspect);
|
||||
node.put("code", code);
|
||||
node.put("duration", duration);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.response.methods;
|
||||
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
|
||||
/**
|
||||
* Serverside JSON RPC responses in Application.*
|
||||
*/
|
||||
public class JSONRPC {
|
||||
|
||||
public static class Ping extends JsonResponse {
|
||||
public final static String METHOD_NAME = "JSONRPC.Ping";
|
||||
|
||||
public Ping(int id) {
|
||||
super(id);
|
||||
setResultToResponse("pong");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,482 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.response.methods;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonUtils;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.nodes.AudioDetailsNode;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.nodes.SubtitleDetailsNode;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.nodes.VideoDetailsNode;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
|
||||
/**
|
||||
* Serverside JSON RPC responses in Methods.Player.*
|
||||
*/
|
||||
public class Player {
|
||||
|
||||
/**
|
||||
* JSON response for Player.Seek request
|
||||
*
|
||||
* Example:
|
||||
* Query: {"jsonrpc":"2.0","method":"Player.Seek","id":41,"params":{"playerid":0,"value":{"hours":0,"milliseconds":0,"minutes":0,"seconds":2}}}
|
||||
* Answer: {"id":41,"jsonrpc":"2.0","result":{"percentage":16.570009231567382812,"time":{"hours":0,"milliseconds":0,"minutes":0,"seconds":2},"totaltime":{"hours":0,"milliseconds":70,"minutes":0,"seconds":12}}}
|
||||
*
|
||||
* @return JSON string
|
||||
*/
|
||||
public static class Seek extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.Seek";
|
||||
|
||||
public Seek(int methodId, double percentage, long timeSec, long totalTime) {
|
||||
super(methodId);
|
||||
ObjectNode resultNode = (ObjectNode) getResultNode(TYPE.OBJECT);
|
||||
resultNode.put("percentage", percentage);
|
||||
resultNode.set("time", JsonUtils.createTimeNode(createObjectNode(), timeSec));
|
||||
resultNode.set("totalTime", JsonUtils.createTimeNode(createObjectNode(), totalTime));
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetShuffle extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.SetShuffle";
|
||||
|
||||
public SetShuffle(int methodId, String result) {
|
||||
super(methodId);
|
||||
setResultToResponse(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetRepeat extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.SetRepeat";
|
||||
|
||||
public SetRepeat(int methodId, String result) {
|
||||
super(methodId);
|
||||
setResultToResponse(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayPause extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.PlayPause";
|
||||
|
||||
public PlayPause(int methodId, int speed) {
|
||||
super(methodId);
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put("speed", speed);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetActivePlayers extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.GetActivePlayers";
|
||||
|
||||
public GetActivePlayers(int methodId, int playerId, String type) {
|
||||
super(methodId);
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.put("playerid", playerId);
|
||||
objectNode.put("type", type);
|
||||
((ArrayNode) getResultNode(TYPE.ARRAY)).add(objectNode);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetProperties extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.GetProperties";
|
||||
|
||||
final static String SPEED = PlayerType.PropertyName.SPEED;
|
||||
final static String PERCENTAGE = PlayerType.PropertyName.PERCENTAGE;
|
||||
final static String POSITION = PlayerType.PropertyName.POSITION;
|
||||
final static String TIME = PlayerType.PropertyName.TIME;
|
||||
final static String TOTALTIME = PlayerType.PropertyName.TOTALTIME;
|
||||
final static String REPEAT = PlayerType.PropertyName.REPEAT;
|
||||
final static String SHUFFLED = PlayerType.PropertyName.SHUFFLED;
|
||||
final static String CURRENTAUDIOSTREAM = PlayerType.PropertyName.CURRENTAUDIOSTREAM;
|
||||
final static String CURRENTSUBTITLE = PlayerType.PropertyName.CURRENTSUBTITLE;
|
||||
final static String AUDIOSTREAMS = PlayerType.PropertyName.AUDIOSTREAMS;
|
||||
final static String SUBTITLES = PlayerType.PropertyName.SUBTITLES;
|
||||
final static String PLAYLISTID = PlayerType.PropertyName.PLAYLISTID;
|
||||
|
||||
public GetProperties(int methodId) {
|
||||
super(methodId);
|
||||
}
|
||||
|
||||
public void addSpeed(int value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(SPEED, value);
|
||||
}
|
||||
|
||||
public void addPercentage(int value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(PERCENTAGE, value);
|
||||
}
|
||||
|
||||
public void addPosition(int value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(POSITION, value);
|
||||
}
|
||||
|
||||
public void addTime(int hours, int minutes, int seconds, int milliseconds) {
|
||||
ObjectNode timeNode = JsonUtils.createTimeNode(createObjectNode(), hours, minutes, seconds, milliseconds);
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).putObject(TIME).setAll(timeNode);
|
||||
}
|
||||
|
||||
public void addTotaltime(int hours, int minutes, int seconds, int milliseconds) {
|
||||
ObjectNode timeNode = JsonUtils.createTimeNode(createObjectNode(), hours, minutes, seconds, milliseconds);
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).putObject(TOTALTIME).setAll(timeNode);
|
||||
}
|
||||
|
||||
public void addRepeat(String value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(REPEAT, value);
|
||||
}
|
||||
|
||||
public void addShuffled(boolean value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(SHUFFLED, value);
|
||||
}
|
||||
|
||||
public void addCurrentAudioStream(int channels, String codec, int bitrate) {
|
||||
ObjectNode objectNode = createAudioStreamNode(channels, codec, bitrate);
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).putObject(CURRENTAUDIOSTREAM).setAll(objectNode);
|
||||
}
|
||||
|
||||
public void addCurrentSubtitle(int index, String language, String name) {
|
||||
ObjectNode objectNode = createSubtitleNode(index, language, name);
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).putObject(CURRENTSUBTITLE).setAll(objectNode);
|
||||
}
|
||||
|
||||
public void addAudioStream(int channels, String codec, int bitrate) {
|
||||
ObjectNode objectNode = createAudioStreamNode(channels, codec, bitrate);
|
||||
addObjectToArray(AUDIOSTREAMS, objectNode);
|
||||
}
|
||||
|
||||
public void addSubtitle(int index, String language, String name) {
|
||||
ObjectNode objectNode = createSubtitleNode(index, language, name);
|
||||
addObjectToArray(SUBTITLES, objectNode);
|
||||
}
|
||||
|
||||
public void addPlaylistId(int value) {
|
||||
((ObjectNode) getResultNode(TYPE.OBJECT)).put(PLAYLISTID, value);
|
||||
}
|
||||
|
||||
private ObjectNode createAudioStreamNode(int channels, String codec, int bitrate) {
|
||||
ObjectNode audioNode = createObjectNode();
|
||||
audioNode.put("channels", channels);
|
||||
audioNode.put("codec", codec);
|
||||
audioNode.put("bitrate", bitrate);
|
||||
return audioNode;
|
||||
}
|
||||
|
||||
private ObjectNode createSubtitleNode(int index, String language, String name) {
|
||||
ObjectNode subtitleNode = createObjectNode();
|
||||
subtitleNode.put("index", index);
|
||||
subtitleNode.put("language", language);
|
||||
subtitleNode.put("name", name);
|
||||
return subtitleNode;
|
||||
}
|
||||
|
||||
private void addObjectToArray(String key, ObjectNode objectNode) {
|
||||
ObjectNode resultNode = (ObjectNode) getResultNode(TYPE.OBJECT);
|
||||
JsonNode jsonNode = resultNode.get(key);
|
||||
|
||||
if(jsonNode == null) {
|
||||
ArrayNode arrayNode = createArrayNode().add(objectNode);
|
||||
resultNode.set(key, arrayNode);
|
||||
} else if(jsonNode.isArray()) {
|
||||
((ArrayNode) jsonNode).add(objectNode);
|
||||
} else {
|
||||
LogUtils.LOGW("Player", "JsonNode at " + key + " is not of type ArrayNode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetItem extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.GetItem";
|
||||
|
||||
final static String ITEM = "item";
|
||||
final static String TYPE = "type";
|
||||
final static String ART = "art";
|
||||
final static String ARTIST = "artist";
|
||||
final static String ALBUMARTIST = "albumartist";
|
||||
final static String ALBUM = "album";
|
||||
final static String CAST = "cast";
|
||||
final static String DIRECTOR = "director";
|
||||
final static String DISPLAYARTIST = "displayartist";
|
||||
final static String DURATION = "duration";
|
||||
final static String EPISODE = "episode";
|
||||
final static String FANART = "fanart";
|
||||
final static String FILE = "file";
|
||||
final static String FIRSTAIRED = "firstaired";
|
||||
final static String GENRE = "genre";
|
||||
final static String IMDBNUMBER = "imdbnumber";
|
||||
final static String PLOT = "plot";
|
||||
final static String PREMIERED = "premiered";
|
||||
final static String RATING = "rating";
|
||||
final static String RESUME = "resume";
|
||||
final static String RUNTIME = "runtime";
|
||||
final static String SEASON = "season";
|
||||
final static String SHOWTITLE = "showtitle";
|
||||
final static String STREAMDETAILS = "streamdetails";
|
||||
final static String STUDIO = "studio";
|
||||
final static String TAGLINE = "tagline";
|
||||
final static String THUMBNAIL = "thumbnail";
|
||||
final static String TITLE = "title";
|
||||
final static String TOP250 = "top250";
|
||||
final static String TRACK = "track";
|
||||
final static String VOTES = "votes";
|
||||
final static String WRITER = "writer";
|
||||
final static String YEAR = "year";
|
||||
final static String DESCRIPTION = "description";
|
||||
final static String LABEL = "label";
|
||||
|
||||
public enum TYPE { UNKNOWN,
|
||||
MOVIE,
|
||||
EPISODE,
|
||||
MUSICVIDEO,
|
||||
SONG,
|
||||
PICTURE,
|
||||
CHANNEL
|
||||
}
|
||||
|
||||
private ObjectNode itemNode;
|
||||
|
||||
public GetItem() {
|
||||
super();
|
||||
ObjectNode resultNode = ((ObjectNode) getResultNode(JsonResponse.TYPE.OBJECT));
|
||||
itemNode = createObjectNode();
|
||||
resultNode.set(ITEM, itemNode);
|
||||
}
|
||||
|
||||
public GetItem(int methodId) {
|
||||
super(methodId);
|
||||
ObjectNode resultNode = ((ObjectNode) getResultNode(JsonResponse.TYPE.OBJECT));
|
||||
itemNode = createObjectNode();
|
||||
resultNode.set(ITEM, itemNode);
|
||||
}
|
||||
|
||||
public void setMethodId(int methodId) {
|
||||
getResponseNode().put(ID_NODE, methodId);
|
||||
}
|
||||
|
||||
public void addType(TYPE type) {
|
||||
String strType;
|
||||
switch (type) {
|
||||
case MOVIE:
|
||||
strType = "movie";
|
||||
break;
|
||||
case EPISODE:
|
||||
strType = "episode";
|
||||
break;
|
||||
case MUSICVIDEO:
|
||||
strType = "musicvideo";
|
||||
break;
|
||||
case SONG:
|
||||
strType = "song";
|
||||
break;
|
||||
case PICTURE:
|
||||
strType = "picture";
|
||||
break;
|
||||
case CHANNEL:
|
||||
strType = "channel";
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
strType = "unknown";
|
||||
break;
|
||||
}
|
||||
itemNode.put(TYPE, strType);
|
||||
}
|
||||
|
||||
public void addArt(String banner, String poster, String fanart, String thumbnail) {
|
||||
ObjectNode objectNode = createArtNode(banner, poster, fanart, thumbnail);
|
||||
itemNode.putObject(ART).setAll(objectNode);
|
||||
}
|
||||
|
||||
public void addArtist(String artist) {
|
||||
addToArrayNode(itemNode, ARTIST, artist);
|
||||
}
|
||||
|
||||
public void addAlbumArtist(String artist) {
|
||||
addToArrayNode(itemNode, ALBUMARTIST, artist);
|
||||
}
|
||||
|
||||
public void addAlbum(String album) {
|
||||
itemNode.put(ALBUM, album);
|
||||
}
|
||||
|
||||
public void addCast(String thumbnail, String name, String role) {
|
||||
addToArrayNode(itemNode, CAST, createCastNode(thumbnail, name, role));
|
||||
}
|
||||
|
||||
public void addDirector(String director) {
|
||||
addToArrayNode(itemNode, DIRECTOR, director);
|
||||
}
|
||||
|
||||
public void addDisplayartist(String displayartist) {
|
||||
itemNode.put(DISPLAYARTIST, displayartist);
|
||||
}
|
||||
|
||||
public void addDuration(int duration) {
|
||||
itemNode.put(DURATION, duration);
|
||||
}
|
||||
|
||||
public void addEpisode(int episode) {
|
||||
itemNode.put(EPISODE, episode);
|
||||
}
|
||||
|
||||
public void addFanart(String fanart) {
|
||||
itemNode.put(FANART, fanart);
|
||||
}
|
||||
|
||||
public void addFile(String file) {
|
||||
itemNode.put(FILE, file);
|
||||
}
|
||||
|
||||
public void addFirstaired(String firstaired) {
|
||||
itemNode.put(FIRSTAIRED, firstaired);
|
||||
}
|
||||
|
||||
public void addGenre(String genre) {
|
||||
itemNode.put(GENRE, genre);
|
||||
}
|
||||
|
||||
public void addImdbnumber(String imdbnumber) {
|
||||
itemNode.put(IMDBNUMBER, imdbnumber);
|
||||
}
|
||||
|
||||
public void addPlot(String plot) {
|
||||
itemNode.put(PLOT, plot);
|
||||
}
|
||||
|
||||
public void addPremiered(String premiered) {
|
||||
itemNode.put(PREMIERED, premiered);
|
||||
}
|
||||
|
||||
public void addRating(int rating) {
|
||||
itemNode.put(RATING, rating);
|
||||
}
|
||||
|
||||
public void addResume(int position, int total) {
|
||||
itemNode.putObject(RESUME).setAll(createResumeNode(position, total));
|
||||
}
|
||||
|
||||
public void addRuntime(int runtime) {
|
||||
itemNode.put(RUNTIME, runtime);
|
||||
}
|
||||
|
||||
public void addSeason(int season) {
|
||||
itemNode.put(SEASON, season);
|
||||
}
|
||||
|
||||
public void addShowtitle(String showtitle) {
|
||||
itemNode.put(SHOWTITLE, showtitle);
|
||||
}
|
||||
|
||||
public void addStreamdetails(AudioDetailsNode audioDetailsNode,
|
||||
VideoDetailsNode videoDetailsNode,
|
||||
SubtitleDetailsNode subtitleDetailsNode) {
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.putObject("audio").setAll(audioDetailsNode.getResponseNode());
|
||||
objectNode.putObject("video").setAll(videoDetailsNode.getResponseNode());
|
||||
objectNode.putObject("subtitle").setAll(subtitleDetailsNode.getResponseNode());
|
||||
|
||||
itemNode.set(STREAMDETAILS, objectNode);
|
||||
}
|
||||
|
||||
public void addStudio(String studio) {
|
||||
addToArrayNode(itemNode, STUDIO, studio);
|
||||
}
|
||||
|
||||
public void addTagline(String tagline) {
|
||||
itemNode.put(TAGLINE, tagline);
|
||||
}
|
||||
|
||||
public void addThumbnail(String thumbnail) {
|
||||
itemNode.put(THUMBNAIL, thumbnail);
|
||||
}
|
||||
|
||||
public void addTitle(String title) {
|
||||
itemNode.put(TITLE, title);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
JsonNode jsonNode = itemNode.get(TITLE);
|
||||
if (jsonNode != null)
|
||||
return jsonNode.asText();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addTop250(int top250) {
|
||||
itemNode.put(TOP250, top250);
|
||||
}
|
||||
|
||||
public void addTrack(int track) {
|
||||
itemNode.put(TRACK, track);
|
||||
}
|
||||
|
||||
public void addVotes(String votes) {
|
||||
itemNode.put(VOTES, votes);
|
||||
}
|
||||
|
||||
public void addWriter(String writer) {
|
||||
addToArrayNode(itemNode, WRITER, writer);
|
||||
}
|
||||
|
||||
public void addYear(int year) {
|
||||
itemNode.put(YEAR, year);
|
||||
}
|
||||
|
||||
public void addDescription(String description) {
|
||||
itemNode.put(DESCRIPTION, description);
|
||||
}
|
||||
|
||||
public void addLabel(String label) {
|
||||
itemNode.put(LABEL, label);
|
||||
}
|
||||
|
||||
private ObjectNode createArtNode(String banner,
|
||||
String poster,
|
||||
String fanart,
|
||||
String thumbnail) {
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.put("poster", poster);
|
||||
objectNode.put("fanart", fanart);
|
||||
objectNode.put("thumbnail", thumbnail);
|
||||
objectNode.put("banner", banner);
|
||||
return objectNode;
|
||||
}
|
||||
|
||||
private ObjectNode createArtworkNode(String banner, String poster, String fanart, String thumbnail) {
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.put("poster", poster);
|
||||
objectNode.put("fanart", fanart);
|
||||
objectNode.put("thumbnail", thumbnail);
|
||||
return objectNode;
|
||||
}
|
||||
|
||||
private ObjectNode createCastNode(String thumbnail, String name, String role) {
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.put("thumbnail", thumbnail);
|
||||
objectNode.put("name", name);
|
||||
objectNode.put("role", role);
|
||||
return objectNode;
|
||||
}
|
||||
|
||||
private ObjectNode createResumeNode(int position, int total) {
|
||||
ObjectNode objectNode = createObjectNode();
|
||||
objectNode.put("position", position);
|
||||
objectNode.put("total", total);
|
||||
return objectNode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright 2016 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.testutils.tcpserver.handlers.jsonrpc.response.notifications;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonResponse;
|
||||
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.JsonUtils;
|
||||
|
||||
public class Player {
|
||||
|
||||
abstract public static class PlayPause extends JsonResponse {
|
||||
public static String TYPE_SONG = "song";
|
||||
public static String TYPE_EPISODE = "episode";
|
||||
public static String TYPE_MOVIE = "movie";
|
||||
public static String TYPE_MUSICVIDEO = "musicvideo";
|
||||
public static String TYPE_VIDEO = "video";
|
||||
public static String TYPE_UNKNOWN = "unknown";
|
||||
public static String TYPE_PICTURE = "picture";
|
||||
public static String TYPE_CHANNEL = "channel";
|
||||
|
||||
private PlayPause(String methodName, int itemId, String itemType, int playerId, int speed) {
|
||||
addMethodToResponse(methodName);
|
||||
|
||||
ObjectNode itemNode = createObjectNode();
|
||||
itemNode.put("id", itemId);
|
||||
if (itemType != null)
|
||||
itemNode.put("type", itemType);
|
||||
addDataToResponse("item", itemNode);
|
||||
|
||||
itemNode = createObjectNode();
|
||||
itemNode.put("playerid", playerId);
|
||||
itemNode.put("speed", speed);
|
||||
addDataToResponse("player", itemNode);
|
||||
|
||||
addParameterToResponse("sender", "xbmc");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON response for Player.OnSpeedChanged notification
|
||||
*
|
||||
* Example:
|
||||
* Answer: {"jsonrpc":"2.0","method":"Player.OnSpeedChanged","params":{"data":{"item":{"id":94,"type":"song"},"player":{"playerid":0,"speed":0}},"sender":"xbmc"}}
|
||||
*/
|
||||
public static class OnSpeedChanged extends PlayPause {
|
||||
public final static String METHOD_NAME = "Player.OnSpeedChanged";
|
||||
|
||||
public OnSpeedChanged(int itemId, String itemType, int playerId, int speed) {
|
||||
super(METHOD_NAME, itemId, itemType, playerId, speed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON response for Player.OnPause notification
|
||||
*
|
||||
* Example:
|
||||
* Answer: {"jsonrpc":"2.0","method":"Player.OnPause","params":{"data":{"item":{"id":94,"type":"song"},"player":{"playerid":0,"speed":0}},"sender":"xbmc"}}
|
||||
*/
|
||||
public static class OnPause extends PlayPause {
|
||||
public final static String METHOD_NAME = "Player.OnPause";
|
||||
|
||||
public OnPause(int itemId, String itemType, int playerId, int speed) {
|
||||
super(METHOD_NAME, itemId, itemType, playerId, speed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON response for Player.OnPlay notification
|
||||
*
|
||||
* Example:
|
||||
* Answer: {"jsonrpc":"2.0","method":"Player.OnPlay","params":{"data":{"item":{"id":1580,"type":"song"},"player":{"playerid":0,"speed":1}},"sender":"xbmc"}}
|
||||
*/
|
||||
public static class OnPlay extends PlayPause {
|
||||
public final static String METHOD_NAME = "Player.OnPlay";
|
||||
|
||||
public OnPlay(int itemId, String itemType, int playerId, int speed) {
|
||||
super(METHOD_NAME, itemId, itemType, playerId, speed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON response for Player.OnPropertyChanged notification
|
||||
*
|
||||
* Example:
|
||||
* {"jsonrpc":"2.0","method":"Player.OnPropertyChanged","params":{"data":{"player":{"playerid":0},"property":{"repeat":"all"}},"sender":"xbmc"}}
|
||||
*/
|
||||
public static class OnPropertyChanged extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.OnPropertyChanged";
|
||||
|
||||
public OnPropertyChanged(String repeatType, Boolean shuffled, int playerId) {
|
||||
super();
|
||||
addMethodToResponse(METHOD_NAME);
|
||||
|
||||
ObjectNode playerIdNode = createObjectNode();
|
||||
playerIdNode.put("playerid", playerId);
|
||||
addDataToResponse("player", playerIdNode);
|
||||
|
||||
if (repeatType != null) {
|
||||
ObjectNode repeatNode = createObjectNode();
|
||||
repeatNode.put("repeat", repeatType);
|
||||
addDataToResponse("property", repeatNode);
|
||||
}
|
||||
|
||||
if (shuffled != null) {
|
||||
ObjectNode repeatNode = createObjectNode();
|
||||
repeatNode.put("shuffled", shuffled);
|
||||
addDataToResponse("property", repeatNode);
|
||||
}
|
||||
|
||||
addParameterToResponse("sender", "xbmc");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON response for Player.OnPropertyChanged notification
|
||||
*
|
||||
* Example:
|
||||
* {"jsonrpc":"2.0","method":"Player.OnSeek", "params":{ "data":{"item":{ "id":127,"type":"episode" },"player":{ "playerid":1,"seekoffset":{ "hours":0,"milliseconds":0, "minutes":0,"seconds":-14 },"speed":0, "time":{"hours":0, "milliseconds":0,"minutes":0, "seconds":2} }},"sender":"xbmc" }}
|
||||
*/
|
||||
public static class OnSeek extends JsonResponse {
|
||||
public final static String METHOD_NAME = "Player.OnSeek";
|
||||
|
||||
public OnSeek(int itemId, String type, int playerId, int speed, long seekOffsetSecs, long timeSecs) {
|
||||
super();
|
||||
addMethodToResponse(METHOD_NAME);
|
||||
|
||||
ObjectNode itemNode = createObjectNode();
|
||||
itemNode.put("id", itemId);
|
||||
itemNode.put("type", type);
|
||||
addDataToResponse("item", itemNode);
|
||||
|
||||
ObjectNode playerNode = createObjectNode();
|
||||
playerNode.put("playerid", playerId);
|
||||
playerNode.set("seekoffset", JsonUtils.createTimeNode(createObjectNode(), seekOffsetSecs));
|
||||
playerNode.set("time", JsonUtils.createTimeNode(createObjectNode(), timeSecs));
|
||||
playerNode.put("speed", speed);
|
||||
addDataToResponse("player", playerNode);
|
||||
|
||||
addParameterToResponse("sender", "xbmc");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue