diff --git a/README.md b/README.md index ee3ae2f7..27bb9b08 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,19 @@ # ScreenStream -Screen Stream over HTTP - an Android mobile app: https://play.google.com/store/apps/details?id=info.dvkr.screenstream +**Screen Stream over HTTP** - an Android mobile app: [Screen Stream over HTTP](https://play.google.com/store/apps/details?id=info.dvkr.screenstream) The application allows viewing the device screen in your web browser. The main idea is to show your device screen during presentations and demos. No need of any additional software except for this app and a web browser. + It uses MJPEG to encode screen images and send them through network socket. So it works with any desktop or mobile browser which supports MJPEG (Chrome, Safari, EDGE, Firefox). The program works only via WiFi connection, so your device has to be connected to WiFi. No Internet required, however, there must be a network connection between the client and the device. -The number of client connections is unlimited, but be aware that each of them requires some CPU resources to send data. +The number of client connections is unlimited, but be aware that each of them requires some CPU resources to send data. It uses Android Cast feature and requires at least Android 5.0 to operate. +**Known problems** +1. On some devices system return image in unknown format. Mostly on devices with no official Android 5.0 or above. Possible Android bug. App will show an error message. Trying to find some workaround. +2. On some devices no notification icon showing but notification is present. Android bug: 213309. + If there are any issues or ideas feel free to contact me. diff --git a/app/build.gradle b/app/build.gradle index 9af10b44..0807883d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:design:24.2.0' + compile 'com.android.support:design:24.2.1' compile 'com.google.firebase:firebase-crash:9.4.0' compile 'org.greenrobot:eventbus:3.0.0' } diff --git a/app/src/main/java/info/dvkr/screenstream/data/ImageGenerator.java b/app/src/main/java/info/dvkr/screenstream/data/ImageGenerator.java index 7b4e3806..55ab4dc4 100644 --- a/app/src/main/java/info/dvkr/screenstream/data/ImageGenerator.java +++ b/app/src/main/java/info/dvkr/screenstream/data/ImageGenerator.java @@ -1,6 +1,5 @@ package info.dvkr.screenstream.data; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; @@ -20,7 +19,6 @@ import java.io.IOException; import info.dvkr.screenstream.service.ForegroundService; -import info.dvkr.screenstream.utils.NotifyImageGenerator; import static info.dvkr.screenstream.ScreenStreamApplication.getAppData; import static info.dvkr.screenstream.ScreenStreamApplication.getAppPreference; @@ -148,19 +146,5 @@ public void stop() { isThreadRunning = false; } } - - public void addDefaultScreen(final Context context) { - getAppData().getImageQueue().clear(); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - final byte[] jpegByteArray = NotifyImageGenerator.getDefaultScreen(context); - if (jpegByteArray != null) { - getAppData().getImageQueue().add(jpegByteArray); - } - } - }, 500); - - } } diff --git a/app/src/main/java/info/dvkr/screenstream/utils/NotifyImageGenerator.java b/app/src/main/java/info/dvkr/screenstream/data/NotifyImageGenerator.java similarity index 64% rename from app/src/main/java/info/dvkr/screenstream/utils/NotifyImageGenerator.java rename to app/src/main/java/info/dvkr/screenstream/data/NotifyImageGenerator.java index 3b9fa791..c7b046ca 100644 --- a/app/src/main/java/info/dvkr/screenstream/utils/NotifyImageGenerator.java +++ b/app/src/main/java/info/dvkr/screenstream/data/NotifyImageGenerator.java @@ -1,4 +1,4 @@ -package info.dvkr.screenstream.utils; +package info.dvkr.screenstream.data; import android.content.Context; import android.graphics.Bitmap; @@ -6,6 +6,8 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; +import android.os.Handler; +import android.support.annotation.Nullable; import com.google.firebase.crash.FirebaseCrash; @@ -13,38 +15,52 @@ import java.io.IOException; import info.dvkr.screenstream.R; -import info.dvkr.screenstream.data.BusMessages; import static info.dvkr.screenstream.ScreenStreamApplication.getAppData; import static info.dvkr.screenstream.ScreenStreamApplication.getAppPreference; public final class NotifyImageGenerator { - private static int sCurrentScreenSizeX; - private static byte[] sCurrentDefaultScreen; + private Context mContext; + private int mCurrentScreenSizeX; + private byte[] mCurrentDefaultScreen; - public static byte[] getDefaultScreen(final Context context) { - if (sCurrentScreenSizeX != getAppData().getScreenSize().x) - sCurrentDefaultScreen = null; - if (sCurrentDefaultScreen != null) return sCurrentDefaultScreen; - - sCurrentDefaultScreen = generateImage(context.getString(R.string.image_generator_press), - context.getString(R.string.main_activity_start_stream).toUpperCase(), - context.getString(R.string.image_generator_on_device)); - - sCurrentScreenSizeX = getAppData().getScreenSize().x; - return sCurrentDefaultScreen; + public NotifyImageGenerator(final Context context) { + mContext = context; } + public void addDefaultScreen() { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if (mCurrentScreenSizeX != getAppData().getScreenSize().x) { + mCurrentDefaultScreen = null; + } + if (mCurrentDefaultScreen == null) { + mCurrentDefaultScreen = generateImage(mContext.getString(R.string.image_generator_press), + mContext.getString(R.string.main_activity_start_stream).toUpperCase(), + mContext.getString(R.string.image_generator_on_device)); + mCurrentScreenSizeX = getAppData().getScreenSize().x; + } + if (mCurrentDefaultScreen != null) { + getAppData().getImageQueue().add(mCurrentDefaultScreen); + } + } + }, 500); + } - public static byte[] getClientNotifyImage(final Context context, final String reason) { + public byte[] getClientNotifyImage(final String reason) { if (BusMessages.MESSAGE_ACTION_HTTP_RESTART.equals(reason)) - return generateImage(context.getString(R.string.image_generator_settings_changed), "", context.getString(R.string.image_generator_go_to_new_address)); + return generateImage(mContext.getString(R.string.image_generator_settings_changed), + "", mContext.getString(R.string.image_generator_go_to_new_address)); + if (BusMessages.MESSAGE_ACTION_PIN_UPDATE.equals(reason)) - return generateImage(context.getString(R.string.image_generator_settings_changed), "", context.getString(R.string.image_generator_reload_this_page)); + return generateImage(mContext.getString(R.string.image_generator_settings_changed), + "", mContext.getString(R.string.image_generator_reload_this_page)); return null; } + @Nullable private static byte[] generateImage(final String text1, final String text2, final String text3) { final Bitmap bitmap = Bitmap.createBitmap(getAppData().getScreenSize().x, getAppData().getScreenSize().y, diff --git a/app/src/main/java/info/dvkr/screenstream/service/ForegroundService.java b/app/src/main/java/info/dvkr/screenstream/service/ForegroundService.java index cffadc9f..ef00f5d5 100644 --- a/app/src/main/java/info/dvkr/screenstream/service/ForegroundService.java +++ b/app/src/main/java/info/dvkr/screenstream/service/ForegroundService.java @@ -24,7 +24,7 @@ import info.dvkr.screenstream.data.BusMessages; import info.dvkr.screenstream.data.HttpServer; import info.dvkr.screenstream.data.ImageGenerator; -import info.dvkr.screenstream.utils.NotifyImageGenerator; +import info.dvkr.screenstream.data.NotifyImageGenerator; import info.dvkr.screenstream.view.MainActivity; import static info.dvkr.screenstream.ScreenStreamApplication.getAppData; @@ -40,6 +40,9 @@ public final class ForegroundService extends Service { private static ForegroundService sServiceInstance; + private static final int NOTIFICATION_START_STREAMING = 10; + private static final int NOTIFICATION_STOP_STREAMING = 11; + private static final String EXTRA_SERVICE_MESSAGE = "info.dvkr.screenstream.extras.EXTRA_SERVICE_MESSAGE"; private static final String SERVICE_MESSAGE_PREPARE_STREAMING = "SERVICE_MESSAGE_PREPARE_STREAMING"; @@ -53,6 +56,7 @@ public final class ForegroundService extends Service { private MediaProjection.Callback mProjectionCallback; private HttpServer mHttpServer; private ImageGenerator mImageGenerator; + private NotifyImageGenerator mNotifyImageGenerator; private ForegroundServiceHandler mForegroundServiceTaskHandler; private BroadcastReceiver mLocalNotificationReceiver; private BroadcastReceiver mBroadcastReceiver; @@ -98,7 +102,9 @@ public void onStop() { }; mHttpServer = new HttpServer(); mImageGenerator = new ImageGenerator(); - mImageGenerator.addDefaultScreen(getApplicationContext()); + getAppData().getImageQueue().clear(); + mNotifyImageGenerator = new NotifyImageGenerator(getApplicationContext()); + mNotifyImageGenerator.addDefaultScreen(); // Starting thread Handler final HandlerThread looperThread = @@ -178,7 +184,9 @@ public void onReceive(Context context, Intent intent) { @Override public int onStartCommand(Intent intent, int flags, int startId) { if (SERVICE_MESSAGE_PREPARE_STREAMING.equals(intent.getStringExtra(EXTRA_SERVICE_MESSAGE))) { - if (!isServicePrepared) startForeground(110, getNotificationStart()); + if (!isServicePrepared) { + startForeground(NOTIFICATION_START_STREAMING, getNotificationStart()); + } isServicePrepared = true; } return START_NOT_STICKY; @@ -205,13 +213,15 @@ public void onMessageEvent(BusMessages busMessage) { serviceStopStreaming(); break; case MESSAGE_ACTION_HTTP_RESTART: - mHttpServer.stop(NotifyImageGenerator.getClientNotifyImage(getApplicationContext(), MESSAGE_ACTION_HTTP_RESTART)); - mImageGenerator.addDefaultScreen(getApplicationContext()); + getAppData().getImageQueue().clear(); + mHttpServer.stop(mNotifyImageGenerator.getClientNotifyImage(MESSAGE_ACTION_HTTP_RESTART)); + mNotifyImageGenerator.addDefaultScreen(); mHttpServer.start(); break; case MESSAGE_ACTION_PIN_UPDATE: - mHttpServer.stop(NotifyImageGenerator.getClientNotifyImage(getApplicationContext(), MESSAGE_ACTION_PIN_UPDATE)); - mImageGenerator.addDefaultScreen(getApplicationContext()); + getAppData().getImageQueue().clear(); + mHttpServer.stop(mNotifyImageGenerator.getClientNotifyImage(MESSAGE_ACTION_PIN_UPDATE)); + mNotifyImageGenerator.addDefaultScreen(); mHttpServer.start(); break; default: @@ -223,7 +233,7 @@ private void serviceStartStreaming() { if (getAppData().isStreamRunning()) return; stopForeground(true); mForegroundServiceTaskHandler.obtainMessage(ForegroundServiceHandler.HANDLER_START_STREAMING).sendToTarget(); - startForeground(120, getNotificationStop()); + startForeground(NOTIFICATION_STOP_STREAMING, getNotificationStop()); if (mMediaProjection != null) mMediaProjection.registerCallback(mProjectionCallback, null); } @@ -231,9 +241,10 @@ private void serviceStopStreaming() { if (!getAppData().isStreamRunning()) return; stopForeground(true); mForegroundServiceTaskHandler.obtainMessage(ForegroundServiceHandler.HANDLER_STOP_STREAMING).sendToTarget(); - startForeground(110, getNotificationStart()); + startForeground(NOTIFICATION_START_STREAMING, getNotificationStart()); if (mMediaProjection != null) mMediaProjection.unregisterCallback(mProjectionCallback); - mImageGenerator.addDefaultScreen(getApplicationContext()); + getAppData().getImageQueue().clear(); + mNotifyImageGenerator.addDefaultScreen(); if (getAppPreference().isEnablePin() && getAppPreference().isAutoChangePin()) { getAppPreference().generateAndSaveNewPin();