Formatting fixes.

- add braces to if/for
- indent with tabs instead of spaces
- remove trailing whitespaces
This commit is contained in:
Felix Ableitner 2014-05-05 16:19:06 +02:00
parent 59df28b2bd
commit 105bb9de94
30 changed files with 980 additions and 913 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -1,38 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal" >
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal" >
<com.github.nutomic.controldlna.utility.RemoteImageView
android:id="@+id/image"
android:layout_width="?android:attr/listPreferredItemHeight"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="4dip"
android:contentDescription="@string/album_art" />
<com.github.nutomic.controldlna.utility.RemoteImageView
android:id="@+id/image"
android:layout_width="?android:attr/listPreferredItemHeight"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="4dip"
android:contentDescription="@string/album_art" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginTop="4dip" >
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginTop="4dip" >
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:lines="1"
android:ellipsize="end" />
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:lines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:lines="1"
android:ellipsize="end" />
android:id="@+id/subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:lines="1"
android:ellipsize="end" />
</LinearLayout>

View file

@ -1,102 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/controls"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
<TextView
android:id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:text="@string/route_list_empty"
android:layout_centerInParent="true"
android:gravity="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:id="@+id/controls"
android:visibility="gone"
android:background="@android:color/white" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/controls"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
<SeekBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:text="@string/route_list_empty"
android:layout_centerInParent="true"
android:gravity="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:id="@+id/controls"
android:visibility="gone"
android:background="@android:color/white" >
<SeekBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingLeft="10dip"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingLeft="10dip"
android:paddingRight="10dip" >
<TextView
android:id="@+id/current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:gravity="right"
android:minEms="2" />
<ImageButton
android:id="@+id/shuffle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/shuffle"
android:layout_toLeftOf="@+id/previous" />
<ImageButton
android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/previous"
android:layout_toLeftOf="@+id/playpause" />
<ImageButton
android:id="@+id/playpause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/play"
android:layout_centerHorizontal="true"/>
<ImageButton
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/next"
android:layout_toRightOf="@id/playpause" />
<ImageButton
android:id="@+id/repeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/repeat"
android:layout_toRightOf="@id/next" />
<TextView
android:id="@+id/current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:gravity="right"
android:minEms="2" />
<TextView
android:id="@+id/total_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:gravity="right"
android:minEms="2"/>
</RelativeLayout>
</LinearLayout>
<ImageButton
android:id="@+id/shuffle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/shuffle"
android:layout_toLeftOf="@+id/previous" />
<ImageButton
android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/previous"
android:layout_toLeftOf="@+id/playpause" />
<ImageButton
android:id="@+id/playpause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/play"
android:layout_centerHorizontal="true"/>
<ImageButton
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/next"
android:layout_toRightOf="@id/playpause" />
<ImageButton
android:id="@+id/repeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/repeat"
android:layout_toRightOf="@id/next" />
<TextView
android:id="@+id/total_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:gravity="right"
android:minEms="2"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

View file

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:text="@string/device_list_empty"
android:layout_centerInParent="true"
android:gravity="center" />
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:text="@string/device_list_empty"
android:layout_centerInParent="true"
android:gravity="center" />
</RelativeLayout>

View file

@ -2,8 +2,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/preferences"
android:title="@string/settings_title" />
<item
android:id="@+id/preferences"
android:title="@string/settings_title" />
</menu>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="enable_wifi_values">
<item>yes</item>
<item>no</item>
<item>ask</item>
</array>
<array name="enable_wifi_values">
<item>yes</item>
<item>no</item>
<item>ask</item>
</array>
</resources>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="currently_playing_background">#BB99CC00</color>
<color name="button_highlight">#ff33b5e5</color>
<color name="currently_playing_background">#BB99CC00</color>
<color name="button_highlight">#ff33b5e5</color>
</resources>

View file

@ -1,7 +1,7 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View file

@ -1,42 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ControlDLNA</string>
<!-- Server Fragment -->
<string name="title_server">Server</string>
<string name="folder_list_empty">Folder is empty</string>
<string name="device_list_empty">No UPNP devices found.
\nNew devices will be added automatically when they are available.</string>
<!-- Route Fragment -->
<string name="title_route">Route</string>
<string name="playlist_empty">Playlist is empty</string>
<string name="route_list_empty">No routes found.
\nNew routes will be added automatically when they are available.</string>
<!-- Playback controls -->
<string name="play">Play</string>
<string name="pause">Pause</string>
<string name="previous">Previous</string>
<string name="next">Next</string>
<string name="shuffle">Shuffle</string>
<string name="repeat">Repeat</string>
<!-- Toasts/Dialogs -->
<string name="exit_renderer">Do you really want to exit the renderer?
Playback will be stopped.</string>
<string name="select_route">Please select a route</string>
<string name="enable_wifi_dialog">Wifi needs to be connected for playback.
Do you want to enable it now?</string>
<string name="app_name">ControlDLNA</string>
<!-- Description for local playback device -->
<string name="local_device">Local Device</string>
<!-- Image alt text -->
<string name="album_art">Album Art</string>
<!-- MediaRouter strings -->
<!-- Server Fragment -->
<string name="title_server">Server</string>
<string name="folder_list_empty">Folder is empty</string>
<string name="device_list_empty">No UPNP devices found.
\nNew devices will be added automatically when they are available.</string>
<!-- Route Fragment -->
<string name="title_route">Route</string>
<string name="playlist_empty">Playlist is empty</string>
<string name="route_list_empty">No routes found.
\nNew routes will be added automatically when they are available.</string>
<!-- Playback controls -->
<string name="play">Play</string>
<string name="pause">Pause</string>
<string name="previous">Previous</string>
<string name="next">Next</string>
<string name="shuffle">Shuffle</string>
<string name="repeat">Repeat</string>
<!-- Toasts/Dialogs -->
<string name="exit_renderer">Do you really want to exit the renderer?
Playback will be stopped.</string>
<string name="select_route">Please select a route</string>
<string name="enable_wifi_dialog">Wifi needs to be connected for playback.
Do you want to enable it now?</string>
<!-- Description for local playback device -->
<string name="local_device">Local Device</string>
<!-- Image alt text -->
<string name="album_art">Album Art</string>
<!-- MediaRouter strings -->
<string name="upnp_route_provider_service">UPNP Route Provider Service</string>
<!-- Developer contact mail with '@', '.' replaced -->
@ -45,18 +45,18 @@
<!-- Title for the SettingsActivity -->
<string name="settings_title">Preferences</string>
<!-- Title for preference to enable wifi on start -->
<string name="enable_wifi_title">Automatically enable Wifi on start</string>
<!-- Title for preference to enable wifi on start -->
<string name="enable_wifi_title">Automatically enable Wifi on start</string>
<!-- Values for preference to auto-enable wifi on start -->
<string-array name="enable_wifi_value_titles">
<item>yes</item>
<item>no</item>
<item>ask</item>
</string-array>
<!-- Values for preference to auto-enable wifi on start -->
<string-array name="enable_wifi_value_titles">
<item>yes</item>
<item>no</item>
<item>ask</item>
</string-array>
<!-- Title for the pause playback on call preference -->
<string name="incoming_phone_call_pause">Pause playback on incoming phone call</string>
<!-- Title for the pause playback on call preference -->
<string name="incoming_phone_call_pause">Pause playback on incoming phone call</string>
<!-- Title for the contact developer preference -->
<string name="contact_dev_title">Contact Developer</string>

View file

@ -1,20 +1,20 @@
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>

View file

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:key="enable_wifi_on_start"
android:title="@string/enable_wifi_title"
android:entries="@array/enable_wifi_value_titles"
android:entryValues="@array/enable_wifi_values"
android:defaultValue="ask"/>
<ListPreference
android:key="enable_wifi_on_start"
android:title="@string/enable_wifi_title"
android:entries="@array/enable_wifi_value_titles"
android:entryValues="@array/enable_wifi_values"
android:defaultValue="ask"/>
<CheckBoxPreference
android:key="incoming_phone_call_pause"
android:title="@string/incoming_phone_call_pause"
android:defaultValue="true" />
<CheckBoxPreference
android:key="incoming_phone_call_pause"
android:title="@string/incoming_phone_call_pause"
android:defaultValue="true" />
<Preference
android:key="contact_dev"
android:title="@string/contact_dev_title" />
<Preference
android:key="contact_dev"
android:title="@string/contact_dev_title" />
</PreferenceScreen>

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,17 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.gui;
import java.util.List;
import org.teleal.cling.support.model.item.Item;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
@ -50,17 +43,16 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBar.TabListener;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.github.nutomic.controldlna.R;
import org.teleal.cling.support.model.item.Item;
import java.util.List;
/**
* Main activity, with tabs for media servers and media routes.
*
@ -114,8 +106,7 @@ public class MainActivity extends ActionBarActivity {
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
@ -143,27 +134,27 @@ public class MainActivity extends ActionBarActivity {
.setText(R.string.title_route)
.setTabListener(tabListener));
final WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (!wifi.isWifiEnabled()){
String value = PreferenceManager.getDefaultSharedPreferences(this)
.getString(PreferencesActivity.KEY_ENABLE_WIFI_ON_START, "ask");
if (value.equals("yes")) {
wifi.setWifiEnabled(true);
}
else if (value.equals("ask")) {
new AlertDialog.Builder(this)
.setMessage(R.string.enable_wifi_dialog)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
wifi.setWifiEnabled(true);
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
}
final WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (!wifi.isWifiEnabled()) {
String value = PreferenceManager.getDefaultSharedPreferences(this)
.getString(PreferencesActivity.KEY_ENABLE_WIFI_ON_START, "ask");
if (value.equals("yes")) {
wifi.setWifiEnabled(true);
}
else if (value.equals("ask")) {
new AlertDialog.Builder(this)
.setMessage(R.string.enable_wifi_dialog)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
wifi.setWifiEnabled(true);
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
}
if (savedInstanceState != null) {
@ -181,30 +172,30 @@ public class MainActivity extends ActionBarActivity {
onNewIntent(getIntent());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.preferences:
Intent i = new Intent(this, PreferencesActivity.class);
startActivity(i);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.preferences:
Intent i = new Intent(this, PreferencesActivity.class);
startActivity(i);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
/**
* Displays the RouteFragment immediately (instead of ServerFragment).
*/
@Override
protected void onNewIntent(Intent intent) {
if (intent.getAction().equals("showRouteFragment")) {
if (intent.getAction() != null && intent.getAction().equals("showRouteFragment")) {
mViewPager.setCurrentItem(1);
mRouteFragment.scrollToCurrent();
}
@ -216,13 +207,13 @@ public class MainActivity extends ActionBarActivity {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Avoid crash if called during startup.
if (mServerFragment != null && mRouteFragment != null) {
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, ServerFragment.class.getName(), mServerFragment);
fm.putFragment(outState, RouteFragment.class.getName(), mRouteFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem());
}
// Avoid crash if called during startup.
if (mServerFragment != null && mRouteFragment != null) {
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, ServerFragment.class.getName(), mServerFragment);
fm.putFragment(outState, RouteFragment.class.getName(), mRouteFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem());
}
}
/**
@ -233,8 +224,9 @@ public class MainActivity extends ActionBarActivity {
public void onBackPressed() {
OnBackPressedListener currentFragment = (OnBackPressedListener)
mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
if (!currentFragment.onBackPressed())
if (!currentFragment.onBackPressed()) {
super.onBackPressed();
}
}
/**
@ -244,12 +236,14 @@ public class MainActivity extends ActionBarActivity {
public boolean dispatchKeyEvent(KeyEvent event) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_VOLUME_UP:
if (event.getAction() == KeyEvent.ACTION_DOWN)
if (event.getAction() == KeyEvent.ACTION_DOWN) {
mRouteFragment.increaseVolume();
}
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (event.getAction() == KeyEvent.ACTION_DOWN)
if (event.getAction() == KeyEvent.ACTION_DOWN) {
mRouteFragment.decreaseVolume();
}
return true;
default:
return super.dispatchKeyEvent(event);

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -43,73 +43,72 @@ import android.view.MenuItem;
import com.github.nutomic.controldlna.R;
public class PreferencesActivity extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String KEY_ENABLE_WIFI_ON_START = "enable_wifi_on_start";
public static final String KEY_INCOMING_PHONE_CALL_PAUSE = "incoming_phone_call_pause";
private static final String KEY_CONTACT_DEV = "contact_dev";
public static final String KEY_ENABLE_WIFI_ON_START = "enable_wifi_on_start";
public static final String KEY_INCOMING_PHONE_CALL_PAUSE = "incoming_phone_call_pause";
private static final String KEY_CONTACT_DEV = "contact_dev";
private ListPreference mEnableWifiOnStart;
private Preference mContactDev;
private ListPreference mEnableWifiOnStart;
private Preference mContactDev;
/**
* Initializes preferences from xml.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Initializes preferences from xml.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// There is currently no way to get ActionBar in PreferenceActivity on pre-honeycomb with
// compatibility library, so we'll have to do a version check.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
// There is currently no way to get ActionBar in PreferenceActivity on pre-honeycomb with
// compatibility library, so we'll have to do a version check.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
addPreferencesFromResource(R.xml.preferences);
final PreferenceScreen screen = getPreferenceScreen();
mEnableWifiOnStart = (ListPreference) screen.findPreference(KEY_ENABLE_WIFI_ON_START);
mEnableWifiOnStart.setSummary(mEnableWifiOnStart.getEntry());
mContactDev = screen.findPreference(KEY_CONTACT_DEV);
}
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
addPreferencesFromResource(R.xml.preferences);
final PreferenceScreen screen = getPreferenceScreen();
mEnableWifiOnStart = (ListPreference) screen.findPreference(KEY_ENABLE_WIFI_ON_START);
mEnableWifiOnStart.setSummary(mEnableWifiOnStart.getEntry());
mContactDev = screen.findPreference(KEY_CONTACT_DEV);
}
/**
* Navigates up from activity on ActionBar back click.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Navigates up from activity on ActionBar back click.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Sends mail intent on contact dev preference click.
*/
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (preference == mContactDev) {
Intent i = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", getString(R.string.contact_mail, "@", "."), null));
startActivity(i);
return true;
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
/**
* Sends mail intent on contact dev preference click.
*/
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (preference == mContactDev) {
startActivity(new Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", getString(R.string.contact_mail, "@", "."), null)));
return true;
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
/**
* Updates summary of list preference (from current item).
*/
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KEY_ENABLE_WIFI_ON_START)) {
mEnableWifiOnStart.setSummary(mEnableWifiOnStart.getEntry());
}
}
/**
* Updates summary of list preference (from current item).
*/
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KEY_ENABLE_WIFI_ON_START)) {
mEnableWifiOnStart.setSummary(mEnableWifiOnStart.getEntry());
}
}
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,10 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.gui;
import java.util.List;
import org.teleal.cling.support.model.item.Item;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
@ -49,7 +45,6 @@ import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.Callback;
import android.support.v7.media.MediaRouter.ProviderInfo;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@ -73,6 +68,10 @@ import com.github.nutomic.controldlna.mediarouter.MediaRouterPlayServiceBinder;
import com.github.nutomic.controldlna.utility.FileArrayAdapter;
import com.github.nutomic.controldlna.utility.RouteAdapter;
import org.teleal.cling.support.model.item.Item;
import java.util.List;
/**
* Controls media playback by showing a list of routes, and after selecting one,
* the current playlist and playback controls.
@ -81,8 +80,8 @@ import com.github.nutomic.controldlna.utility.RouteAdapter;
*
*/
public class RouteFragment extends MediaRouteDiscoveryFragment implements
OnBackPressedListener, OnItemClickListener, OnClickListener,
OnSeekBarChangeListener, OnScrollListener {
OnBackPressedListener, OnItemClickListener, OnClickListener,
OnSeekBarChangeListener, OnScrollListener {
private ListView mListView;
@ -93,6 +92,7 @@ OnSeekBarChangeListener, OnScrollListener {
private ImageButton mRepeat;
private TextView mCurrentTimeView;
private TextView mTotalTimeView;
private TextView mEmptyView;
private View mCurrentTrackView;
@ -126,8 +126,9 @@ OnSeekBarChangeListener, OnScrollListener {
mPlaylistAdapter.add(mMediaRouterPlayService.getPlaylist());
applyColors();
RouteInfo currentRoute = mMediaRouterPlayService.getCurrentRoute();
if (currentRoute != null)
if (currentRoute != null) {
playlistMode(currentRoute);
}
}
public void onServiceDisconnected(ComponentName className) {
@ -168,7 +169,9 @@ OnSeekBarChangeListener, OnScrollListener {
mListView.setAdapter(mRouteAdapter);
mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(this);
mListView.setEmptyView(getView().findViewById(android.R.id.empty));
mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
mListView.setEmptyView(mEmptyView);
mControls = getView().findViewById(R.id.controls);
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
@ -201,19 +204,17 @@ OnSeekBarChangeListener, OnScrollListener {
new Intent(getActivity(), MediaRouterPlayService.class));
getActivity().getApplicationContext().bindService(
new Intent(getActivity(), MediaRouterPlayService.class),
mPlayServiceConnection,
Context.BIND_AUTO_CREATE
);
mPlayServiceConnection, Context.BIND_AUTO_CREATE);
if (savedInstanceState != null)
if (savedInstanceState != null) {
mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state"));
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//outState.putBoolean("route_selected", mSelectedRoute != null);
outState.putParcelable("list_state", mListView.onSaveInstanceState());
}
@ -229,8 +230,8 @@ OnSeekBarChangeListener, OnScrollListener {
*/
@Override
public int onPrepareCallbackFlags() {
return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY
| MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN;
return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY |
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN;
}
@Override
@ -238,16 +239,18 @@ OnSeekBarChangeListener, OnScrollListener {
return new MediaRouter.Callback() {
@Override
public void onRouteAdded(MediaRouter router, RouteInfo route) {
for (int i = 0; i < mRouteAdapter.getCount(); i++)
for (int i = 0; i < mRouteAdapter.getCount(); i++) {
if (mRouteAdapter.getItem(i).getId().equals(route.getId())) {
mRouteAdapter.remove(mRouteAdapter.getItem(i));
break;
}
}
mRouteAdapter.add(route);
RouteInfo current = mMediaRouterPlayService.getCurrentRoute();
if (current != null && route.getId().equals(current.getId()))
playlistMode(current);
RouteInfo current = mMediaRouterPlayService.getCurrentRoute();
if (current != null && route.getId().equals(current.getId())) {
playlistMode(current);
}
}
@Override
@ -301,8 +304,9 @@ OnSeekBarChangeListener, OnScrollListener {
*/
@Override
public void onItemClick(AdapterView<?> a, View v, final int position, long id) {
if (mListView.getAdapter() == mRouteAdapter)
if (mListView.getAdapter() == mRouteAdapter) {
playlistMode(mRouteAdapter.getItem(position));
}
else {
mMediaRouterPlayService.play(position);
changePlayPauseState(true);
@ -317,8 +321,7 @@ OnSeekBarChangeListener, OnScrollListener {
mListView.setAdapter(mRouteAdapter);
disableTrackHighlight();
mSelectedRoute = null;
TextView emptyView = (TextView) mListView.getEmptyView();
emptyView.setText(R.string.route_list_empty);
mEmptyView.setText(R.string.route_list_empty);
}
/**
@ -334,37 +337,39 @@ OnSeekBarChangeListener, OnScrollListener {
changePlayPauseState(true);
mStartPlayingOnSelect = -1;
}
TextView emptyView = (TextView) mListView.getEmptyView();
emptyView.setText(R.string.playlist_empty);
mListView.post(new Runnable() {
@Override
public void run() {
scrollToCurrent();
}
});
mEmptyView.setText(R.string.playlist_empty);
mListView.post(new Runnable() {
@Override
public void run() {
scrollToCurrent();
}
});
}
/**
* Sets colored background on the item that is currently playing.
*/
private void enableTrackHighlight() {
if (mListView.getAdapter() == mRouteAdapter || mMediaRouterPlayService == null || !isVisible())
if (mListView.getAdapter() == mRouteAdapter ||
mMediaRouterPlayService == null || !isVisible())
return;
disableTrackHighlight();
mCurrentTrackView = mListView.getChildAt(mMediaRouterPlayService.getCurrentTrack()
- mListView.getFirstVisiblePosition() + mListView.getHeaderViewsCount());
if (mCurrentTrackView != null)
if (mCurrentTrackView != null) {
mCurrentTrackView.setBackgroundColor(
getResources().getColor(R.color.currently_playing_background));
}
}
/**
* Removes highlight from the item that was last highlighted.
*/
private void disableTrackHighlight() {
if (mCurrentTrackView != null)
if (mCurrentTrackView != null) {
mCurrentTrackView.setBackgroundColor(Color.TRANSPARENT);
}
}
/**
@ -373,24 +378,25 @@ OnSeekBarChangeListener, OnScrollListener {
@Override
public boolean onBackPressed() {
if (mListView.getAdapter() == mPlaylistAdapter) {
if (mPlaying)
if (mPlaying) {
new AlertDialog.Builder(getActivity())
.setMessage(R.string.exit_renderer)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
mMediaRouterPlayService.stop();
changePlayPauseState(false);
deviceListMode();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
else
.setMessage(R.string.exit_renderer)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
mMediaRouterPlayService.stop();
changePlayPauseState(false);
deviceListMode();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
else {
deviceListMode();
}
return true;
}
return false;
@ -470,8 +476,9 @@ OnSeekBarChangeListener, OnScrollListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if (fromUser)
if (fromUser) {
mMediaRouterPlayService.seek(progress);
}
}
@Override
@ -514,9 +521,10 @@ OnSeekBarChangeListener, OnScrollListener {
if (mSelectedRoute != null) {
mMediaRouterPlayService.play(start);
changePlayPauseState(true);
} else {
}
else {
Toast.makeText(getActivity(), R.string.select_route, Toast.LENGTH_SHORT)
.show();
.show();
mStartPlayingOnSelect = start;
}
}
@ -528,16 +536,17 @@ OnSeekBarChangeListener, OnScrollListener {
* @return Formatted time string.
*/
private String generateTimeString(int time) {
assert(time >= 0);
int seconds = time % 60;
int minutes = time / 60;
if (minutes > 99)
if (minutes > 99) {
return "99:99";
else
}
else {
return Integer.toString(minutes) + ":" +
((seconds > 9)
? seconds
((seconds > 9)
? seconds
: "0" + Integer.toString(seconds));
}
}
/**
@ -559,13 +568,16 @@ OnSeekBarChangeListener, OnScrollListener {
if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PLAYING ||
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_BUFFERING ||
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PENDING)
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PENDING) {
changePlayPauseState(true);
else
}
else {
changePlayPauseState(false);
}
if (mListView.getAdapter() == mPlaylistAdapter)
if (mListView.getAdapter() == mPlaylistAdapter) {
enableTrackHighlight();
}
}
/**

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,24 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.gui;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.android.AndroidUpnpServiceImpl;
import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.model.types.UDN;
import org.teleal.cling.support.contentdirectory.callback.Browse;
import org.teleal.cling.support.model.BrowseFlag;
import org.teleal.cling.support.model.DIDLContent;
import org.teleal.cling.support.model.container.Container;
import org.teleal.cling.support.model.item.Item;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@ -70,12 +52,30 @@ import com.github.nutomic.controldlna.gui.MainActivity.OnBackPressedListener;
import com.github.nutomic.controldlna.utility.DeviceArrayAdapter;
import com.github.nutomic.controldlna.utility.FileArrayAdapter;
import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.android.AndroidUpnpServiceImpl;
import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.model.types.UDN;
import org.teleal.cling.support.contentdirectory.callback.Browse;
import org.teleal.cling.support.model.BrowseFlag;
import org.teleal.cling.support.model.DIDLContent;
import org.teleal.cling.support.model.container.Container;
import org.teleal.cling.support.model.item.Item;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* Shows a list of media servers, upon selecting one, allows browsing their
* directories.
*
*
* @author Felix Ableitner
*
*
*/
public class ServerFragment extends ListFragment implements OnBackPressedListener {
@ -107,6 +107,8 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
*/
private Stack<String> mCurrentPath = new Stack<String>();
private TextView mEmptyView;
/**
* Holds the scroll position in the list view at each directory level.
*/
@ -148,7 +150,6 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.server_fragment, null);
};
@ -166,21 +167,23 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
setListAdapter(mServerAdapter);
getActivity().getApplicationContext().bindService(
new Intent(getActivity(), AndroidUpnpServiceImpl.class),
mUpnpServiceConnection,
Context.BIND_AUTO_CREATE
);
mUpnpServiceConnection, Context.BIND_AUTO_CREATE);
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
getActivity().registerReceiver(mWifiReceiver, filter);
mEmptyView = (TextView) getListView().getEmptyView();
if (savedInstanceState != null) {
mRestoreServer = savedInstanceState.getString("current_server");
mCurrentPath.addAll(savedInstanceState.getStringArrayList("path"));
mListState.addAll(savedInstanceState.getParcelableArrayList("list_state"));
} else
}
else {
mListState.push(getListView().onSaveInstanceState());
}
}
/**
@ -191,7 +194,7 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
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());
@ -210,19 +213,23 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
*/
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (getListAdapter() == mServerAdapter)
if (getListAdapter() == mServerAdapter) {
browsingMode(mServerAdapter.getItem(position));
else if (getListAdapter() == mFileAdapter)
}
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)
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);
}
}
}
/**
@ -231,8 +238,7 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
private void serverMode() {
setListAdapter(mServerAdapter);
mCurrentServer = null;
TextView emptyView = (TextView) getListView().getEmptyView();
emptyView.setText(R.string.device_list_empty);
mEmptyView.setText(R.string.device_list_empty);
getListView().onRestoreInstanceState(mListState.pop());
}
@ -243,8 +249,7 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
setListAdapter(mFileAdapter);
mCurrentServer = server;
getFiles(ROOT_DIRECTORY);
TextView emptyView = (TextView) getListView().getEmptyView();
emptyView.setText(R.string.folder_list_empty);
mEmptyView.setText(R.string.folder_list_empty);
}
/**
@ -258,7 +263,7 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
/**
* 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
* mListState.
@ -281,14 +286,18 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
@Override
public void run() {
mFileAdapter.clear();
for (Container c : didl.getContainers())
for (Container c : didl.getContainers()) {
mFileAdapter.add(c);
for (Item i : didl.getItems())
}
for (Item i : didl.getItems()) {
mFileAdapter.add(i);
if (restoreListState)
}
if (restoreListState) {
getListView().onRestoreInstanceState(mListState.pop());
else
}
else {
getListView().setSelectionFromTop(0, 0);
}
}
});
}
@ -317,10 +326,12 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
return false;
mCurrentPath.pop();
if (mCurrentPath.empty())
if (mCurrentPath.empty()) {
serverMode();
else
}
else {
getFiles(true);
}
return true;
}
@ -340,11 +351,13 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
if (wifi.isConnected()) {
if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint()
.getRegistry().getDevices())
.getRegistry().getDevices()) {
mServerAdapter.deviceAdded(d);
}
mUpnpService.getControlPoint().search();
}
} else
}
else {
for (int i = 0; i < mServerAdapter.getCount(); i++) {
Device<?, ?, ?> d = mServerAdapter.getItem(i);
UDN udn = new UDN(d.getIdentity().getUdn().toString());
@ -358,6 +371,7 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
}
}
}
}
}
};

View file

@ -4,14 +4,14 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,8 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.localroute;
import java.io.IOException;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
@ -41,13 +39,15 @@ import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouter.ControlRequestCallback;
import android.util.Log;
import java.io.IOException;
/**
* Receives control intents through media route and executes them on a MediaPlayer.
*
*
* @author felix
*
*/
public class Controller extends MediaRouteProvider.RouteController implements
public class Controller extends MediaRouteProvider.RouteController implements
MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener {
private static final String TAG = "Controller";
@ -55,104 +55,106 @@ public class Controller extends MediaRouteProvider.RouteController implements
private Context mContext;
private AudioManager mAudio;
AudioManager.OnAudioFocusChangeListener mFocusListener;
AudioManager.OnAudioFocusChangeListener mFocusListener;
private final String mRouteId;
private String mItemId;
private int mState;
private final String mRouteId;
private String mItemId;
private int mState;
private MediaPlayer mPlayer = new MediaPlayer();
public Controller(String routeId, Context context) {
mContext = context;
mRouteId = routeId;
mAudio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(this);
}
public Controller(String routeId, Context context) {
mContext = context;
mRouteId = routeId;
mAudio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(this);
}
@Override
public void onRelease() {
mPlayer.release();
}
@Override
public void onRelease() {
mPlayer.release();
}
@Override
public void onSelect() {
mAudio.requestAudioFocus(mFocusListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
}
@Override
public void onSelect() {
mAudio.requestAudioFocus(mFocusListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
}
@Override
public void onUnselect() {
mAudio.abandonAudioFocus(mFocusListener);
}
@Override
public void onUnselect() {
mAudio.abandonAudioFocus(mFocusListener);
}
@Override
public void onSetVolume(int volume) {
mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
}
@Override
public void onSetVolume(int volume) {
mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
}
@Override
public void onUpdateVolume(int delta) {
int currentVolume = mAudio.getStreamVolume(AudioManager.STREAM_MUSIC);
mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume + delta, 0);
}
@Override
public void onUpdateVolume(int delta) {
int currentVolume = mAudio.getStreamVolume(AudioManager.STREAM_MUSIC);
mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume + delta, 0);
}
@Override
public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)) {
try {
mPlayer.reset();
@Override
public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)) {
try {
mPlayer.reset();
mPlayer.setDataSource(mContext, intent.getData());
mPlayer.prepareAsync();
mItemId = intent.getDataString();
mState = MediaItemStatus.PLAYBACK_STATE_BUFFERING;
getStatus(mItemId, mRouteId, callback);
return true;
} catch (IllegalArgumentException e) {
mState = MediaItemStatus.PLAYBACK_STATE_ERROR;
Log.d(TAG, "Failed to start playback", e);
} catch (IOException e) {
return true;
}
catch (IllegalArgumentException e) {
mState = MediaItemStatus.PLAYBACK_STATE_ERROR;
Log.d(TAG, "Failed to start playback", e);
}
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_PAUSE)) {
mPlayer.pause();
mState = MediaItemStatus.PLAYBACK_STATE_PAUSED;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_RESUME)) {
mPlayer.start();
mState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_STOP)) {
mPlayer.stop();
mState = MediaItemStatus.PLAYBACK_STATE_CANCELED;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_SEEK)) {
mPlayer.seekTo((int) intent.getLongExtra(
MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0));
getStatus(itemId, sessionId, callback);
return true;
}
else if(intent.getAction().equals(MediaControlIntent.ACTION_GET_STATUS)) {
getStatus(itemId, sessionId, callback);
return true;
}
catch (IOException e) {
mState = MediaItemStatus.PLAYBACK_STATE_ERROR;
Log.d(TAG, "Failed to start playback", e);
}
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_PAUSE)) {
mPlayer.pause();
mState = MediaItemStatus.PLAYBACK_STATE_PAUSED;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_RESUME)) {
mPlayer.start();
mState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_STOP)) {
mPlayer.stop();
mState = MediaItemStatus.PLAYBACK_STATE_CANCELED;
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_SEEK)) {
mPlayer.seekTo((int) intent.getLongExtra(
MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0));
getStatus(itemId, sessionId, callback);
return true;
}
else if(intent.getAction().equals(MediaControlIntent.ACTION_GET_STATUS)) {
getStatus(itemId, sessionId, callback);
return true;
}
return false;
}
private void getStatus(String itemId, String sessionId, ControlRequestCallback callback) {
if (callback == null)
return;
}
private void getStatus(String itemId, String sessionId, ControlRequestCallback callback) {
if (callback == null)
return;
Bundle status = null;
@ -161,24 +163,26 @@ public class Controller extends MediaRouteProvider.RouteController implements
.setContentPosition(mPlayer.getCurrentPosition())
.setContentDuration(mPlayer.getDuration())
.setTimestamp(SystemClock.elapsedRealtime())
.build().asBundle();
.build()
.asBundle();
status.putString(MediaControlIntent.EXTRA_SESSION_ID, mRouteId);
status.putString(MediaControlIntent.EXTRA_ITEM_ID, mItemId);
}
else
else {
status = new MediaItemStatus.Builder(MediaItemStatus.PLAYBACK_STATE_INVALIDATED)
.build().asBundle();
}
callback.onResult(status);
}
callback.onResult(status);
}
/**
* Sets state to finished.
*
* Note: Do not set the listener before play() is called
* (or this will be called immediately).
*/
/**
* Sets state to finished.
*
* Note: Do not set the listener before play() is called
* (or this will be called immediately).
*/
@Override
public void onCompletion(MediaPlayer mp) {
mState = MediaItemStatus.PLAYBACK_STATE_FINISHED;
@ -191,7 +195,7 @@ public class Controller extends MediaRouteProvider.RouteController implements
@Override
public void onPrepared(MediaPlayer mp) {
mPlayer.start();
mState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
mPlayer.setOnCompletionListener(this);
mState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
mPlayer.setOnCompletionListener(this);
}
}

View file

@ -4,14 +4,14 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -43,63 +43,64 @@ import android.support.v7.media.MediaRouter;
import com.github.nutomic.controldlna.R;
/**
* MediaRouteProvider that details the local audio route with its
* controls to the system.
*
* MediaRouteProvider that details the local audio route with its
* controls to the system.
*
* @author felix
*
*/
final class Provider extends MediaRouteProvider {
private static final String ROUTE_ID = "local_route";
AudioManager mAudio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
private static final String ROUTE_ID = "local_route";
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");
addDataTypeUnchecked(f, "audio/*");
AudioManager mAudio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
CONTROL_FILTERS = new ArrayList<IntentFilter>();
CONTROL_FILTERS.add(f);
}
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");
addDataTypeUnchecked(f, "audio/*");
private static void addDataTypeUnchecked(IntentFilter filter, String type) {
try {
filter.addDataType(type);
} catch (MalformedMimeTypeException ex) {
throw new RuntimeException(ex);
}
}
CONTROL_FILTERS = new ArrayList<IntentFilter>();
CONTROL_FILTERS.add(f);
}
public Provider(Context context) {
super(context);
private static void addDataTypeUnchecked(IntentFilter filter, String type) {
try {
filter.addDataType(type);
}
catch (MalformedMimeTypeException ex) {
throw new RuntimeException(ex);
}
}
MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
ROUTE_ID, context.getResources().getString(R.string.local_device))
.addControlFilters(CONTROL_FILTERS)
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolume(mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.build();
public Provider(Context context) {
super(context);
MediaRouteProviderDescriptor providerDescriptor =
new MediaRouteProviderDescriptor.Builder()
.addRoute(routeDescriptor)
.build();
setDescriptor(providerDescriptor);
}
MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
ROUTE_ID, context.getResources().getString(R.string.local_device))
.addControlFilters(CONTROL_FILTERS)
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolume(mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.build();
@Override
public RouteController onCreateRouteController(String routeId) {
return new Controller(routeId, getContext());
}
MediaRouteProviderDescriptor providerDescriptor =
new MediaRouteProviderDescriptor.Builder()
.addRoute(routeDescriptor)
.build();
setDescriptor(providerDescriptor);
}
@Override
public RouteController onCreateRouteController(String routeId) {
return new Controller(routeId, getContext());
}
}

View file

@ -4,14 +4,14 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -36,8 +36,9 @@ public class ProviderService extends MediaRouteProviderService {
@Override
public MediaRouteProvider onCreateMediaRouteProvider() {
if (mProvider == null)
if (mProvider == null) {
mProvider = new Provider(this);
}
return mProvider;
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,17 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.mediarouter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.teleal.cling.support.contentdirectory.DIDLParser;
import org.teleal.cling.support.model.DIDLContent;
import org.teleal.cling.support.model.DIDLObject;
import org.teleal.cling.support.model.item.Item;
import org.teleal.cling.support.model.item.MusicTrack;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
@ -66,9 +55,20 @@ import com.github.nutomic.controldlna.gui.PreferencesActivity;
import com.github.nutomic.controldlna.gui.RouteFragment;
import com.github.nutomic.controldlna.utility.LoadImageTask;
import org.teleal.cling.support.contentdirectory.DIDLParser;
import org.teleal.cling.support.model.DIDLContent;
import org.teleal.cling.support.model.DIDLObject;
import org.teleal.cling.support.model.item.Item;
import org.teleal.cling.support.model.item.MusicTrack;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Background service that handles media playback to a single UPNP media renderer.
*
*
* @author Felix Ableitner
*
*/
@ -120,22 +120,24 @@ public class MediaRouterPlayService extends Service {
new MediaRouter.Callback() {
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
if (route.equals(mCurrentRoute))
if (route.equals(mCurrentRoute)) {
stopForeground(true);
}
if (!mBound && !mPollingStatus)
if (!mBound && !mPollingStatus) {
stopSelf();
}
}
@Override
public void onRouteAdded(MediaRouter router, RouteInfo route) {
if (route.getId().equals(mCurrentRoute.getId())) {
selectRoute(route);
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
}
}
};
@Override
public void onRouteAdded(MediaRouter router, RouteInfo route) {
if (route.getId().equals(mCurrentRoute.getId())) {
selectRoute(route);
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
}
}
};
/**
* Creates a notification after the icon bitmap is loaded.
@ -150,15 +152,16 @@ public class MediaRouterPlayService extends Service {
title = mPlaylist.get(mCurrentTrack).getTitle();
if (mPlaylist.get(mCurrentTrack) instanceof MusicTrack) {
MusicTrack track = (MusicTrack) mPlaylist.get(mCurrentTrack);
if (track.getArtists().length > 0)
if (track.getArtists().length > 0) {
artist = track.getArtists()[0].getName();
}
}
}
Intent intent = new Intent(MediaRouterPlayService.this, MainActivity.class);
intent.setAction("showRouteFragment");
Notification notification = new NotificationCompat.Builder(MediaRouterPlayService.this)
.setContentIntent(PendingIntent.getActivity(MediaRouterPlayService.this, 0,
intent, 0))
.setContentIntent(PendingIntent.getActivity(MediaRouterPlayService.this, 0,
intent, 0))
.setContentTitle(title)
.setContentText(artist)
.setLargeIcon(result)
@ -170,35 +173,35 @@ public class MediaRouterPlayService extends Service {
}
/**
* Listens for incoming phone calls and pauses playback then.
*/
private class PhoneCallListener extends PhoneStateListener {
/**
* Listens for incoming phone calls and pauses playback then.
*/
private class PhoneCallListener extends PhoneStateListener {
private boolean mPausedForCall = false;
private boolean mPausedForCall = false;
@Override
public void onCallStateChanged(int state, String incomingNumber) {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (!PreferenceManager.getDefaultSharedPreferences(MediaRouterPlayService.this)
.getBoolean(PreferencesActivity.KEY_INCOMING_PHONE_CALL_PAUSE, true)) {
return;
}
if (!PreferenceManager.getDefaultSharedPreferences(MediaRouterPlayService.this)
.getBoolean(PreferencesActivity.KEY_INCOMING_PHONE_CALL_PAUSE, true)) {
return;
}
if (TelephonyManager.CALL_STATE_RINGING == state ||
TelephonyManager.CALL_STATE_OFFHOOK == state) {
// phone ringing or call active
pause();
mPausedForCall = true;
}
if (TelephonyManager.CALL_STATE_RINGING == state ||
TelephonyManager.CALL_STATE_OFFHOOK == state) {
// phone ringing or call active
pause();
mPausedForCall = true;
}
if (mPausedForCall && TelephonyManager.CALL_STATE_IDLE == state) {
// run when class initial and phone call ended
resume();
mPausedForCall = false;
}
}
}
if (mPausedForCall && TelephonyManager.CALL_STATE_IDLE == state) {
// run when class initial and phone call ended
resume();
mPausedForCall = false;
}
}
}
@Override
public void onCreate() {
@ -206,11 +209,10 @@ public class MediaRouterPlayService extends Service {
mMediaRouter = MediaRouter.getInstance(this);
pollStatus();
PhoneCallListener phoneListener = new PhoneCallListener();
TelephonyManager telephonyManager = (TelephonyManager) this
.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(phoneListener,
PhoneStateListener.LISTEN_CALL_STATE);
PhoneCallListener phoneListener = new PhoneCallListener();
TelephonyManager telephonyManager =
(TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
@ -225,8 +227,9 @@ public class MediaRouterPlayService extends Service {
*/
@Override
public boolean onUnbind(Intent intent) {
if (!mPollingStatus)
if (!mPollingStatus) {
stopSelf();
}
mBound = false;
return super.onUnbind(intent);
}
@ -239,8 +242,8 @@ public class MediaRouterPlayService extends Service {
mMediaRouter.removeCallback(mRouteRemovedCallback);
mMediaRouter.selectRoute(route);
MediaRouteSelector selector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build();
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build();
mMediaRouter.addCallback(selector, mRouteRemovedCallback, 0);
mCurrentRoute = route;
@ -287,8 +290,9 @@ public class MediaRouterPlayService extends Service {
new CreateNotificationTask().execute(mPlaylist.get(mCurrentTrack)
.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class));
if (mRouterFragment.get() != null)
if (mRouterFragment.get() != null) {
mRouterFragment.get().scrollToCurrent();
}
}
});
}
@ -347,23 +351,25 @@ public class MediaRouterPlayService extends Service {
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mItemId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION,
(long) seconds * 1000);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, (long) seconds * 1000);
mMediaRouter.getSelectedRoute().sendControlRequest(intent, null);
}
/**
* Sets a new playlist and starts playing.
*
*
* @param playlist The media files in the playlist.
*/
public void setPlaylist(List<Item> playlist) {
mPlaylist = playlist;
}
/**
* Plays the track after current in the playlist.
*
*
* @return True if another item is played, false if the end
* of the playlist is reached.
*/
@ -389,8 +395,9 @@ public class MediaRouterPlayService extends Service {
else {
// Playlist over, stop playback.
stop();
if (!mBound)
if (!mBound) {
stopSelf();
}
mPollingStatus = false;
return false;
}
@ -404,11 +411,13 @@ public class MediaRouterPlayService extends Service {
if (mCurrentTrack == -1)
return;
if (mShuffle)
if (mShuffle) {
// Play random item.
play(new Random().nextInt(mPlaylist.size()));
else
}
else {
play(mCurrentTrack - 1);
}
}
/**
@ -437,16 +446,19 @@ public class MediaRouterPlayService extends Service {
if (status == null)
return;
if (mRouterFragment.get() != null)
if (mRouterFragment.get() != null) {
mRouterFragment.get().receivePlaybackStatus(status);
}
if (status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PENDING &&
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_BUFFERING &&
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING)
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING) {
stopForeground(true);
}
if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED ||
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_CANCELED)
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_CANCELED) {
playNext();
}
}
});
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -31,7 +31,7 @@ import android.os.Binder;
/**
* Provides connection to PlayService.
*
*
* @author Felix Ableitner
*
*/

View file

@ -4,14 +4,14 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -24,11 +24,11 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.controldlna.upnp;
import android.os.Messenger;
interface IRemotePlayService {
void startSearch(in Messenger listener);
void selectRenderer(String id);

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,12 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.upnp;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@ -54,10 +48,17 @@ import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouteProviderDescriptor.Builder;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.ControlRequestCallback;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;
/**
* Allows playing to a DLNA renderer from a remote app.
*
@ -65,6 +66,8 @@ import android.widget.Toast;
*/
final class Provider extends MediaRouteProvider {
private static final String TAG = "Provider";
// Device has been added.
// param: Device device
public static final int MSG_RENDERER_ADDED = 1;
@ -93,8 +96,8 @@ final class Provider extends MediaRouteProvider {
public int volume;
public int volumeMax;
public static final Parcelable.Creator<Device> CREATOR
= new Parcelable.Creator<Device>() {
public static final Parcelable.Creator<Device> CREATOR =
new Parcelable.Creator<Device>() {
public Device createFromParcel(Parcel in) {
return new Device(in);
}
@ -187,8 +190,9 @@ final class Provider extends MediaRouteProvider {
@Override
public void handleMessage(Message msg) {
if (mService.get() != null)
if (mService.get() != null) {
mService.get().handleMessage(msg);
}
}
}
@ -196,11 +200,8 @@ final class Provider extends MediaRouteProvider {
public Provider(Context context) {
super(context);
context.bindService(
new Intent(context, RemotePlayService.class),
mConnection,
Context.BIND_AUTO_CREATE
);
context.bindService(new Intent(context, RemotePlayService.class),
mConnection, Context.BIND_AUTO_CREATE);
}
public void close() {
@ -210,9 +211,11 @@ final class Provider extends MediaRouteProvider {
@Override
public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
try {
if (request != null && request.isActiveScan() && mIRemotePlayService != null)
if (request != null && request.isActiveScan() && mIRemotePlayService != null) {
mIRemotePlayService.startSearch(mListener);
} catch (RemoteException e) {
}
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@ -259,7 +262,8 @@ final class Provider extends MediaRouteProvider {
public void onSelect() {
try {
mIRemotePlayService.selectRenderer(mRouteId);
} catch (RemoteException e) {
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@ -268,7 +272,8 @@ final class Provider extends MediaRouteProvider {
public void onUnselect() {
try {
mIRemotePlayService.unselectRenderer(mRouteId);
} catch (RemoteException e) {
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@ -280,7 +285,8 @@ final class Provider extends MediaRouteProvider {
try {
mIRemotePlayService.setVolume(volume);
} catch (RemoteException e) {
}
catch (RemoteException e) {
e.printStackTrace();
}
mDevices.get(mRouteId).volume = volume;
@ -301,13 +307,13 @@ final class Provider extends MediaRouteProvider {
if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)) {
String metadata = (intent.hasExtra(MediaControlIntent.EXTRA_ITEM_METADATA))
? intent.getExtras().getString(MediaControlIntent.EXTRA_ITEM_METADATA)
: null;
mIRemotePlayService.play(intent.getDataString(), metadata);
// Store in intent extras for later.
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mRouteId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, intent.getDataString());
getItemStatus(intent, callback);
return true;
: null;
mIRemotePlayService.play(intent.getDataString(), metadata);
// Store in intent extras for later.
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mRouteId);
intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, intent.getDataString());
getItemStatus(intent, callback);
return true;
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_PAUSE)) {
mIRemotePlayService.pause(mRouteId);
@ -323,10 +329,8 @@ final class Provider extends MediaRouteProvider {
}
else if (intent.getAction().equals(MediaControlIntent.ACTION_SEEK)) {
mIRemotePlayService.seek(mRouteId,
intent.getStringExtra(
MediaControlIntent.EXTRA_ITEM_ID),
intent.getLongExtra(
MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0));
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID),
intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0));
getItemStatus(intent, callback);
return true;
}
@ -334,8 +338,9 @@ final class Provider extends MediaRouteProvider {
getItemStatus(intent, callback);
return true;
}
} catch (RemoteException e) {
e.printStackTrace();
}
catch (RemoteException e) {
Log.w(TAG, "Failed to execute control request", e);
}
return false;
}
@ -357,8 +362,7 @@ final class Provider extends MediaRouteProvider {
mRequests.put(r, pair);
mIRemotePlayService.getItemStatus(
intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID),
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID),
r);
intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID), r);
}
/**
@ -382,12 +386,14 @@ final class Provider extends MediaRouteProvider {
mRequests.get(data.getInt("hash"));
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,
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,
pair.first.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID));
}
pair.second.onResult(status);
break;
case MSG_ERROR:

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -36,8 +36,9 @@ public class ProviderService extends MediaRouteProviderService {
@Override
public MediaRouteProvider onCreateMediaRouteProvider() {
if (mProvider == null)
if (mProvider == null) {
mProvider = new Provider(this);
}
return mProvider;
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -64,7 +64,7 @@ import android.util.Log;
/**
* Allows UPNP playback from different apps by providing a proxy interface.
* You can communicate to this service via RemotePlayServiceBinder.
*
*
* @author Felix Ableitner
*
*/
@ -87,11 +87,14 @@ public class RemotePlayService extends Service implements RegistryListener {
public void onServiceConnected(ComponentName className, IBinder service) {
mUpnpService = (AndroidUpnpService) service;
mUpnpService.getRegistry().addListener(RemotePlayService.this);
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
if (d instanceof LocalDevice)
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices()) {
if (d instanceof LocalDevice) {
localDeviceAdded(mUpnpService.getRegistry(), (LocalDevice) d);
else
}
else {
remoteDeviceAdded(mUpnpService.getRegistry(), (RemoteDevice) d);
}
}
mUpnpService.getControlPoint().search();
}
@ -119,11 +122,8 @@ public class RemotePlayService extends Service implements RegistryListener {
@Override
public void onCreate() {
super.onCreate();
bindService(
new Intent(this, AndroidUpnpServiceImpl.class),
mUpnpServiceConnection,
Context.BIND_AUTO_CREATE
);
bindService(new Intent(this, AndroidUpnpServiceImpl.class),
mUpnpServiceConnection, Context.BIND_AUTO_CREATE);
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
@ -144,7 +144,8 @@ public class RemotePlayService extends Service implements RegistryListener {
void sendMessage(Message msg) {
try {
mListener.send(msg);
} catch (RemoteException e) {
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@ -173,21 +174,28 @@ public class RemotePlayService extends Service implements RegistryListener {
if (wifi.isConnected()) {
if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
for (Device<?, ?, ?> d :
mUpnpService.getControlPoint().getRegistry().getDevices()) {
deviceAdded(d);
}
mUpnpService.getControlPoint().search();
}
} else
for (Entry<String, Device<?, ?, ?>> d : mDevices.entrySet())
}
else {
for (Entry<String, Device<?, ?, ?>> d : mDevices.entrySet()) {
if (mUpnpService.getControlPoint().getRegistry()
.getDevice(new UDN(d.getKey()), false) == null) {
deviceRemoved(d.getValue());
for (RemotePlayServiceBinder b : mBinders.keySet())
if (b.mCurrentRenderer != null && b.mCurrentRenderer.equals(d.getValue())) {
for (RemotePlayServiceBinder b : mBinders.keySet()) {
if (b.mCurrentRenderer != null &&
b.mCurrentRenderer.equals(d.getValue())) {
b.mSubscriptionCallback.end();
b.mCurrentRenderer = null;
}
}
}
}
}
}
};
@ -196,8 +204,7 @@ public class RemotePlayService extends Service implements RegistryListener {
*/
org.teleal.cling.model.meta.Service<?, ?> getService(
Device<?, ?, ?> device, String name) {
return device.findService(
new ServiceType("schemas-upnp-org", name));
return device.findService(new ServiceType("schemas-upnp-org", name));
}
/**
@ -207,8 +214,7 @@ public class RemotePlayService extends Service implements RegistryListener {
if (mDevices.containsValue(device))
return;
final org.teleal.cling.model.meta.Service<?, ?> rc =
getService(device, "RenderingControl");
final org.teleal.cling.model.meta.Service<?, ?> rc = getService(device, "RenderingControl");
if (rc == null || mListener == null)
return;
@ -233,7 +239,8 @@ public class RemotePlayService extends Service implements RegistryListener {
int maxVolume = 100;
if (rc.getStateVariable("Volume") != null) {
StateVariableAllowedValueRange volumeRange =
rc.getStateVariable("Volume").getTypeDetails().getAllowedValueRange();
rc.getStateVariable("Volume")
.getTypeDetails().getAllowedValueRange();
maxVolume = (int) volumeRange.getMaximum();
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -83,26 +83,27 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
private RemotePlayService mRps;
private boolean mStartingPlayback = false;
public RemotePlayServiceBinder(RemotePlayService rps) {
mRps = rps;
}
@Override
public void startSearch(Messenger listener)
throws RemoteException {
public void startSearch(Messenger listener) throws RemoteException {
mRps.mListener = listener;
}
@Override
public void selectRenderer(String id) throws RemoteException {
mCurrentRenderer = mRps.mDevices.get(id);
for (RemotePlayServiceBinder b : mRps.mBinders.keySet())
if (b != this && mCurrentRenderer.equals(b.mCurrentRenderer))
for (RemotePlayServiceBinder b : mRps.mBinders.keySet()) {
if (b != this && mCurrentRenderer.equals(b.mCurrentRenderer)) {
b.unselectRenderer("");
}
}
mSubscriptionCallback = new SubscriptionCallback(
mCurrentRenderer.findService(
new ServiceType("schemas-upnp-org", "AVTransport")), 600) {
mSubscriptionCallback = new SubscriptionCallback(mCurrentRenderer.findService(
new ServiceType("schemas-upnp-org", "AVTransport")), 600) {
@SuppressWarnings("rawtypes")
@Override
@ -111,8 +112,7 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
@SuppressWarnings("rawtypes")
@Override
protected void ended(GENASubscription sub, CancelReason reason,
UpnpResponse response) {
protected void ended(GENASubscription sub, CancelReason reason, UpnpResponse response) {
}
@SuppressWarnings("rawtypes")
@ -121,16 +121,14 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
@SuppressWarnings("unchecked")
Map<String, StateVariableValue> m = sub.getCurrentValues();
try {
LastChange lastChange = new LastChange(
new AVTransportLastChangeParser(),
LastChange lastChange = new LastChange(new AVTransportLastChangeParser(),
m.get("LastChange").toString());
if (mStartingPlayback || lastChange.getEventedValue(0,
AVTransportVariable.TransportState.class) == null)
return;
switch (lastChange.getEventedValue(0,
AVTransportVariable.TransportState.class)
.getValue()) {
AVTransportVariable.TransportState.class).getValue()) {
case PLAYING:
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
break;
@ -149,7 +147,8 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
default:
}
} catch (Exception e) {
}
catch (Exception e) {
Log.w(TAG, "Failed to parse UPNP event", e);
mRps.sendError("Failed to parse UPNP event");
}
@ -157,8 +156,7 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
@SuppressWarnings("rawtypes")
@Override
protected void eventsMissed(GENASubscription sub,
int numberOfMissedEvents) {
protected void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
}
@SuppressWarnings("rawtypes")
@ -177,10 +175,12 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
*/
@Override
public void unselectRenderer(String sessionId) throws RemoteException {
if (mRps.mDevices.get(sessionId) != null)
if (mRps.mDevices.get(sessionId) != null) {
stop(sessionId);
if (mSubscriptionCallback != null)
}
if (mSubscriptionCallback != null) {
mSubscriptionCallback.end();
}
mCurrentRenderer = null;
}
@ -246,19 +246,19 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
new Play(mRps.getService(mCurrentRenderer,
"AVTransport")) {
@Override
public void success(ActionInvocation invocation) {
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
mStartingPlayback = false;
}
@Override
public void success(ActionInvocation invocation) {
mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PLAYING;
mStartingPlayback = false;
}
@Override
public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Play failed: " + defaultMessage);
mRps.sendError("Play failed: " + defaultMessage);
mStartingPlayback = false;
}
@Override
public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Play failed: " + defaultMessage);
mRps.sendError("Play failed: " + defaultMessage);
mStartingPlayback = false;
}
});
}
});
@ -284,8 +284,10 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
// Sometimes stop works even though pause does not.
try {
stop(sessionId);
} catch (RemoteException e) {
e.printStackTrace();
}
catch (RemoteException e) {
Log.w(TAG, "Calling stop failed", e);
mRps.sendError("Calling stop failed: " + e.toString());
}
}
});
@ -372,6 +374,7 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Get position failed: " + defaultMessage);
mRps.sendError("Get position failed: " + defaultMessage);
}
@SuppressWarnings("rawtypes")
@ -381,17 +384,20 @@ public class RemotePlayServiceBinder extends IRemotePlayService.Stub {
Message msg = Message.obtain(null, Provider.MSG_STATUS_INFO, 0, 0);
Builder status = null;
status = new MediaItemStatus.Builder(mPlaybackState);
if (positionInfo.getTrackURI() != null && positionInfo.getTrackURI().equals(itemId)) {
try {
status.setContentPosition(positionInfo.getTrackElapsedSeconds() * 1000)
.setContentDuration(positionInfo.getTrackDurationSeconds() * 1000)
.setTimestamp(positionInfo.getAbsCount());
}
catch (NumberFormatException e) {
Log.d(TAG, "Failed to read track position or duration", e);
}
}
status = new MediaItemStatus.Builder(mPlaybackState);
if (positionInfo.getTrackURI() != null &&
positionInfo.getTrackURI().equals(itemId)) {
try {
status.setContentPosition(positionInfo.getTrackElapsedSeconds() * 1000)
.setContentDuration(positionInfo.getTrackDurationSeconds() * 1000)
.setTimestamp(positionInfo.getAbsCount());
}
catch (NumberFormatException e) {
Log.d(TAG, "Failed to read track position or duration", e);
mRps.sendError("Failed to read track position or duration: " +
e.toString());
}
}
msg.getData().putBundle("media_item_status", status.build().asBundle());
msg.getData().putInt("hash", requestHash);

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,16 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.utility;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Comparator;
import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.registry.Registry;
import org.teleal.cling.registry.RegistryListener;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
@ -48,6 +38,16 @@ import android.widget.TextView;
import com.github.nutomic.controldlna.R;
import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.registry.Registry;
import org.teleal.cling.registry.RegistryListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Comparator;
/**
* Displays the devices that are inserted through the RegistryListener (either
* of type RENDERER or SERVER).
@ -60,8 +60,6 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
private static final String TAG = "DeviceArrayAdapter";
public static final String RENDERER = "MediaRenderer";
public static final String SERVER = "MediaServer";
private Activity mActivity;
@ -91,15 +89,16 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
if (getItem(position).hasIcons()) {
URI uri = getItem(position).getIcons()[0].getUri();
if (getItem(position) instanceof RemoteDevice)
if (getItem(position) instanceof RemoteDevice) {
try {
RemoteDevice device = (RemoteDevice) getItem(position);
uri = device.normalizeURI(uri).toURI();
} catch (URISyntaxException e) {
}
catch (URISyntaxException e) {
Log.w(TAG, "Failed to get device icon URI", e);
}
RemoteImageView icon =
(RemoteImageView) convertView.findViewById(R.id.image);
}
RemoteImageView icon = (RemoteImageView) convertView.findViewById(R.id.image);
icon.setImageUri(uri);
}
@ -110,9 +109,11 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
* Adds a new device to the list if its type equals mDeviceType.
*/
public void deviceAdded(final Device<?, ?, ?> device) {
for (int i = 0; i < getCount(); i++)
if (getItem(i).equals(device))
for (int i = 0; i < getCount(); i++) {
if (getItem(i).equals(device)) {
return;
}
}
mActivity.runOnUiThread(new Runnable() {
@ -141,8 +142,9 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
@Override
public void run() {
if (getPosition(device) != -1)
if (getPosition(device) != -1) {
remove(device);
}
}
});
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.github.nutomic.controldlna.utility;
import java.net.URI;
import java.util.List;
import java.util.List;
import org.teleal.cling.support.model.DIDLObject;
import org.teleal.cling.support.model.item.AudioItem;
@ -78,32 +78,38 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
MusicTrack track = (MusicTrack) item;
String trackNumber = (track.getOriginalTrackNumber() != null)
? Integer.toString(track.getOriginalTrackNumber()) + ". "
: "";
: "";
title.setText(trackNumber + item.getTitle());
if (track.getArtists().length > 0)
if (track.getArtists().length > 0) {
artist.setText(track.getArtists()[0].getName());
}
}
else
else {
title.setText(item.getTitle());
}
RemoteImageView image = (RemoteImageView) convertView.findViewById(R.id.image);
URI icon = item.getFirstPropertyValue(
DIDLObject.Property.UPNP.ALBUM_ART_URI.class);
URI icon = item.getFirstPropertyValue(DIDLObject.Property.UPNP.ALBUM_ART_URI.class);
if (icon != null) {
image.setImageUri(icon);
}
else {
int resId;
if (item instanceof AudioItem)
if (item instanceof AudioItem) {
resId = R.drawable.ic_doc_audio_am;
else if (item instanceof VideoItem)
}
else if (item instanceof VideoItem) {
resId = R.drawable.ic_doc_video_am;
else if (item instanceof ImageItem)
}
else if (item instanceof ImageItem) {
resId = R.drawable.ic_doc_image;
else if (item instanceof PlaylistItem)
}
else if (item instanceof PlaylistItem) {
resId = R.drawable.ic_doc_album;
else
}
else {
resId = R.drawable.ic_root_folder_am;
}
image.setImageResource(resId);
}
@ -114,8 +120,9 @@ public class FileArrayAdapter extends ArrayAdapter<DIDLObject> {
* Replacement for addAll, which is not implemented on lower API levels.
*/
public void add(List<Item> playlist) {
for (DIDLObject d : playlist)
for (DIDLObject d : playlist) {
add(d);
}
}
}

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -40,8 +40,8 @@ import android.os.AsyncTask;
import android.util.Log;
/**
* Handles background task of loading a bitmap by URI.
*
* Loads an image by URI in the background and returns it.
*
* @author Felix Ableitner
*
*/
@ -56,15 +56,15 @@ public class LoadImageTask extends AsyncTask<URI, Void, Bitmap> {
Bitmap bm = null;
try {
URLConnection conn = new URL(uri[0].toString())
.openConnection();
URLConnection conn = new URL(uri[0].toString()).openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
} catch (IOException e) {
}
catch (IOException e) {
Log.w(TAG, "Failed to load artwork image", e);
}
return bm;

View file

@ -5,13 +5,13 @@ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* 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
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -36,7 +36,7 @@ import android.widget.ImageView;
/**
* ImageView that can directly load from a UPNP URI.
*
*
* @author Felix Ableitner
*
*/
@ -44,7 +44,7 @@ public class RemoteImageView extends ImageView {
/**
* Assigns the icon as image drawable when it is loaded.
*
*
* @author Felix
*
*/
@ -52,8 +52,9 @@ public class RemoteImageView extends ImageView {
@Override
protected void onPostExecute(Bitmap bm) {
if (bm != null)
if (bm != null) {
setImageBitmap(bm);
}
}
};

View file

@ -41,7 +41,7 @@ public class RouteAdapter extends ArrayAdapter<RouteInfo> {
public void add(List<RouteInfo> routes) {
for (RouteInfo r : routes) {
add(r);
}
}
}
}