我用Sinch创建了一个视频和语音通话应用程序。我准备了所有布局,并且工作正常,还需要实现一项新功能,如果有的话-正在进行一个视频通话。如果另一位最终用户启用了视频,则一个用户可以禁用他们的视频,并且可以看到其他视频。在这种情况下,我没有任何想法可言,因为我尝试了很多,但没有从任何地方得到任何答复。
呼叫屏幕布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<RelativeLayout
android:id="@+id/remoteVideo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/userImage1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_placeholder"
android:tint="#99000000"
tools:ignore="ContentDescription" />
<View
android:id="@+id/tintBlue"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/vertical_gradient" />
<TextView
android:id="@+id/txt_calling"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/layout_btns"
android:layout_centerHorizontal="true"
android:fontFamily="@font/roboto_medium"
android:gravity="center"
android:text="Infinite Messenger"
android:textColor="@android:color/white"
android:textSize="12sp"
tools:ignore="HardcodedText" />
<ImageView
android:id="@+id/userImage2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_above="@+id/txt_calling"
android:layout_centerHorizontal="true"
android:layout_marginTop="34dp"
android:scaleType="centerCrop"
android:visibility="gone"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/remoteUser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/txt_calling"
android:layout_centerHorizontal="true"
android:ellipsize="marquee"
android:fontFamily="@font/roboto_medium"
android:gravity="center"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="22sp" />
<TextView
android:id="@+id/callState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/txt_calling"
android:layout_centerHorizontal="true"
android:fontFamily="@font/roboto_light"
android:padding="@dimen/_8sdp"
android:textColor="@android:color/white"
android:textSize="14sp"
android:visibility="gone" />
<TextView
android:id="@+id/callDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/txt_calling"
android:layout_centerHorizontal="true"
android:fontFamily="@font/roboto_light"
android:textColor="@android:color/white"
android:textSize="16sp" />
<ImageView
android:id="@+id/hangupButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/_16sdp"
android:background="@drawable/round_red_hangup"
android:padding="16dp"
android:src="@drawable/ic_call_end_black_24dp"
tools:ignore="ContentDescription" />
<RelativeLayout
android:id="@+id/localVideo"
android:layout_width="100dp"
android:layout_height="130dp"
android:layout_above="@+id/layout_btns"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="@dimen/_50sdp"
tools:ignore="RtlHardcoded" />
<LinearLayout
android:id="@+id/layout_btns"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/hangupButton"
android:layout_marginBottom="@dimen/_16sdp"
android:orientation="horizontal"
android:padding="@dimen/_10sdp"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:id="@+id/switchVideo_LLY"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:visibility="visible">
<ImageView
android:id="@+id/switchVideo"
android:layout_width="@dimen/_50sdp"
android:layout_height="@dimen/_50sdp"
android:padding="4dp"
android:src="@drawable/ic_camera_turn"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:id="@+id/switchVolume"
android:layout_width="@dimen/_50sdp"
android:layout_height="@dimen/_50sdp"
android:padding="4dp"
android:src="@drawable/ic_speaker"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:id="@+id/switchMic"
android:layout_width="@dimen/_50sdp"
android:layout_height="@dimen/_50sdp"
android:padding="4dp"
android:src="@drawable/ic_mic_on"
tools:ignore="ContentDescription" />
</LinearLayout>
</LinearLayout>
<com.google.android.gms.ads.AdView xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="@+id/adView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ads:adSize="BANNER"
ads:adUnitId="@string/bannerad_id" />
</RelativeLayout>
呼叫屏幕类别:
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.os.PowerManager;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.infinite.messenger.models.Status;
import com.infinite.messenger.utils.OnDragTouchListener;
import com.sinch.android.rtc.AudioController;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.calling.CallState;
import com.sinch.android.rtc.video.VideoCallListener;
import com.sinch.android.rtc.video.VideoController;
import com.infinite.messenger.R;
import com.infinite.messenger.models.Contact;
import com.infinite.messenger.models.Group;
import com.infinite.messenger.models.LogCall;
import com.infinite.messenger.models.User;
import com.infinite.messenger.services.SinchService;
import com.infinite.messenger.utils.AudioPlayer;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
public class CallScreenActivity extends BaseActivity implements SensorEventListener {
static final String TAG = CallScreenActivity.class.getSimpleName();
static final String ADDED_LISTENER = "addedListener";
private static String EXTRA_DATA_USER = "extradatauser";
private static String EXTRA_DATA_IN_OR_OUT = "extradatainorout";
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId, inOrOut;
private boolean mAddedListener, mLocalVideoViewAdded, mRemoteVideoViewAdded, isVideo, isMute, isSpeaker, alphaInvisible, logSaved;
private int mCallDurationSecond = 0;
private RelativeLayout myCallScreenRootRLY;
private TextView mCallDuration, mCallState, mCallerName, myTxtCalling;
private ImageView userImage1, userImage2, switchVideo, switchMic, switchVolume;
private View tintBlue, bottomButtons;
private RelativeLayout localVideo, remoteVideo;
private LinearLayout mySwitchCameraLLY;
private SensorManager mSensorManager;
private Sensor mProximity;
PowerManager.WakeLock wlOff = null, wlOn = null;
@Override
protected void onDestroy() {
super.onDestroy();
try {
if (wlOff != null && wlOff.isHeld()) {
wlOff.release();
} else if (wlOn != null && wlOn.isHeld()) {
wlOn.release();
}
} catch (RuntimeException ex) {
}
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
float distance = sensorEvent.values[0];
if (!isVideo && !isSpeaker) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (distance < 4) {
if (wlOn != null && wlOn.isHeld()) {
wlOn.release();
}
if (pm != null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
if (wlOff == null)
wlOff = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "tag");
if (!wlOff.isHeld()) wlOff.acquire();
}
}
} else {
if (wlOff != null && wlOff.isHeld()) {
wlOff.release();
}
if (pm != null) {
if (wlOn == null)
wlOn = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "tag");
if (!wlOn.isHeld()) wlOn.acquire();
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
private class UpdateCallDurationTask extends TimerTask {
@Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCallDuration();
}
});
}
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean(ADDED_LISTENER, mAddedListener);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
mAddedListener = savedInstanceState.getBoolean(ADDED_LISTENER);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_screen);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
Intent intent = getIntent();
user = intent.getParcelableExtra(EXTRA_DATA_USER);
mCallId = intent.getStringExtra(SinchService.CALL_ID);
inOrOut = intent.getStringExtra(EXTRA_DATA_IN_OR_OUT);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = findViewById(R.id.callDuration);
mCallerName = findViewById(R.id.remoteUser);
mCallState = findViewById(R.id.callState);
userImage1 = findViewById(R.id.userImage1);
userImage2 = findViewById(R.id.userImage2);
myTxtCalling = findViewById(R.id.txt_calling);
tintBlue = findViewById(R.id.tintBlue);
localVideo = findViewById(R.id.localVideo);
remoteVideo = findViewById(R.id.remoteVideo);
switchVideo = findViewById(R.id.switchVideo);
switchMic = findViewById(R.id.switchMic);
switchVolume = findViewById(R.id.switchVolume);
bottomButtons = findViewById(R.id.layout_btns);
mySwitchCameraLLY = findViewById(R.id.switchVideo_LLY);
findViewById(R.id.hangupButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
endCall();
}
});
remoteVideo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAlphaAnimation();
}
});
switchMic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
isMute = !isMute;
setMuteUnmute();
}
});
switchVolume.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
isSpeaker = !isSpeaker;
enableSpeaker(isSpeaker);
}
});
switchVideo.setClickable(false);
}
@Override
public void onPause() {
super.onPause();
mDurationTask.cancel();
mTimer.cancel();
mSensorManager.unregisterListener(this);
removeVideoViews();
}
@Override
public void onResume() {
super.onResume();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
updateUI();
}
@Override
public void onBackPressed() {
// User should exit activity by ending call, not by going back.
}
private void endCall() {
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
if (user == null) {
user = new User(call.getRemoteUserId(), call.getRemoteUserId(), getString(R.string.app_name), "");
}
call.hangup();
}
saveLog();
finish();
}
private void saveLog() {
if (!logSaved) {
rChatDb.beginTransaction();
rChatDb.copyToRealm(new LogCall(user, System.currentTimeMillis(), mCallDurationSecond,
isVideo, inOrOut, userMe.getId(), user.getId()));
rChatDb.commitTransaction();
logSaved = true;
}
}
private String formatTimespan(int totalSeconds) {
long minutes = totalSeconds / 60;
long seconds = totalSeconds % 60;
return String.format(Locale.US, "%02d:%02d", minutes, seconds);
}
private void updateCallDuration() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
mCallDurationSecond = call.getDetails().getDuration();
mCallDuration.setText(formatTimespan(call.getDetails().getDuration()));
}
}
@Override
void onSinchConnected() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
if (!mAddedListener) {
call.addCallListener(new SinchCallListener());
mAddedListener = true;
}
mCallerName.setText(user != null ? user.getNameToDisplay() : call.getRemoteUserId());
mCallState.setText(call.getState().toString());
if (user != null) {
// Glide.with(this).load(user.getImage()).apply(new RequestOptions().placeholder(R.drawable.ic_logo_)).into(userImage1);
// Glide.with(this).load(user.getImage()).apply(RequestOptions.circleCropTransform().placeholder(R.drawable.ic_logo_)).into(userImage2);
if (user.getImage() != null && !user.getImage().isEmpty()) {
Picasso.get()
.load(user.getImage())
.tag(this)
.error(R.drawable.ic_avatar)
.placeholder(R.drawable.ic_avatar)
.into(userImage1);
Picasso.get()
.load(user.getImage())
.tag(this)
.error(R.drawable.ic_avatar)
.placeholder(R.drawable.ic_avatar)
.into(userImage2);
} else {
userImage1.setBackgroundResource(R.drawable.ic_avatar);
userImage2.setBackgroundResource(R.drawable.ic_avatar);
}
// tintBlue.setVisibility(View.INVISIBLE);
// remoteVideo.setVisibility(View.INVISIBLE);
}
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
updateUI();
}
private void updateUI() {
if (getSinchServiceInterface() == null) {
return; // early
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
mCallerName.setText(user != null ? user.getNameToDisplay() : call.getRemoteUserId());
mCallState.setText(call.getState().toString());
isVideo = call.getDetails().isVideoOffered();
if (isVideo) {
addLocalView();
if (call.getState() == CallState.ESTABLISHED) {
addRemoteView();
}
mySwitchCameraLLY.setVisibility(View.VISIBLE);
} else {
mySwitchCameraLLY.setVisibility(View.GONE);
}
myTxtCalling.setText(getResources().getString(R.string.app_name) + (isVideo ? " Video Calling" : " Voice Calling"));
tintBlue.setVisibility(isVideo ? View.GONE : View.VISIBLE);
localVideo.setVisibility(!isVideo ? View.GONE : View.VISIBLE);
}
}
private void removeVideoViews() {
if (getSinchServiceInterface() == null) {
return; // early
}
VideoController vc = getSinchServiceInterface().getVideoController();
if (vc != null) {
remoteVideo.removeView(vc.getRemoteView());
localVideo.removeView(vc.getLocalView());
mLocalVideoViewAdded = false;
mRemoteVideoViewAdded = false;
}
}
private void addLocalView() {
if (mLocalVideoViewAdded || getSinchServiceInterface() == null) {
return; //early
}
final VideoController vc = getSinchServiceInterface().getVideoController();
if (vc != null) {
localVideo.addView(vc.getLocalView());
switchVideo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
vc.toggleCaptureDevicePosition();
}
});
mLocalVideoViewAdded = true;
localVideo.setOnTouchListener(new OnDragTouchListener(localVideo, remoteVideo));
}
}
private void addRemoteView() {
if (mRemoteVideoViewAdded || getSinchServiceInterface() == null) {
return; //early
}
final VideoController vc = getSinchServiceInterface().getVideoController();
if (vc != null) {
RelativeLayout view = (RelativeLayout) findViewById(R.id.remoteVideo);
view.addView(vc.getRemoteView());
mRemoteVideoViewAdded = true;
}
}
private void startAlphaAnimation() {
AlphaAnimation animation1 = new AlphaAnimation(alphaInvisible ? 0.0f : 1.0f, alphaInvisible ? 1.0f : 0.0f);
animation1.setDuration(500);
animation1.setStartOffset(25);
animation1.setFillAfter(true);
myTxtCalling.startAnimation(animation1);
userImage2.startAnimation(animation1);
mCallerName.startAnimation(animation1);
mCallState.startAnimation(animation1);
mCallDuration.startAnimation(animation1);
bottomButtons.startAnimation(animation1);
alphaInvisible = !alphaInvisible;
}
private void enableSpeaker(boolean enable) {
AudioController audioController = getSinchServiceInterface().getAudioController();
if (enable)
audioController.enableSpeaker();
else
audioController.disableSpeaker();
switchVolume.setImageDrawable(ContextCompat.getDrawable(this, isSpeaker ? R.drawable.ic_speaker : R.drawable.ic_speaker_off));
}
private void setMuteUnmute() {
AudioController audioController = getSinchServiceInterface().getAudioController();
if (isMute) {
audioController.mute();
switchMic.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_mic_off));
} else {
audioController.unmute();
switchMic.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_mic_on));
}
}
@Override
void onSinchDisconnected() {
}
private class SinchCallListener implements VideoCallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString());
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = call.getDetails().toString();
Log.d(TAG, "Call ended. Reason: " + endMsg);
if (user == null) {
user = new User(call.getRemoteUserId(), call.getRemoteUserId(), getString(R.string.app_name), "");
}
endCall();
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
isSpeaker = call.getDetails().isVideoOffered();
enableSpeaker(isSpeaker);
switchVideo.setClickable(call.getDetails().isVideoOffered());
if (call.getDetails().isVideoOffered()) {
mySwitchCameraLLY.setVisibility(View.VISIBLE);
} else {
mySwitchCameraLLY.setVisibility(View.GONE);
}
switchVideo.setAlpha(call.getDetails().isVideoOffered() ? 1f : 0.4f);
userImage1.setVisibility(isVideo ? View.GONE : View.VISIBLE);
Log.d(TAG, "Call offered video: " + call.getDetails().isVideoOffered());
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
@Override
public void onVideoTrackAdded(Call call) {
Log.d(TAG, "Video track added");
addRemoteView();
}
@Override
public void onVideoTrackPaused(Call call) {
}
@Override
public void onVideoTrackResumed(Call call) {
}
}
public static Intent newIntent(Context context, User user, String callId, String inOrOut) {
Intent intent = new Intent(context, CallScreenActivity.class);
intent.putExtra(EXTRA_DATA_USER, user);
intent.putExtra(EXTRA_DATA_IN_OR_OUT, inOrOut);
intent.putExtra(SinchService.CALL_ID, callId);
return intent;
}
@Override
void myUsersResult(ArrayList<User> myUsers) {
}
@Override
void myContactsResult(ArrayList<Contact> myContacts) {
}
@Override
void userAdded(User valueUser) {
}
@Override
void groupAdded(Group valueGroup) {
}
@Override
void userUpdated(User valueUser) {
}
@Override
void groupUpdated(Group valueGroup) {
}
@Override
void statusAdded(Status status) {
}
@Override
void statusUpdated(Status status) {
}
}
就这些。