Store and restore user requested audio route.
Go back to user requested route when going out of video activity.
This commit is contained in:
parent
4a6ba5f8fd
commit
5fe6a522ba
7 changed files with 108 additions and 31 deletions
|
@ -27,6 +27,7 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener;
|
import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener;
|
||||||
|
import org.linphone.LinphoneSimpleListener.LinphoneAudioChangedListener;
|
||||||
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
|
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
|
||||||
import org.linphone.core.LinphoneAddress;
|
import org.linphone.core.LinphoneAddress;
|
||||||
import org.linphone.core.LinphoneCall;
|
import org.linphone.core.LinphoneCall;
|
||||||
|
@ -64,6 +65,7 @@ import android.widget.ToggleButton;
|
||||||
*/
|
*/
|
||||||
public class ConferenceActivity extends ListActivity implements
|
public class ConferenceActivity extends ListActivity implements
|
||||||
LinphoneManagerReadyListener,
|
LinphoneManagerReadyListener,
|
||||||
|
LinphoneAudioChangedListener,
|
||||||
LinphoneOnCallStateChangedListener, Comparator<LinphoneCall>,
|
LinphoneOnCallStateChangedListener, Comparator<LinphoneCall>,
|
||||||
OnClickListener {
|
OnClickListener {
|
||||||
|
|
||||||
|
@ -149,6 +151,10 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
updateConfState();
|
updateConfState();
|
||||||
updateSimpleControlButtons();
|
updateSimpleControlButtons();
|
||||||
CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter();
|
CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter();
|
||||||
|
if (adapter.linphoneCalls.size() != lc().getCallsNb()) {
|
||||||
|
adapter.linphoneCalls.clear();
|
||||||
|
adapter.linphoneCalls.addAll(getInitialCalls());
|
||||||
|
}
|
||||||
adapter.notifyDataSetInvalidated();
|
adapter.notifyDataSetInvalidated();
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
LinphoneManager.startProximitySensorForActivity(this);
|
LinphoneManager.startProximitySensorForActivity(this);
|
||||||
|
@ -353,9 +359,9 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
break;
|
break;
|
||||||
case R.id.toggleSpeaker:
|
case R.id.toggleSpeaker:
|
||||||
if (((ToggleButton) v).isChecked()) {
|
if (((ToggleButton) v).isChecked()) {
|
||||||
LinphoneManager.getInstance().routeAudioToSpeaker();
|
LinphoneManager.getInstance().routeAudioToSpeaker(true);
|
||||||
} else {
|
} else {
|
||||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
LinphoneManager.getInstance().routeAudioToReceiver(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -422,6 +428,10 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
case R.id.remove_from_conference:
|
case R.id.remove_from_conference:
|
||||||
lc().removeFromConference(call);
|
lc().removeFromConference(call);
|
||||||
break;
|
break;
|
||||||
|
case R.id.addVideo:
|
||||||
|
VideoCallActivity.call = call;
|
||||||
|
LinphoneActivity.instance().startVideoActivity();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("unknown id " + v.getId());
|
throw new RuntimeException("unknown id " + v.getId());
|
||||||
}
|
}
|
||||||
|
@ -561,7 +571,8 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
final int numberOfCalls = linphoneCalls.size();
|
final int numberOfCalls = linphoneCalls.size();
|
||||||
boolean showAddVideo = State.StreamsRunning == state && !isInConference
|
boolean showAddVideo = State.StreamsRunning == state && !isInConference
|
||||||
&& Version.isVideoCapable() && LinphoneManager.getInstance().isVideoEnabled();
|
&& Version.isVideoCapable() && LinphoneManager.getInstance().isVideoEnabled();
|
||||||
setVisibility(v, R.id.addVideo, showAddVideo);
|
View addVideoButton = v.findViewById(R.id.addVideo);
|
||||||
|
setVisibility(addVideoButton, showAddVideo);
|
||||||
|
|
||||||
boolean statusPaused = state== State.Paused || state == State.PausedByRemote;
|
boolean statusPaused = state== State.Paused || state == State.PausedByRemote;
|
||||||
setVisibility(v, R.id.callee_status_paused, statusPaused);
|
setVisibility(v, R.id.callee_status_paused, statusPaused);
|
||||||
|
@ -575,6 +586,7 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
resumeButton.setOnClickListener(l);
|
resumeButton.setOnClickListener(l);
|
||||||
unhookCallButton.setOnClickListener(l);
|
unhookCallButton.setOnClickListener(l);
|
||||||
removeFromConfButton.setOnClickListener(l);
|
removeFromConfButton.setOnClickListener(l);
|
||||||
|
addVideoButton.setOnClickListener(l);
|
||||||
|
|
||||||
v.setOnClickListener(new OnClickListener() {
|
v.setOnClickListener(new OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -749,6 +761,25 @@ public class ConferenceActivity extends ListActivity implements
|
||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioStateChanged(final AudioState state) {
|
||||||
|
mSpeakerButton.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
switch (state) {
|
||||||
|
case SPEAKER:
|
||||||
|
mSpeakerButton.setChecked(true);
|
||||||
|
break;
|
||||||
|
case EARPIECE:
|
||||||
|
mSpeakerButton.setChecked(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unkown audio state " + state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* public int compare(LinphoneCall c1, LinphoneCall c2) { if (c1 == c2)
|
* public int compare(LinphoneCall c1, LinphoneCall c2) { if (c1 == c2)
|
||||||
* return 0;
|
* return 0;
|
||||||
|
|
|
@ -288,7 +288,7 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void exitCallMode() {
|
private void exitCallMode(LinphoneCall call) {
|
||||||
if (getResources().getBoolean(R.bool.use_incoming_call_activity)) {
|
if (getResources().getBoolean(R.bool.use_incoming_call_activity)) {
|
||||||
finishActivity(INCOMING_CALL_ACTIVITY);
|
finishActivity(INCOMING_CALL_ACTIVITY);
|
||||||
} else if (getResources().getBoolean(R.bool.use_incoming_call_dialog)) {
|
} else if (getResources().getBoolean(R.bool.use_incoming_call_dialog)) {
|
||||||
|
@ -316,7 +316,8 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin
|
||||||
mHangup.setEnabled(false);
|
mHangup.setEnabled(false);
|
||||||
|
|
||||||
|
|
||||||
if (useVideoActivity && LinphoneManager.getLc().isVideoEnabled()) {
|
if (useVideoActivity && LinphoneManager.getLc().isVideoEnabled()
|
||||||
|
&& VideoCallActivity.call == call) {
|
||||||
LinphoneActivity.instance().finishVideoActivity();
|
LinphoneActivity.instance().finishVideoActivity();
|
||||||
BandwidthManager.getInstance().setUserRestriction(false);
|
BandwidthManager.getInstance().setUserRestriction(false);
|
||||||
LinphoneManager.getInstance().resetCameraFromPreferences();
|
LinphoneManager.getInstance().resetCameraFromPreferences();
|
||||||
|
@ -464,12 +465,12 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin
|
||||||
showToast(R.string.call_error, message);
|
showToast(R.string.call_error, message);
|
||||||
if (lc.getCallsNb() == 0){
|
if (lc.getCallsNb() == 0){
|
||||||
if (mWakeLock.isHeld()) mWakeLock.release();
|
if (mWakeLock.isHeld()) mWakeLock.release();
|
||||||
exitCallMode();
|
exitCallMode(call);
|
||||||
LinphoneActivity.instance().stopOrientationSensor();
|
LinphoneActivity.instance().stopOrientationSensor();
|
||||||
}
|
}
|
||||||
}else if (state==LinphoneCall.State.CallEnd){
|
}else if (state==LinphoneCall.State.CallEnd){
|
||||||
if (lc.getCallsNb() == 0){
|
if (lc.getCallsNb() == 0){
|
||||||
exitCallMode();
|
exitCallMode(call);
|
||||||
LinphoneActivity.instance().stopOrientationSensor();
|
LinphoneActivity.instance().stopOrientationSensor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,7 +257,8 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
|
||||||
|
|
||||||
if (isFinishing()) {
|
if (isFinishing()) {
|
||||||
//restore audio settings
|
//restore audio settings
|
||||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
boolean isUserRequest = false;
|
||||||
|
LinphoneManager.getInstance().routeAudioToReceiver(isUserRequest);
|
||||||
LinphoneManager.stopProximitySensorForActivity(this);
|
LinphoneManager.stopProximitySensorForActivity(this);
|
||||||
instance = null;
|
instance = null;
|
||||||
}
|
}
|
||||||
|
@ -469,7 +470,8 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
|
||||||
video_activity);
|
video_activity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
LinphoneManager.getInstance().routeAudioToSpeaker();
|
boolean isUserRequest = false;
|
||||||
|
LinphoneManager.getInstance().routeAudioToSpeaker(isUserRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startConferenceActivity() {
|
public void startConferenceActivity() {
|
||||||
|
|
|
@ -46,7 +46,9 @@ import java.util.Set;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import org.linphone.LinphoneSimpleListener.LinphoneAudioChangedListener;
|
||||||
import org.linphone.LinphoneSimpleListener.LinphoneServiceListener;
|
import org.linphone.LinphoneSimpleListener.LinphoneServiceListener;
|
||||||
|
import org.linphone.LinphoneSimpleListener.LinphoneAudioChangedListener.AudioState;
|
||||||
import org.linphone.core.LinphoneAddress;
|
import org.linphone.core.LinphoneAddress;
|
||||||
import org.linphone.core.LinphoneAuthInfo;
|
import org.linphone.core.LinphoneAuthInfo;
|
||||||
import org.linphone.core.LinphoneCall;
|
import org.linphone.core.LinphoneCall;
|
||||||
|
@ -177,6 +179,12 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
getInstance().routeAudioToSpeakerHelperHelper(speakerOn);
|
getInstance().routeAudioToSpeakerHelperHelper(speakerOn);
|
||||||
}
|
}
|
||||||
private void routeAudioToSpeakerHelperHelper(boolean speakerOn) {
|
private void routeAudioToSpeakerHelperHelper(boolean speakerOn) {
|
||||||
|
boolean different = isSpeakerOn() ^ speakerOn;
|
||||||
|
if (!different) {
|
||||||
|
Log.d("Skipping change audio route by the same route ",
|
||||||
|
speakerOn ? "speaker" : "earpiece");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Hacks.needGalaxySAudioHack() || lpm.useGalaxySHack())
|
if (Hacks.needGalaxySAudioHack() || lpm.useGalaxySHack())
|
||||||
setAudioModeIncallForGalaxyS();
|
setAudioModeIncallForGalaxyS();
|
||||||
|
|
||||||
|
@ -191,6 +199,9 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
} else {
|
} else {
|
||||||
mAudioManager.setSpeakerphoneOn(speakerOn);
|
mAudioManager.setSpeakerphoneOn(speakerOn);
|
||||||
}
|
}
|
||||||
|
for (LinphoneAudioChangedListener listener : getSimpleListeners(LinphoneAudioChangedListener.class)) {
|
||||||
|
listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : AudioState.EARPIECE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private synchronized void routeAudioToSpeakerHelper(boolean speakerOn) {
|
private synchronized void routeAudioToSpeakerHelper(boolean speakerOn) {
|
||||||
final LinphoneCall call = mLc.getCurrentCall();
|
final LinphoneCall call = mLc.getCurrentCall();
|
||||||
|
@ -202,8 +213,24 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean sUserRequestedSpeaker;
|
||||||
|
public static final boolean isUserRequestedSpeaker() {return sUserRequestedSpeaker;}
|
||||||
|
|
||||||
public void routeAudioToSpeaker() {
|
public void restoreUserRequestedSpeaker() {
|
||||||
|
if (sUserRequestedSpeaker) {
|
||||||
|
routeAudioToSpeaker(false);
|
||||||
|
} else {
|
||||||
|
routeAudioToReceiver(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param isUserRequest true if the setting is permanent, otherwise it can be lost
|
||||||
|
* eg: video activity imply speaker on, which is not a request from the user.
|
||||||
|
* when the activity stops, the sound is routed to the previously user requested route.
|
||||||
|
*/
|
||||||
|
public void routeAudioToSpeaker(boolean isUserRequest) {
|
||||||
|
if (isUserRequest) sUserRequestedSpeaker = true;
|
||||||
routeAudioToSpeakerHelper(true);
|
routeAudioToSpeakerHelper(true);
|
||||||
if (mLc.isIncall()) {
|
if (mLc.isIncall()) {
|
||||||
/*disable EC, it is not efficient enough on speaker mode due to bad quality of speakers and saturation*/
|
/*disable EC, it is not efficient enough on speaker mode due to bad quality of speakers and saturation*/
|
||||||
|
@ -214,7 +241,14 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void routeAudioToReceiver() {
|
/**
|
||||||
|
*
|
||||||
|
* @param isUserRequest true if the setting is permanent, otherwise it can be lost
|
||||||
|
* eg: video activity imply speaker on, which is not a request from the user.
|
||||||
|
* when the activity stops, the sound is routed to the previously user requested route.
|
||||||
|
*/
|
||||||
|
public void routeAudioToReceiver(boolean isUserRequest) {
|
||||||
|
if (isUserRequest) sUserRequestedSpeaker = false;
|
||||||
routeAudioToSpeakerHelper(false);
|
routeAudioToSpeakerHelper(false);
|
||||||
if (mLc.isIncall()) {
|
if (mLc.isIncall()) {
|
||||||
//Restore default value
|
//Restore default value
|
||||||
|
@ -677,7 +711,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
void onEcCalibrationStatus(EcCalibratorStatus status, int delayMs);
|
void onEcCalibrationStatus(EcCalibratorStatus status, int delayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenerDispatcher listenerDispatcher = new ListenerDispatcher(simpleListeners);
|
private ListenerDispatcher listenerDispatcher = new ListenerDispatcher();
|
||||||
private LinphoneCall ringingCall;
|
private LinphoneCall ringingCall;
|
||||||
|
|
||||||
private MediaPlayer mRingerPlayer;
|
private MediaPlayer mRingerPlayer;
|
||||||
|
@ -746,7 +780,6 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
} else if (call == ringingCall && isRinging) {
|
} else if (call == ringingCall && isRinging) {
|
||||||
//previous state was ringing, so stop ringing
|
//previous state was ringing, so stop ringing
|
||||||
stopRinging();
|
stopRinging();
|
||||||
routeAudioToReceiver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == CallEnd || state == Error) {
|
if (state == CallEnd || state == Error) {
|
||||||
|
@ -853,6 +886,8 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
|
|
||||||
isRinging = false;
|
isRinging = false;
|
||||||
// You may need to call galaxys audio hack after this method
|
// You may need to call galaxys audio hack after this method
|
||||||
|
boolean isUserRequest = false;
|
||||||
|
routeAudioToReceiver(isUserRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1067,21 +1102,17 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
return getLc();
|
return getLc();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ListenerDispatcher implements LinphoneServiceListener {
|
@SuppressWarnings("unchecked")
|
||||||
private LinphoneServiceListener serviceListener;
|
private <T> List<T> getSimpleListeners(Class<T> clazz) {
|
||||||
List<LinphoneSimpleListener> simpleListeners;
|
List<T> list = new ArrayList<T>();
|
||||||
public ListenerDispatcher(List<LinphoneSimpleListener> simpleListeners) {
|
for (LinphoneSimpleListener l : simpleListeners) {
|
||||||
this.simpleListeners = simpleListeners; // yes, really keeps a reference, not a copy
|
if (clazz.isInstance(l)) list.add((T) l);
|
||||||
}
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private class ListenerDispatcher implements LinphoneServiceListener {
|
||||||
private <T> List<T> getSimpleListeners(Class<T> clazz) {
|
private LinphoneServiceListener serviceListener;
|
||||||
List<T> list = new ArrayList<T>();
|
|
||||||
for (LinphoneSimpleListener l : simpleListeners) {
|
|
||||||
if (clazz.isInstance(l)) list.add((T) l);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServiceListener(LinphoneServiceListener s) {
|
public void setServiceListener(LinphoneServiceListener s) {
|
||||||
this.serviceListener = s;
|
this.serviceListener = s;
|
||||||
|
@ -1098,6 +1129,9 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
|
|
||||||
public void onCallStateChanged(LinphoneCall call, State state,
|
public void onCallStateChanged(LinphoneCall call, State state,
|
||||||
String message) {
|
String message) {
|
||||||
|
if (state == State.CallEnd && mLc.getCallsNb() == 0) {
|
||||||
|
routeAudioToReceiver(true);
|
||||||
|
}
|
||||||
if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message);
|
if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message);
|
||||||
for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) {
|
for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) {
|
||||||
l.onCallStateChanged(call, state, message);
|
l.onCallStateChanged(call, state, message);
|
||||||
|
|
|
@ -47,4 +47,9 @@ public interface LinphoneSimpleListener {
|
||||||
public static interface LinphoneOnCallStateChangedListener extends LinphoneSimpleListener {
|
public static interface LinphoneOnCallStateChangedListener extends LinphoneSimpleListener {
|
||||||
void onCallStateChanged(LinphoneCall call, State state, String message);
|
void onCallStateChanged(LinphoneCall call, State state, String message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static interface LinphoneAudioChangedListener extends LinphoneSimpleListener {
|
||||||
|
public enum AudioState {EARPIECE, SPEAKER}
|
||||||
|
void onAudioStateChanged(AudioState state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class VideoCallActivity extends Activity {
|
||||||
private SurfaceView mVideoViewReady;
|
private SurfaceView mVideoViewReady;
|
||||||
private SurfaceView mVideoCaptureViewReady;
|
private SurfaceView mVideoCaptureViewReady;
|
||||||
public static boolean launched = false;
|
public static boolean launched = false;
|
||||||
|
public static LinphoneCall call;
|
||||||
private WakeLock mWakeLock;
|
private WakeLock mWakeLock;
|
||||||
private Handler refreshHandler = new Handler();
|
private Handler refreshHandler = new Handler();
|
||||||
|
|
||||||
|
@ -280,7 +281,10 @@ public class VideoCallActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
Log.d("onPause VideoCallActivity (isFinishing:", isFinishing(), ", inCall:", LinphoneManager.getLc().isIncall(), ", changingConf:", getChangingConfigurations());
|
Log.d("onPause VideoCallActivity (isFinishing:", isFinishing(), ", inCall:", LinphoneManager.getLc().isIncall(), ", changingConf:", getChangingConfigurations());
|
||||||
|
if (isFinishing()) {
|
||||||
|
call = null; // release reference
|
||||||
|
}
|
||||||
|
LinphoneManager.getInstance().restoreUserRequestedSpeaker();
|
||||||
launched=false;
|
launched=false;
|
||||||
synchronized (androidVideoWindowImpl) {
|
synchronized (androidVideoWindowImpl) {
|
||||||
/* this call will destroy native opengl renderer
|
/* this call will destroy native opengl renderer
|
||||||
|
|
|
@ -48,9 +48,9 @@ public class SpeakerButton extends ToggleImageButton implements OnCheckedChangeL
|
||||||
|
|
||||||
public void onCheckedChanged(ToggleImageButton button, boolean checked) {
|
public void onCheckedChanged(ToggleImageButton button, boolean checked) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
LinphoneManager.getInstance().routeAudioToSpeaker();
|
LinphoneManager.getInstance().routeAudioToSpeaker(true);
|
||||||
} else {
|
} else {
|
||||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
LinphoneManager.getInstance().routeAudioToReceiver(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue