package skylink.temasys.com.sg.skylinkshare;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import sg.com.temasys.skylink.sdk.rtc.SkylinkConfig;
import sg.com.temasys.skylink.sdk.listener.DataTransferListener;
import sg.com.temasys.skylink.sdk.listener.LifeCycleListener;
import sg.com.temasys.skylink.sdk.listener.RemotePeerListener;
import sg.com.temasys.skylink.sdk.rtc.SkylinkConnection;
/**
* Simple application demonstrating the use of
* Android Media Projection API and Skylink for Android
*
* Media projection code referred from
* https://github.com/mtsahakis/MediaProjectionDemo
*/
public class MainActivity extends Activity
implements RemotePeerListener, DataTransferListener, LifeCycleListener {
private static final String TAG = MainActivity.class.getName();
private static final String APP_KEY = "";
private static final String APP_SECRET = "";
private static final String ROOM_NAME = "SkylinkShare";
private static final int REQUEST_CODE = 1;
private static final int TIME_OUT = 60;
private int displayWidth;
private int displayHeight;
private int imagesProduced;
private MediaProjectionManager projectionManager;
private ImageReader imageReader;
private MediaProjection mediaProjection;
private Handler handler;
private SkylinkConnection skylinkConnection;
private String currentRemotePeerId;
private boolean projectionStarted;
private boolean connected;
private ImageView imgViewer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgViewer = (ImageView) findViewById(R.id.image_view);
projectionManager = (MediaProjectionManager)
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
// Use the device id as the username
String deviceId = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
//Initialize skylinkConnection and connect to the room
if(APP_KEY.isEmpty()||APP_SECRET.isEmpty()){
showInitializationFailedAlert();
return;
}
initializeSkylinkConnection();
skylinkConnection.connectToRoom(APP_SECRET, ROOM_NAME, deviceId);
// Start capture handling thread
new Thread() {
@Override
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}.start();
}
private void showInitializationFailedAlert() {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setMessage(R.string.dialog_init_fail).setPositiveButton(
getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
});
alertBuilder.show();
}
@Override
public void onDestroy() {
//close the connection when the fragment is detached, so the streams are not open.
if (skylinkConnection != null && connected) {
skylinkConnection.disconnectFromRoom();
skylinkConnection.setLifeCycleListener(null);
skylinkConnection.setMediaListener(null);
skylinkConnection.setRemotePeerListener(null);
connected = false;
}
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
if (projectionStarted) {
stopProjection();
item.setTitle("Share Screen");
} else {
startProjection();
item.setTitle("Stop Sharing");
}
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Initializes the skylink connection
*/
private void initializeSkylinkConnection() {
if (skylinkConnection == null) {
skylinkConnection = SkylinkConnection.getInstance();
// The app_key and app_secret is obtained from the temasys developer console.
skylinkConnection.init(APP_KEY, getSkylinkConfig(), this);
// Set listeners to receive callbacks when events are triggered
skylinkConnection.setRemotePeerListener(this);
skylinkConnection.setDataTransferListener(this);
skylinkConnection.setLifeCycleListener(this);
}
}
/**
* Returns the skylink config
*
* @return
*/
private SkylinkConfig getSkylinkConfig() {
SkylinkConfig config = new SkylinkConfig();
// AudioVideo config options can be NO_AUDIO_NO_VIDEO, AUDIO_ONLY, VIDEO_ONLY, AUDIO_AND_VIDEO;
config.setAudioVideoSendConfig(SkylinkConfig.AudioVideoConfig.NO_AUDIO_NO_VIDEO);
config.setHasDataTransfer(true);
config.setTimeout(TIME_OUT);
return config;
}
/**
* Requests to start projection
*/
public void startProjection() {
startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_CODE);
}
/**
* Request to stop projection
*/
public void stopProjection() {
projectionStarted = false;
handler.post(new Runnable() {
@Override
public void run() {
if (mediaProjection != null) {
mediaProjection.stop();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
mediaProjection = projectionManager.getMediaProjection(resultCode, data);
if (mediaProjection != null) {
projectionStarted = true;
// Initialize the media projection
DisplayMetrics metrics = getResources().getDisplayMetrics();
int density = metrics.densityDpi;
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
| DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x;
displayHeight = size.y;
imageReader = ImageReader.newInstance(displayWidth, displayHeight
, PixelFormat.RGBA_8888, 2);
mediaProjection.createVirtualDisplay("screencap",
displayWidth, displayHeight, density,
flags, imageReader.getSurface(), null, handler);
imageReader.setOnImageAvailableListener(new ImageAvailableListener(), handler);
}
}
}
private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
FileOutputStream fos = null;
Bitmap bitmap = null;
ByteArrayOutputStream stream = null;
try {
image = imageReader.acquireLatestImage();
if (image != null) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * displayWidth;
// create bitmap
bitmap = Bitmap.createBitmap(displayWidth + rowPadding / pixelStride,
displayHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
if (skylinkConnection != null && !TextUtils.isEmpty(currentRemotePeerId)) {
stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 5, stream);
skylinkConnection.sendData(currentRemotePeerId, stream.toByteArray());
Log.d(TAG, "sending data to peer :" + currentRemotePeerId);
}
imagesProduced++;
Log.e(TAG, "captured image: " + imagesProduced);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
if (stream != null) {
try {
stream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
if (bitmap != null) {
bitmap.recycle();
}
if (image != null) {
image.close();
}
}
}
}
/**
* LifeCycleListener implementation
*/
@Override
public void onConnect(boolean isSuccessful, String message) {
showToast("onConnect " + isSuccessful);
connected = isSuccessful;
}
@Override
public void onWarning(int errorCode, String message) {
showToast("onWarning " + message);
}
@Override
public void onDisconnect(int errorCode, String message) {
showToast("onDisconnect " + message);
}
@Override
public void onReceiveLog(String message) {
showToast("onReceiveLog " + message);
}
@Override
public void onLockRoomStatusChange(String remotePeerId, boolean locked) {
showToast("onLockRoomStatusChange " + remotePeerId + " locked " + locked);
}
/**
* RemotePeerListener implementation
*/
@Override
public void onRemotePeerJoin(String remotePeerId, Object userData, boolean hasDataChannel) {
if (!TextUtils.isEmpty(this.currentRemotePeerId)) {
showToast("A remote peer is already in the room");
return;
}
this.currentRemotePeerId = remotePeerId;
showToast("onRemotePeerJoin " + remotePeerId);
}
@Override
public void onRemotePeerUserDataReceive(String remotePeerId, Object userData) {
showToast("onRemotePeerUserDataReceive " + remotePeerId);
}
@Override
public void onOpenDataConnection(String remotePeerId) {
showToast("onOpenDataConnection " + remotePeerId);
}
@Override
public void onRemotePeerLeave(String remotePeerId, String message) {
if (remotePeerId.equals(this.currentRemotePeerId)) {
this.currentRemotePeerId = null;
}
showToast("onRemotePeerLeave " + remotePeerId);
}
/**
* DataTransferListener implementation
*/
@Override
public void onDataReceive(String remotePeerId, final byte[] data) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "onDataReceive: " + data.length);
if (data != null && data.length != 0) {
Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
Log.d(TAG, "Set Image : " + bm.toString());
imgViewer.setImageBitmap(bm);
}
}
});
}
private void showToast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}