/** * 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.incall; 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.ServiceConnection; import android.media.AudioManager; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import com.csipsimple.R; import com.csipsimple.api.ISipService; import com.csipsimple.api.SipCallSession; import com.csipsimple.api.SipConfigManager; import com.csipsimple.api.SipManager; import com.csipsimple.utils.Log; import java.util.Timer; import java.util.TimerTask; public class InCallMediaControl extends Activity implements OnSeekBarChangeListener, OnCheckedChangeListener, OnClickListener { protected static final String THIS_FILE = "inCallMediaCtrl"; private SeekBar speakerAmplification; private SeekBar microAmplification; private Button saveButton; private CheckBox echoCancellation; // private Button recordButton; private boolean isAutoClose = false; private final static int AUTO_QUIT_DELAY = 3000; private Timer quitTimer; private ProgressBar txProgress; private ProgressBar rxProgress; private LinearLayout okBar; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.in_call_media); speakerAmplification = (SeekBar) findViewById(R.id.speaker_level); microAmplification = (SeekBar) findViewById(R.id.micro_level); saveButton = (Button) findViewById(R.id.save_bt); echoCancellation = (CheckBox) findViewById(R.id.echo_cancellation); okBar = (LinearLayout) findViewById(R.id.ok_bar); speakerAmplification.setMax((int) (max * subdivision * 2)); microAmplification.setMax((int) (max * subdivision * 2)); speakerAmplification.setOnSeekBarChangeListener(this); microAmplification.setOnSeekBarChangeListener(this); saveButton.setOnClickListener(this); echoCancellation.setOnCheckedChangeListener(this); rxProgress = (ProgressBar) findViewById(R.id.rx_bar); txProgress = (ProgressBar) findViewById(R.id.tx_bar); } @Override protected void onResume() { super.onResume(); Intent sipServiceIntent = new Intent(SipManager.INTENT_SIP_SERVICE); // Optional, but here we bundle so just ensure we are using csipsimple package sipServiceIntent.setPackage(getPackageName()); bindService(sipServiceIntent , sipConnection, BIND_AUTO_CREATE); int direction = getIntent().getIntExtra(Intent.EXTRA_KEY_EVENT, 0); if(direction == AudioManager.ADJUST_LOWER || direction == AudioManager.ADJUST_RAISE) { isAutoClose = true; okBar.setVisibility(View.GONE); delayedQuit(AUTO_QUIT_DELAY); }else { okBar.setVisibility(View.VISIBLE); isAutoClose = false; } registerReceiver(callStateReceiver, new IntentFilter(SipManager.ACTION_SIP_CALL_CHANGED)); if (monitorThread == null) { monitorThread = new MonitorThread(); monitorThread.start(); } } @Override protected void onPause() { super.onPause(); try { unbindService(sipConnection); }catch(Exception e) { //Just ignore that } if(quitTimer != null) { quitTimer.cancel(); quitTimer.purge(); quitTimer = null; } try { unregisterReceiver(callStateReceiver); }catch (IllegalArgumentException e) { //That's the case if not registered (early quit) } if (monitorThread != null) { monitorThread.markFinished(); monitorThread = null; } sipService = null; } private BroadcastReceiver callStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(action.equals(SipManager.ACTION_SIP_CALL_CHANGED)){ if(sipService != null) { try { SipCallSession[] callsInfo = sipService.getCalls(); SipCallSession currentCallInfo = null; if(callsInfo != null) { for(SipCallSession callInfo : callsInfo) { int state = callInfo.getCallState(); switch (state) { case SipCallSession.InvState.NULL: case SipCallSession.InvState.DISCONNECTED: break; default: currentCallInfo = callInfo; break; } if(currentCallInfo != null) { break; } } } if(currentCallInfo == null) { finish(); } } catch (RemoteException e) { Log.e(THIS_FILE, "Not able to retrieve calls"); } } } } }; private class LockTimerTask extends TimerTask{ @Override public void run() { finish(); } }; public void delayedQuit(int time) { if(quitTimer != null) { quitTimer.cancel(); quitTimer.purge(); quitTimer = null; } quitTimer = new Timer("Quit-timer-media"); quitTimer.schedule(new LockTimerTask(), time); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: if(speakerAmplification != null) { int step = (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)? - 1 : + 1; int newValue = speakerAmplification.getProgress() + step; if(newValue >= 0 && newValue < speakerAmplification.getMax()) { speakerAmplification.setProgress(newValue); } } return true; case KeyEvent.KEYCODE_CALL: case KeyEvent.KEYCODE_ENDCALL: case KeyEvent.KEYCODE_SEARCH: //Prevent search return true; default: //Nothing to do } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_CALL: case KeyEvent.KEYCODE_ENDCALL: case KeyEvent.KEYCODE_SEARCH: return true; } return super.onKeyUp(keyCode, event); } private ISipService sipService; private ServiceConnection sipConnection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName arg0, IBinder arg1) { Log.d(THIS_FILE, "SipService is connected"); sipService = ISipService.Stub.asInterface(arg1); updateUIFromMedia(); } @Override public void onServiceDisconnected(ComponentName arg0) { } }; private void updateUIFromMedia() { boolean useBT = false; if (sipService != null) { try { useBT = sipService.getCurrentMediaState().isBluetoothScoOn; } catch (RemoteException e) { Log.e(THIS_FILE, "Sip service not avail for request ", e); } } Float speakerLevel = SipConfigManager.getPreferenceFloatValue(this, useBT ? SipConfigManager.SND_BT_SPEAKER_LEVEL : SipConfigManager.SND_SPEAKER_LEVEL); speakerAmplification.setProgress(valueToProgressUnit(speakerLevel)); Float microLevel = SipConfigManager.getPreferenceFloatValue(this, useBT ? SipConfigManager.SND_BT_MIC_LEVEL : SipConfigManager.SND_MIC_LEVEL); microAmplification.setProgress(valueToProgressUnit(microLevel)); echoCancellation.setChecked(SipConfigManager.getPreferenceBooleanValue(this, SipConfigManager.ECHO_CANCELLATION)); } private double subdivision = 5; private double max = 15; private int valueToProgressUnit(float val) { Log.d(THIS_FILE, "Value is " + val); double dB = (10.0f * Math.log10(val)); return (int) ( (dB + max) * subdivision); } private float progressUnitToValue(int pVal) { Log.d(THIS_FILE, "Progress is " + pVal); double dB = pVal / subdivision - max; return (float) Math.pow(10, dB / 10.0f); } @Override public void onProgressChanged(SeekBar arg0, int value, boolean arg2) { Log.d(THIS_FILE, "Progress has changed"); if(sipService != null) { try { float newValue = progressUnitToValue( value ); String key; boolean useBT = sipService.getCurrentMediaState().isBluetoothScoOn; int sId = arg0.getId(); if (sId == R.id.speaker_level) { sipService.confAdjustTxLevel(0, newValue); key = useBT ? SipConfigManager.SND_BT_SPEAKER_LEVEL : SipConfigManager.SND_SPEAKER_LEVEL; SipConfigManager.setPreferenceFloatValue(this, key, newValue); } else if (sId == R.id.micro_level) { sipService.confAdjustRxLevel(0, newValue); key = useBT ? SipConfigManager.SND_BT_MIC_LEVEL : SipConfigManager.SND_MIC_LEVEL; SipConfigManager.setPreferenceFloatValue(this, key, newValue); } } catch (RemoteException e) { Log.e(THIS_FILE, "Impossible to set mic/speaker level", e); } }else { //TODO : revert changes here ! } //Update quit timer if(isAutoClose) { delayedQuit(AUTO_QUIT_DELAY); } } @Override public void onStartTrackingTouch(SeekBar arg0) { // Nothing to do } @Override public void onStopTrackingTouch(SeekBar arg0) { // Nothing to do } @Override public void onCheckedChanged(CompoundButton arg0, boolean value) { if(sipService != null) { try { int bId = arg0.getId(); if (bId == R.id.echo_cancellation) { sipService.setEchoCancellation(value); SipConfigManager.setPreferenceBooleanValue(this, SipConfigManager.ECHO_CANCELLATION, value); } //Update quit timer if(isAutoClose) { delayedQuit(AUTO_QUIT_DELAY); } }catch (RemoteException e) { Log.e(THIS_FILE, "Impossible to set mic/speaker level", e); } } } @Override public void onClick(View v) { if (v.getId() == R.id.save_bt) { finish(); } } private MonitorThread monitorThread; private class MonitorThread extends Thread { private boolean finished = false; public synchronized void markFinished() { finished = true; } @Override public void run() { super.run(); while (true) { if (sipService != null) { try { long value = sipService.confGetRxTxLevel(0); runOnUiThread(new UpdateConfLevelRunnable((int) ((value >> 8) & 0xff), (int) (value & 0xff))); } catch (RemoteException e) { Log.e(THIS_FILE, "Problem with remote service", e); break; } } // End of loop, sleep for a while and exit if necessary try { sleep(100); } catch (InterruptedException e) { Log.e(THIS_FILE, "Interupted monitor thread", e); } synchronized (this) { if (finished) { break; } } } } } private class UpdateConfLevelRunnable implements Runnable { private final int mRx, mTx; UpdateConfLevelRunnable(int rx, int tx){ mRx = rx; mTx = tx; } @Override public void run() { txProgress.setProgress(mTx); rxProgress.setProgress(mRx); } } }