Improved handling if wifi is disconnected.

This commit is contained in:
Felix Ableitner 2013-10-18 15:01:26 +02:00
parent 92768d518b
commit 0184355397
8 changed files with 227 additions and 27 deletions

View file

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

View file

@ -12,5 +12,8 @@
<string name="select_renderer">Please select a renderer</string>
<string name="album_art">Album Art</string>
<string name="route_description">DLNA Playback</string>
<string name="warning_wifi_not_connected">Wifi needs to be connected for playback</string>
<string name="auto_enable_wifi">Enable Wifi automatically on start</string>
<string name="dont_show_dialog_again">"Do not show this dialog again"</string>
</resources>

View file

@ -31,6 +31,11 @@ import java.util.List;
import org.teleal.cling.support.model.item.Item;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@ -42,6 +47,10 @@ import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBar.TabListener;
import android.support.v7.app.ActionBarActivity;
import android.view.KeyEvent;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import com.github.nutomic.controldlna.R;
@ -86,7 +95,8 @@ public class MainActivity extends ActionBarActivity {
ViewPager mViewPager;
/**
* Initializes tab navigation.
* Initializes tab navigation. If wifi is not connected,
* shows a warning dialog.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -128,6 +138,50 @@ public class MainActivity extends ActionBarActivity {
.setText(R.string.title_renderer)
.setTabListener(tabListener));
ConnectivityManager connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
final SharedPreferences prefs = getSharedPreferences("preferences.db", 0);
if (!wifi.isConnected() && !prefs.getBoolean("wifi_skip_dialog", false) &&
!prefs.getBoolean("wifi_auto_enable", false)) {
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
CheckBox cbAutoEnable = new CheckBox(this);
cbAutoEnable.setText(R.string.auto_enable_wifi);
cbAutoEnable.setChecked(prefs.getBoolean("wifi_auto_enable", false));
cbAutoEnable.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean("wifi_auto_enable", isChecked)
.commit();
if (isChecked)
wifiManager.setWifiEnabled(true);
}
});
layout.addView(cbAutoEnable);
CheckBox cbShowOnce = new CheckBox(this);
cbShowOnce.setText(R.string.dont_show_dialog_again);
cbShowOnce.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean("wifi_skip_dialog", isChecked)
.commit();
}
});
layout.addView(cbShowOnce);
new AlertDialog.Builder(this)
.setView(layout)
.setTitle(R.string.warning_wifi_not_connected)
.setPositiveButton(android.R.string.ok, null)
.show();
}
else if (!wifiManager.isWifiEnabled() && prefs.getBoolean("wifi_auto_enable", false))
wifiManager.setWifiEnabled(true);
if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager();
mServerFragment = (ServerFragment) fm.getFragment(

View file

@ -94,7 +94,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
private FileArrayAdapter mPlaylistAdapter;
private boolean mRouteSelected = false;
private RouteInfo mSelectedRoute;
/**
* If true, the item at this position will be played as soon as a route is selected.
@ -177,7 +177,8 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
if (savedInstanceState != null) {
mListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_state"));
if (savedInstanceState.getBoolean("route_selected")) {
mRouteSelected = true;
mSelectedRoute = MediaRouter.getInstance(getActivity())
.getSelectedRoute();
mListView.setAdapter(mPlaylistAdapter);
mControls.setVisibility(View.VISIBLE);
}
@ -188,7 +189,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("route_selected", mRouteSelected);
outState.putBoolean("route_selected", mSelectedRoute != null);
outState.putParcelable("list_state", mListView.onSaveInstanceState());
}
@ -225,6 +226,10 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
mRouteAdapter.remove(route);
if (route.equals(mSelectedRoute)) {
mPlaying = false;
onBackPressed();
}
}
@Override
@ -265,8 +270,8 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
@Override
public void onItemClick(AdapterView<?> a, View v, final int position, long id) {
if (mListView.getAdapter() == mRouteAdapter) {
mMediaRouterPlayService.getService().selectRoute(mRouteAdapter.getItem(position));
mRouteSelected = true;
mSelectedRoute = mRouteAdapter.getItem(position);
mMediaRouterPlayService.getService().selectRoute(mSelectedRoute);
mListView.setAdapter(mPlaylistAdapter);
mControls.setVisibility(View.VISIBLE);
if (mStartPlayingOnSelect != -1) {
@ -321,7 +326,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mControls.setVisibility(View.GONE);
mListView.setAdapter(mRouteAdapter);
disableTrackHighlight();
mRouteSelected = false;
mSelectedRoute = null;
}
})
.setNegativeButton(android.R.string.no, null)
@ -331,7 +336,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mControls.setVisibility(View.GONE);
mListView.setAdapter(mRouteAdapter);
disableTrackHighlight();
mRouteSelected = false;
mSelectedRoute = null;
}
return true;
}
@ -406,7 +411,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mPlaylistAdapter.addAll(playlist);
mMediaRouterPlayService.getService().setPlaylist(playlist);
if (mRouteSelected)
if (mSelectedRoute != null)
mMediaRouterPlayService.getService().play(start);
else {
Toast.makeText(getActivity(), R.string.select_renderer, Toast.LENGTH_SHORT)

View file

@ -36,8 +36,6 @@ 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.LocalDevice;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.model.types.UDN;
@ -47,10 +45,15 @@ 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;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
@ -115,13 +118,8 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
public void onServiceConnected(ComponentName className, IBinder service) {
mUpnpService = (AndroidUpnpService) service;
mUpnpService.getRegistry().addListener(mServerAdapter);
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices()) {
if (d instanceof LocalDevice)
mServerAdapter.localDeviceAdded(mUpnpService.getRegistry(), (LocalDevice) d);
else
mServerAdapter.remoteDeviceAdded(mUpnpService.getRegistry(), (RemoteDevice) d);
}
Log.i(TAG, "Starting device search");
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
mServerAdapter.deviceAdded(d);
mUpnpService.getControlPoint().search();
if (mRestoreServer != null) {
@ -141,7 +139,8 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
};
/**
* Initializes ListView adapters, launches Cling UPNP service.
* Initializes ListView adapters, launches Cling UPNP service, registers
* wifi state change listener and restores instance state if possible.
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@ -157,6 +156,11 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
Context.BIND_AUTO_CREATE
);
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
getActivity().registerReceiver(mWifiReceiver, filter);
if (savedInstanceState != null) {
mRestoreServer = savedInstanceState.getString("current_server");
mCurrentPath.addAll(savedInstanceState.getStringArrayList("path"));
@ -166,6 +170,9 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
mListState.push(getListView().onSaveInstanceState());
}
/**
* Stores current server and path/list state stacks.
*/
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@ -232,6 +239,9 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
* which means we restore scroll position etc.
*/
private void getFiles(final boolean restoreListState) {
if (mCurrentServer == null)
return;
Service<?, ?> service = mCurrentServer.findService(
new ServiceType("schemas-upnp-org", "ContentDirectory"));
mUpnpService.getControlPoint().execute(new Browse(service,
@ -274,8 +284,8 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
}
/**
* Handles back button press to traverse directories (while in directory
* browsing mode).
* Handles back button press to traverse directories (while browsing
* directories).
*/
public boolean onBackPressed() {
if (getListAdapter() == mServerAdapter)
@ -293,4 +303,46 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
return true;
}
/**
* Starts device search on wifi connect, removes unreachable
* devices on wifi disconnect.
*/
private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
getActivity();
ConnectivityManager connManager = (ConnectivityManager)
getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (wifi.isConnected()) {
if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint()
.getRegistry().getDevices())
mServerAdapter.deviceAdded(d);
mUpnpService.getControlPoint().search();
}
}
else {
for (int i = 0; i < mServerAdapter.getCount(); i++) {
Device<?, ?, ?> d = mServerAdapter.getItem(i);
UDN udn = new UDN(d.getIdentity().getUdn().toString());
if (mUpnpService.getControlPoint().getRegistry()
.getDevice(udn, false) == null) {
mServerAdapter.deviceRemoved(d);
if (d.equals(mCurrentServer)) {
mCurrentPath.clear();
mListState.setSize(1);
setListAdapter(mServerAdapter);
getListView().onRestoreInstanceState(mListState.firstElement());
mCurrentServer = null;
}
i--;
}
}
}
}
};
}

View file

@ -49,6 +49,7 @@ import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaItemStatus;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.ControlRequestCallback;
import android.support.v7.media.MediaRouter.RouteInfo;
@ -89,12 +90,31 @@ public class MediaRouterPlayService extends Service {
private String mSessionId;
private WeakReference<RouteFragment> mRouterFragment = new WeakReference<RouteFragment>(null);
private WeakReference<RouteFragment> mRouterFragment =
new WeakReference<RouteFragment>(null);
private boolean mPollingStatus = false;
private boolean mBound;
/**
* Route that is currently being played to. May be invalid.
*/
private RouteInfo mCurrentRoute;
/*
* Stops foreground mode and notification if the current route
* has been removed.
*/
private MediaRouter.Callback mRouteRemovedCallback =
new MediaRouter.Callback() {
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
if (route.equals(mCurrentRoute))
stopForeground(true);
}
};
/**
* Creates a notification after the icon bitmap is loaded.
*/
@ -160,7 +180,14 @@ public class MediaRouterPlayService extends Service {
}
public void selectRoute(RouteInfo route) {
mMediaRouter.removeCallback(mRouteRemovedCallback);
mMediaRouter.selectRoute(route);
MediaRouteSelector selector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build();
mMediaRouter.addCallback(selector, mRouteRemovedCallback, 0);
mCurrentRoute = route;
}
public void sendControlRequest(Intent intent) {

View file

@ -29,6 +29,7 @@ package com.github.nutomic.controldlna.upnp;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.android.AndroidUpnpServiceImpl;
@ -43,6 +44,7 @@ import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.StateVariableAllowedValueRange;
import org.teleal.cling.model.state.StateVariableValue;
import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.model.types.UDN;
import org.teleal.cling.registry.Registry;
import org.teleal.cling.registry.RegistryListener;
import org.teleal.cling.support.avtransport.callback.GetPositionInfo;
@ -60,10 +62,15 @@ import org.teleal.cling.support.renderingcontrol.callback.GetVolume;
import org.teleal.cling.support.renderingcontrol.callback.SetVolume;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
@ -109,7 +116,6 @@ public class RemotePlayService extends Service implements RegistryListener {
else
remoteDeviceAdded(mUpnpService.getRegistry(), (RemoteDevice) d);
}
Log.i(TAG, "Starting device search");
mUpnpService.getControlPoint().search();
}
@ -204,9 +210,13 @@ public class RemotePlayService extends Service implements RegistryListener {
mUpnpService.getControlPoint().execute(mSubscriptionCallback);
}
/**
* Ends selection, stops playback if possible.
*/
@Override
public void unselectRenderer(String sessionId) throws RemoteException {
stop(sessionId);
if (mDevices.get(sessionId) != null)
stop(sessionId);
mSubscriptionCallback.end();
mCurrentRenderer = null;
}
@ -398,6 +408,9 @@ public class RemotePlayService extends Service implements RegistryListener {
return mBinder;
}
/**
* Binds to cling service, registers wifi state change listener.
*/
@Override
public void onCreate() {
super.onCreate();
@ -406,6 +419,11 @@ public class RemotePlayService extends Service implements RegistryListener {
mUpnpServiceConnection,
Context.BIND_AUTO_CREATE
);
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mWifiReceiver, filter);
}
@Override
@ -414,6 +432,38 @@ public class RemotePlayService extends Service implements RegistryListener {
unbindService(mUpnpServiceConnection);
}
/**
* Starts device search on wifi connect, removes unreachable
* devices on wifi disconnect.
*/
private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connManager = (ConnectivityManager)
getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (wifi.isConnected()) {
if (mUpnpService != null) {
for (Device<?, ?, ?> d : mUpnpService.getControlPoint().getRegistry().getDevices())
deviceAdded(d);
mUpnpService.getControlPoint().search();
}
}
else {
for (Entry<String, Device<?, ?, ?>> d : mDevices.entrySet()) {
if (mUpnpService.getControlPoint().getRegistry()
.getDevice(new UDN(d.getKey()), false) == null) {
deviceRemoved(d.getValue());
if (d.getValue().equals(mCurrentRenderer))
mCurrentRenderer = null;
}
}
}
}
};
/**
* Returns a device service by name for direct queries.
*/
@ -427,6 +477,9 @@ public class RemotePlayService extends Service implements RegistryListener {
* Gather device data and send it to Provider.
*/
private void deviceAdded(final Device<?, ?, ?> device) {
if (mDevices.containsValue(device))
return;
final org.teleal.cling.model.meta.Service<?, ?> rc =
getService(device, "RenderingControl");
if (rc == null || mListener == null)

View file

@ -108,7 +108,12 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
/**
* Adds a new device to the list if its type equals mDeviceType.
*/
private void deviceAdded(final Device<?, ?, ?> device) {
public void deviceAdded(final Device<?, ?, ?> device) {
for (int i = 0; i < getCount(); i++) {
if (getItem(i).equals(device))
return;
}
mActivity.runOnUiThread(new Runnable() {
@Override
@ -122,7 +127,7 @@ public class DeviceArrayAdapter extends ArrayAdapter<Device<?, ?, ?>>
/**
* Removes the device from the list (if it is an element).
*/
private void deviceRemoved(final Device<?, ?, ?> device) {
public void deviceRemoved(final Device<?, ?, ?> device) {
mActivity.runOnUiThread(new Runnable() {
@Override