//package pl.polidea.asl;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.UUID;
import android.app.Service;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.Matrix;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
class ScreenshotService extends Service {
/*
* Action name for intent used to bind to service.
*/
public static final String BIND = "pl.polidea.asl.ScreenshotService.BIND";
/*
* Name of the native process.
*/
private static final String NATIVE_PROCESS_NAME = "asl-native";
/*
* Port number used to communicate with native process.
*/
private static final int PORT = 42380;
/*
* Timeout allowed in communication with native process.
*/
private static final int TIMEOUT = 1000;
/*
* Directory where screenshots are being saved.
*/
private static final String SCREENSHOT_FOLDER = "/sdcard/screens/";
@Override
public void onCreate() {
Log.i("service", "Service created.");
}
/*
* Checks whether the internal native application is running,
*/
private boolean isNativeRunning() {
try {
Socket sock = new Socket();
sock.connect(new InetSocketAddress("localhost", PORT), 10); // short timeout
}
catch (Exception e) {
return false;
}
return true;
// ActivityManager am = (ActivityManager)getSystemService(Service.ACTIVITY_SERVICE);
// List ps = am.getRunningAppProcesses();
//
// if (am != null) {
// for (ActivityManager.RunningAppProcessInfo rapi : ps) {
// if (rapi.processName.contains(NATIVE_PROCESS_NAME))
// // native application found
// return true;
// }
//
// }
// return false;
}
/*
* Internal class describing a screenshot.
*/
static final class Screenshot {
public Buffer pixels;
public int width;
public int height;
public int bpp;
public boolean isValid() {
if (pixels == null || pixels.capacity() == 0 || pixels.limit() == 0) return false;
if (width <= 0 || height <= 0) return false;
return true;
}
}
/*
* Determines whether the phone's screen is rotated.
*/
private int getScreenRotation() {
WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
Display disp = wm.getDefaultDisplay();
// check whether we operate under Android 2.2 or later
try {
Class> displayClass = disp.getClass();
Method getRotation = displayClass.getMethod("getRotation");
int rot = ((Integer)getRotation.invoke(disp)).intValue();
switch (rot) {
case Surface.ROTATION_0: return 0;
case Surface.ROTATION_90: return 90;
case Surface.ROTATION_180: return 180;
case Surface.ROTATION_270: return 270;
default: return 0;
}
} catch (NoSuchMethodException e) {
// no getRotation() method -- fall back to getOrientation()
int orientation = disp.getOrientation();
// Sometimes you may get undefined orientation Value is 0
// simple logic solves the problem compare the screen
// X,Y Co-ordinates and determine the Orientation in such cases
if(orientation==Configuration.ORIENTATION_UNDEFINED){
Configuration config = getResources().getConfiguration();
orientation = config.orientation;
if(orientation==Configuration.ORIENTATION_UNDEFINED){
//if height and widht of screen are equal then
// it is square orientation
if(disp.getWidth()==disp.getHeight()){
orientation = Configuration.ORIENTATION_SQUARE;
}else{ //if widht is less than height than it is portrait
if(disp.getWidth() < disp.getHeight()){
orientation = Configuration.ORIENTATION_PORTRAIT;
}else{ // if it is not any of the above it will defineitly be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
return orientation == 1 ? 0 : 90; // 1 for portrait, 2 for landscape
} catch (Exception e) {
return 0; // bad, I know ;P
}
}
/*
* Communicates with the native service and retrieves a screenshot from it
* as a 2D array of bytes.
*/
private Screenshot retreiveRawScreenshot() throws Exception {
try {
// connect to native application
Socket s = new Socket();
s.connect(new InetSocketAddress("localhost", PORT), TIMEOUT);
// send command to take screenshot
OutputStream os = s.getOutputStream();
os.write("SCREEN".getBytes("ASCII"));
// retrieve response -- first the size and BPP of the screenshot
InputStream is = s.getInputStream();
StringBuilder sb = new StringBuilder();
int c;
while ((c = is.read()) != -1) {
if (c == 0) break;
sb.append((char)c);
}
// parse it
String[] screenData = sb.toString().split(" ");
if (screenData.length >= 3) {
Screenshot ss = new Screenshot();
ss.width = Integer.parseInt(screenData[0]);
ss.height = Integer.parseInt(screenData[1]);
ss.bpp = Integer.parseInt(screenData[2]);
// retreive the screenshot
// (this method - via ByteBuffer - seems to be the fastest)
ByteBuffer bytes = ByteBuffer.allocate (ss.width * ss.height * ss.bpp / 8);
is = new BufferedInputStream(is); // buffering is very important apparently
is.read(bytes.array()); // reading all at once for speed
bytes.position(0); // reset position to the beginning of ByteBuffer
ss.pixels = bytes;
return ss;
}
}
catch (Exception e) {
throw new Exception(e);
}
finally {}
return null;
}
/*
* Saves given array of bytes into image file in the PNG format.
*/
private void writeImageFile(Screenshot ss, String file) {
if (ss == null || !ss.isValid()) throw new IllegalArgumentException();
if (file == null || file.length() == 0) throw new IllegalArgumentException();
// resolve screenshot's BPP to actual bitmap pixel format
Bitmap.Config pf;
switch (ss.bpp) {
case 16: pf = Config.RGB_565; break;
case 32: pf = Config.ARGB_8888; break;
default: pf = Config.ARGB_8888; break;
}
// create appropriate bitmap and fill it wit data
Bitmap bmp = Bitmap.createBitmap(ss.width, ss.height, pf);
bmp.copyPixelsFromBuffer(ss.pixels);
// handle the screen rotation
int rot = getScreenRotation();
if (rot != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(-rot);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
}
// save it in PNG format
FileOutputStream fos;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
throw new InvalidParameterException();
}
bmp.compress(CompressFormat.PNG, 100, fos);
}
/*
* Takes screenshot and saves to a file.
*/
private String takeScreenshot() throws IOException {
// make sure the path to save screens exists
File screensPath = new File(SCREENSHOT_FOLDER);
if (!screensPath.mkdirs()) return null;
// construct screenshot file name
StringBuilder sb = new StringBuilder();
sb.append(SCREENSHOT_FOLDER);
sb.append(Integer.toHexString(UUID.randomUUID().hashCode())); // hash code of UUID should be quite random yet short
sb.append(".png");
String file = sb.toString();
// fetch the screen and save it
Screenshot ss = null;
try {
ss = retreiveRawScreenshot();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
writeImageFile(ss, file);
return file;
}
}