Removed trailing whitespaces, replaced space intendation with tabs.

This commit is contained in:
Felix Ableitner 2014-02-08 21:16:30 +01:00
parent 10e5f86ecf
commit 2314e895d1
14 changed files with 1428 additions and 1437 deletions

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -65,62 +65,62 @@ public class MainActivity extends ActionBarActivity {
/** /**
* Interface which allows listening to "back" button presses. * Interface which allows listening to "back" button presses.
*/ */
public interface OnBackPressedListener { public interface OnBackPressedListener {
boolean onBackPressed(); boolean onBackPressed();
} }
FragmentStatePagerAdapter mSectionsPagerAdapter = FragmentStatePagerAdapter mSectionsPagerAdapter =
new FragmentStatePagerAdapter(getSupportFragmentManager()) { new FragmentStatePagerAdapter(getSupportFragmentManager()) {
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
switch (position) { switch (position) {
case 0: return mServerFragment; case 0: return mServerFragment;
case 1: return mRouteFragment; case 1: return mRouteFragment;
default: return null; default: return null;
} }
} }
@Override @Override
public int getCount() { public int getCount() {
return 2; return 2;
} }
}; };
private ServerFragment mServerFragment; private ServerFragment mServerFragment;
private RouteFragment mRouteFragment; private RouteFragment mRouteFragment;
ViewPager mViewPager; ViewPager mViewPager;
/** /**
* Initializes tab navigation. If wifi is not connected, * Initializes tab navigation. If wifi is not connected,
* shows a warning dialog. * shows a warning dialog.
*/ */
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final ActionBar actionBar = getSupportActionBar(); final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false); actionBar.setDisplayShowHomeEnabled(false);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter); mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener( mViewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() { new ViewPager.SimpleOnPageChangeListener() {
@Override @Override
public void onPageSelected(int position) { public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position); actionBar.setSelectedNavigationItem(position);
} }
}); });
TabListener tabListener = new ActionBar.TabListener() { TabListener tabListener = new ActionBar.TabListener() {
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition()); mViewPager.setCurrentItem(tab.getPosition());
} }
@Override @Override
public void onTabReselected(Tab arg0, FragmentTransaction arg1) { public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
@ -129,111 +129,111 @@ public class MainActivity extends ActionBarActivity {
@Override @Override
public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
} }
}; };
actionBar.addTab(actionBar.newTab() actionBar.addTab(actionBar.newTab()
.setText(R.string.title_server) .setText(R.string.title_server)
.setTabListener(tabListener)); .setTabListener(tabListener));
actionBar.addTab(actionBar.newTab() actionBar.addTab(actionBar.newTab()
.setText(R.string.title_route) .setText(R.string.title_route)
.setTabListener(tabListener)); .setTabListener(tabListener));
ConnectivityManager connManager = (ConnectivityManager) ConnectivityManager connManager = (ConnectivityManager)
getSystemService(CONNECTIVITY_SERVICE); getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final SharedPreferences prefs = getSharedPreferences("preferences.db", 0); final SharedPreferences prefs = getSharedPreferences("preferences.db", 0);
if (!wifi.isConnected() && !prefs.getBoolean("wifi_skip_dialog", false)) { if (!wifi.isConnected() && !prefs.getBoolean("wifi_skip_dialog", false)) {
View checkBoxView = View.inflate(this, R.layout.dialog_wifi_disabled, null); View checkBoxView = View.inflate(this, R.layout.dialog_wifi_disabled, null);
CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.dont_show_again); CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.dont_show_again);
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean("wifi_skip_dialog", isChecked) prefs.edit().putBoolean("wifi_skip_dialog", isChecked)
.commit(); .commit();
} }
}); });
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setView(checkBoxView) .setView(checkBoxView)
.setTitle(R.string.warning_wifi_not_connected) .setTitle(R.string.warning_wifi_not_connected)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show(); .show();
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
mServerFragment = (ServerFragment) fm.getFragment( mServerFragment = (ServerFragment) fm.getFragment(
savedInstanceState, ServerFragment.class.getName()); savedInstanceState, ServerFragment.class.getName());
mRouteFragment = (RouteFragment) fm.getFragment( mRouteFragment = (RouteFragment) fm.getFragment(
savedInstanceState, RouteFragment.class.getName()); savedInstanceState, RouteFragment.class.getName());
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab")); mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
} }
else { else {
mServerFragment = new ServerFragment(); mServerFragment = new ServerFragment();
mRouteFragment = new RouteFragment(); mRouteFragment = new RouteFragment();
} }
onNewIntent(getIntent()); onNewIntent(getIntent());
} }
/** /**
* Displays the RouteFragment immediately (instead of ServerFragment). * Displays the RouteFragment immediately (instead of ServerFragment).
*/ */
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
if (intent.getAction().equals("showRouteFragment")) if (intent.getAction().equals("showRouteFragment"))
mViewPager.setCurrentItem(1); mViewPager.setCurrentItem(1);
} }
/** /**
* Saves fragments. * Saves fragments.
*/ */
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, ServerFragment.class.getName(), mServerFragment); fm.putFragment(outState, ServerFragment.class.getName(), mServerFragment);
fm.putFragment(outState, RouteFragment.class.getName(), mRouteFragment); fm.putFragment(outState, RouteFragment.class.getName(), mRouteFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem()); outState.putInt("currentTab", mViewPager.getCurrentItem());
} }
/** /**
* Forwards back press to active Fragment (unless the fragment is * Forwards back press to active Fragment (unless the fragment is
* showing its root view). * showing its root view).
*/ */
@Override @Override
public void onBackPressed() { public void onBackPressed() {
OnBackPressedListener currentFragment = (OnBackPressedListener) OnBackPressedListener currentFragment = (OnBackPressedListener)
mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem()); mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
if (!currentFragment.onBackPressed()) if (!currentFragment.onBackPressed())
super.onBackPressed(); super.onBackPressed();
} }
/** /**
* Changes volume on key press (via RouteFragment). * Changes volume on key press (via RouteFragment).
*/ */
@Override @Override
public boolean dispatchKeyEvent(KeyEvent event) { public boolean dispatchKeyEvent(KeyEvent event) {
switch (event.getKeyCode()) { switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_UP:
if (event.getAction() == KeyEvent.ACTION_DOWN) if (event.getAction() == KeyEvent.ACTION_DOWN)
mRouteFragment.increaseVolume(); mRouteFragment.increaseVolume();
return true; return true;
case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_DOWN:
if (event.getAction() == KeyEvent.ACTION_DOWN) if (event.getAction() == KeyEvent.ACTION_DOWN)
mRouteFragment.decreaseVolume(); mRouteFragment.decreaseVolume();
return true; return true;
default: default:
return super.dispatchKeyEvent(event); return super.dispatchKeyEvent(event);
} }
} }
/** /**
* Starts playing the playlist from item start (via RouteFragment). * Starts playing the playlist from item start (via RouteFragment).
*/ */
public void play(List<Item> playlist, int start) { public void play(List<Item> playlist, int start) {
mViewPager.setCurrentItem(1); mViewPager.setCurrentItem(1);
mRouteFragment.play(playlist, start); mRouteFragment.play(playlist, start);
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -128,172 +128,171 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
applyColors(); applyColors();
if (mRestorePlaylistMode) if (mRestorePlaylistMode)
playlistMode(mMediaRouterPlayService.getService().getCurrentRoute()); playlistMode(mMediaRouterPlayService.getService().getCurrentRoute());
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
mMediaRouterPlayService = null; mMediaRouterPlayService = null;
} }
}; };
/** /**
* Selects remote playback route category. * Selects remote playback route category.
*/ */
public RouteFragment() { public RouteFragment() {
MediaRouteSelector mSelector = new MediaRouteSelector.Builder() MediaRouteSelector mSelector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build(); .build();
setRouteSelector(mSelector); setRouteSelector(mSelector);
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
return inflater.inflate(R.layout.route_fragment, null); return inflater.inflate(R.layout.route_fragment, null);
}; };
/** /**
* Initializes views, connects to service, adds default route. * Initializes views, connects to service, adds default route.
*/ */
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
mRouteAdapter = new RouteAdapter(getActivity()); mRouteAdapter = new RouteAdapter(getActivity());
mRouteAdapter.add(MediaRouter.getInstance(getActivity()).getRoutes()); mRouteAdapter.add(MediaRouter.getInstance(getActivity()).getRoutes());
mRouteAdapter.remove(MediaRouter.getInstance(getActivity()).getDefaultRoute()); mRouteAdapter.remove(MediaRouter.getInstance(getActivity()).getDefaultRoute());
mPlaylistAdapter = new FileArrayAdapter(getActivity()); mPlaylistAdapter = new FileArrayAdapter(getActivity());
mListView = (ListView) getView().findViewById(R.id.listview); mListView = (ListView) getView().findViewById(R.id.listview);
mListView.setAdapter(mRouteAdapter); mListView.setAdapter(mRouteAdapter);
mListView.setOnItemClickListener(this); mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(this); mListView.setOnScrollListener(this);
mListView.setEmptyView(getView().findViewById(android.R.id.empty)); mListView.setEmptyView(getView().findViewById(android.R.id.empty));
mControls = getView().findViewById(R.id.controls); mControls = getView().findViewById(R.id.controls);
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar); mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
mProgressBar.setOnSeekBarChangeListener(this); mProgressBar.setOnSeekBarChangeListener(this);
mShuffle = (ImageButton) getView().findViewById(R.id.shuffle); mShuffle = (ImageButton) getView().findViewById(R.id.shuffle);
mShuffle.setImageResource(R.drawable.ic_action_shuffle); mShuffle.setImageResource(R.drawable.ic_action_shuffle);
mShuffle.setOnClickListener(this); mShuffle.setOnClickListener(this);
ImageButton previous = (ImageButton) getView().findViewById(R.id.previous); ImageButton previous = (ImageButton) getView().findViewById(R.id.previous);
previous.setImageResource(R.drawable.ic_action_previous); previous.setImageResource(R.drawable.ic_action_previous);
previous.setOnClickListener(this); previous.setOnClickListener(this);
ImageButton next = (ImageButton) getView().findViewById(R.id.next); ImageButton next = (ImageButton) getView().findViewById(R.id.next);
next.setImageResource(R.drawable.ic_action_next); next.setImageResource(R.drawable.ic_action_next);
next.setOnClickListener(this); next.setOnClickListener(this);
mRepeat = (ImageButton) getView().findViewById(R.id.repeat); mRepeat = (ImageButton) getView().findViewById(R.id.repeat);
mRepeat.setImageResource(R.drawable.ic_action_repeat); mRepeat.setImageResource(R.drawable.ic_action_repeat);
mRepeat.setOnClickListener(this); mRepeat.setOnClickListener(this);
mPlayPause = (ImageButton) getView().findViewById(R.id.playpause); mPlayPause = (ImageButton) getView().findViewById(R.id.playpause);
mPlayPause.setOnClickListener(this); mPlayPause.setOnClickListener(this);
mPlayPause.setImageResource(R.drawable.ic_action_play); mPlayPause.setImageResource(R.drawable.ic_action_play);
mCurrentTimeView = (TextView) getView().findViewById(R.id.current_time); mCurrentTimeView = (TextView) getView().findViewById(R.id.current_time);
mTotalTimeView = (TextView) getView().findViewById(R.id.total_time); mTotalTimeView = (TextView) getView().findViewById(R.id.total_time);
getActivity().getApplicationContext().startService( getActivity().getApplicationContext().startService(
new Intent(getActivity(), MediaRouterPlayService.class)); new Intent(getActivity(), MediaRouterPlayService.class));
getActivity().getApplicationContext().bindService( getActivity().getApplicationContext().bindService(
new Intent(getActivity(), MediaRouterPlayService.class), new Intent(getActivity(), MediaRouterPlayService.class),
mPlayServiceConnection, mPlayServiceConnection,
Context.BIND_AUTO_CREATE Context.BIND_AUTO_CREATE
); );
if (savedInstanceState != null) { if (savedInstanceState != null) {
mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state")); mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state"));
mRestorePlaylistMode = savedInstanceState.getBoolean("route_selected"); mRestorePlaylistMode = savedInstanceState.getBoolean("route_selected");
} }
} }
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putBoolean("route_selected", mSelectedRoute != null); outState.putBoolean("route_selected", mSelectedRoute != null);
outState.putParcelable("list_state", mListView.onSaveInstanceState()); outState.putParcelable("list_state", mListView.onSaveInstanceState());
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
getActivity().getApplicationContext().unbindService(mPlayServiceConnection); getActivity().getApplicationContext().unbindService(mPlayServiceConnection);
} }
/** /**
* Starts active route discovery (which is automatically stopped on * Starts active route discovery (which is automatically stopped on
* fragment stop by parent class). * fragment stop by parent class).
*/ */
@Override @Override
public int onPrepareCallbackFlags() { public int onPrepareCallbackFlags() {
return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY
| MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN; | MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN;
} }
@Override @Override
public Callback onCreateCallback() { public Callback onCreateCallback() {
return new MediaRouter.Callback() { return new MediaRouter.Callback() {
@Override @Override
public void onRouteAdded(MediaRouter router, RouteInfo route) { public void onRouteAdded(MediaRouter router, RouteInfo route) {
for (int i = 0; i < mRouteAdapter.getCount(); i++) { for (int i = 0; i < mRouteAdapter.getCount(); i++)
if (mRouteAdapter.getItem(i).getId().equals(route.getId())) { if (mRouteAdapter.getItem(i).getId().equals(route.getId())) {
mRouteAdapter.remove(mRouteAdapter.getItem(i)); mRouteAdapter.remove(mRouteAdapter.getItem(i));
break; break;
} }
} mRouteAdapter.add(route);
mRouteAdapter.add(route); }
}
@Override @Override
public void onRouteChanged(MediaRouter router, RouteInfo route) { public void onRouteChanged(MediaRouter router, RouteInfo route) {
mRouteAdapter.notifyDataSetChanged(); mRouteAdapter.notifyDataSetChanged();
} }
@Override @Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) { public void onRouteRemoved(MediaRouter router, RouteInfo route) {
mRouteAdapter.remove(route); mRouteAdapter.remove(route);
if (route.equals(mSelectedRoute)) { if (route.equals(mSelectedRoute)) {
mPlaying = false; mPlaying = false;
onBackPressed(); onBackPressed();
} }
} }
@Override @Override
public void onRouteSelected(MediaRouter router, RouteInfo route) { public void onRouteSelected(MediaRouter router, RouteInfo route) {
} }
@Override @Override
public void onRouteUnselected(MediaRouter router, RouteInfo route) { public void onRouteUnselected(MediaRouter router, RouteInfo route) {
} }
@Override @Override
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
} }
@Override @Override
public void onRoutePresentationDisplayChanged( public void onRoutePresentationDisplayChanged(
MediaRouter router, RouteInfo route) { MediaRouter router, RouteInfo route) {
} }
@Override @Override
public void onProviderAdded(MediaRouter router, ProviderInfo provider) { public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
} }
@Override @Override
public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
} }
@Override @Override
public void onProviderChanged(MediaRouter router, ProviderInfo provider) { public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
} }
}; };
} }
/** /**
* Selects a route or starts playback (depending on current ListAdapter). * Selects a route or starts playback (depending on current ListAdapter).
@ -367,25 +366,24 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
@Override @Override
public boolean onBackPressed() { public boolean onBackPressed() {
if (mListView.getAdapter() == mPlaylistAdapter) { if (mListView.getAdapter() == mPlaylistAdapter) {
if (mPlaying) { if (mPlaying)
new AlertDialog.Builder(getActivity()) new AlertDialog.Builder(getActivity())
.setMessage(R.string.exit_renderer) .setMessage(R.string.exit_renderer)
.setPositiveButton(android.R.string.yes, .setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int which) { int which) {
mMediaRouterPlayService.getService().stop(); mMediaRouterPlayService.getService().stop();
deviceListMode(); deviceListMode();
} }
}) })
.setNegativeButton(android.R.string.no, null) .setNegativeButton(android.R.string.no, null)
.show(); .show();
}
else else
deviceListMode(); deviceListMode();
return true; return true;
} }
return false; return false;
} }
@ -413,24 +411,24 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
break; break;
case R.id.previous: case R.id.previous:
mPreviousTapCount++; mPreviousTapCount++;
Handler handler = new Handler(); Handler handler = new Handler();
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
// Single tap. // Single tap.
mPreviousTapCount = 0; mPreviousTapCount = 0;
s.play(s.getCurrentTrack()); s.play(s.getCurrentTrack());
changePlayPauseState(true); changePlayPauseState(true);
} }
}; };
if (mPreviousTapCount == 1) if (mPreviousTapCount == 1)
handler.postDelayed(r, ViewConfiguration.getDoubleTapTimeout()); handler.postDelayed(r, ViewConfiguration.getDoubleTapTimeout());
else if(mPreviousTapCount == 2) { else if(mPreviousTapCount == 2) {
// Double tap. // Double tap.
mPreviousTapCount = 0; mPreviousTapCount = 0;
s.playPrevious(); s.playPrevious();
} }
break; break;
case R.id.next: case R.id.next:
boolean stillPlaying = s.playNext(); boolean stillPlaying = s.playNext();
@ -454,10 +452,10 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mShuffle.setColorFilter((s.getShuffleEnabled()) mShuffle.setColorFilter((s.getShuffleEnabled())
? highlight ? highlight
: transparent); : transparent);
mRepeat.setColorFilter((s.getRepeatEnabled()) mRepeat.setColorFilter((s.getRepeatEnabled())
? highlight ? highlight
: transparent); : transparent);
} }
/** /**
@ -512,7 +510,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
changePlayPauseState(true); changePlayPauseState(true);
} else { } else {
Toast.makeText(getActivity(), R.string.select_route, Toast.LENGTH_SHORT) Toast.makeText(getActivity(), R.string.select_route, Toast.LENGTH_SHORT)
.show(); .show();
mStartPlayingOnSelect = start; mStartPlayingOnSelect = start;
} }
} }
@ -525,14 +523,14 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
*/ */
private String generateTimeString(int time) { private String generateTimeString(int time) {
assert(time >= 0); assert(time >= 0);
int seconds = (int) time % 60; int seconds = time % 60;
int minutes = (int) time / 60; int minutes = time / 60;
if (minutes > 99) if (minutes > 99)
return "99:99"; return "99:99";
else else
return Integer.toString(minutes) + ":" + return Integer.toString(minutes) + ":" +
((seconds > 9) ((seconds > 9)
? seconds ? seconds
: "0" + Integer.toString(seconds)); : "0" + Integer.toString(seconds));
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -112,210 +112,209 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
*/ */
private Stack<Parcelable> mListState = new Stack<Parcelable>(); private Stack<Parcelable> mListState = new Stack<Parcelable>();
protected AndroidUpnpService mUpnpService; protected AndroidUpnpService mUpnpService;
private ServiceConnection mUpnpServiceConnection = new ServiceConnection() { private ServiceConnection mUpnpServiceConnection = new ServiceConnection() {
/** /**
* Registers DeviceListener, adds known devices and starts search if requested. * Registers DeviceListener, adds known devices and starts search if requested.
*/ */
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
mUpnpService = (AndroidUpnpService) service; mUpnpService = (AndroidUpnpService) service;
mUpnpService.getRegistry().addListener(mServerAdapter); mUpnpService.getRegistry().addListener(mServerAdapter);
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices()) for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
mServerAdapter.deviceAdded(d); mServerAdapter.deviceAdded(d);
mUpnpService.getControlPoint().search(); mUpnpService.getControlPoint().search();
if (mRestoreServer != null) { if (mRestoreServer != null) {
mCurrentServer = mUpnpService.getControlPoint().getRegistry() mCurrentServer = mUpnpService.getControlPoint().getRegistry()
.getDevice(new UDN(mRestoreServer.replace("uuid:", "")), false); .getDevice(new UDN(mRestoreServer.replace("uuid:", "")), false);
if (mCurrentServer != null) { if (mCurrentServer != null) {
setListAdapter(mFileAdapter); setListAdapter(mFileAdapter);
// Duplicate the top element because getFiles will remove it. // Duplicate the top element because getFiles will remove it.
mListState.add(mListState.peek()); mListState.add(mListState.peek());
getFiles(true); getFiles(true);
} }
getListView().onRestoreInstanceState(mListState.peek()); getListView().onRestoreInstanceState(mListState.peek());
} }
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
mUpnpService = null; mUpnpService = null;
} }
}; };
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
return inflater.inflate(R.layout.server_fragment, null); return inflater.inflate(R.layout.server_fragment, null);
}; };
/** /**
* Initializes ListView adapters, launches Cling UPNP service, registers * Initializes ListView adapters, launches Cling UPNP service, registers
* wifi state change listener and restores instance state if possible. * wifi state change listener and restores instance state if possible.
*/ */
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
mFileAdapter = new FileArrayAdapter(getActivity()); mFileAdapter = new FileArrayAdapter(getActivity());
mServerAdapter = new DeviceArrayAdapter( mServerAdapter = new DeviceArrayAdapter(
getActivity(), DeviceArrayAdapter.SERVER); getActivity(), DeviceArrayAdapter.SERVER);
setListAdapter(mServerAdapter); setListAdapter(mServerAdapter);
getActivity().getApplicationContext().bindService( getActivity().getApplicationContext().bindService(
new Intent(getActivity(), AndroidUpnpServiceImpl.class), new Intent(getActivity(), AndroidUpnpServiceImpl.class),
mUpnpServiceConnection, mUpnpServiceConnection,
Context.BIND_AUTO_CREATE Context.BIND_AUTO_CREATE
); );
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
getActivity().registerReceiver(mWifiReceiver, filter); getActivity().registerReceiver(mWifiReceiver, filter);
if (savedInstanceState != null) { if (savedInstanceState != null) {
mRestoreServer = savedInstanceState.getString("current_server"); mRestoreServer = savedInstanceState.getString("current_server");
mCurrentPath.addAll(savedInstanceState.getStringArrayList("path")); mCurrentPath.addAll(savedInstanceState.getStringArrayList("path"));
mListState.addAll(savedInstanceState.getParcelableArrayList("list_state")); mListState.addAll(savedInstanceState.getParcelableArrayList("list_state"));
} else } else
mListState.push(getListView().onSaveInstanceState()); mListState.push(getListView().onSaveInstanceState());
} }
/** /**
* Stores current server and path/list state stacks. * Stores current server and path/list state stacks.
*/ */
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putString("current_server", (mCurrentServer != null) outState.putString("current_server", (mCurrentServer != null)
? mCurrentServer.getIdentity().getUdn().toString() ? mCurrentServer.getIdentity().getUdn().toString()
: ""); : "");
outState.putStringArrayList("path", new ArrayList<String>(mCurrentPath)); outState.putStringArrayList("path", new ArrayList<String>(mCurrentPath));
mListState.pop(); mListState.pop();
mListState.push(getListView().onSaveInstanceState()); mListState.push(getListView().onSaveInstanceState());
outState.putParcelableArrayList("list_state", new ArrayList<Parcelable>(mListState)); outState.putParcelableArrayList("list_state", new ArrayList<Parcelable>(mListState));
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
getActivity().getApplicationContext().unbindService(mUpnpServiceConnection); getActivity().getApplicationContext().unbindService(mUpnpServiceConnection);
getActivity().unregisterReceiver(mWifiReceiver); getActivity().unregisterReceiver(mWifiReceiver);
} }
/** /**
* Enters directory browsing mode or enters a deeper level directory. * Enters directory browsing mode or enters a deeper level directory.
*/ */
@Override @Override
public void onListItemClick(ListView l, View v, int position, long id) { public void onListItemClick(ListView l, View v, int position, long id) {
if (getListAdapter() == mServerAdapter) if (getListAdapter() == mServerAdapter)
browsingMode(mServerAdapter.getItem(position)); browsingMode(mServerAdapter.getItem(position));
else if (getListAdapter() == mFileAdapter) { else if (getListAdapter() == mFileAdapter)
if (mFileAdapter.getItem(position) instanceof Container) if (mFileAdapter.getItem(position) instanceof Container)
getFiles(((Container) mFileAdapter.getItem(position)).getId()); getFiles(((Container) mFileAdapter.getItem(position)).getId());
else { else {
List<Item> playlist = new ArrayList<Item>(); List<Item> playlist = new ArrayList<Item>();
for (int i = 0; i < mFileAdapter.getCount(); i++) { for (int i = 0; i < mFileAdapter.getCount(); i++)
if (mFileAdapter.getItem(i) instanceof Item) if (mFileAdapter.getItem(i) instanceof Item)
playlist.add((Item) mFileAdapter.getItem(i)); playlist.add((Item) mFileAdapter.getItem(i));
} MainActivity activity = (MainActivity) getActivity();
MainActivity activity = (MainActivity) getActivity(); activity.play(playlist, position);
activity.play(playlist, position); }
} }
}
}
/** /**
* Displays available servers in the ListView. * Displays available servers in the ListView.
*/ */
private void serverMode() { private void serverMode() {
setListAdapter(mServerAdapter); setListAdapter(mServerAdapter);
mCurrentServer = null; mCurrentServer = null;
TextView emptyView = (TextView) getListView().getEmptyView(); TextView emptyView = (TextView) getListView().getEmptyView();
emptyView.setText(R.string.device_list_empty); emptyView.setText(R.string.device_list_empty);
getListView().onRestoreInstanceState(mListState.pop()); getListView().onRestoreInstanceState(mListState.pop());
} }
/** /**
* Displays files for server (starting from root). * Displays files for server (starting from root).
*/ */
private void browsingMode(Device<?, ?, ?> server) { private void browsingMode(Device<?, ?, ?> server) {
setListAdapter(mFileAdapter); setListAdapter(mFileAdapter);
mCurrentServer = server; mCurrentServer = server;
getFiles(ROOT_DIRECTORY); getFiles(ROOT_DIRECTORY);
TextView emptyView = (TextView) getListView().getEmptyView(); TextView emptyView = (TextView) getListView().getEmptyView();
emptyView.setText(R.string.folder_list_empty); emptyView.setText(R.string.folder_list_empty);
} }
/**
* Opens a new directory and displays it.
*/
private void getFiles(String directory) {
mListState.push(getListView().onSaveInstanceState());
mCurrentPath.push(directory);
getFiles(false);
}
/** /**
* Displays the current directory on the ListView. * Opens a new directory and displays it.
* */
* @param restoreListState True if we are going back up the directory tree, private void getFiles(String directory) {
* which means we restore scroll position etc. This pops mListState.push(getListView().onSaveInstanceState());
* mListState. mCurrentPath.push(directory);
*/ getFiles(false);
private void getFiles(final boolean restoreListState) { }
if (mCurrentServer == null)
return;
Service<?, ?> service = mCurrentServer.findService( /**
new ServiceType("schemas-upnp-org", "ContentDirectory")); * Displays the current directory on the ListView.
mUpnpService.getControlPoint().execute(new Browse(service, *
* @param restoreListState True if we are going back up the directory tree,
* which means we restore scroll position etc. This pops
* mListState.
*/
private void getFiles(final boolean restoreListState) {
if (mCurrentServer == null)
return;
Service<?, ?> service = mCurrentServer.findService(
new ServiceType("schemas-upnp-org", "ContentDirectory"));
mUpnpService.getControlPoint().execute(new Browse(service,
mCurrentPath.peek(), BrowseFlag.DIRECT_CHILDREN) { mCurrentPath.peek(), BrowseFlag.DIRECT_CHILDREN) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void received(ActionInvocation actionInvocation, public void received(ActionInvocation actionInvocation,
final DIDLContent didl) { final DIDLContent didl) {
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mFileAdapter.clear();
for (Container c : didl.getContainers())
mFileAdapter.add(c);
for (Item i : didl.getItems())
mFileAdapter.add(i);
if (restoreListState)
getListView().onRestoreInstanceState(mListState.pop());
else
getListView().setSelectionFromTop(0, 0);
}
});
}
@Override @Override
public void updateStatus(Status status) { public void run() {
mFileAdapter.clear();
for (Container c : didl.getContainers())
mFileAdapter.add(c);
for (Item i : didl.getItems())
mFileAdapter.add(i);
if (restoreListState)
getListView().onRestoreInstanceState(mListState.pop());
else
getListView().setSelectionFromTop(0, 0);
} }
@SuppressWarnings("rawtypes")
@Override
public void failure(ActionInvocation actionInvocation,
UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Failed to load directory contents: " +
defaultMessage);
}
}); });
} }
/** @Override
* Handles back button press to traverse directories (while browsing public void updateStatus(Status status) {
* directories). }
*/
@SuppressWarnings("rawtypes")
@Override
public void failure(ActionInvocation actionInvocation,
UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Failed to load directory contents: " +
defaultMessage);
}
});
}
/**
* Handles back button press to traverse directories (while browsing
* directories).
*/
public boolean onBackPressed() { public boolean onBackPressed() {
if (getListAdapter() == mServerAdapter) if (getListAdapter() == mServerAdapter)
return false; return false;
mCurrentPath.pop(); mCurrentPath.pop();
if (mCurrentPath.empty()) if (mCurrentPath.empty())
@ -331,37 +330,35 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
*/ */
private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() { private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
getActivity(); getActivity();
ConnectivityManager connManager = (ConnectivityManager) ConnectivityManager connManager = (ConnectivityManager)
getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (wifi.isConnected()) { if (wifi.isConnected()) {
if (mUpnpService != null) { if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint() for (Device<?, ?, ?> d : mUpnpService.getControlPoint()
.getRegistry().getDevices()) .getRegistry().getDevices())
mServerAdapter.deviceAdded(d); mServerAdapter.deviceAdded(d);
mUpnpService.getControlPoint().search(); mUpnpService.getControlPoint().search();
} }
} } else
else { for (int i = 0; i < mServerAdapter.getCount(); i++) {
for (int i = 0; i < mServerAdapter.getCount(); i++) { Device<?, ?, ?> d = mServerAdapter.getItem(i);
Device<?, ?, ?> d = mServerAdapter.getItem(i); UDN udn = new UDN(d.getIdentity().getUdn().toString());
UDN udn = new UDN(d.getIdentity().getUdn().toString()); if (mUpnpService.getControlPoint().getRegistry()
if (mUpnpService.getControlPoint().getRegistry() .getDevice(udn, false) == null) {
.getDevice(udn, false) == null) { mServerAdapter.deviceRemoved(d);
mServerAdapter.deviceRemoved(d); if (d.equals(mCurrentServer)) {
if (d.equals(mCurrentServer)) { mListState.setSize(2);
mListState.setSize(2); mCurrentPath.clear();
mCurrentPath.clear(); serverMode();
serverMode(); }
} }
} }
} }
}
}
}; };
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -118,7 +118,7 @@ public class MediaRouterPlayService extends Service {
if (route.equals(mCurrentRoute)) if (route.equals(mCurrentRoute))
stopForeground(true); stopForeground(true);
} }
}; };
/** /**
* Creates a notification after the icon bitmap is loaded. * Creates a notification after the icon bitmap is loaded.
@ -132,16 +132,16 @@ public class MediaRouterPlayService extends Service {
if (mCurrentTrack < mPlaylist.size()) { if (mCurrentTrack < mPlaylist.size()) {
title = mPlaylist.get(mCurrentTrack).getTitle(); title = mPlaylist.get(mCurrentTrack).getTitle();
if (mPlaylist.get(mCurrentTrack) instanceof MusicTrack) { if (mPlaylist.get(mCurrentTrack) instanceof MusicTrack) {
MusicTrack track = (MusicTrack) mPlaylist.get(mCurrentTrack); MusicTrack track = (MusicTrack) mPlaylist.get(mCurrentTrack);
if (track.getArtists().length > 0) if (track.getArtists().length > 0)
artist = track.getArtists()[0].getName(); artist = track.getArtists()[0].getName();
} }
} }
Intent intent = new Intent(MediaRouterPlayService.this, MainActivity.class); Intent intent = new Intent(MediaRouterPlayService.this, MainActivity.class);
intent.setAction("showRouteFragment"); intent.setAction("showRouteFragment");
Notification notification = new NotificationCompat.Builder(MediaRouterPlayService.this) Notification notification = new NotificationCompat.Builder(MediaRouterPlayService.this)
.setContentIntent(PendingIntent.getActivity(MediaRouterPlayService.this, 0, .setContentIntent(PendingIntent.getActivity(MediaRouterPlayService.this, 0,
intent, 0)) intent, 0))
.setContentTitle(title) .setContentTitle(title)
.setContentText(artist) .setContentText(artist)
.setLargeIcon(result) .setLargeIcon(result)
@ -156,7 +156,7 @@ public class MediaRouterPlayService extends Service {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mMediaRouter = MediaRouter.getInstance(this); mMediaRouter = MediaRouter.getInstance(this);
pollStatus(); pollStatus();
} }
@ -174,9 +174,9 @@ public class MediaRouterPlayService extends Service {
public boolean onUnbind(Intent intent) { public boolean onUnbind(Intent intent) {
new Handler().postDelayed(new Runnable() { new Handler().postDelayed(new Runnable() {
public void run() { public void run() {
if (!mPollingStatus && !mBound) if (!mPollingStatus && !mBound)
stopSelf(); stopSelf();
} }
}, 5000); }, 5000);
mBound = false; mBound = false;
return super.onUnbind(intent); return super.onUnbind(intent);
@ -189,12 +189,12 @@ public class MediaRouterPlayService extends Service {
public void selectRoute(RouteInfo route) { public void selectRoute(RouteInfo route) {
mMediaRouter.removeCallback(mRouteRemovedCallback); mMediaRouter.removeCallback(mRouteRemovedCallback);
mMediaRouter.selectRoute(route); mMediaRouter.selectRoute(route);
MediaRouteSelector selector = new MediaRouteSelector.Builder() MediaRouteSelector selector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build(); .build();
mMediaRouter.addCallback(selector, mRouteRemovedCallback, 0); mMediaRouter.addCallback(selector, mRouteRemovedCallback, 0);
mCurrentRoute = route; mCurrentRoute = route;
} }
public void sendControlRequest(Intent intent) { public void sendControlRequest(Intent intent) {
@ -211,7 +211,7 @@ public class MediaRouterPlayService extends Service {
mCurrentTrack = trackNumber; mCurrentTrack = trackNumber;
Item track = mPlaylist.get(trackNumber); Item track = mPlaylist.get(trackNumber);
DIDLParser parser = new DIDLParser(); DIDLParser parser = new DIDLParser();
DIDLContent didl = new DIDLContent(); DIDLContent didl = new DIDLContent();
didl.addItem(track); didl.addItem(track);
String metadata = ""; String metadata = "";
@ -222,26 +222,26 @@ public class MediaRouterPlayService extends Service {
Log.w(TAG, "Metadata generation failed", e); Log.w(TAG, "Metadata generation failed", e);
} }
Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.setData(Uri.parse(track.getFirstResource().getValue())); intent.setData(Uri.parse(track.getFirstResource().getValue()));
intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata); intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, mMediaRouter.getSelectedRoute().sendControlRequest(intent,
new ControlRequestCallback() { new ControlRequestCallback() {
@Override @Override
public void onResult(Bundle data) { public void onResult(Bundle data) {
mSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID); mSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
mItemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID); mItemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
mPollingStatus = true; mPollingStatus = true;
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack) new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class)); .getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
if (mRouterFragment.get() != null) if (mRouterFragment.get() != null)
mRouterFragment.get().receiveIsPlaying(mCurrentTrack); mRouterFragment.get().receiveIsPlaying(mCurrentTrack);
} }
}); });
} }
/** /**
@ -251,12 +251,12 @@ public class MediaRouterPlayService extends Service {
if (mPlaylist.isEmpty()) if (mPlaylist.isEmpty())
return; return;
Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE); Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null); mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
mPollingStatus = false; mPollingStatus = false;
stopForeground(true); stopForeground(true);
} }
/** /**
@ -266,13 +266,13 @@ public class MediaRouterPlayService extends Service {
if (mPlaylist.isEmpty()) if (mPlaylist.isEmpty())
return; return;
Intent intent = new Intent(MediaControlIntent.ACTION_RESUME); Intent intent = new Intent(MediaControlIntent.ACTION_RESUME);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null); mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
mPollingStatus = true; mPollingStatus = true;
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack) new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class)); .getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
} }
/** /**
@ -282,25 +282,25 @@ public class MediaRouterPlayService extends Service {
if (mPlaylist.isEmpty()) if (mPlaylist.isEmpty())
return; return;
Intent intent = new Intent(MediaControlIntent.ACTION_STOP); Intent intent = new Intent(MediaControlIntent.ACTION_STOP);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null); mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
mPollingStatus = false; mPollingStatus = false;
stopForeground(true); stopForeground(true);
} }
public void seek(int seconds) { public void seek(int seconds) {
if (mPlaylist.isEmpty()) if (mPlaylist.isEmpty())
return; return;
Intent intent = new Intent(MediaControlIntent.ACTION_SEEK); Intent intent = new Intent(MediaControlIntent.ACTION_SEEK);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mItemId); intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mItemId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION,
(long) seconds * 1000); (long) seconds * 1000);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null); mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
} }
/** /**
@ -381,23 +381,23 @@ public class MediaRouterPlayService extends Service {
i.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mItemId); i.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mItemId);
mMediaRouter.getSelectedRoute().sendControlRequest(i, mMediaRouter.getSelectedRoute().sendControlRequest(i,
new ControlRequestCallback() { new ControlRequestCallback() {
@Override @Override
public void onResult(Bundle data) { public void onResult(Bundle data) {
MediaItemStatus status = MediaItemStatus.fromBundle(data); MediaItemStatus status = MediaItemStatus.fromBundle(data);
if (status == null) if (status == null)
return; return;
if (mRouterFragment.get() != null) if (mRouterFragment.get() != null)
mRouterFragment.get().receivePlaybackStatus(status); mRouterFragment.get().receivePlaybackStatus(status);
if (status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PENDING && if (status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PENDING &&
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_BUFFERING && status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_BUFFERING &&
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING) status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING)
stopForeground(true); stopForeground(true);
if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED) if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED)
playNext(); playNext();
} }
}); });
} }
new Handler().postDelayed(new Runnable() { new Handler().postDelayed(new Runnable() {
@ -405,7 +405,7 @@ public class MediaRouterPlayService extends Service {
@Override @Override
public void run() { public void run() {
pollStatus(); pollStatus();
} }
}, 1000); }, 1000);
} }
@ -440,4 +440,5 @@ public class MediaRouterPlayService extends Service {
public RouteInfo getCurrentRoute() { public RouteInfo getCurrentRoute() {
return mCurrentRoute; return mCurrentRoute;
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -44,6 +44,7 @@ public class MediaRouterPlayServiceBinder extends Binder {
} }
public MediaRouterPlayService getService() { public MediaRouterPlayService getService() {
return mService; return mService;
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -70,13 +70,16 @@ final class Provider extends MediaRouteProvider {
// Device has been added. // Device has been added.
// param: Device device // param: Device device
public static final int MSG_RENDERER_ADDED = 1; public static final int MSG_RENDERER_ADDED = 1;
// Device has been removed. // Device has been removed.
// param: int id // param: int id
public static final int MSG_RENDERER_REMOVED = 2; public static final int MSG_RENDERER_REMOVED = 2;
// Playback status information, retrieved after RemotePlayService.MSG_GET_STATUS. // Playback status information, retrieved after RemotePlayService.MSG_GET_STATUS.
// param: bundle media_item_status // param: bundle media_item_status
// param: int hash // param: int hash
public static final int MSG_STATUS_INFO = 3; public static final int MSG_STATUS_INFO = 3;
// Indicates an error in communication between RemotePlayService and renderer. // Indicates an error in communication between RemotePlayService and renderer.
// param: String error // param: String error
public static final int MSG_ERROR = 4; public static final int MSG_ERROR = 4;
@ -93,23 +96,23 @@ final class Provider extends MediaRouteProvider {
public int volumeMax; public int volumeMax;
public static final Parcelable.Creator<Device> CREATOR public static final Parcelable.Creator<Device> CREATOR
= new Parcelable.Creator<Device>() { = new Parcelable.Creator<Device>() {
public Device createFromParcel(Parcel in) { public Device createFromParcel(Parcel in) {
return new Device(in); return new Device(in);
} }
public Device[] newArray(int size) { public Device[] newArray(int size) {
return new Device[size]; return new Device[size];
} }
}; };
private Device(Parcel in) { private Device(Parcel in) {
id = in.readString(); id = in.readString();
name = in.readString(); name = in.readString();
description = in.readString(); description = in.readString();
volume = in.readInt(); volume = in.readInt();
volumeMax = in.readInt(); volumeMax = in.readInt();
} }
public Device(String id, String name, String description, int volume, int volumeMax) { public Device(String id, String name, String description, int volume, int volumeMax) {
this.id = id; this.id = id;
@ -135,267 +138,267 @@ final class Provider extends MediaRouteProvider {
} }
private HashMap<String, Device> mDevices = new HashMap<String, Device>(); private HashMap<String, Device> mDevices = new HashMap<String, Device>();
private SparseArray<Pair<Intent, ControlRequestCallback>> mRequests = private SparseArray<Pair<Intent, ControlRequestCallback>> mRequests =
new SparseArray<Pair<Intent, ControlRequestCallback>>(); new SparseArray<Pair<Intent, ControlRequestCallback>>();
IRemotePlayService mIRemotePlayService; IRemotePlayService mIRemotePlayService;
private ServiceConnection mConnection = new ServiceConnection() { private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
mIRemotePlayService = IRemotePlayService.Stub.asInterface(service); mIRemotePlayService = IRemotePlayService.Stub.asInterface(service);
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
mIRemotePlayService = null; mIRemotePlayService = null;
} }
}; };
private static final ArrayList<IntentFilter> CONTROL_FILTERS; private static final ArrayList<IntentFilter> CONTROL_FILTERS;
static { static {
IntentFilter f = new IntentFilter(); IntentFilter f = new IntentFilter();
f.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); f.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
f.addAction(MediaControlIntent.ACTION_PLAY); f.addAction(MediaControlIntent.ACTION_PLAY);
f.addAction(MediaControlIntent.ACTION_PAUSE); f.addAction(MediaControlIntent.ACTION_PAUSE);
f.addAction(MediaControlIntent.ACTION_SEEK); f.addAction(MediaControlIntent.ACTION_SEEK);
f.addAction(MediaControlIntent.ACTION_STOP); f.addAction(MediaControlIntent.ACTION_STOP);
f.addDataScheme("http"); f.addDataScheme("http");
f.addDataScheme("https"); f.addDataScheme("https");
try { try {
f.addDataType("video/*"); f.addDataType("video/*");
f.addDataType("audio/*"); f.addDataType("audio/*");
} catch (MalformedMimeTypeException ex) { } catch (MalformedMimeTypeException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
CONTROL_FILTERS = new ArrayList<IntentFilter>(); CONTROL_FILTERS = new ArrayList<IntentFilter>();
CONTROL_FILTERS.add(f); CONTROL_FILTERS.add(f);
} }
/** /**
* Listens for messages about devices. * Listens for messages about devices.
*/ */
static private class DeviceListener extends Handler { static private class DeviceListener extends Handler {
private final WeakReference<Provider> mService; private final WeakReference<Provider> mService;
DeviceListener(Provider provider) { DeviceListener(Provider provider) {
mService = new WeakReference<Provider>(provider); mService = new WeakReference<Provider>(provider);
} }
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
if (mService.get() != null) if (mService.get() != null)
mService.get().handleMessage(msg); mService.get().handleMessage(msg);
} }
} }
final Messenger mListener = new Messenger(new DeviceListener(this)); final Messenger mListener = new Messenger(new DeviceListener(this));
public Provider(Context context) { public Provider(Context context) {
super(context); super(context);
context.bindService( context.bindService(
new Intent(context, RemotePlayService.class), new Intent(context, RemotePlayService.class),
mConnection, mConnection,
Context.BIND_AUTO_CREATE Context.BIND_AUTO_CREATE
); );
} }
public void close() { public void close() {
getContext().unbindService(mConnection); getContext().unbindService(mConnection);
} }
@Override @Override
public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
try { try {
if (request != null && request.isActiveScan()) if (request != null && request.isActiveScan())
mIRemotePlayService.startSearch(mListener); mIRemotePlayService.startSearch(mListener);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override @Override
public RouteController onCreateRouteController(String routeId) { public RouteController onCreateRouteController(String routeId) {
return new RouteController(routeId); return new RouteController(routeId);
} }
private void updateRoutes() { private void updateRoutes() {
Builder builder = new Builder(); Builder builder = new Builder();
for (Entry<String, Device> d : mDevices.entrySet()) { for (Entry<String, Device> d : mDevices.entrySet()) {
MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder( MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
d.getValue().id, d.getValue().id,
d.getValue().name) d.getValue().name)
.setDescription(getContext().getResources() .setDescription(getContext().getResources()
.getString(R.string.route_description)) .getString(R.string.route_description))
.addControlFilters(CONTROL_FILTERS) .addControlFilters(CONTROL_FILTERS)
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(d.getValue().volumeMax) .setVolumeMax(d.getValue().volumeMax)
.setVolume(d.getValue().volume) .setVolume(d.getValue().volume)
.build(); .build();
builder.addRoute(routeDescriptor); builder.addRoute(routeDescriptor);
} }
setDescriptor(builder.build()); setDescriptor(builder.build());
} }
/** /**
* Receives and forwards device selections, volume change * Receives and forwards device selections, volume change
* requests and control requests. * requests and control requests.
*/ */
private final class RouteController extends MediaRouteProvider.RouteController { private final class RouteController extends MediaRouteProvider.RouteController {
private final String mRouteId;
public RouteController(String routeId) { private final String mRouteId;
mRouteId = routeId;
}
@Override public RouteController(String routeId) {
public void onRelease() { mRouteId = routeId;
} }
@Override @Override
public void onSelect() { public void onRelease() {
try { }
@Override
public void onSelect() {
try {
mIRemotePlayService.selectRenderer(mRouteId); mIRemotePlayService.selectRenderer(mRouteId);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override @Override
public void onUnselect() { public void onUnselect() {
try { try {
mIRemotePlayService.unselectRenderer(mRouteId); mIRemotePlayService.unselectRenderer(mRouteId);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override @Override
public void onSetVolume(int volume) { public void onSetVolume(int volume) {
if (volume < 0 || volume > mDevices.get(mRouteId).volumeMax) if (volume < 0 || volume > mDevices.get(mRouteId).volumeMax)
return; return;
try { try {
mIRemotePlayService.setVolume(volume); mIRemotePlayService.setVolume(volume);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
mDevices.get(mRouteId).volume = volume; mDevices.get(mRouteId).volume = volume;
updateRoutes(); updateRoutes();
} }
@Override @Override
public void onUpdateVolume(int delta) { public void onUpdateVolume(int delta) {
onSetVolume(mDevices.get(mRouteId).volume + delta); onSetVolume(mDevices.get(mRouteId).volume + delta);
} }
/** /**
* Handles play, pause, resume, stop, seek and get_status requests for this route. * Handles play, pause, resume, stop, seek and get_status requests for this route.
*/ */
@Override @Override
public boolean onControlRequest(Intent intent, ControlRequestCallback callback) { public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
try { try {
if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)) { if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)) {
String metadata = (intent.hasExtra(MediaControlIntent.EXTRA_ITEM_METADATA)) String metadata = (intent.hasExtra(MediaControlIntent.EXTRA_ITEM_METADATA))
? intent.getExtras().getString(MediaControlIntent.EXTRA_ITEM_METADATA) ? intent.getExtras().getString(MediaControlIntent.EXTRA_ITEM_METADATA)
: null; : null;
mIRemotePlayService.play(intent.getDataString(), metadata); mIRemotePlayService.play(intent.getDataString(), metadata);
// Store in intent extras for later. // Store in intent extras for later.
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mRouteId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mRouteId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, intent.getDataString()); intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, intent.getDataString());
getItemStatus(intent, callback); getItemStatus(intent, callback);
return true; return true;
} }
else if (intent.getAction().equals(MediaControlIntent.ACTION_PAUSE)) { else if (intent.getAction().equals(MediaControlIntent.ACTION_PAUSE)) {
mIRemotePlayService.pause(mRouteId); mIRemotePlayService.pause(mRouteId);
return true; return true;
} }
else if (intent.getAction().equals(MediaControlIntent.ACTION_RESUME)) { else if (intent.getAction().equals(MediaControlIntent.ACTION_RESUME)) {
mIRemotePlayService.resume(mRouteId); mIRemotePlayService.resume(mRouteId);
return true; return true;
} }
else if (intent.getAction().equals(MediaControlIntent.ACTION_STOP)) { else if (intent.getAction().equals(MediaControlIntent.ACTION_STOP)) {
mIRemotePlayService.stop(mRouteId); mIRemotePlayService.stop(mRouteId);
return true; return true;
} }
else if (intent.getAction().equals(MediaControlIntent.ACTION_SEEK)) { else if (intent.getAction().equals(MediaControlIntent.ACTION_SEEK)) {
mIRemotePlayService.seek(mRouteId, mIRemotePlayService.seek(mRouteId,
intent.getStringExtra( intent.getStringExtra(
MediaControlIntent.EXTRA_ITEM_ID), MediaControlIntent.EXTRA_ITEM_ID),
intent.getLongExtra( intent.getLongExtra(
MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0)); MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0));
getItemStatus(intent, callback); getItemStatus(intent, callback);
return true; return true;
} }
else if(intent.getAction().equals(MediaControlIntent.ACTION_GET_STATUS)) { else if(intent.getAction().equals(MediaControlIntent.ACTION_GET_STATUS)) {
getItemStatus(intent, callback); getItemStatus(intent, callback);
return true; return true;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
return false; return false;
} }
}
/** }
* Requests status info via RemotePlayService, stores intent and callback to
* access later in handleMessage.
*/
private void getItemStatus(Intent intent, ControlRequestCallback callback)
throws RemoteException {
if (callback == null)
return;
Pair<Intent, ControlRequestCallback> pair = /**
new Pair<Intent, ControlRequestCallback>(intent, callback); * Requests status info via RemotePlayService, stores intent and callback to
int r = new Random().nextInt(); * access later in handleMessage.
mRequests.put(r, pair); */
mIRemotePlayService.getItemStatus( private void getItemStatus(Intent intent, ControlRequestCallback callback)
intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID), throws RemoteException {
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID), if (callback == null)
r); return;
}
/** Pair<Intent, ControlRequestCallback> pair =
* Handles device add and remove as well as sending status info requested earlier. new Pair<Intent, ControlRequestCallback>(intent, callback);
*/ int r = new Random().nextInt();
public void handleMessage(Message msg) { mRequests.put(r, pair);
Bundle data = msg.getData(); mIRemotePlayService.getItemStatus(
intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID),
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID),
r);
}
/**
* Handles device add and remove as well as sending status info requested earlier.
*/
public void handleMessage(Message msg) {
Bundle data = msg.getData();
switch (msg.what) { switch (msg.what) {
case MSG_RENDERER_ADDED: case MSG_RENDERER_ADDED:
msg.getData().setClassLoader(Device.class.getClassLoader()); msg.getData().setClassLoader(Device.class.getClassLoader());
Device device = (Device) data.getParcelable("device"); Device device = (Device) data.getParcelable("device");
mDevices.put(device.id, device); mDevices.put(device.id, device);
updateRoutes(); updateRoutes();
break; break;
case MSG_RENDERER_REMOVED: case MSG_RENDERER_REMOVED:
mDevices.remove(data.getString("id")); mDevices.remove(data.getString("id"));
updateRoutes(); updateRoutes();
break; break;
case MSG_STATUS_INFO: case MSG_STATUS_INFO:
Pair<Intent, ControlRequestCallback> pair = Pair<Intent, ControlRequestCallback> pair =
mRequests.get(data.getInt("hash")); mRequests.get(data.getInt("hash"));
Bundle status = data.getBundle("media_item_status"); Bundle status = data.getBundle("media_item_status");
if (pair.first.hasExtra(MediaControlIntent.EXTRA_SESSION_ID)) { if (pair.first.hasExtra(MediaControlIntent.EXTRA_SESSION_ID))
status.putString(MediaControlIntent.EXTRA_SESSION_ID, status.putString(MediaControlIntent.EXTRA_SESSION_ID,
pair.first.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID)); pair.first.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID));
} if (pair.first.hasExtra(MediaControlIntent.EXTRA_ITEM_ID))
if (pair.first.hasExtra(MediaControlIntent.EXTRA_ITEM_ID)) { status.putString(MediaControlIntent.EXTRA_ITEM_ID,
status.putString(MediaControlIntent.EXTRA_ITEM_ID, pair.first.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID));
pair.first.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID)); pair.second.onResult(status);
} break;
pair.second.onResult(status);
break;
case MSG_ERROR: case MSG_ERROR:
Toast.makeText(getContext(), data.getString("error"), Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), data.getString("error"), Toast.LENGTH_SHORT).show();
break; break;
} }
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -33,11 +33,11 @@ import android.support.v7.media.MediaRouteProviderService;
public class ProviderService extends MediaRouteProviderService { public class ProviderService extends MediaRouteProviderService {
private Provider mProvider; private Provider mProvider;
@Override @Override
public MediaRouteProvider onCreateMediaRouteProvider() { public MediaRouteProvider onCreateMediaRouteProvider() {
if (mProvider == null) { if (mProvider == null)
mProvider = new Provider(this); mProvider = new Provider(this);
}
return mProvider; return mProvider;
} }
@ -47,4 +47,5 @@ public class ProviderService extends MediaRouteProviderService {
mProvider.close(); mProvider.close();
mProvider = null; mProvider = null;
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -61,7 +61,6 @@ import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
/** /**
* Allows UPNP playback from different apps by providing a proxy interface. * Allows UPNP playback from different apps by providing a proxy interface.
* You can communicate to this service via RemotePlayServiceBinder. * You can communicate to this service via RemotePlayServiceBinder.
@ -73,41 +72,39 @@ public class RemotePlayService extends Service implements RegistryListener {
private static final String TAG = "RemotePlayService"; private static final String TAG = "RemotePlayService";
Messenger mListener; Messenger mListener;
ConcurrentHashMap<String, Device<?, ?, ?>> mDevices = ConcurrentHashMap<String, Device<?, ?, ?>> mDevices =
new ConcurrentHashMap<String, Device<?, ?, ?>>(); new ConcurrentHashMap<String, Device<?, ?, ?>>();
protected AndroidUpnpService mUpnpService; protected AndroidUpnpService mUpnpService;
private ServiceConnection mUpnpServiceConnection = new ServiceConnection() { private ServiceConnection mUpnpServiceConnection = new ServiceConnection() {
/** /**
* Registers DeviceListener, adds known devices and starts search if requested. * Registers DeviceListener, adds known devices and starts search if requested.
*/ */
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
mUpnpService = (AndroidUpnpService) service; mUpnpService = (AndroidUpnpService) service;
mUpnpService.getRegistry().addListener(RemotePlayService.this); mUpnpService.getRegistry().addListener(RemotePlayService.this);
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices()) { for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
if (d instanceof LocalDevice) if (d instanceof LocalDevice)
localDeviceAdded(mUpnpService.getRegistry(), (LocalDevice) d); localDeviceAdded(mUpnpService.getRegistry(), (LocalDevice) d);
else else
remoteDeviceAdded(mUpnpService.getRegistry(), (RemoteDevice) d); remoteDeviceAdded(mUpnpService.getRegistry(), (RemoteDevice) d);
} mUpnpService.getControlPoint().search();
mUpnpService.getControlPoint().search(); }
}
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
mUpnpService = null; mUpnpService = null;
} }
}; };
/**
/** * All active binders. The Hashmap value is unused.
* All active binders. The Hashmap value is unused. */
*/ WeakHashMap<RemotePlayServiceBinder, Boolean> mBinders =
WeakHashMap<RemotePlayServiceBinder, Boolean> mBinders = new WeakHashMap<RemotePlayServiceBinder, Boolean>();
new WeakHashMap<RemotePlayServiceBinder, Boolean>();
@Override @Override
public IBinder onBind(Intent itnent) { public IBinder onBind(Intent itnent) {
@ -122,23 +119,23 @@ public class RemotePlayService extends Service implements RegistryListener {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
bindService( bindService(
new Intent(this, AndroidUpnpServiceImpl.class), new Intent(this, AndroidUpnpServiceImpl.class),
mUpnpServiceConnection, mUpnpServiceConnection,
Context.BIND_AUTO_CREATE Context.BIND_AUTO_CREATE
); );
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mWifiReceiver, filter); registerReceiver(mWifiReceiver, filter);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
unbindService(mUpnpServiceConnection); unbindService(mUpnpServiceConnection);
unregisterReceiver(mWifiReceiver); unregisterReceiver(mWifiReceiver);
} }
/** /**
@ -146,10 +143,10 @@ public class RemotePlayService extends Service implements RegistryListener {
*/ */
void sendMessage(Message msg) { void sendMessage(Message msg) {
try { try {
mListener.send(msg); mListener.send(msg);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
@ -158,8 +155,8 @@ public class RemotePlayService extends Service implements RegistryListener {
*/ */
void sendError(String error) { void sendError(String error) {
Message msg = Message.obtain(null, Provider.MSG_ERROR, 0, 0); Message msg = Message.obtain(null, Provider.MSG_ERROR, 0, 0);
msg.getData().putString("error", error); msg.getData().putString("error", error);
sendMessage(msg); sendMessage(msg);
} }
/** /**
@ -168,43 +165,39 @@ public class RemotePlayService extends Service implements RegistryListener {
*/ */
private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() { private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
ConnectivityManager connManager = (ConnectivityManager) ConnectivityManager connManager = (ConnectivityManager)
getSystemService(CONNECTIVITY_SERVICE); getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (wifi.isConnected()) { if (wifi.isConnected()) {
if (mUpnpService != null) { if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices()) for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
deviceAdded(d); deviceAdded(d);
mUpnpService.getControlPoint().search(); mUpnpService.getControlPoint().search();
} }
} } else
else { for (Entry<String, Device<?, ?, ?>> d : mDevices.entrySet())
for (Entry<String, Device<?, ?, ?>> d : mDevices.entrySet()) { if (mUpnpService.getControlPoint().getRegistry()
if (mUpnpService.getControlPoint().getRegistry() .getDevice(new UDN(d.getKey()), false) == null) {
.getDevice(new UDN(d.getKey()), false) == null) { deviceRemoved(d.getValue());
deviceRemoved(d.getValue()); for (RemotePlayServiceBinder b : mBinders.keySet())
for (RemotePlayServiceBinder b : mBinders.keySet()) { if (b.mCurrentRenderer.equals(d.getValue())) {
if (b.mCurrentRenderer.equals(d.getValue())) { b.mSubscriptionCallback.end();
b.mSubscriptionCallback.end(); b.mCurrentRenderer = null;
b.mCurrentRenderer = null; }
} }
} }
}
}
}
}
}; };
/** /**
* Returns a device service by name for direct queries. * Returns a device service by name for direct queries.
*/ */
org.teleal.cling.model.meta.Service<?, ?> getService( org.teleal.cling.model.meta.Service<?, ?> getService(
Device<?, ?, ?> device, String name) { Device<?, ?, ?> device, String name) {
return device.findService( return device.findService(
new ServiceType("schemas-upnp-org", name)); new ServiceType("schemas-upnp-org", name));
} }
/** /**
@ -221,44 +214,44 @@ public class RemotePlayService extends Service implements RegistryListener {
if (device.getType().getType().equals("MediaRenderer") && if (device.getType().getType().equals("MediaRenderer") &&
device instanceof RemoteDevice) { device instanceof RemoteDevice) {
mDevices.put(device.getIdentity().getUdn().toString(), device); mDevices.put(device.getIdentity().getUdn().toString(), device);
try { try {
mUpnpService.getControlPoint().execute(new GetVolume(rc) { mUpnpService.getControlPoint().execute(new GetVolume(rc) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Failed to get current Volume: " + defaultMessage); Log.w(TAG, "Failed to get current Volume: " + defaultMessage);
sendError("Failed to get current Volume: " + defaultMessage); sendError("Failed to get current Volume: " + defaultMessage);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void received(ActionInvocation invocation, int currentVolume) { public void received(ActionInvocation invocation, int currentVolume) {
int maxVolume = 100; int maxVolume = 100;
if (rc.getStateVariable("Volume") != null) { if (rc.getStateVariable("Volume") != null) {
StateVariableAllowedValueRange volumeRange = StateVariableAllowedValueRange volumeRange =
rc.getStateVariable("Volume").getTypeDetails().getAllowedValueRange(); rc.getStateVariable("Volume").getTypeDetails().getAllowedValueRange();
maxVolume = (int) volumeRange.getMaximum(); maxVolume = (int) volumeRange.getMaximum();
} }
Message msg = Message.obtain(null, Provider.MSG_RENDERER_ADDED, 0, 0); Message msg = Message.obtain(null, Provider.MSG_RENDERER_ADDED, 0, 0);
msg.getData().putParcelable("device", new Provider.Device( msg.getData().putParcelable("device", new Provider.Device(
device.getIdentity().getUdn().toString(), device.getIdentity().getUdn().toString(),
device.getDisplayString(), device.getDisplayString(),
device.getDetails().getManufacturerDetails().getManufacturer(), device.getDetails().getManufacturerDetails().getManufacturer(),
currentVolume, currentVolume,
maxVolume)); maxVolume));
sendMessage(msg); sendMessage(msg);
} }
}); });
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
e.printStackTrace(); e.printStackTrace();
return; return;
} }
} }
} }
@ -271,9 +264,9 @@ public class RemotePlayService extends Service implements RegistryListener {
Message msg = Message.obtain(null, Provider.MSG_RENDERER_REMOVED, 0, 0); Message msg = Message.obtain(null, Provider.MSG_RENDERER_REMOVED, 0, 0);
String udn = device.getIdentity().getUdn().toString(); String udn = device.getIdentity().getUdn().toString();
msg.getData().putString("id", udn); msg.getData().putString("id", udn);
mDevices.remove(udn); mDevices.remove(udn);
sendMessage(msg); sendMessage(msg);
} }
} }
@ -327,4 +320,5 @@ public class RemotePlayService extends Service implements RegistryListener {
public void remoteDeviceUpdated(Registry registry, RemoteDevice device) { public void remoteDeviceUpdated(Registry registry, RemoteDevice device) {
deviceUpdated(device); deviceUpdated(device);
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -69,11 +69,11 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
private static final String TAG = "RemotePlayServiceBinder"; private static final String TAG = "RemotePlayServiceBinder";
Device<?, ?, ?> mCurrentRenderer; Device<?, ?, ?> mCurrentRenderer;
private int mPlaybackState; private int mPlaybackState;
private boolean mManuallyStopped; private boolean mManuallyStopped;
SubscriptionCallback mSubscriptionCallback; SubscriptionCallback mSubscriptionCallback;
@ -86,20 +86,19 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
@Override @Override
public void startSearch(Messenger listener) public void startSearch(Messenger listener)
throws RemoteException { throws RemoteException {
mRps.mListener = listener; mRps.mListener = listener;
} }
@Override @Override
public void selectRenderer(String id) throws RemoteException { public void selectRenderer(String id) throws RemoteException {
mCurrentRenderer = mRps.mDevices.get(id); mCurrentRenderer = mRps.mDevices.get(id);
for (RemotePlayServiceBinder b : mRps.mBinders.keySet()) { for (RemotePlayServiceBinder b : mRps.mBinders.keySet())
if (b != this && mCurrentRenderer.equals(b.mCurrentRenderer)) if (b != this && mCurrentRenderer.equals(b.mCurrentRenderer))
b.unselectRenderer(""); b.unselectRenderer("");
}
mSubscriptionCallback = new SubscriptionCallback( mSubscriptionCallback = new SubscriptionCallback(
mCurrentRenderer.findService( mCurrentRenderer.findService(
new ServiceType("schemas-upnp-org", "AVTransport")), 600) { new ServiceType("schemas-upnp-org", "AVTransport")), 600) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
@ -127,29 +126,29 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
switch (lastChange.getEventedValue(0, switch (lastChange.getEventedValue(0,
AVTransportVariable.TransportState.class) AVTransportVariable.TransportState.class)
.getValue()) { .getValue()) {
case PLAYING: case PLAYING:
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
break; break;
case PAUSED_PLAYBACK: case PAUSED_PLAYBACK:
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PAUSED; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PAUSED;
break; break;
case STOPPED: case STOPPED:
if (mManuallyStopped) { if (mManuallyStopped) {
mManuallyStopped = false; mManuallyStopped = false;
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_CANCELED; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_CANCELED;
} }
else else
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_FINISHED; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_FINISHED;
break; break;
case TRANSITIONING: case TRANSITIONING:
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PENDING; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PENDING;
break; break;
case NO_MEDIA_PRESENT: case NO_MEDIA_PRESENT:
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_ERROR; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_ERROR;
break; break;
default: default:
} }
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Failed to parse UPNP event", e); Log.w(TAG, "Failed to parse UPNP event", e);
@ -183,27 +182,27 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
stop(sessionId); stop(sessionId);
if (mSubscriptionCallback != null) if (mSubscriptionCallback != null)
mSubscriptionCallback.end(); mSubscriptionCallback.end();
mCurrentRenderer = null; mCurrentRenderer = null;
} }
/** /**
* Sets an absolute volume. The value is assumed to be within the valid * Sets an absolute volume. The value is assumed to be within the valid
* volume range. * volume range.
*/ */
@Override @Override
public void setVolume(int volume) throws RemoteException { public void setVolume(int volume) throws RemoteException {
mRps.mUpnpService.getControlPoint().execute( mRps.mUpnpService.getControlPoint().execute(
new SetVolume(mRps.getService(mCurrentRenderer, new SetVolume(mRps.getService(mCurrentRenderer,
"RenderingControl"), volume) { "RenderingControl"), volume) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Set volume failed: " + defaultMessage); Log.w(TAG, "Set volume failed: " + defaultMessage);
mRps.sendError("Set volume failed: " + defaultMessage); mRps.sendError("Set volume failed: " + defaultMessage);
} }
}); });
} }
/** /**
@ -215,36 +214,36 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_BUFFERING; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_BUFFERING;
mRps.mUpnpService.getControlPoint().execute(new SetAVTransportURI( mRps.mUpnpService.getControlPoint().execute(new SetAVTransportURI(
mRps.getService(mCurrentRenderer, "AVTransport"), mRps.getService(mCurrentRenderer, "AVTransport"),
uri, metadata) { uri, metadata) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMsg) { UpnpResponse operation, String defaultMsg) {
Log.w(TAG, "Set URI failed: " + defaultMsg); Log.w(TAG, "Set URI failed: " + defaultMsg);
mRps.sendError("Set URI failed: " + defaultMsg); mRps.sendError("Set URI failed: " + defaultMsg);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void success(ActionInvocation invocation) { public void success(ActionInvocation invocation) {
mRps.mUpnpService.getControlPoint().execute( mRps.mUpnpService.getControlPoint().execute(
new Play(mRps.getService(mCurrentRenderer, new Play(mRps.getService(mCurrentRenderer,
"AVTransport")) { "AVTransport")) {
@Override @Override
public void success(ActionInvocation invocation) { public void success(ActionInvocation invocation) {
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING; mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
} }
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Play failed: " + defaultMessage); Log.w(TAG, "Play failed: " + defaultMessage);
mRps.sendError("Play failed: " + defaultMessage); mRps.sendError("Play failed: " + defaultMessage);
} }
}); });
} }
}); });
} }
/** /**
@ -255,20 +254,20 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
mRps.mUpnpService.getControlPoint().execute( mRps.mUpnpService.getControlPoint().execute(
new Pause(mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) { new Pause(mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Pause failed, trying stop: " + defaultMessage); Log.w(TAG, "Pause failed, trying stop: " + defaultMessage);
mRps.sendError("Pause failed, trying stop: " + defaultMessage); mRps.sendError("Pause failed, trying stop: " + defaultMessage);
// Sometimes stop works even though pause does not. // Sometimes stop works even though pause does not.
try { try {
stop(sessionId); stop(sessionId);
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
}); });
} }
@Override @Override
@ -277,14 +276,14 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
new Play(mRps.getService(mRps.mDevices.get(sessionId), new Play(mRps.getService(mRps.mDevices.get(sessionId),
"AVTransport")) { "AVTransport")) {
@Override @Override
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Resume failed: " + defaultMessage); Log.w(TAG, "Resume failed: " + defaultMessage);
mRps.sendError("Resume failed: " + defaultMessage); mRps.sendError("Resume failed: " + defaultMessage);
} }
}); });
} }
/** /**
@ -294,29 +293,29 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
public void stop(String sessionId) throws RemoteException { public void stop(String sessionId) throws RemoteException {
mManuallyStopped = true; mManuallyStopped = true;
mRps.mUpnpService.getControlPoint().execute( mRps.mUpnpService.getControlPoint().execute(
new Stop(mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) { new Stop(mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
org.teleal.cling.model.message.UpnpResponse operation, org.teleal.cling.model.message.UpnpResponse operation,
String defaultMessage) { String defaultMessage) {
Log.w(TAG, "Stop failed: " + defaultMessage); Log.w(TAG, "Stop failed: " + defaultMessage);
mRps.sendError("Stop failed: " + defaultMessage); mRps.sendError("Stop failed: " + defaultMessage);
} }
}); });
} }
/** /**
* Seeks to the given absolute time in seconds. * Seeks to the given absolute time in seconds.
*/ */
@Override @Override
public void seek(String sessionId, String itemId, long milliseconds) public void seek(String sessionId, String itemId, long milliseconds)
throws RemoteException { throws RemoteException {
mRps.mUpnpService.getControlPoint().execute(new Seek( mRps.mUpnpService.getControlPoint().execute(new Seek(
mRps.getService(mRps.mDevices.get(sessionId), "AVTransport"), mRps.getService(mRps.mDevices.get(sessionId), "AVTransport"),
SeekMode.REL_TIME, SeekMode.REL_TIME,
Integer.toString((int) milliseconds / 1000)) { Integer.toString((int) milliseconds / 1000)) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
@ -342,39 +341,38 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
public void getItemStatus(String sessionId, final String itemId, final int requestHash) public void getItemStatus(String sessionId, final String itemId, final int requestHash)
throws RemoteException { throws RemoteException {
mRps.mUpnpService.getControlPoint().execute(new GetPositionInfo( mRps.mUpnpService.getControlPoint().execute(new GetPositionInfo(
mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) { mRps.getService(mRps.mDevices.get(sessionId), "AVTransport")) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Get position failed: " + defaultMessage); Log.w(TAG, "Get position failed: " + defaultMessage);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void received(ActionInvocation invocation, PositionInfo positionInfo) { public void received(ActionInvocation invocation, PositionInfo positionInfo) {
if (positionInfo.getTrackURI() == null) if (positionInfo.getTrackURI() == null)
return; return;
Message msg = Message.obtain(null, Provider.MSG_STATUS_INFO, 0, 0); Message msg = Message.obtain(null, Provider.MSG_STATUS_INFO, 0, 0);
Builder status = null; Builder status = null;
if (positionInfo.getTrackURI().equals(itemId)) { if (positionInfo.getTrackURI().equals(itemId))
status = new MediaItemStatus.Builder(mPlaybackState) status = new MediaItemStatus.Builder(mPlaybackState)
.setContentPosition(positionInfo.getTrackElapsedSeconds() * 1000) .setContentPosition(positionInfo.getTrackElapsedSeconds() * 1000)
.setContentDuration(positionInfo.getTrackDurationSeconds() * 1000) .setContentDuration(positionInfo.getTrackDurationSeconds() * 1000)
.setTimestamp(positionInfo.getAbsCount()); .setTimestamp(positionInfo.getAbsCount());
} else
else { status = new MediaItemStatus.Builder(
status = new MediaItemStatus.Builder( MediaItemStatus.PLAYBACK_STATE_INVALIDATED);
MediaItemStatus.PLAYBACK_STATE_INVALIDATED);
}
msg.getData().putBundle("media_item_status", status.build().asBundle()); msg.getData().putBundle("media_item_status", status.build().asBundle());
msg.getData().putInt("hash", requestHash); msg.getData().putInt("hash", requestHash);
mRps.sendMessage(msg); mRps.sendMessage(msg);
} }
}); });
} }
}; };

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -47,7 +47,6 @@ import android.widget.TextView;
import com.github.nutomic.controldlna.R; import com.github.nutomic.controldlna.R;
/** /**
* Displays the devices that are inserted through the RegistryListener (either * Displays the devices that are inserted through the RegistryListener (either
* of type RENDERER or SERVER). * of type RENDERER or SERVER).
@ -80,39 +79,37 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, parent, false); convertView = inflater.inflate(R.layout.list_item, parent, false);
} }
TextView tv = (TextView) convertView.findViewById(R.id.title); TextView tv = (TextView) convertView.findViewById(R.id.title);
RemoteImageView image = RemoteImageView image =
(RemoteImageView) convertView.findViewById(R.id.image); (RemoteImageView) convertView.findViewById(R.id.image);
tv.setText(getItem(position).getDetails().getFriendlyName()); tv.setText(getItem(position).getDetails().getFriendlyName());
if (getItem(position).hasIcons()) { if (getItem(position).hasIcons()) {
URI uri = getItem(position).getIcons()[0].getUri(); URI uri = getItem(position).getIcons()[0].getUri();
if (getItem(position) instanceof RemoteDevice) { if (getItem(position) instanceof RemoteDevice)
try { try {
RemoteDevice device = (RemoteDevice) getItem(position); RemoteDevice device = (RemoteDevice) getItem(position);
uri = device.normalizeURI(uri).toURI(); uri = device.normalizeURI(uri).toURI();
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
Log.w(TAG, "Failed to get device icon URI", e); Log.w(TAG, "Failed to get device icon URI", e);
} }
}
image.setImageUri(uri); image.setImageUri(uri);
} }
return convertView; return convertView;
} }
/** /**
* Adds a new device to the list if its type equals mDeviceType. * Adds a new device to the list if its type equals mDeviceType.
*/ */
public void deviceAdded(final Device<?, ?, ?> device) { public void deviceAdded(final Device<?, ?, ?> device) {
for (int i = 0; i < getCount(); i++) { for (int i = 0; i < getCount(); i++)
if (getItem(i).equals(device)) if (getItem(i).equals(device))
return; return;
}
mActivity.runOnUiThread(new Runnable() { mActivity.runOnUiThread(new Runnable() {
@ -179,4 +176,5 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
@Override @Override
public void afterShutdown() { public void afterShutdown() {
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -82,30 +82,30 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, parent, false); convertView = inflater.inflate(R.layout.list_item, parent, false);
} }
DIDLObject item = getItem(position); DIDLObject item = getItem(position);
TextView title = (TextView) convertView.findViewById(R.id.title); TextView title = (TextView) convertView.findViewById(R.id.title);
TextView artist = (TextView) convertView.findViewById(R.id.subtitle); TextView artist = (TextView) convertView.findViewById(R.id.subtitle);
artist.setText(""); artist.setText("");
RemoteImageView image = (RemoteImageView) convertView.findViewById(R.id.image); RemoteImageView image = (RemoteImageView) convertView.findViewById(R.id.image);
if (item instanceof MusicTrack) { if (item instanceof MusicTrack) {
MusicTrack track = (MusicTrack) item; MusicTrack track = (MusicTrack) item;
String trackNumber = (track.getOriginalTrackNumber() != null) String trackNumber = (track.getOriginalTrackNumber() != null)
? Integer.toString(track.getOriginalTrackNumber()) + ". " ? Integer.toString(track.getOriginalTrackNumber()) + ". "
: ""; : "";
title.setText(trackNumber + item.getTitle()); title.setText(trackNumber + item.getTitle());
if (track.getArtists().length > 0) if (track.getArtists().length > 0)
artist.setText(track.getArtists()[0].getName()); artist.setText(track.getArtists()[0].getName());
} }
else else
title.setText(item.getTitle()); title.setText(item.getTitle());
image.setImageUri(item.getFirstPropertyValue( image.setImageUri(item.getFirstPropertyValue(
DIDLObject.Property.UPNP.ALBUM_ART_URI.class)); DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
return convertView; return convertView;
} }
/** /**

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -49,25 +49,25 @@ public class LoadImageTask extends AsyncTask<URI, Void, Bitmap> {
private static final String TAG = "LoadImageTask"; private static final String TAG = "LoadImageTask";
@Override @Override
protected Bitmap doInBackground(URI... uri) { protected Bitmap doInBackground(URI... uri) {
if (uri[0] == null) if (uri[0] == null)
return null; return null;
Bitmap bm = null; Bitmap bm = null;
try { try {
URLConnection conn = new URL(uri[0].toString()) URLConnection conn = new URL(uri[0].toString())
.openConnection(); .openConnection();
conn.connect(); conn.connect();
InputStream is = conn.getInputStream(); InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is); BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(bis); bm = BitmapFactory.decodeStream(bis);
bis.close(); bis.close();
is.close(); is.close();
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, "Failed to load artwork image", e); Log.w(TAG, "Failed to load artwork image", e);
} }
return bm; return bm;
} }
} }

View file

@ -4,12 +4,12 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the * Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission. derived from this software without specific prior written permission.
@ -50,13 +50,13 @@ public class RemoteImageView extends ImageView {
*/ */
private class AssignImageTask extends LoadImageTask { private class AssignImageTask extends LoadImageTask {
@Override @Override
protected void onPostExecute(Bitmap bm) { protected void onPostExecute(Bitmap bm) {
if (bm != null) if (bm != null)
setImageBitmap(bm); setImageBitmap(bm);
else else
setImageDrawable(null); setImageDrawable(null);
} }
}; };
@ -65,12 +65,12 @@ public class RemoteImageView extends ImageView {
} }
public RemoteImageView(Context context, AttributeSet attrs) { public RemoteImageView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
} }
public RemoteImageView(Context context, AttributeSet attrs, int defStyle) { public RemoteImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
} }
/** /**
* Sets the URI where the image should be loaded from, loads and assigns it. * Sets the URI where the image should be loaded from, loads and assigns it.

View file

@ -21,18 +21,18 @@ public class RouteAdapter extends ArrayAdapter<RouteInfo> {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, parent, false); convertView = inflater.inflate(R.layout.list_item, parent, false);
} }
TextView title = (TextView) convertView.findViewById(R.id.title); TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(getItem(position).getName()); title.setText(getItem(position).getName());
TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle); TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
subtitle.setText(getItem(position).getDescription()); subtitle.setText(getItem(position).getDescription());
return convertView; return convertView;
} }
/** /**