/*-
* Copyright (C) 2009 Peter Baldwin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.peterbaldwin.vlcremote.fragment;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListAdapter;
import android.widget.Toast;
import java.net.HttpURLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.peterbaldwin.client.android.vlcremote.R;
import org.peterbaldwin.vlcremote.app.PickServerActivity;
import org.peterbaldwin.vlcremote.intent.Intents;
import org.peterbaldwin.vlcremote.model.Preferences;
import org.peterbaldwin.vlcremote.model.Server;
import org.peterbaldwin.vlcremote.preference.ProgressCategory;
import org.peterbaldwin.vlcremote.receiver.PhoneStateChangedReceiver;
import org.peterbaldwin.vlcremote.sweep.PortSweeper;
import org.peterbaldwin.vlcremote.widget.Buttons;
public final class PickServerFragment extends PreferenceFragment implements PortSweeper.Callback,
ServerInfoDialog.ServerInfoDialogListener, OnPreferenceChangeListener {
private static final String TAG = "PickServer";
private static final String PACKAGE_NAME = R.class.getPackage().getName();
private static final ComponentName PHONE_STATE_RECEIVER = new ComponentName(PACKAGE_NAME,
PhoneStateChangedReceiver.class.getName());
private static final String KEY_SCREEN_INTERFACE = "preference_screen_interface";
private static final String KEY_SCREEN_PRESETS = "preference_screen_presets";
private static final String KEY_SCREEN_BUTTONS = "preference_screen_buttons";
private static final String KEY_SCREEN_NOTIFICATION = "preference_screen_notification";
private static final String KEY_WIFI = "wifi";
private static final String KEY_SERVERS = "servers";
private static final String KEY_ADD_SERVER = "add_server";
private static final String KEY_PAUSE_FOR_CALL = "pause_for_call";
public static final String STATE_HOSTS = "hosts";
private static final String DIALOG_ADD_SERVER = "add_server";
private static final String DIALOG_EDIT_SERVER = "edit_server";
private static final int CONTEXT_FORGET = Menu.FIRST;
private static final int CONTEXT_EDIT_SERVER = 2;
private BroadcastReceiver mReceiver;
private int mPort;
private Map<String, Server> mRemembered;
private CheckBoxPreference mPreferenceWiFi;
private ListPreference mPreferenceButtonFirst;
private ListPreference mPreferenceButtonSecond;
private ListPreference mPreferenceButtonThird;
private ListPreference mPreferenceButtonFourth;
private ListPreference mPreferenceButtonFifth;
private ProgressCategory mProgressCategory;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((PickServerActivity) activity).setServerInfoDialogListener(this);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.server_settings);
PreferenceScreen preferenceScreen = getPreferenceScreen();
mPreferenceWiFi = (CheckBoxPreference) preferenceScreen.findPreference(KEY_WIFI);
mPreferenceButtonFirst = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FIRST);
mPreferenceButtonSecond = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_SECOND);
mPreferenceButtonThird = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_THIRD);
mPreferenceButtonFourth = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FOURTH);
mPreferenceButtonFifth = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FIFTH);
mProgressCategory = (ProgressCategory) preferenceScreen.findPreference(KEY_SERVERS);
CheckBoxPreference preferencePauseForCall = (CheckBoxPreference) preferenceScreen.findPreference(KEY_PAUSE_FOR_CALL);
EditTextPreference preferenceSeekTime = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_SEEK_TIME);
EditTextPreference preferenceAudioDelay = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_AUDIO_DELAY);
EditTextPreference preferenceSubtitleDelay = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_SUBTITLE_DELAY);
mPreferenceButtonFirst.setOnPreferenceChangeListener(this);
mPreferenceButtonSecond.setOnPreferenceChangeListener(this);
mPreferenceButtonThird.setOnPreferenceChangeListener(this);
mPreferenceButtonFourth.setOnPreferenceChangeListener(this);
mPreferenceButtonFifth.setOnPreferenceChangeListener(this);
preferenceAudioDelay.setOnPreferenceChangeListener(this);
preferenceSubtitleDelay.setOnPreferenceChangeListener(this);
preferenceSeekTime.setOnPreferenceChangeListener(this);
preferencePauseForCall.setOnPreferenceChangeListener(this);
preferencePauseForCall.setChecked(getPauseForCall());
Intent intent = getActivity().getIntent();
mPort = intent.getIntExtra(PortSweeper.EXTRA_PORT, 0);
if (mPort == 0) {
throw new IllegalArgumentException("Port must be specified");
}
mRemembered = buildRememberedServersMap(Preferences.get(getActivity()).getRememberedServers());
mReceiver = new MyBroadcastReceiver();
setButtonIcons();
updateWifiInfo();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
registerForContextMenu(v.findViewById(android.R.id.list));
return v;
}
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
getActivity().registerReceiver(mReceiver, filter);
}
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mReceiver);
}
@Override
public void onDetach() {
super.onDetach();
((PickServerActivity) getActivity()).setServerInfoDialogListener(null);
}
private Map<String, Server> buildRememberedServersMap(List<String> keyList) {
Map<String, Server> map = new HashMap<String, Server>(keyList.size());
for(String key : keyList) {
Server s = Server.fromKey(key);
map.put(s.getUri().getAuthority(), s);
}
return map;
}
private ArrayList<String> buildRememberedServers() {
ArrayList<String> list = new ArrayList<String>(mRemembered.size());
for(Server server : mRemembered.values()) {
list.add(server.toKey());
}
return list;
}
private Preference createServerPreference(Server server) {
Preference preference = new Preference(getActivity());
preference.setKey(server.toKey());
preference.setTitle(server.getNickname().isEmpty() ? server.getHostAndPort() : server.getNickname());
preference.setPersistent(false);
String authority = server.getUri().getAuthority();
if(mRemembered.containsKey(authority) && authority.equals(Preferences.get(getActivity()).getAuthority())) {
preference.setWidgetLayoutResource(R.layout.tick_image);
}
switch (server.getResponseCode()) {
case HttpURLConnection.HTTP_UNAUTHORIZED:
preference.setSummary(R.string.summary_unauthorized);
preference.setIcon(R.drawable.ic_vlc_server_auth);
break;
case HttpURLConnection.HTTP_FORBIDDEN:
preference.setSummary(R.string.summary_forbidden);
preference.setIcon(R.drawable.ic_vlc_server_forbidden);
break;
default:
if(server.hasUserInfo()) {
preference.setIcon(R.drawable.ic_vlc_server_auth);
} else {
preference.setIcon(R.drawable.ic_vlc_server);
}
}
return preference;
}
private WifiInfo getConnectionInfo() {
WifiManager manager = (WifiManager) getActivity().getSystemService(Activity.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
if (info != null) {
SupplicantState state = info.getSupplicantState();
if (state.equals(SupplicantState.COMPLETED)) {
return info;
}
}
return null;
}
@Override
public void onProgress(int progress, int max) {
if(getActivity() == null) {
return;
}
if (progress == 0) {
mProgressCategory.removeAll();
for (String authority : mRemembered.keySet()) {
Preference preference = createServerPreference(mRemembered.get(authority));
preference.setSummary(R.string.summary_remembered);
mProgressCategory.addPreference(preference);
}
}
mProgressCategory.setProgress(progress != max);
}
@Override
public void onHostFound(String hostname, int responseCode) {
Server server = new Server(hostname + ":" + mPort, responseCode);
switch (responseCode) {
case HttpURLConnection.HTTP_OK:
case HttpURLConnection.HTTP_FORBIDDEN:
case HttpURLConnection.HTTP_UNAUTHORIZED:
if (!mRemembered.containsKey(server.getUri().getAuthority()) && getActivity() != null) {
Preference preference = createServerPreference(server);
mProgressCategory.addPreference(preference);
}
break;
default:
Log.d(TAG, "Unexpected response code: " + responseCode);
break;
}
}
@Override
public void onAddServer(Server server) {
Intent data = new Intent();
data.setData(server.getUri());
if (!mRemembered.containsKey(server.getUri().getAuthority())) {
mRemembered.put(server.getUri().getAuthority(), server);
}
Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
@Override
public void onEditServer(Server newServer, String oldServerKey) {
String oldAuthority = Server.fromKey(oldServerKey).getUri().getAuthority();
if(mRemembered.containsKey(oldAuthority)) {
mRemembered.remove(oldAuthority);
mRemembered.put(newServer.getUri().getAuthority(), newServer);
}
Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
}
private void forget(Server server) {
mRemembered.remove(server.getUri().getAuthority());
int count = mProgressCategory.getPreferenceCount();
for (int i = 0; i < count; i++) {
Preference preference = mProgressCategory.getPreference(i);
if (server.toKey().equals(preference.getKey())) {
mProgressCategory.removePreference(preference);
break;
}
}
Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
}
private boolean isHandledByChangeListener(String preferenceKey) {
return KEY_SCREEN_INTERFACE.equals(preferenceKey) ||
KEY_SCREEN_PRESETS.equals(preferenceKey) ||
KEY_SCREEN_BUTTONS.equals(preferenceKey) ||
KEY_SCREEN_NOTIFICATION.equals(preferenceKey) ||
KEY_PAUSE_FOR_CALL.contains(preferenceKey) ||
Preferences.isButtonKey(preferenceKey) ||
Preferences.KEY_SEEK_TIME.equals(preferenceKey) ||
Preferences.KEY_AUDIO_DELAY.equals(preferenceKey) ||
Preferences.KEY_SUBTITLE_DELAY.equals(preferenceKey);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if(isHandledByChangeListener(preference.getKey())) {
return super.onPreferenceTreeClick(preferenceScreen, preference);
} else if(Preferences.KEY_HIDE_DVD_TAB.equals(preference.getKey())) {
Preferences.get(getActivity()).setHideDVDTab(((CheckBoxPreference) preference).isChecked());
return true;
} else if(Preferences.KEY_SORT_DIRECTORIES_FIRST.equals(preference.getKey())) {
Preferences.get(getActivity()).setSortDirectoriesFirst(((CheckBoxPreference) preference).isChecked());
return true;
} else if(Preferences.KEY_PARSE_PLAYLIST_ITEMS.equals(preference.getKey())) {
Preferences.get(getActivity()).setParsePlaylistItems(((CheckBoxPreference) preference).isChecked());
return true;
} else if (Preferences.KEY_SERVER_SUBTITLE.equals(preference.getKey())) {
Preferences.get(getActivity()).setServerSubtitle(((CheckBoxPreference) preference).isChecked());
return true;
} else if (Preferences.KEY_NOTIFICATION.equals(preference.getKey())) {
boolean isChecked = ((CheckBoxPreference) preference).isChecked();
Intent i = Intents.service(getActivity(), isChecked ? Intents.ACTION_NOTIFICATION_CREATE : Intents.ACTION_NOTIFICATION_CANCEL);
Preferences.get(getActivity()).setNotification(isChecked);
getActivity().startService(i);
return true;
} else if(Preferences.KEY_NOTIFICATION_ICON_TRANSPARENT.equals(preference.getKey())) {
Preferences.get(getActivity()).setNotificationIconTransparent(((CheckBoxPreference) preference).isChecked());
getActivity().startService(Intents.service(getActivity(), Intents.ACTION_NOTIFICATION_CREATE));
return true;
} else if(KEY_ADD_SERVER.equals(preference.getKey())) {
ServerInfoDialog.addServerInstance().show(getFragmentManager(), DIALOG_ADD_SERVER);
return true;
} else if (KEY_WIFI.equals(preference.getKey())) {
startActivity(new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK));
updateWifiInfo(); // Undo checkbox toggle
return true;
} else {
Server server = Server.fromKey(preference.getKey());
if(server.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
ServerInfoDialog.addAuthServerInstance(server.toKey()).show(getFragmentManager(), DIALOG_ADD_SERVER);
} else {
onAddServer(server);
}
return true;
}
}
/** {@inheritDoc} */
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (KEY_PAUSE_FOR_CALL.equals(preference.getKey())) {
setPauseForCall(Boolean.TRUE.equals(newValue));
return true;
} else if(Preferences.KEY_SEEK_TIME.equals(preference.getKey())) {
if(isNumericOrToast(preference, (String) newValue, "The seek time entered is not valid")) {
Preferences.get(getActivity()).setSeekTime((String) newValue);
return true;
}
return false;
} else if(Preferences.KEY_AUDIO_DELAY.equals(preference.getKey())) {
if(isNumericOrToast(preference, (String) newValue, "The audio delay entered is not valid")) {
Preferences.get(getActivity()).setAudioDelayToggle(Integer.valueOf((String) newValue));
return true;
}
return false;
} else if(Preferences.KEY_SUBTITLE_DELAY.equals(preference.getKey())) {
if(isNumericOrToast(preference, (String) newValue, "The subtitle delay entered is not valid")) {
Preferences.get(getActivity()).setSubtitleDelayToggle(Integer.valueOf((String) newValue));
return true;
}
return false;
} else if(Preferences.isButtonKey(preference.getKey())) {
Preferences.get(getActivity()).setButton(preference.getKey(), (String) newValue);
setButtonIcon(preference, (String) newValue);
return true;
}
return false;
}
private boolean isNumericOrToast(Preference preference, String value, String toastMessage) {
try {
Long.parseLong(value);
return true;
} catch(NumberFormatException e) {
Toast.makeText(getActivity(), toastMessage, Toast.LENGTH_SHORT).show();
return false;
}
}
private void setButtonIcon(Preference preference, String button) {
preference.setIcon(Buttons.getButton(preference.getKey(), button).getIconId());
}
private void setButtonIcons() {
Preferences p = Preferences.get(getActivity());
mPreferenceButtonFirst.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FIRST, p).getIconId());
mPreferenceButtonSecond.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_SECOND, p).getIconId());
mPreferenceButtonThird.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_THIRD, p).getIconId());
mPreferenceButtonFourth.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FOURTH, p).getIconId());
mPreferenceButtonFifth.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FIFTH, p).getIconId());
}
private Preference getPreferenceFromMenuInfo(ContextMenuInfo menuInfo) {
if (menuInfo != null) {
if (menuInfo instanceof AdapterContextMenuInfo) {
AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
PreferenceScreen screen = getPreferenceScreen();
ListAdapter root = screen.getRootAdapter();
Object item = root.getItem(adapterMenuInfo.position);
return (Preference) item;
}
}
return null;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
Preference preference = getPreferenceFromMenuInfo(menuInfo);
if (preference != null) {
String authority = Server.fromKey(preference.getKey()).getUri().getAuthority();
if (mRemembered.containsKey(authority)) {
menu.add(Menu.NONE, CONTEXT_EDIT_SERVER, Menu.NONE, R.string.edit_server);
menu.add(Menu.NONE, CONTEXT_FORGET, Menu.NONE, R.string.context_forget);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
Preference preference;
switch (item.getItemId()) {
case CONTEXT_FORGET:
preference = getPreferenceFromMenuInfo(item.getMenuInfo());
if (preference != null) {
forget(Server.fromKey(preference.getKey()));
}
return true;
case CONTEXT_EDIT_SERVER:
preference = getPreferenceFromMenuInfo(item.getMenuInfo());
if(preference != null) {
ServerInfoDialog d = ServerInfoDialog.editServerInstance(preference.getKey());
d.show(getFragmentManager(), DIALOG_EDIT_SERVER);
}
return true;
default:
return super.onContextItemSelected(item);
}
}
private void updateWifiInfo() {
WifiInfo info = getConnectionInfo();
if (info != null) {
mPreferenceWiFi.setChecked(true);
String ssid = info.getSSID();
String template = getString(R.string.summary_wifi_connected);
Object[] objects = {
ssid != null ? ssid : ""
};
mPreferenceWiFi.setSummary(MessageFormat.format(template, objects));
} else {
mPreferenceWiFi.setChecked(false);
mPreferenceWiFi.setSummary(R.string.summary_wifi_disconnected);
}
}
private boolean getPauseForCall() {
switch (getActivity().getPackageManager().getComponentEnabledSetting(PHONE_STATE_RECEIVER)) {
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
return true;
default:
return false;
}
}
private void setPauseForCall(boolean enabled) {
getActivity().getPackageManager().setComponentEnabledSetting(
PHONE_STATE_RECEIVER,
enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
private class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
updateWifiInfo();
}
}
}