/**
* Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
* This file is part of CSipSimple.
*
* CSipSimple 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.
* If you own a pjsip commercial license you can also redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as an android library.
*
* CSipSimple 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 CSipSimple. If not, see <http://www.gnu.org/licenses/>.
*/
package com.csipsimple.ui.prefs;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
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;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.SimpleAdapter;
import android.widget.SimpleAdapter.ViewBinder;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockListFragment;
import com.csipsimple.R;
import com.csipsimple.api.SipConfigManager;
import com.csipsimple.utils.Log;
import com.csipsimple.utils.PreferencesWrapper;
import com.csipsimple.widgets.DragnDropListView;
import com.csipsimple.widgets.DragnDropListView.DropListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CodecsFragment extends SherlockListFragment implements OnCheckedChangeListener {
protected static final String THIS_FILE = "CodecsFragment";
private static final String CODEC_NAME = "codec_name";
private static final String CODEC_ID = "codec_id";
private static final String CODEC_PRIORITY = "codec_priority";
public static final String BAND_TYPE = "band_type";
public static final String MEDIA_TYPE = "media_type";
public static final int MEDIA_AUDIO = 0;
public static final int MEDIA_VIDEO = 1;
public static final int MENU_ITEM_ACTIVATE = Menu.FIRST + 1;
private SimpleAdapter mAdapter;
private List<Map<String, Object>> codecsList;
private PreferencesWrapper prefsWrapper;
// Type for codec bandwidth
private String bandtype;
// Type for media (audio/video)
private Integer mediatype;
private boolean useCodecsPerSpeed = true;
private static final Map<String, String> NON_FREE_CODECS = new HashMap<String, String>();
static {
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
prefsWrapper = new PreferencesWrapper(getActivity());
useCodecsPerSpeed = SipConfigManager.getPreferenceBooleanValue(getActivity(), SipConfigManager.CODECS_PER_BANDWIDTH);
initDatas();
setHasOptionsMenu(true);
// Adapter
mAdapter = new SimpleAdapter(getActivity(), codecsList, R.layout.codecs_list_item, new String[] {
CODEC_NAME,
CODEC_NAME,
CODEC_PRIORITY
}, new int[] {
R.id.line1,
R.id.AccCheckBoxActive,
R.id.entiere_line
});
mAdapter.setViewBinder(new ViewBinder() {
@Override
public boolean setViewValue(View view, Object data, String textRepresentation) {
if (view.getId() == R.id.entiere_line) {
Log.d(THIS_FILE, "Entiere line is binded ");
TextView tv = (TextView) view.findViewById(R.id.line1);
ImageView grabber = (ImageView) view.findViewById(R.id.icon);
CompoundButton checker = (CompoundButton) view.findViewById(R.id.AccCheckBoxActive);
checker.setOnCheckedChangeListener(CodecsFragment.this);
if ((Short) data == 0) {
tv.setTextColor(Color.GRAY);
grabber.setVisibility(View.GONE);
checker.setChecked(false);
} else {
tv.setTextColor(Color.WHITE);
grabber.setVisibility(View.VISIBLE);
checker.setChecked(true);
}
return true;
}else if(view.getId() == R.id.AccCheckBoxActive) {
view.setTag(data);
return true;
}
return false;
}
});
setListAdapter(mAdapter);
registerForContextMenu(getListView());
}
/**
* Initialize datas list
*/
private void initDatas() {
if(codecsList == null) {
codecsList = new ArrayList<Map<String, Object>>();
}else {
codecsList.clear();
}
bandtype = (String) getArguments().get(BAND_TYPE);
mediatype = (Integer) getArguments().get(MEDIA_TYPE);
String[] codecNames;
if(mediatype == MEDIA_AUDIO) {
codecNames = prefsWrapper.getCodecList();
}else {
codecNames = prefsWrapper.getVideoCodecList();
}
int current_prio = 130;
for(String codecName : codecNames) {
Log.d(THIS_FILE, "Fill codec "+codecName+" for "+bandtype);
String[] codecParts = codecName.split("/");
if(codecParts.length >=2 ) {
HashMap<String, Object> codecInfo = new HashMap<String, Object>();
codecInfo.put(CODEC_ID, codecName);
if(mediatype == MEDIA_AUDIO) {
codecInfo.put(CODEC_NAME, codecParts[0]+" "+codecParts[1].substring(0, codecParts[1].length()-3)+" kHz");
}else if(mediatype == MEDIA_VIDEO) {
codecInfo.put(CODEC_NAME, codecParts[0]);
}
codecInfo.put(CODEC_PRIORITY, prefsWrapper.getCodecPriority(codecName, bandtype, Integer.toString(current_prio)));
codecsList.add(codecInfo);
current_prio --;
Log.d(THIS_FILE, "Found priority is "+codecInfo.get(CODEC_PRIORITY));
}
}
Collections.sort(codecsList, codecsComparator);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.codecs_list, container, false);
DragnDropListView listView = (DragnDropListView) v.findViewById(android.R.id.list);
listView.setOnDropListener(new DropListener() {
@Override
public void drop(int from, int to) {
@SuppressWarnings("unchecked")
HashMap<String, Object> item = (HashMap<String, Object>) getListAdapter().getItem(from);
Log.d(THIS_FILE, "Dropped "+item.get(CODEC_NAME)+" -> "+to);
//Prevent disabled codecsList to be reordered
if((Short) item.get(CODEC_PRIORITY) <= 0 ) {
return ;
}
codecsList.remove(from);
codecsList.add(to, item);
//Update priorities
short currentPriority = 130;
for(Map<String, Object> codec : codecsList) {
if((Short) codec.get(CODEC_PRIORITY) > 0) {
if(currentPriority != (Short) codec.get(CODEC_PRIORITY)) {
setCodecPriority((String)codec.get(CODEC_ID), currentPriority);
codec.put(CODEC_PRIORITY, currentPriority);
}
//Log.d(THIS_FILE, "Reorder : "+codec.toString());
currentPriority --;
}
}
//Log.d(THIS_FILE, "Data set "+codecsList.toString());
mAdapter.notifyDataSetChanged();
}
});
listView.setOnCreateContextMenuListener(this);
return v;
}
private void setCodecPriority(String codecName, short priority) {
if(useCodecsPerSpeed) {
prefsWrapper.setCodecPriority(codecName, bandtype, Short.toString(priority));
}else {
prefsWrapper.setCodecPriority(codecName, SipConfigManager.CODEC_NB, Short.toString(priority));
prefsWrapper.setCodecPriority(codecName, SipConfigManager.CODEC_WB, Short.toString(priority));
}
}
@Override
@SuppressWarnings("unchecked")
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo info;
try {
info = (AdapterView.AdapterContextMenuInfo) menuInfo;
} catch (ClassCastException e) {
Log.e(THIS_FILE, "bad menuInfo", e);
return;
}
HashMap<String, Object> codec = (HashMap<String, Object>) mAdapter.getItem(info.position);
if (codec == null) {
// If for some reason the requested item isn't available, do nothing
return;
}
boolean isDisabled = ((Short)codec.get(CODEC_PRIORITY) == 0);
menu.add(0, MENU_ITEM_ACTIVATE, 0, isDisabled ? R.string.activate : R.string.deactivate);
}
@Override
@SuppressWarnings("unchecked")
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info;
try {
info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
} catch (ClassCastException e) {
Log.e(THIS_FILE, "bad menuInfo", e);
return false;
}
HashMap<String, Object> codec = null;
codec = (HashMap<String, Object>) mAdapter.getItem(info.position);
if (codec == null) {
// If for some reason the requested item isn't available, do nothing
return false;
}
int selId = item.getItemId();
if (selId == MENU_ITEM_ACTIVATE) {
boolean isDisabled = ((Short) codec.get(CODEC_PRIORITY) == 0);
userActivateCodec(codec, isDisabled);
return true;
}
return false;
}
private void userActivateCodec(final Map<String, Object> codec, boolean activate) {
String codecName = (String) codec.get(CODEC_ID);
final short newPrio = activate ? (short) 1 : (short) 0;
boolean isDisabled = ((Short) codec.get(CODEC_PRIORITY) == 0);
if(isDisabled == !activate) {
// Nothing to do, this codec is already enabled
return;
}
if(NON_FREE_CODECS.containsKey(codecName) && activate) {
final TextView message = new TextView(getActivity());
final SpannableString s = new SpannableString(getString(R.string.this_codec_is_not_free) + NON_FREE_CODECS.get(codecName));
Linkify.addLinks(s, Linkify.WEB_URLS);
message.setText(s);
message.setMovementMethod(LinkMovementMethod.getInstance());
message.setPadding(10, 10, 10, 10);
//Alert user that we will disable for all incoming calls as he want to quit
new AlertDialog.Builder(getActivity())
.setTitle(R.string.warning)
.setView(message)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setCodecActivated(codec, newPrio);
}
})
.setNegativeButton(R.string.cancel, null)
.show();
}else {
setCodecActivated(codec, newPrio);
}
}
/**
* Internal method to activate codec priority
* @param codec the codec to activate
* @param newPrio the new priority of the codec (0 to disable)
*/
private void setCodecActivated(Map<String, Object> codec, short newPrio) {
setCodecPriority((String) codec.get(CODEC_ID), newPrio);
codec.put(CODEC_PRIORITY, newPrio);
Collections.sort(codecsList, codecsComparator);
mAdapter.notifyDataSetChanged();
}
/**
* Class to compare the codecs based on their priority
*/
private final Comparator<Map<String, Object>> codecsComparator = new Comparator<Map<String, Object>>() {
@Override
public int compare(Map<String, Object> infos1, Map<String, Object> infos2) {
if (infos1 != null && infos2 != null) {
short c1 = (Short)infos1.get(CODEC_PRIORITY);
short c2 = (Short)infos2.get(CODEC_PRIORITY);
if (c1 > c2) {
return -1;
}
if (c1 < c2) {
return 1;
}
}
return 0;
}
};
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String codecName = (String) buttonView.getTag();
if(codecName != null) {
HashMap<String, Object> codec = null;
for( int i = 0; i < mAdapter.getCount(); i++) {
@SuppressWarnings("unchecked")
HashMap<String, Object> tCodec = (HashMap<String, Object>) mAdapter.getItem(i);
if(codecName.equalsIgnoreCase( (String) tCodec.get(CODEC_NAME))) {
codec = tCodec;
break;
}
}
if(codec != null) {
userActivateCodec(codec, isChecked);
}
}
}
}