Removed trailing whitespaces, replaced space intendation with tabs.
This commit is contained in:
parent
10e5f86ecf
commit
2314e895d1
14 changed files with 1428 additions and 1437 deletions
|
@ -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,66 +61,66 @@ import com.github.nutomic.controldlna.R;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MainActivity extends ActionBarActivity {
|
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 =
|
|
||||||
new FragmentStatePagerAdapter(getSupportFragmentManager()) {
|
|
||||||
|
|
||||||
@Override
|
FragmentStatePagerAdapter mSectionsPagerAdapter =
|
||||||
public Fragment getItem(int position) {
|
new FragmentStatePagerAdapter(getSupportFragmentManager()) {
|
||||||
switch (position) {
|
|
||||||
case 0: return mServerFragment;
|
|
||||||
case 1: return mRouteFragment;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public Fragment getItem(int position) {
|
||||||
return 2;
|
switch (position) {
|
||||||
}
|
case 0: return mServerFragment;
|
||||||
|
case 1: return mRouteFragment;
|
||||||
};
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ServerFragment mServerFragment;
|
@Override
|
||||||
|
public int getCount() {
|
||||||
private RouteFragment mRouteFragment;
|
return 2;
|
||||||
|
}
|
||||||
ViewPager mViewPager;
|
|
||||||
|
|
||||||
/**
|
};
|
||||||
* Initializes tab navigation. If wifi is not connected,
|
|
||||||
* shows a warning dialog.
|
|
||||||
*/
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
final ActionBar actionBar = getSupportActionBar();
|
|
||||||
|
|
||||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
private ServerFragment mServerFragment;
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
|
||||||
actionBar.setDisplayShowHomeEnabled(false);
|
|
||||||
setContentView(R.layout.activity_main);
|
|
||||||
|
|
||||||
mViewPager = (ViewPager) findViewById(R.id.pager);
|
|
||||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
|
||||||
mViewPager.setOnPageChangeListener(
|
|
||||||
new ViewPager.SimpleOnPageChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(int position) {
|
|
||||||
actionBar.setSelectedNavigationItem(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
TabListener tabListener = new ActionBar.TabListener() {
|
private RouteFragment mRouteFragment;
|
||||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
|
||||||
mViewPager.setCurrentItem(tab.getPosition());
|
ViewPager mViewPager;
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Initializes tab navigation. If wifi is not connected,
|
||||||
|
* shows a warning dialog.
|
||||||
|
*/
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
final ActionBar actionBar = getSupportActionBar();
|
||||||
|
|
||||||
|
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
actionBar.setDisplayShowHomeEnabled(false);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
mViewPager = (ViewPager) findViewById(R.id.pager);
|
||||||
|
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||||
|
mViewPager.setOnPageChangeListener(
|
||||||
|
new ViewPager.SimpleOnPageChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
actionBar.setSelectedNavigationItem(position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TabListener tabListener = new ActionBar.TabListener() {
|
||||||
|
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||||
|
mViewPager.setCurrentItem(tab.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
|
public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
|
||||||
|
@ -129,112 +129,112 @@ 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)
|
|
||||||
getSystemService(CONNECTIVITY_SERVICE);
|
|
||||||
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
|
||||||
final SharedPreferences prefs = getSharedPreferences("preferences.db", 0);
|
|
||||||
|
|
||||||
if (!wifi.isConnected() && !prefs.getBoolean("wifi_skip_dialog", false)) {
|
ConnectivityManager connManager = (ConnectivityManager)
|
||||||
View checkBoxView = View.inflate(this, R.layout.dialog_wifi_disabled, null);
|
getSystemService(CONNECTIVITY_SERVICE);
|
||||||
CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.dont_show_again);
|
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
||||||
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
final SharedPreferences prefs = getSharedPreferences("preferences.db", 0);
|
||||||
|
|
||||||
@Override
|
if (!wifi.isConnected() && !prefs.getBoolean("wifi_skip_dialog", false)) {
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
View checkBoxView = View.inflate(this, R.layout.dialog_wifi_disabled, null);
|
||||||
prefs.edit().putBoolean("wifi_skip_dialog", isChecked)
|
CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.dont_show_again);
|
||||||
.commit();
|
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||||
}
|
|
||||||
});
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
new AlertDialog.Builder(this)
|
prefs.edit().putBoolean("wifi_skip_dialog", isChecked)
|
||||||
.setView(checkBoxView)
|
.commit();
|
||||||
.setTitle(R.string.warning_wifi_not_connected)
|
}
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
});
|
||||||
.show();
|
|
||||||
}
|
new AlertDialog.Builder(this)
|
||||||
|
.setView(checkBoxView)
|
||||||
if (savedInstanceState != null) {
|
.setTitle(R.string.warning_wifi_not_connected)
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
mServerFragment = (ServerFragment) fm.getFragment(
|
.show();
|
||||||
savedInstanceState, ServerFragment.class.getName());
|
}
|
||||||
mRouteFragment = (RouteFragment) fm.getFragment(
|
|
||||||
savedInstanceState, RouteFragment.class.getName());
|
if (savedInstanceState != null) {
|
||||||
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
}
|
mServerFragment = (ServerFragment) fm.getFragment(
|
||||||
else {
|
savedInstanceState, ServerFragment.class.getName());
|
||||||
mServerFragment = new ServerFragment();
|
mRouteFragment = (RouteFragment) fm.getFragment(
|
||||||
mRouteFragment = new RouteFragment();
|
savedInstanceState, RouteFragment.class.getName());
|
||||||
}
|
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
|
||||||
onNewIntent(getIntent());
|
}
|
||||||
}
|
else {
|
||||||
|
mServerFragment = new ServerFragment();
|
||||||
/**
|
mRouteFragment = new RouteFragment();
|
||||||
* Displays the RouteFragment immediately (instead of ServerFragment).
|
}
|
||||||
*/
|
onNewIntent(getIntent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
|
||||||
* showing its root view).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
OnBackPressedListener currentFragment = (OnBackPressedListener)
|
|
||||||
mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
|
|
||||||
if (!currentFragment.onBackPressed())
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes volume on key press (via RouteFragment).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
||||||
switch (event.getKeyCode()) {
|
|
||||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_DOWN)
|
|
||||||
mRouteFragment.increaseVolume();
|
|
||||||
return true;
|
|
||||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_DOWN)
|
|
||||||
mRouteFragment.decreaseVolume();
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.dispatchKeyEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts playing the playlist from item start (via RouteFragment).
|
* Forwards back press to active Fragment (unless the fragment is
|
||||||
*/
|
* showing its root view).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
OnBackPressedListener currentFragment = (OnBackPressedListener)
|
||||||
|
mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
|
||||||
|
if (!currentFragment.onBackPressed())
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes volume on key press (via RouteFragment).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
|
switch (event.getKeyCode()) {
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN)
|
||||||
|
mRouteFragment.increaseVolume();
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN)
|
||||||
|
mRouteFragment.decreaseVolume();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.dispatchKeyEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -73,18 +73,18 @@ import com.github.nutomic.controldlna.utility.FileArrayAdapter;
|
||||||
import com.github.nutomic.controldlna.utility.RouteAdapter;
|
import com.github.nutomic.controldlna.utility.RouteAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls media playback by showing a list of routes, and after selecting one,
|
* Controls media playback by showing a list of routes, and after selecting one,
|
||||||
* the current playlist and playback controls.
|
* the current playlist and playback controls.
|
||||||
*
|
*
|
||||||
* @author Felix Ableitner
|
* @author Felix Ableitner
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
OnBackPressedListener, OnItemClickListener, OnClickListener,
|
OnBackPressedListener, OnItemClickListener, OnClickListener,
|
||||||
OnSeekBarChangeListener, OnScrollListener {
|
OnSeekBarChangeListener, OnScrollListener {
|
||||||
|
|
||||||
private ListView mListView;
|
private ListView mListView;
|
||||||
|
|
||||||
private View mControls;
|
private View mControls;
|
||||||
private SeekBar mProgressBar;
|
private SeekBar mProgressBar;
|
||||||
private ImageButton mPlayPause;
|
private ImageButton mPlayPause;
|
||||||
|
@ -92,32 +92,32 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
private ImageButton mRepeat;
|
private ImageButton mRepeat;
|
||||||
private TextView mCurrentTimeView;
|
private TextView mCurrentTimeView;
|
||||||
private TextView mTotalTimeView;
|
private TextView mTotalTimeView;
|
||||||
|
|
||||||
private View mCurrentTrackView;
|
private View mCurrentTrackView;
|
||||||
|
|
||||||
private boolean mPlaying;
|
private boolean mPlaying;
|
||||||
|
|
||||||
private boolean mRestorePlaylistMode;
|
private boolean mRestorePlaylistMode;
|
||||||
|
|
||||||
private RouteAdapter mRouteAdapter;
|
private RouteAdapter mRouteAdapter;
|
||||||
|
|
||||||
private FileArrayAdapter mPlaylistAdapter;
|
private FileArrayAdapter mPlaylistAdapter;
|
||||||
|
|
||||||
private RouteInfo mSelectedRoute;
|
private RouteInfo mSelectedRoute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count of the number of taps on the previous button within the
|
* Count of the number of taps on the previous button within the
|
||||||
* doubletap interval.
|
* doubletap interval.
|
||||||
*/
|
*/
|
||||||
private int mPreviousTapCount = 0;
|
private int mPreviousTapCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, the item at this position will be played as soon as a route is selected.
|
* If true, the item at this position will be played as soon as a route is selected.
|
||||||
*/
|
*/
|
||||||
private int mStartPlayingOnSelect = -1;
|
private int mStartPlayingOnSelect = -1;
|
||||||
|
|
||||||
private MediaRouterPlayServiceBinder mMediaRouterPlayService;
|
private MediaRouterPlayServiceBinder mMediaRouterPlayService;
|
||||||
|
|
||||||
private ServiceConnection mPlayServiceConnection = new ServiceConnection() {
|
private ServiceConnection mPlayServiceConnection = new ServiceConnection() {
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
|
@ -128,173 +128,172 @@ 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);
|
|
||||||
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
|
|
||||||
mProgressBar.setOnSeekBarChangeListener(this);
|
|
||||||
|
|
||||||
mShuffle = (ImageButton) getView().findViewById(R.id.shuffle);
|
|
||||||
mShuffle.setImageResource(R.drawable.ic_action_shuffle);
|
|
||||||
mShuffle.setOnClickListener(this);
|
|
||||||
|
|
||||||
ImageButton previous = (ImageButton) getView().findViewById(R.id.previous);
|
|
||||||
previous.setImageResource(R.drawable.ic_action_previous);
|
|
||||||
previous.setOnClickListener(this);
|
|
||||||
|
|
||||||
ImageButton next = (ImageButton) getView().findViewById(R.id.next);
|
|
||||||
next.setImageResource(R.drawable.ic_action_next);
|
|
||||||
next.setOnClickListener(this);
|
|
||||||
|
|
||||||
mRepeat = (ImageButton) getView().findViewById(R.id.repeat);
|
|
||||||
mRepeat.setImageResource(R.drawable.ic_action_repeat);
|
|
||||||
mRepeat.setOnClickListener(this);
|
|
||||||
|
|
||||||
mPlayPause = (ImageButton) getView().findViewById(R.id.playpause);
|
|
||||||
mPlayPause.setOnClickListener(this);
|
|
||||||
mPlayPause.setImageResource(R.drawable.ic_action_play);
|
|
||||||
|
|
||||||
mCurrentTimeView = (TextView) getView().findViewById(R.id.current_time);
|
|
||||||
mTotalTimeView = (TextView) getView().findViewById(R.id.total_time);
|
|
||||||
|
|
||||||
getActivity().getApplicationContext().startService(
|
mControls = getView().findViewById(R.id.controls);
|
||||||
new Intent(getActivity(), MediaRouterPlayService.class));
|
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
|
||||||
getActivity().getApplicationContext().bindService(
|
mProgressBar.setOnSeekBarChangeListener(this);
|
||||||
new Intent(getActivity(), MediaRouterPlayService.class),
|
|
||||||
mPlayServiceConnection,
|
|
||||||
Context.BIND_AUTO_CREATE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state"));
|
|
||||||
mRestorePlaylistMode = savedInstanceState.getBoolean("route_selected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
|
|
||||||
outState.putBoolean("route_selected", mSelectedRoute != null);
|
|
||||||
outState.putParcelable("list_state", mListView.onSaveInstanceState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
getActivity().getApplicationContext().unbindService(mPlayServiceConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
mShuffle = (ImageButton) getView().findViewById(R.id.shuffle);
|
||||||
* Starts active route discovery (which is automatically stopped on
|
mShuffle.setImageResource(R.drawable.ic_action_shuffle);
|
||||||
* fragment stop by parent class).
|
mShuffle.setOnClickListener(this);
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int onPrepareCallbackFlags() {
|
|
||||||
return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY
|
|
||||||
| MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Callback onCreateCallback() {
|
|
||||||
return new MediaRouter.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onRouteAdded(MediaRouter router, RouteInfo route) {
|
|
||||||
for (int i = 0; i < mRouteAdapter.getCount(); i++) {
|
|
||||||
if (mRouteAdapter.getItem(i).getId().equals(route.getId())) {
|
|
||||||
mRouteAdapter.remove(mRouteAdapter.getItem(i));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mRouteAdapter.add(route);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
ImageButton previous = (ImageButton) getView().findViewById(R.id.previous);
|
||||||
public void onRouteChanged(MediaRouter router, RouteInfo route) {
|
previous.setImageResource(R.drawable.ic_action_previous);
|
||||||
mRouteAdapter.notifyDataSetChanged();
|
previous.setOnClickListener(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
ImageButton next = (ImageButton) getView().findViewById(R.id.next);
|
||||||
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
next.setImageResource(R.drawable.ic_action_next);
|
||||||
mRouteAdapter.remove(route);
|
next.setOnClickListener(this);
|
||||||
if (route.equals(mSelectedRoute)) {
|
|
||||||
mPlaying = false;
|
|
||||||
onBackPressed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
mRepeat = (ImageButton) getView().findViewById(R.id.repeat);
|
||||||
public void onRouteSelected(MediaRouter router, RouteInfo route) {
|
mRepeat.setImageResource(R.drawable.ic_action_repeat);
|
||||||
}
|
mRepeat.setOnClickListener(this);
|
||||||
|
|
||||||
@Override
|
mPlayPause = (ImageButton) getView().findViewById(R.id.playpause);
|
||||||
public void onRouteUnselected(MediaRouter router, RouteInfo route) {
|
mPlayPause.setOnClickListener(this);
|
||||||
}
|
mPlayPause.setImageResource(R.drawable.ic_action_play);
|
||||||
|
|
||||||
@Override
|
mCurrentTimeView = (TextView) getView().findViewById(R.id.current_time);
|
||||||
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
|
mTotalTimeView = (TextView) getView().findViewById(R.id.total_time);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
getActivity().getApplicationContext().startService(
|
||||||
public void onRoutePresentationDisplayChanged(
|
new Intent(getActivity(), MediaRouterPlayService.class));
|
||||||
MediaRouter router, RouteInfo route) {
|
getActivity().getApplicationContext().bindService(
|
||||||
}
|
new Intent(getActivity(), MediaRouterPlayService.class),
|
||||||
|
mPlayServiceConnection,
|
||||||
|
Context.BIND_AUTO_CREATE
|
||||||
|
);
|
||||||
|
|
||||||
@Override
|
if (savedInstanceState != null) {
|
||||||
public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
|
mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state"));
|
||||||
}
|
mRestorePlaylistMode = savedInstanceState.getBoolean("route_selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
}
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putBoolean("route_selected", mSelectedRoute != null);
|
||||||
|
outState.putParcelable("list_state", mListView.onSaveInstanceState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
getActivity().getApplicationContext().unbindService(mPlayServiceConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts active route discovery (which is automatically stopped on
|
||||||
|
* fragment stop by parent class).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int onPrepareCallbackFlags() {
|
||||||
|
return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY
|
||||||
|
| MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback onCreateCallback() {
|
||||||
|
return new MediaRouter.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onRouteAdded(MediaRouter router, RouteInfo route) {
|
||||||
|
for (int i = 0; i < mRouteAdapter.getCount(); i++)
|
||||||
|
if (mRouteAdapter.getItem(i).getId().equals(route.getId())) {
|
||||||
|
mRouteAdapter.remove(mRouteAdapter.getItem(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mRouteAdapter.add(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteChanged(MediaRouter router, RouteInfo route) {
|
||||||
|
mRouteAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
||||||
|
mRouteAdapter.remove(route);
|
||||||
|
if (route.equals(mSelectedRoute)) {
|
||||||
|
mPlaying = false;
|
||||||
|
onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteSelected(MediaRouter router, RouteInfo route) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteUnselected(MediaRouter router, RouteInfo route) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRoutePresentationDisplayChanged(
|
||||||
|
MediaRouter router, RouteInfo route) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
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).
|
||||||
*/
|
*/
|
||||||
|
@ -307,7 +306,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
changePlayPauseState(true);
|
changePlayPauseState(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays UPNP devices in the ListView.
|
* Displays UPNP devices in the ListView.
|
||||||
*/
|
*/
|
||||||
|
@ -319,7 +318,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
TextView emptyView = (TextView) mListView.getEmptyView();
|
TextView emptyView = (TextView) mListView.getEmptyView();
|
||||||
emptyView.setText(R.string.route_list_empty);
|
emptyView.setText(R.string.route_list_empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays playlist for route in the ListView.
|
* Displays playlist for route in the ListView.
|
||||||
*/
|
*/
|
||||||
|
@ -334,25 +333,25 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
mStartPlayingOnSelect = -1;
|
mStartPlayingOnSelect = -1;
|
||||||
}
|
}
|
||||||
TextView emptyView = (TextView) mListView.getEmptyView();
|
TextView emptyView = (TextView) mListView.getEmptyView();
|
||||||
emptyView.setText(R.string.playlist_empty);
|
emptyView.setText(R.string.playlist_empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets colored background on the item that is currently playing.
|
* Sets colored background on the item that is currently playing.
|
||||||
*/
|
*/
|
||||||
private void enableTrackHighlight() {
|
private void enableTrackHighlight() {
|
||||||
if (mListView.getAdapter() == mRouteAdapter || mMediaRouterPlayService == null || !isVisible())
|
if (mListView.getAdapter() == mRouteAdapter || mMediaRouterPlayService == null || !isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
disableTrackHighlight();
|
disableTrackHighlight();
|
||||||
mCurrentTrackView = mListView.getChildAt(mMediaRouterPlayService.getService()
|
mCurrentTrackView = mListView.getChildAt(mMediaRouterPlayService.getService()
|
||||||
.getCurrentTrack()
|
.getCurrentTrack()
|
||||||
- mListView.getFirstVisiblePosition() + mListView.getHeaderViewsCount());
|
- mListView.getFirstVisiblePosition() + mListView.getHeaderViewsCount());
|
||||||
if (mCurrentTrackView != null)
|
if (mCurrentTrackView != null)
|
||||||
mCurrentTrackView.setBackgroundColor(
|
mCurrentTrackView.setBackgroundColor(
|
||||||
getResources().getColor(R.color.currently_playing_background));
|
getResources().getColor(R.color.currently_playing_background));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes highlight from the item that was last highlighted.
|
* Removes highlight from the item that was last highlighted.
|
||||||
*/
|
*/
|
||||||
|
@ -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();
|
||||||
|
@ -440,9 +438,9 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
s.toggleRepeatEnabled();
|
s.toggleRepeatEnabled();
|
||||||
applyColors();
|
applyColors();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables highlighting on shuffle/repeat buttons (depending
|
* Enables or disables highlighting on shuffle/repeat buttons (depending
|
||||||
* if they are enabled or disabled).
|
* if they are enabled or disabled).
|
||||||
|
@ -451,20 +449,20 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
MediaRouterPlayService s = mMediaRouterPlayService.getService();
|
MediaRouterPlayService s = mMediaRouterPlayService.getService();
|
||||||
int highlight = getResources().getColor(R.color.button_highlight);
|
int highlight = getResources().getColor(R.color.button_highlight);
|
||||||
int transparent = getResources().getColor(android.R.color.transparent);
|
int transparent = getResources().getColor(android.R.color.transparent);
|
||||||
|
|
||||||
mShuffle.setColorFilter((s.getShuffleEnabled())
|
mShuffle.setColorFilter((s.getShuffleEnabled())
|
||||||
? highlight
|
? highlight
|
||||||
: transparent);
|
: transparent);
|
||||||
mRepeat.setColorFilter((s.getRepeatEnabled())
|
mRepeat.setColorFilter((s.getRepeatEnabled())
|
||||||
? highlight
|
? highlight
|
||||||
: transparent);
|
: transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends manual seek on progress bar to renderer.
|
* Sends manual seek on progress bar to renderer.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress,
|
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||||
boolean fromUser) {
|
boolean fromUser) {
|
||||||
if (fromUser)
|
if (fromUser)
|
||||||
mMediaRouterPlayService.getService().seek(progress);
|
mMediaRouterPlayService.getService().seek(progress);
|
||||||
|
@ -490,11 +488,11 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
public void onScrollStateChanged(AbsListView arg0, int arg1) {
|
public void onScrollStateChanged(AbsListView arg0, int arg1) {
|
||||||
enableTrackHighlight();
|
enableTrackHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseVolume() {
|
public void increaseVolume() {
|
||||||
mMediaRouterPlayService.getService().increaseVolume();
|
mMediaRouterPlayService.getService().increaseVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decreaseVolume() {
|
public void decreaseVolume() {
|
||||||
mMediaRouterPlayService.getService().decreaseVolume();
|
mMediaRouterPlayService.getService().decreaseVolume();
|
||||||
}
|
}
|
||||||
|
@ -506,13 +504,13 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
mPlaylistAdapter.clear();
|
mPlaylistAdapter.clear();
|
||||||
mPlaylistAdapter.add(playlist);
|
mPlaylistAdapter.add(playlist);
|
||||||
mMediaRouterPlayService.getService().setPlaylist(playlist);
|
mMediaRouterPlayService.getService().setPlaylist(playlist);
|
||||||
|
|
||||||
if (mSelectedRoute != null) {
|
if (mSelectedRoute != null) {
|
||||||
mMediaRouterPlayService.getService().play(start);
|
mMediaRouterPlayService.getService().play(start);
|
||||||
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,17 +523,17 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent by MediaRouterPlayService when playback of a new track is started.
|
* Sent by MediaRouterPlayService when playback of a new track is started.
|
||||||
*
|
*
|
||||||
|
@ -544,7 +542,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
public void receiveIsPlaying(int track) {
|
public void receiveIsPlaying(int track) {
|
||||||
mListView.smoothScrollToPosition(track);
|
mListView.smoothScrollToPosition(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives information from MediaRouterPlayService about playback status.
|
* Receives information from MediaRouterPlayService about playback status.
|
||||||
*/
|
*/
|
||||||
|
@ -552,13 +550,13 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
// Views may not exist if fragment was just created/destroyed.
|
// Views may not exist if fragment was just created/destroyed.
|
||||||
if (getView() == null)
|
if (getView() == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int currentTime = (int) status.getContentPosition() / 1000;
|
int currentTime = (int) status.getContentPosition() / 1000;
|
||||||
int totalTime = (int) status.getContentDuration() / 1000;
|
int totalTime = (int) status.getContentDuration() / 1000;
|
||||||
|
|
||||||
mCurrentTimeView.setText(generateTimeString(currentTime));
|
mCurrentTimeView.setText(generateTimeString(currentTime));
|
||||||
mTotalTimeView.setText(generateTimeString(totalTime));
|
mTotalTimeView.setText(generateTimeString(totalTime));
|
||||||
|
|
||||||
mProgressBar.setProgress(currentTime);
|
mProgressBar.setProgress(currentTime);
|
||||||
mProgressBar.setMax(totalTime);
|
mProgressBar.setMax(totalTime);
|
||||||
|
|
||||||
|
@ -568,13 +566,13 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
changePlayPauseState(true);
|
changePlayPauseState(true);
|
||||||
else
|
else
|
||||||
changePlayPauseState(false);
|
changePlayPauseState(false);
|
||||||
|
|
||||||
if (mListView.getAdapter() == mPlaylistAdapter)
|
if (mListView.getAdapter() == mPlaylistAdapter)
|
||||||
enableTrackHighlight();
|
enableTrackHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the state of mPlayPause button to pause/resume according to
|
* Changes the state of mPlayPause button to pause/resume according to
|
||||||
* current playback state, also sets mPlaying.
|
* current playback state, also sets mPlaying.
|
||||||
*
|
*
|
||||||
* @param playing True if an item is currently being played, false otherwise.
|
* @param playing True if an item is currently being played, false otherwise.
|
||||||
|
@ -587,7 +585,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mPlayPause.setImageResource(R.drawable.ic_action_play);
|
mPlayPause.setImageResource(R.drawable.ic_action_play);
|
||||||
mPlayPause.setContentDescription(getResources().getString(R.string.play));
|
mPlayPause.setContentDescription(getResources().getString(R.string.play));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -75,293 +75,290 @@ import com.github.nutomic.controldlna.utility.FileArrayAdapter;
|
||||||
* directories.
|
* directories.
|
||||||
*
|
*
|
||||||
* @author Felix Ableitner
|
* @author Felix Ableitner
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ServerFragment extends ListFragment implements OnBackPressedListener {
|
public class ServerFragment extends ListFragment implements OnBackPressedListener {
|
||||||
|
|
||||||
private final static String TAG = "ServerFragment";
|
private final static String TAG = "ServerFragment";
|
||||||
|
|
||||||
private final static String ROOT_DIRECTORY = "0";
|
private final static String ROOT_DIRECTORY = "0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListView adapter for showing a list of DLNA media servers.
|
* ListView adapter for showing a list of DLNA media servers.
|
||||||
*/
|
*/
|
||||||
private DeviceArrayAdapter mServerAdapter;
|
private DeviceArrayAdapter mServerAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the media server of which folders are currently shown.
|
* Reference to the media server of which folders are currently shown.
|
||||||
* Null if media servers are shown.
|
* Null if media servers are shown.
|
||||||
*/
|
*/
|
||||||
private Device<?, ?, ?> mCurrentServer;
|
private Device<?, ?, ?> mCurrentServer;
|
||||||
|
|
||||||
private String mRestoreServer;
|
private String mRestoreServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListView adapter for showing a list of files/folders.
|
* ListView adapter for showing a list of files/folders.
|
||||||
*/
|
*/
|
||||||
private FileArrayAdapter mFileAdapter;
|
private FileArrayAdapter mFileAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds path to current directory on top, paths for higher directories
|
* Holds path to current directory on top, paths for higher directories
|
||||||
* behind that.
|
* behind that.
|
||||||
*/
|
*/
|
||||||
private Stack<String> mCurrentPath = new Stack<String>();
|
private Stack<String> mCurrentPath = new Stack<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the scroll position in the list view at each directory level.
|
* Holds the scroll position in the list view at each directory level.
|
||||||
*/
|
*/
|
||||||
private Stack<Parcelable> mListState = new Stack<Parcelable>();
|
private Stack<Parcelable> mListState = new Stack<Parcelable>();
|
||||||
|
|
||||||
protected AndroidUpnpService mUpnpService;
|
|
||||||
|
|
||||||
private ServiceConnection mUpnpServiceConnection = new ServiceConnection() {
|
protected AndroidUpnpService mUpnpService;
|
||||||
|
|
||||||
/**
|
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) {
|
|
||||||
mCurrentServer = mUpnpService.getControlPoint().getRegistry()
|
|
||||||
.getDevice(new UDN(mRestoreServer.replace("uuid:", "")), false);
|
|
||||||
if (mCurrentServer != null) {
|
|
||||||
setListAdapter(mFileAdapter);
|
|
||||||
// Duplicate the top element because getFiles will remove it.
|
|
||||||
mListState.add(mListState.peek());
|
|
||||||
getFiles(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getListView().onRestoreInstanceState(mListState.peek());
|
if (mRestoreServer != null) {
|
||||||
}
|
mCurrentServer = mUpnpService.getControlPoint().getRegistry()
|
||||||
}
|
.getDevice(new UDN(mRestoreServer.replace("uuid:", "")), false);
|
||||||
|
if (mCurrentServer != null) {
|
||||||
|
setListAdapter(mFileAdapter);
|
||||||
|
// Duplicate the top element because getFiles will remove it.
|
||||||
|
mListState.add(mListState.peek());
|
||||||
|
getFiles(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getListView().onRestoreInstanceState(mListState.peek());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
mUpnpService = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
|
||||||
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();
|
|
||||||
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
|
||||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
||||||
getActivity().registerReceiver(mWifiReceiver, filter);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mRestoreServer = savedInstanceState.getString("current_server");
|
|
||||||
mCurrentPath.addAll(savedInstanceState.getStringArrayList("path"));
|
|
||||||
mListState.addAll(savedInstanceState.getParcelableArrayList("list_state"));
|
|
||||||
} else
|
|
||||||
mListState.push(getListView().onSaveInstanceState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores current server and path/list state stacks.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString("current_server", (mCurrentServer != null)
|
|
||||||
? mCurrentServer.getIdentity().getUdn().toString()
|
|
||||||
: "");
|
|
||||||
outState.putStringArrayList("path", new ArrayList<String>(mCurrentPath));
|
|
||||||
mListState.pop();
|
|
||||||
mListState.push(getListView().onSaveInstanceState());
|
|
||||||
outState.putParcelableArrayList("list_state", new ArrayList<Parcelable>(mListState));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
getActivity().getApplicationContext().unbindService(mUpnpServiceConnection);
|
|
||||||
getActivity().unregisterReceiver(mWifiReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enters directory browsing mode or enters a deeper level directory.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
|
||||||
if (getListAdapter() == mServerAdapter)
|
|
||||||
browsingMode(mServerAdapter.getItem(position));
|
|
||||||
else if (getListAdapter() == mFileAdapter) {
|
|
||||||
if (mFileAdapter.getItem(position) instanceof Container)
|
|
||||||
getFiles(((Container) mFileAdapter.getItem(position)).getId());
|
|
||||||
else {
|
|
||||||
List<Item> playlist = new ArrayList<Item>();
|
|
||||||
for (int i = 0; i < mFileAdapter.getCount(); i++) {
|
|
||||||
if (mFileAdapter.getItem(i) instanceof Item)
|
|
||||||
playlist.add((Item) mFileAdapter.getItem(i));
|
|
||||||
}
|
|
||||||
MainActivity activity = (MainActivity) getActivity();
|
|
||||||
activity.play(playlist, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
IntentFilter filter = new IntentFilter();
|
||||||
* Displays available servers in the ListView.
|
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
||||||
*/
|
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||||
private void serverMode() {
|
getActivity().registerReceiver(mWifiReceiver, filter);
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mRestoreServer = savedInstanceState.getString("current_server");
|
||||||
|
mCurrentPath.addAll(savedInstanceState.getStringArrayList("path"));
|
||||||
|
mListState.addAll(savedInstanceState.getParcelableArrayList("list_state"));
|
||||||
|
} else
|
||||||
|
mListState.push(getListView().onSaveInstanceState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores current server and path/list state stacks.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putString("current_server", (mCurrentServer != null)
|
||||||
|
? mCurrentServer.getIdentity().getUdn().toString()
|
||||||
|
: "");
|
||||||
|
outState.putStringArrayList("path", new ArrayList<String>(mCurrentPath));
|
||||||
|
mListState.pop();
|
||||||
|
mListState.push(getListView().onSaveInstanceState());
|
||||||
|
outState.putParcelableArrayList("list_state", new ArrayList<Parcelable>(mListState));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
getActivity().getApplicationContext().unbindService(mUpnpServiceConnection);
|
||||||
|
getActivity().unregisterReceiver(mWifiReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters directory browsing mode or enters a deeper level directory.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
if (getListAdapter() == mServerAdapter)
|
||||||
|
browsingMode(mServerAdapter.getItem(position));
|
||||||
|
else if (getListAdapter() == mFileAdapter)
|
||||||
|
if (mFileAdapter.getItem(position) instanceof Container)
|
||||||
|
getFiles(((Container) mFileAdapter.getItem(position)).getId());
|
||||||
|
else {
|
||||||
|
List<Item> playlist = new ArrayList<Item>();
|
||||||
|
for (int i = 0; i < mFileAdapter.getCount(); i++)
|
||||||
|
if (mFileAdapter.getItem(i) instanceof Item)
|
||||||
|
playlist.add((Item) mFileAdapter.getItem(i));
|
||||||
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
|
activity.play(playlist, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays available servers in the ListView.
|
||||||
|
*/
|
||||||
|
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.
|
/**
|
||||||
*/
|
* Opens a new directory and displays it.
|
||||||
private void getFiles(String directory) {
|
*/
|
||||||
mListState.push(getListView().onSaveInstanceState());
|
private void getFiles(String directory) {
|
||||||
mCurrentPath.push(directory);
|
mListState.push(getListView().onSaveInstanceState());
|
||||||
getFiles(false);
|
mCurrentPath.push(directory);
|
||||||
}
|
getFiles(false);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Displays the current directory on the ListView.
|
/**
|
||||||
*
|
* Displays the current directory on the ListView.
|
||||||
* @param restoreListState True if we are going back up the directory tree,
|
*
|
||||||
* which means we restore scroll position etc. This pops
|
* @param restoreListState True if we are going back up the directory tree,
|
||||||
* mListState.
|
* which means we restore scroll position etc. This pops
|
||||||
*/
|
* mListState.
|
||||||
private void getFiles(final boolean restoreListState) {
|
*/
|
||||||
if (mCurrentServer == null)
|
private void getFiles(final boolean restoreListState) {
|
||||||
return;
|
if (mCurrentServer == null)
|
||||||
|
return;
|
||||||
Service<?, ?> service = mCurrentServer.findService(
|
|
||||||
new ServiceType("schemas-upnp-org", "ContentDirectory"));
|
Service<?, ?> service = mCurrentServer.findService(
|
||||||
mUpnpService.getControlPoint().execute(new Browse(service,
|
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
|
||||||
|
public void received(ActionInvocation actionInvocation,
|
||||||
|
final DIDLContent didl) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void received(ActionInvocation actionInvocation,
|
public void run() {
|
||||||
final DIDLContent didl) {
|
mFileAdapter.clear();
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
for (Container c : didl.getContainers())
|
||||||
|
mFileAdapter.add(c);
|
||||||
@Override
|
for (Item i : didl.getItems())
|
||||||
public void run() {
|
mFileAdapter.add(i);
|
||||||
mFileAdapter.clear();
|
if (restoreListState)
|
||||||
for (Container c : didl.getContainers())
|
getListView().onRestoreInstanceState(mListState.pop());
|
||||||
mFileAdapter.add(c);
|
else
|
||||||
for (Item i : didl.getItems())
|
getListView().setSelectionFromTop(0, 0);
|
||||||
mFileAdapter.add(i);
|
|
||||||
if (restoreListState)
|
|
||||||
getListView().onRestoreInstanceState(mListState.pop());
|
|
||||||
else
|
|
||||||
getListView().setSelectionFromTop(0, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
@Override
|
}
|
||||||
public void updateStatus(Status status) {
|
|
||||||
}
|
@Override
|
||||||
|
public void updateStatus(Status status) {
|
||||||
@SuppressWarnings("rawtypes")
|
}
|
||||||
@Override
|
|
||||||
public void failure(ActionInvocation actionInvocation,
|
@SuppressWarnings("rawtypes")
|
||||||
UpnpResponse operation, String defaultMessage) {
|
@Override
|
||||||
Log.w(TAG, "Failed to load directory contents: " +
|
public void failure(ActionInvocation actionInvocation,
|
||||||
defaultMessage);
|
UpnpResponse operation, String defaultMessage) {
|
||||||
}
|
Log.w(TAG, "Failed to load directory contents: " +
|
||||||
|
defaultMessage);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* Handles back button press to traverse directories (while browsing
|
|
||||||
* directories).
|
/**
|
||||||
*/
|
* 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())
|
||||||
serverMode();
|
serverMode();
|
||||||
else
|
else
|
||||||
getFiles(true);
|
getFiles(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts device search on wifi connect, removes unreachable
|
* Starts device search on wifi connect, removes unreachable
|
||||||
* devices on wifi disconnect.
|
* devices on wifi disconnect.
|
||||||
*/
|
*/
|
||||||
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();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,61 +70,61 @@ import com.github.nutomic.controldlna.utility.LoadImageTask;
|
||||||
public class MediaRouterPlayService extends Service {
|
public class MediaRouterPlayService extends Service {
|
||||||
|
|
||||||
private static final String TAG = "PlayService";
|
private static final String TAG = "PlayService";
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 1;
|
private static final int NOTIFICATION_ID = 1;
|
||||||
|
|
||||||
private final MediaRouterPlayServiceBinder mBinder = new MediaRouterPlayServiceBinder(this);
|
private final MediaRouterPlayServiceBinder mBinder = new MediaRouterPlayServiceBinder(this);
|
||||||
|
|
||||||
private MediaRouter mMediaRouter;
|
private MediaRouter mMediaRouter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Media items that should be played.
|
* Media items that should be played.
|
||||||
*/
|
*/
|
||||||
private List<Item> mPlaylist = new ArrayList<Item>();
|
private List<Item> mPlaylist = new ArrayList<Item>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The track that is currently being played.
|
* The track that is currently being played.
|
||||||
*/
|
*/
|
||||||
private int mCurrentTrack = -1;
|
private int mCurrentTrack = -1;
|
||||||
|
|
||||||
private boolean mShuffle = false;
|
private boolean mShuffle = false;
|
||||||
|
|
||||||
private boolean mRepeat = false;
|
private boolean mRepeat = false;
|
||||||
|
|
||||||
private String mItemId;
|
private String mItemId;
|
||||||
|
|
||||||
private String mSessionId;
|
private String mSessionId;
|
||||||
|
|
||||||
private WeakReference<RouteFragment> mRouterFragment =
|
private WeakReference<RouteFragment> mRouterFragment =
|
||||||
new WeakReference<RouteFragment>(null);
|
new WeakReference<RouteFragment>(null);
|
||||||
|
|
||||||
private boolean mPollingStatus = false;
|
private boolean mPollingStatus = false;
|
||||||
|
|
||||||
private boolean mBound;
|
private boolean mBound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route that is currently being played to. May be invalid.
|
* Route that is currently being played to. May be invalid.
|
||||||
*/
|
*/
|
||||||
private RouteInfo mCurrentRoute;
|
private RouteInfo mCurrentRoute;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stops foreground mode and notification if the current route
|
* Stops foreground mode and notification if the current route
|
||||||
* has been removed.
|
* has been removed.
|
||||||
*/
|
*/
|
||||||
private MediaRouter.Callback mRouteRemovedCallback =
|
private MediaRouter.Callback mRouteRemovedCallback =
|
||||||
new MediaRouter.Callback() {
|
new MediaRouter.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
private class CreateNotificationTask extends LoadImageTask {
|
private class CreateNotificationTask extends LoadImageTask {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Bitmap result) {
|
protected void onPostExecute(Bitmap result) {
|
||||||
String title = "";
|
String title = "";
|
||||||
|
@ -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)
|
||||||
|
@ -150,68 +150,68 @@ public class MediaRouterPlayService extends Service {
|
||||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||||
startForeground(NOTIFICATION_ID, notification);
|
startForeground(NOTIFICATION_ID, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
mMediaRouter = MediaRouter.getInstance(this);
|
mMediaRouter = MediaRouter.getInstance(this);
|
||||||
pollStatus();
|
pollStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
mBound = true;
|
mBound = true;
|
||||||
return mBinder;
|
return mBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops service after a delay if no media is playing (delay in case the
|
* Stops service after a delay if no media is playing (delay in case the
|
||||||
* fragment is recreated for screen rotation).
|
* fragment is recreated for screen rotation).
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRouterFragment(RouteFragment rf) {
|
public void setRouterFragment(RouteFragment rf) {
|
||||||
mRouterFragment = new WeakReference<RouteFragment>(rf);
|
mRouterFragment = new WeakReference<RouteFragment>(rf);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
|
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets current track in renderer to specified item in playlist, then
|
* Sets current track in renderer to specified item in playlist, then
|
||||||
* starts playback.
|
* starts playback.
|
||||||
*/
|
*/
|
||||||
public void play(int trackNumber) {
|
public void play(int trackNumber) {
|
||||||
if (trackNumber < 0 || trackNumber >= mPlaylist.size())
|
if (trackNumber < 0 || trackNumber >= mPlaylist.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
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 = "";
|
||||||
|
@ -221,88 +221,88 @@ public class MediaRouterPlayService extends Service {
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Log.w(TAG, "Metadata generation failed", e);
|
Log.w(TAG, "Metadata generation failed", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
|
|
||||||
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
|
|
||||||
intent.setData(Uri.parse(track.getFirstResource().getValue()));
|
|
||||||
intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
|
|
||||||
|
|
||||||
mMediaRouter.getSelectedRoute().sendControlRequest(intent,
|
|
||||||
new ControlRequestCallback() {
|
|
||||||
@Override
|
|
||||||
public void onResult(Bundle data) {
|
|
||||||
mSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
|
|
||||||
mItemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
|
|
||||||
mPollingStatus = true;
|
|
||||||
|
|
||||||
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
|
|
||||||
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
|
|
||||||
|
|
||||||
if (mRouterFragment.get() != null)
|
Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
|
||||||
mRouterFragment.get().receiveIsPlaying(mCurrentTrack);
|
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
|
||||||
}
|
intent.setData(Uri.parse(track.getFirstResource().getValue()));
|
||||||
});
|
intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
|
||||||
|
|
||||||
|
mMediaRouter.getSelectedRoute().sendControlRequest(intent,
|
||||||
|
new ControlRequestCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Bundle data) {
|
||||||
|
mSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
|
||||||
|
mItemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
|
||||||
|
mPollingStatus = true;
|
||||||
|
|
||||||
|
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
|
||||||
|
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
|
||||||
|
|
||||||
|
if (mRouterFragment.get() != null)
|
||||||
|
mRouterFragment.get().receiveIsPlaying(mCurrentTrack);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends 'pause' signal to current renderer.
|
* Sends 'pause' signal to current renderer.
|
||||||
*/
|
*/
|
||||||
public void pause() {
|
public void pause() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends 'resume' signal to current renderer.
|
* Sends 'resume' signal to current renderer.
|
||||||
*/
|
*/
|
||||||
public void resume() {
|
public void resume() {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends 'stop' signal to current renderer.
|
* Sends 'stop' signal to current renderer.
|
||||||
*/
|
*/
|
||||||
public void stop() {
|
public void stop() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a new playlist and starts playing.
|
* Sets a new playlist and starts playing.
|
||||||
*
|
*
|
||||||
|
@ -311,17 +311,17 @@ public class MediaRouterPlayService extends Service {
|
||||||
public void setPlaylist(List<Item> playlist) {
|
public void setPlaylist(List<Item> playlist) {
|
||||||
mPlaylist = playlist;
|
mPlaylist = playlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays the track after current in the playlist.
|
* Plays the track after current in the playlist.
|
||||||
*
|
*
|
||||||
* @return True if another item is played, false if the end
|
* @return True if another item is played, false if the end
|
||||||
* of the playlist is reached.
|
* of the playlist is reached.
|
||||||
*/
|
*/
|
||||||
public boolean playNext() {
|
public boolean playNext() {
|
||||||
if (mCurrentTrack == -1)
|
if (mCurrentTrack == -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mShuffle) {
|
if (mShuffle) {
|
||||||
// Play random item.
|
// Play random item.
|
||||||
play(new Random().nextInt(mPlaylist.size()));
|
play(new Random().nextInt(mPlaylist.size()));
|
||||||
|
@ -346,21 +346,21 @@ public class MediaRouterPlayService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays the track before current in the playlist.
|
* Plays the track before current in the playlist.
|
||||||
*/
|
*/
|
||||||
public void playPrevious() {
|
public void playPrevious() {
|
||||||
if (mCurrentTrack == -1)
|
if (mCurrentTrack == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mShuffle)
|
if (mShuffle)
|
||||||
// Play random item.
|
// Play random item.
|
||||||
play(new Random().nextInt(mPlaylist.size()));
|
play(new Random().nextInt(mPlaylist.size()));
|
||||||
else
|
else
|
||||||
play(mCurrentTrack - 1);
|
play(mCurrentTrack - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns index of the track that is currently played (zero-based).
|
* Returns index of the track that is currently played (zero-based).
|
||||||
* @return
|
* @return
|
||||||
|
@ -368,7 +368,7 @@ public class MediaRouterPlayService extends Service {
|
||||||
public int getCurrentTrack() {
|
public int getCurrentTrack() {
|
||||||
return mCurrentTrack;
|
return mCurrentTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests playback information every second, as long as RendererFragment
|
* Requests playback information every second, as long as RendererFragment
|
||||||
* is attached or media is playing.
|
* is attached or media is playing.
|
||||||
|
@ -379,60 +379,60 @@ public class MediaRouterPlayService extends Service {
|
||||||
i.setAction(MediaControlIntent.ACTION_GET_STATUS);
|
i.setAction(MediaControlIntent.ACTION_GET_STATUS);
|
||||||
i.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
|
i.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
|
||||||
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() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
pollStatus();
|
pollStatus();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseVolume() {
|
public void increaseVolume() {
|
||||||
mMediaRouter.getSelectedRoute().requestUpdateVolume(1);
|
mMediaRouter.getSelectedRoute().requestUpdateVolume(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decreaseVolume() {
|
public void decreaseVolume() {
|
||||||
mMediaRouter.getSelectedRoute().requestUpdateVolume(-1);
|
mMediaRouter.getSelectedRoute().requestUpdateVolume(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Item> getPlaylist() {
|
public List<Item> getPlaylist() {
|
||||||
return mPlaylist;
|
return mPlaylist;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleShuffleEnabled() {
|
public void toggleShuffleEnabled() {
|
||||||
mShuffle = !mShuffle;
|
mShuffle = !mShuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getShuffleEnabled() {
|
public boolean getShuffleEnabled() {
|
||||||
return mShuffle;
|
return mShuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleRepeatEnabled() {
|
public void toggleRepeatEnabled() {
|
||||||
mRepeat = !mRepeat;
|
mRepeat = !mRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getRepeatEnabled() {
|
public boolean getRepeatEnabled() {
|
||||||
return mRepeat;
|
return mRepeat;
|
||||||
}
|
}
|
||||||
|
@ -440,4 +440,5 @@ public class MediaRouterPlayService extends Service {
|
||||||
public RouteInfo getCurrentRoute() {
|
public RouteInfo getCurrentRoute() {
|
||||||
return mCurrentRoute;
|
return mCurrentRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -36,14 +36,15 @@ import android.os.Binder;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MediaRouterPlayServiceBinder extends Binder {
|
public class MediaRouterPlayServiceBinder extends Binder {
|
||||||
|
|
||||||
MediaRouterPlayService mService;
|
MediaRouterPlayService mService;
|
||||||
|
|
||||||
public MediaRouterPlayServiceBinder(MediaRouterPlayService service) {
|
public MediaRouterPlayServiceBinder(MediaRouterPlayService service) {
|
||||||
mService = service;
|
mService = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaRouterPlayService getService() {
|
public MediaRouterPlayService getService() {
|
||||||
return mService;
|
return mService;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -66,51 +66,54 @@ import com.github.nutomic.controldlna.R;
|
||||||
* @author Felix Ableitner
|
* @author Felix Ableitner
|
||||||
*/
|
*/
|
||||||
final class Provider extends MediaRouteProvider {
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows passing and storing basic information about a device.
|
* Allows passing and storing basic information about a device.
|
||||||
*/
|
*/
|
||||||
static public class Device implements Parcelable {
|
static public class Device implements Parcelable {
|
||||||
|
|
||||||
public String id;
|
public String id;
|
||||||
public String name;
|
public String name;
|
||||||
public String description;
|
public String description;
|
||||||
public int volume;
|
public int volume;
|
||||||
public int volumeMax;
|
public int volumeMax;
|
||||||
|
|
||||||
public static final Parcelable.Creator<Device> CREATOR
|
|
||||||
= new Parcelable.Creator<Device>() {
|
|
||||||
public Device createFromParcel(Parcel in) {
|
|
||||||
return new Device(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Device[] newArray(int size) {
|
public static final Parcelable.Creator<Device> CREATOR
|
||||||
return new Device[size];
|
= new Parcelable.Creator<Device>() {
|
||||||
}
|
public Device createFromParcel(Parcel in) {
|
||||||
};
|
return new Device(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Device[] newArray(int 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;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -131,271 +134,271 @@ final class Provider extends MediaRouteProvider {
|
||||||
dest.writeString(description);
|
dest.writeString(description);
|
||||||
dest.writeInt(volume);
|
dest.writeInt(volume);
|
||||||
dest.writeInt(volumeMax);
|
dest.writeInt(volumeMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashMap<String, Device> mDevices = new HashMap<String, Device>();
|
|
||||||
|
|
||||||
private SparseArray<Pair<Intent, ControlRequestCallback>> mRequests =
|
|
||||||
new SparseArray<Pair<Intent, ControlRequestCallback>>();
|
|
||||||
|
|
||||||
IRemotePlayService mIRemotePlayService;
|
|
||||||
|
|
||||||
private ServiceConnection mConnection = new ServiceConnection() {
|
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
|
||||||
mIRemotePlayService = IRemotePlayService.Stub.asInterface(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
private HashMap<String, Device> mDevices = new HashMap<String, Device>();
|
||||||
mIRemotePlayService = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final ArrayList<IntentFilter> CONTROL_FILTERS;
|
private SparseArray<Pair<Intent, ControlRequestCallback>> mRequests =
|
||||||
static {
|
new SparseArray<Pair<Intent, ControlRequestCallback>>();
|
||||||
IntentFilter f = new IntentFilter();
|
|
||||||
f.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
|
IRemotePlayService mIRemotePlayService;
|
||||||
f.addAction(MediaControlIntent.ACTION_PLAY);
|
|
||||||
f.addAction(MediaControlIntent.ACTION_PAUSE);
|
private ServiceConnection mConnection = new ServiceConnection() {
|
||||||
f.addAction(MediaControlIntent.ACTION_SEEK);
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
f.addAction(MediaControlIntent.ACTION_STOP);
|
mIRemotePlayService = IRemotePlayService.Stub.asInterface(service);
|
||||||
f.addDataScheme("http");
|
}
|
||||||
f.addDataScheme("https");
|
|
||||||
try {
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
f.addDataType("video/*");
|
mIRemotePlayService = null;
|
||||||
f.addDataType("audio/*");
|
}
|
||||||
} catch (MalformedMimeTypeException ex) {
|
};
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
private static final ArrayList<IntentFilter> CONTROL_FILTERS;
|
||||||
|
static {
|
||||||
|
IntentFilter f = new IntentFilter();
|
||||||
|
f.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
|
||||||
|
f.addAction(MediaControlIntent.ACTION_PLAY);
|
||||||
|
f.addAction(MediaControlIntent.ACTION_PAUSE);
|
||||||
|
f.addAction(MediaControlIntent.ACTION_SEEK);
|
||||||
|
f.addAction(MediaControlIntent.ACTION_STOP);
|
||||||
|
f.addDataScheme("http");
|
||||||
|
f.addDataScheme("https");
|
||||||
|
try {
|
||||||
|
f.addDataType("video/*");
|
||||||
|
f.addDataType("audio/*");
|
||||||
|
} catch (MalformedMimeTypeException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONTROL_FILTERS = new ArrayList<IntentFilter>();
|
||||||
|
CONTROL_FILTERS.add(f);
|
||||||
|
}
|
||||||
|
|
||||||
CONTROL_FILTERS = new ArrayList<IntentFilter>();
|
|
||||||
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;
|
|
||||||
|
|
||||||
DeviceListener(Provider provider) {
|
private final WeakReference<Provider> mService;
|
||||||
mService = new WeakReference<Provider>(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
if (mService.get() != null)
|
|
||||||
mService.get().handleMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Messenger mListener = new Messenger(new DeviceListener(this));
|
|
||||||
|
|
||||||
public Provider(Context context) {
|
DeviceListener(Provider provider) {
|
||||||
super(context);
|
mService = new WeakReference<Provider>(provider);
|
||||||
context.bindService(
|
}
|
||||||
new Intent(context, RemotePlayService.class),
|
|
||||||
mConnection,
|
@Override
|
||||||
Context.BIND_AUTO_CREATE
|
public void handleMessage(Message msg) {
|
||||||
);
|
if (mService.get() != null)
|
||||||
}
|
mService.get().handleMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Messenger mListener = new Messenger(new DeviceListener(this));
|
||||||
|
|
||||||
|
public Provider(Context context) {
|
||||||
|
super(context);
|
||||||
|
context.bindService(
|
||||||
|
new Intent(context, RemotePlayService.class),
|
||||||
|
mConnection,
|
||||||
|
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);
|
|
||||||
int r = new Random().nextInt();
|
/**
|
||||||
mRequests.put(r, pair);
|
* Requests status info via RemotePlayService, stores intent and callback to
|
||||||
mIRemotePlayService.getItemStatus(
|
* access later in handleMessage.
|
||||||
intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID),
|
*/
|
||||||
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID),
|
private void getItemStatus(Intent intent, ControlRequestCallback callback)
|
||||||
r);
|
throws RemoteException {
|
||||||
}
|
if (callback == null)
|
||||||
|
return;
|
||||||
/**
|
|
||||||
* Handles device add and remove as well as sending status info requested earlier.
|
Pair<Intent, ControlRequestCallback> pair =
|
||||||
*/
|
new Pair<Intent, ControlRequestCallback>(intent, callback);
|
||||||
public void handleMessage(Message msg) {
|
int r = new Random().nextInt();
|
||||||
Bundle data = msg.getData();
|
mRequests.put(r, pair);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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,18 +33,19 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
mProvider.close();
|
mProvider.close();
|
||||||
mProvider = null;
|
mProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -72,139 +71,133 @@ import android.util.Log;
|
||||||
public class RemotePlayService extends Service implements RegistryListener {
|
public class RemotePlayService extends Service implements RegistryListener {
|
||||||
|
|
||||||
private static final String TAG = "RemotePlayService";
|
private static final String TAG = "RemotePlayService";
|
||||||
|
|
||||||
Messenger mListener;
|
|
||||||
|
|
||||||
ConcurrentHashMap<String, Device<?, ?, ?>> mDevices =
|
|
||||||
new ConcurrentHashMap<String, Device<?, ?, ?>>();
|
|
||||||
|
|
||||||
protected AndroidUpnpService mUpnpService;
|
|
||||||
|
|
||||||
private ServiceConnection mUpnpServiceConnection = new ServiceConnection() {
|
Messenger mListener;
|
||||||
|
|
||||||
/**
|
ConcurrentHashMap<String, Device<?, ?, ?>> mDevices =
|
||||||
* Registers DeviceListener, adds known devices and starts search if requested.
|
new ConcurrentHashMap<String, Device<?, ?, ?>>();
|
||||||
*/
|
|
||||||
|
protected AndroidUpnpService mUpnpService;
|
||||||
|
|
||||||
|
private ServiceConnection mUpnpServiceConnection = new ServiceConnection() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
WeakHashMap<RemotePlayServiceBinder, Boolean> mBinders =
|
||||||
|
new WeakHashMap<RemotePlayServiceBinder, Boolean>();
|
||||||
|
|
||||||
/**
|
|
||||||
* All active binders. The Hashmap value is unused.
|
|
||||||
*/
|
|
||||||
WeakHashMap<RemotePlayServiceBinder, Boolean> mBinders =
|
|
||||||
new WeakHashMap<RemotePlayServiceBinder, Boolean>();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent itnent) {
|
public IBinder onBind(Intent itnent) {
|
||||||
RemotePlayServiceBinder b = new RemotePlayServiceBinder(this);
|
RemotePlayServiceBinder b = new RemotePlayServiceBinder(this);
|
||||||
mBinders.put(b, true);
|
mBinders.put(b, true);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds to cling service, registers wifi state change listener.
|
* Binds to cling service, registers wifi state change listener.
|
||||||
*/
|
*/
|
||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends msg via Messenger to Provider.
|
* Sends msg via Messenger to Provider.
|
||||||
*/
|
*/
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the error as a message via Messenger.
|
* Sends the error as a message via Messenger.
|
||||||
* @param error
|
* @param error
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts device search on wifi connect, removes unreachable
|
* Starts device search on wifi connect, removes unreachable
|
||||||
* devices on wifi disconnect.
|
* devices on wifi disconnect.
|
||||||
*/
|
*/
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,52 +206,52 @@ public class RemotePlayService extends Service implements RegistryListener {
|
||||||
private void deviceAdded(final Device<?, ?, ?> device) {
|
private void deviceAdded(final Device<?, ?, ?> device) {
|
||||||
if (mDevices.containsValue(device))
|
if (mDevices.containsValue(device))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
final org.teleal.cling.model.meta.Service<?, ?> rc =
|
final org.teleal.cling.model.meta.Service<?, ?> rc =
|
||||||
getService(device, "RenderingControl");
|
getService(device, "RenderingControl");
|
||||||
if (rc == null || mListener == null)
|
if (rc == null || mListener == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (device.getType().getType().equals("MediaRenderer") &&
|
|
||||||
device instanceof RemoteDevice) {
|
|
||||||
mDevices.put(device.getIdentity().getUdn().toString(), device);
|
|
||||||
|
|
||||||
try {
|
if (device.getType().getType().equals("MediaRenderer") &&
|
||||||
mUpnpService.getControlPoint().execute(new GetVolume(rc) {
|
device instanceof RemoteDevice) {
|
||||||
|
mDevices.put(device.getIdentity().getUdn().toString(), device);
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
@Override
|
try {
|
||||||
public void failure(ActionInvocation invocation,
|
mUpnpService.getControlPoint().execute(new GetVolume(rc) {
|
||||||
UpnpResponse operation, String defaultMessage) {
|
|
||||||
Log.w(TAG, "Failed to get current Volume: " + defaultMessage);
|
@SuppressWarnings("rawtypes")
|
||||||
sendError("Failed to get current Volume: " + defaultMessage);
|
@Override
|
||||||
}
|
public void failure(ActionInvocation invocation,
|
||||||
|
UpnpResponse operation, String defaultMessage) {
|
||||||
@SuppressWarnings("rawtypes")
|
Log.w(TAG, "Failed to get current Volume: " + defaultMessage);
|
||||||
@Override
|
sendError("Failed to get current Volume: " + defaultMessage);
|
||||||
public void received(ActionInvocation invocation, int currentVolume) {
|
}
|
||||||
int maxVolume = 100;
|
|
||||||
if (rc.getStateVariable("Volume") != null) {
|
@SuppressWarnings("rawtypes")
|
||||||
StateVariableAllowedValueRange volumeRange =
|
@Override
|
||||||
rc.getStateVariable("Volume").getTypeDetails().getAllowedValueRange();
|
public void received(ActionInvocation invocation, int currentVolume) {
|
||||||
maxVolume = (int) volumeRange.getMaximum();
|
int maxVolume = 100;
|
||||||
}
|
if (rc.getStateVariable("Volume") != null) {
|
||||||
|
StateVariableAllowedValueRange volumeRange =
|
||||||
Message msg = Message.obtain(null, Provider.MSG_RENDERER_ADDED, 0, 0);
|
rc.getStateVariable("Volume").getTypeDetails().getAllowedValueRange();
|
||||||
msg.getData().putParcelable("device", new Provider.Device(
|
maxVolume = (int) volumeRange.getMaximum();
|
||||||
device.getIdentity().getUdn().toString(),
|
}
|
||||||
device.getDisplayString(),
|
|
||||||
device.getDetails().getManufacturerDetails().getManufacturer(),
|
Message msg = Message.obtain(null, Provider.MSG_RENDERER_ADDED, 0, 0);
|
||||||
currentVolume,
|
msg.getData().putParcelable("device", new Provider.Device(
|
||||||
maxVolume));
|
device.getIdentity().getUdn().toString(),
|
||||||
sendMessage(msg);
|
device.getDisplayString(),
|
||||||
}
|
device.getDetails().getManufacturerDetails().getManufacturer(),
|
||||||
});
|
currentVolume,
|
||||||
}
|
maxVolume));
|
||||||
catch (IllegalArgumentException e) {
|
sendMessage(msg);
|
||||||
e.printStackTrace();
|
}
|
||||||
return;
|
});
|
||||||
}
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,25 +259,25 @@ public class RemotePlayService extends Service implements RegistryListener {
|
||||||
* Remove the device from Provider.
|
* Remove the device from Provider.
|
||||||
*/
|
*/
|
||||||
private void deviceRemoved(Device<?, ?, ?> device) {
|
private void deviceRemoved(Device<?, ?, ?> device) {
|
||||||
if (device.getType().getType().equals("MediaRenderer") &&
|
if (device.getType().getType().equals("MediaRenderer") &&
|
||||||
device instanceof RemoteDevice) {
|
device instanceof RemoteDevice) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a device was updated, we just add it again (devices are stored in
|
* If a device was updated, we just add it again (devices are stored in
|
||||||
* maps, so adding the same one again just overwrites the old one).
|
* maps, so adding the same one again just overwrites the old one).
|
||||||
*/
|
*/
|
||||||
private void deviceUpdated(Device<?, ?, ?> device) {
|
private void deviceUpdated(Device<?, ?, ?> device) {
|
||||||
deviceAdded(device);
|
deviceAdded(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterShutdown() {
|
public void afterShutdown() {
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -66,40 +66,39 @@ import android.util.Log;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
|
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;
|
||||||
|
|
||||||
private RemotePlayService mRps;
|
private RemotePlayService mRps;
|
||||||
|
|
||||||
public RemotePlayServiceBinder(RemotePlayService rps) {
|
public RemotePlayServiceBinder(RemotePlayService rps) {
|
||||||
mRps = rps;
|
mRps = rps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
@ -109,69 +108,69 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
protected void ended(GENASubscription sub, CancelReason reason,
|
protected void ended(GENASubscription sub, CancelReason reason,
|
||||||
UpnpResponse response) {
|
UpnpResponse response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
protected void eventReceived(final GENASubscription sub) {
|
protected void eventReceived(final GENASubscription sub) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, StateVariableValue> m = sub.getCurrentValues();
|
Map<String, StateVariableValue> m = sub.getCurrentValues();
|
||||||
try {
|
try {
|
||||||
LastChange lastChange = new LastChange(
|
LastChange lastChange = new LastChange(
|
||||||
new AVTransportLastChangeParser(),
|
new AVTransportLastChangeParser(),
|
||||||
m.get("LastChange").toString());
|
m.get("LastChange").toString());
|
||||||
if (lastChange.getEventedValue(0,
|
if (lastChange.getEventedValue(0,
|
||||||
AVTransportVariable.TransportState.class) == null)
|
AVTransportVariable.TransportState.class) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
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);
|
||||||
mRps.sendError("Failed to parse UPNP event");
|
mRps.sendError("Failed to parse UPNP event");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
protected void eventsMissed(GENASubscription sub,
|
protected void eventsMissed(GENASubscription sub,
|
||||||
int numberOfMissedEvents) {
|
int numberOfMissedEvents) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
protected void failed(GENASubscription sub, UpnpResponse responseStatus,
|
protected void failed(GENASubscription sub, UpnpResponse responseStatus,
|
||||||
Exception exception, String defaultMsg) {
|
Exception exception, String defaultMsg) {
|
||||||
Log.w(TAG, "Register Subscription Callback failed: " + defaultMsg, exception);
|
Log.w(TAG, "Register Subscription Callback failed: " + defaultMsg, exception);
|
||||||
mRps.sendError("Register Subscription Callback failed: " + defaultMsg);
|
mRps.sendError("Register Subscription Callback failed: " + defaultMsg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mRps.mUpnpService.getControlPoint().execute(mSubscriptionCallback);
|
mRps.mUpnpService.getControlPoint().execute(mSubscriptionCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,70 +182,70 @@ 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets playback source and metadata, then starts playing on
|
* Sets playback source and metadata, then starts playing on
|
||||||
* current renderer.
|
* current renderer.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void play(String uri, String metadata) throws RemoteException {
|
public void play(String uri, String metadata) throws RemoteException {
|
||||||
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")
|
|
||||||
@Override
|
|
||||||
public void success(ActionInvocation invocation) {
|
|
||||||
mRps.mUpnpService.getControlPoint().execute(
|
|
||||||
new Play(mRps.getService(mCurrentRenderer,
|
|
||||||
"AVTransport")) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void success(ActionInvocation invocation) {
|
|
||||||
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failure(ActionInvocation invocation,
|
|
||||||
UpnpResponse operation, String defaultMessage) {
|
|
||||||
Log.w(TAG, "Play failed: " + defaultMessage);
|
|
||||||
mRps.sendError("Play failed: " + defaultMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public void success(ActionInvocation invocation) {
|
||||||
|
mRps.mUpnpService.getControlPoint().execute(
|
||||||
|
new Play(mRps.getService(mCurrentRenderer,
|
||||||
|
"AVTransport")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void success(ActionInvocation invocation) {
|
||||||
|
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(ActionInvocation invocation,
|
||||||
|
UpnpResponse operation, String defaultMessage) {
|
||||||
|
Log.w(TAG, "Play failed: " + defaultMessage);
|
||||||
|
mRps.sendError("Play failed: " + defaultMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses playback on current renderer.
|
* Pauses playback on current renderer.
|
||||||
*/
|
*/
|
||||||
|
@ -254,37 +253,37 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
|
||||||
public void pause(final String sessionId) throws RemoteException {
|
public void pause(final String sessionId) throws RemoteException {
|
||||||
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
|
||||||
public void resume(String sessionId) throws RemoteException {
|
public void resume(String sessionId) throws RemoteException {
|
||||||
mRps.mUpnpService.getControlPoint().execute(
|
mRps.mUpnpService.getControlPoint().execute(
|
||||||
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,44 +293,44 @@ 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")
|
||||||
|
@Override
|
||||||
|
public void failure(ActionInvocation invocation,
|
||||||
|
org.teleal.cling.model.message.UpnpResponse operation,
|
||||||
|
String defaultMessage) {
|
||||||
|
Log.w(TAG, "Stop failed: " + defaultMessage);
|
||||||
|
mRps.sendError("Stop failed: " + defaultMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeks to the given absolute time in seconds.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void seek(String sessionId, String itemId, long milliseconds)
|
||||||
|
throws RemoteException {
|
||||||
|
mRps.mUpnpService.getControlPoint().execute(new Seek(
|
||||||
|
mRps.getService(mRps.mDevices.get(sessionId), "AVTransport"),
|
||||||
|
SeekMode.REL_TIME,
|
||||||
|
Integer.toString((int) milliseconds / 1000)) {
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
public void failure(ActionInvocation invocation,
|
public void failure(ActionInvocation invocation,
|
||||||
org.teleal.cling.model.message.UpnpResponse operation,
|
|
||||||
String defaultMessage) {
|
|
||||||
Log.w(TAG, "Stop failed: " + defaultMessage);
|
|
||||||
mRps.sendError("Stop failed: " + defaultMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Seeks to the given absolute time in seconds.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void seek(String sessionId, String itemId, long milliseconds)
|
|
||||||
throws RemoteException {
|
|
||||||
mRps.mUpnpService.getControlPoint().execute(new Seek(
|
|
||||||
mRps.getService(mRps.mDevices.get(sessionId), "AVTransport"),
|
|
||||||
SeekMode.REL_TIME,
|
|
||||||
Integer.toString((int) milliseconds / 1000)) {
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
@Override
|
|
||||||
public void failure(ActionInvocation invocation,
|
|
||||||
UpnpResponse operation, String defaultMessage) {
|
UpnpResponse operation, String defaultMessage) {
|
||||||
Log.w(TAG, "Seek failed: " + defaultMessage);
|
Log.w(TAG, "Seek failed: " + defaultMessage);
|
||||||
mRps.sendError("Seek failed: " + defaultMessage);
|
mRps.sendError("Seek failed: " + defaultMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a message with current status for the route and item.
|
* Sends a message with current status for the route and item.
|
||||||
*
|
*
|
||||||
* If itemId does not match with the item currently played,
|
* If itemId does not match with the item currently played,
|
||||||
* MediaItemStatus.PLAYBACK_STATE_INVALIDATED is returned.
|
* MediaItemStatus.PLAYBACK_STATE_INVALIDATED is returned.
|
||||||
*
|
*
|
||||||
* @param sessionId Identifier of the session (equivalent to route) to get info for.
|
* @param sessionId Identifier of the session (equivalent to route) to get info for.
|
||||||
|
@ -339,42 +338,41 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
|
||||||
* @param requestHash Passed back in message to find original request object.
|
* @param requestHash Passed back in message to find original request object.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
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().putInt("hash", requestHash);
|
||||||
msg.getData().putBundle("media_item_status", status.build().asBundle());
|
mRps.sendMessage(msg);
|
||||||
msg.getData().putInt("hash", requestHash);
|
}
|
||||||
mRps.sendMessage(msg);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
|
@ -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,27 +47,26 @@ 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).
|
||||||
*
|
*
|
||||||
* @author Felix Ableitner
|
* @author Felix Ableitner
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
|
public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
|
||||||
implements RegistryListener {
|
implements RegistryListener {
|
||||||
|
|
||||||
private static final String TAG = "DeviceArrayAdapter";
|
private static final String TAG = "DeviceArrayAdapter";
|
||||||
|
|
||||||
public static final String RENDERER = "MediaRenderer";
|
public static final String RENDERER = "MediaRenderer";
|
||||||
|
|
||||||
public static final String SERVER = "MediaServer";
|
public static final String SERVER = "MediaServer";
|
||||||
|
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
|
|
||||||
private String mDeviceType;
|
private String mDeviceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param deviceType One of RENDERER or SERVER.
|
* @param deviceType One of RENDERER or SERVER.
|
||||||
*/
|
*/
|
||||||
|
@ -76,46 +75,44 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
mDeviceType = deviceType;
|
mDeviceType = deviceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (device.getType().getType().equals(mDeviceType))
|
if (device.getType().getType().equals(mDeviceType))
|
||||||
|
@ -124,18 +121,18 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the device from the list (if it is an element).
|
* Removes the device from the list (if it is an element).
|
||||||
*/
|
*/
|
||||||
public void deviceRemoved(final Device<?, ?, ?> device) {
|
public void deviceRemoved(final Device<?, ?, ?> device) {
|
||||||
mActivity.runOnUiThread(new Runnable() {
|
mActivity.runOnUiThread(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (getPosition(device) != -1)
|
if (getPosition(device) != -1)
|
||||||
remove(device);
|
remove(device);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,4 +176,5 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
|
||||||
@Override
|
@Override
|
||||||
public void afterShutdown() {
|
public void afterShutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ import com.github.nutomic.controldlna.R;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
|
public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides sorting of elements by track number.
|
* Provides sorting of elements by track number.
|
||||||
*/
|
*/
|
||||||
|
@ -61,7 +61,7 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(DIDLObject lhs, DIDLObject rhs) {
|
public int compare(DIDLObject lhs, DIDLObject rhs) {
|
||||||
if (lhs instanceof MusicTrack && rhs instanceof MusicTrack)
|
if (lhs instanceof MusicTrack && rhs instanceof MusicTrack)
|
||||||
return ((MusicTrack) rhs).getOriginalTrackNumber() -
|
return ((MusicTrack) rhs).getOriginalTrackNumber() -
|
||||||
((MusicTrack) lhs).getOriginalTrackNumber();
|
((MusicTrack) lhs).getOriginalTrackNumber();
|
||||||
else if (lhs instanceof Item && rhs instanceof Container)
|
else if (lhs instanceof Item && rhs instanceof Container)
|
||||||
|
@ -75,37 +75,37 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a view with folder/media title, and artist name (for audio only).
|
* Returns a view with folder/media title, and artist name (for audio only).
|
||||||
*/
|
*/
|
||||||
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,5 +115,5 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
|
||||||
for (DIDLObject d : playlist)
|
for (DIDLObject d : playlist)
|
||||||
add(d);
|
add(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -46,28 +46,28 @@ import android.util.Log;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LoadImageTask extends AsyncTask<URI, Void, Bitmap> {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ import android.widget.ImageView;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RemoteImageView extends ImageView {
|
public class RemoteImageView extends ImageView {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns the icon as image drawable when it is loaded.
|
* Assigns the icon as image drawable when it is loaded.
|
||||||
*
|
*
|
||||||
|
@ -49,28 +49,28 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public RemoteImageView(Context context) {
|
public RemoteImageView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
||||||
|
@ -79,5 +79,5 @@ public class RemoteImageView extends ImageView {
|
||||||
setImageDrawable(null);
|
setImageDrawable(null);
|
||||||
new AssignImageTask().execute(uri);
|
new AssignImageTask().execute(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,22 +17,22 @@ public class RouteAdapter extends ArrayAdapter<RouteInfo> {
|
||||||
public RouteAdapter(Context context) {
|
public RouteAdapter(Context context) {
|
||||||
super(context, R.layout.list_item);
|
super(context, R.layout.list_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Reference in a new issue