package com.steamcommunity.siplus.steamscreenshots;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.provider.MediaStore;
public class TakeService extends Service {
static final String EXTRA_ACCOUNT = Utility.PACKAGE + ".TakeService.EXTRA_ACCOUNT";
static final int NOTIFICATION_FAILURE = R.string.notification_failure;
static final int NOTIFICATION_SUCCESS = R.string.notification_success;
static final int NOTIFICATION_TAKE = R.string.notification_take;
SteamshotsAccount mAccount;
ActivityManager mActivityManager;
Context mApplicationContext;
ContentResolver mContentResolver;
Cursor mCursor;
NotificationManager mNotificationManager;
TakeContentObserver mObserver;
int mOldCount;
PackageManager mPackageManager;
Resources mResources;
String mToBeUpdated;
@SuppressWarnings("deprecation")
void addScreenshot(String source) {
List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
if (tasks.isEmpty()) {
return;
}
String packageName = tasks.get(0).topActivity.getPackageName();
if (packageName.equals(Utility.PACKAGE)) {
return;
}
mNotificationManager.cancel(NOTIFICATION_SUCCESS);
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_stat_notify);
String game = Utility.applicationLabel(mPackageManager, packageName);
int name = addScreenshotInner(source, packageName);
Resources resources = mResources;
if (name != 0) {
builder
.setContentTitle(resources.getString(R.string.notification_success))
.setContentText(resources.getString(R.string.notification_success_info, game))
.setLargeIcon(notificationBitmap(name, packageName));
} else {
builder
.setContentTitle(resources.getString(R.string.notification_failure))
.setContentText(resources.getString(R.string.notification_failure_info, game));
}
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notification = builder.build();
} else {
notification = builder.getNotification();
}
mNotificationManager.notify(NOTIFICATION_SUCCESS, notification);
}
@SuppressWarnings("resource")
int addScreenshotInner(String source, String packageName) {
Calendar date = Calendar.getInstance();
int dateYear = date.get(Calendar.YEAR) - 2006;
if ((dateYear < 0) || (dateYear > 31)) {
return 0;
}
long steamID = mAccount.mSteamID;
String path = ScreenshotName.folderPath(steamID, packageName);
File pathFile = new File(path);
if (!Utility.makeDirectories(pathFile)) {
return 0;
}
int dateInt = (date.get(Calendar.DAY_OF_MONTH) << ScreenshotName.DAY_SHIFT) |
((date.get(Calendar.MONTH) + 1) << ScreenshotName.MONTH_SHIFT) |
(dateYear << ScreenshotName.YEAR_SHIFT);
ScreenshotFileFilter filter = new ScreenshotFileFilter(dateInt);
pathFile.listFiles(filter);
int highest = filter.mHighest;
int highestUploaded = UploadedCaption.highestToDateSorted(
UploadedCaption.fromFileSorted(steamID, packageName), dateInt);
if (highestUploaded > highest) {
highest = highestUploaded;
}
int targetNameInt = dateInt | ((highest + 1) % 100000);
String targetName = ScreenshotName.nameToString(targetNameInt);
String targetPath = path + targetName;
Bitmap bitmap = BitmapFactory.decodeFile(source);
if (bitmap == null) {
return 0;
}
FileOutputStream targetFile;
try {
targetFile = new FileOutputStream(targetPath);
} catch (FileNotFoundException e) {
bitmap.recycle();
return 0;
}
int height;
int width;
try {
height = bitmap.getHeight();
width = bitmap.getWidth();
if ((height == 0) || (width == 0)) {
throw new IOException();
}
if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 90, targetFile)) {
throw new IOException();
}
targetFile.flush();
Utility.closeCloseable(targetFile);
} catch (IOException e) {
bitmap.recycle();
Utility.closeCloseable(targetFile);
return 0;
}
if ((height > 200) || (width > 200)) {
Bitmap thumb;
if (height > width) {
width = (int)(width / (height / 200.0F));
if (width == 0) {
width = 1;
}
thumb = Bitmap.createScaledBitmap(bitmap, width, 200, true);
} else {
height = (int)(height / (width / 200.0F));
if (height == 0) {
height = 1;
}
thumb = Bitmap.createScaledBitmap(bitmap, 200, height, true);
}
bitmap.recycle();
bitmap = thumb;
}
try {
targetFile = new FileOutputStream(targetPath + ScreenshotName.THUMB_SUFFIX);
} catch (FileNotFoundException e) {
bitmap.recycle();
return 0;
}
try {
if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 95, targetFile)) {
throw new IOException();
}
targetFile.flush();
Utility.closeCloseable(targetFile);
} catch (IOException e) {
bitmap.recycle();
Utility.closeCloseable(targetFile);
return 0;
}
long imageDate = System.currentTimeMillis();
ContentValues values = new ContentValues(7);
values.put(MediaStore.Images.ImageColumns.DATA, targetPath);
values.put(MediaStore.Images.ImageColumns.DATE_ADDED, imageDate);
values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, imageDate);
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, imageDate);
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, targetName);
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.ImageColumns.TITLE, targetName);
values.put(MediaStore.Images.ImageColumns.SIZE, (new File(targetPath)).length());
mContentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
MediaStore.Images.ImageColumns.DATA + "=?", new String[] {targetPath}); // If deleted externally
mContentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
return targetNameInt;
}
void mediaStoreChanged() {
Cursor cursor = queryMediaStore();
if (cursor == null) {
return;
}
int oldCount = mOldCount;
int count = cursor.getCount();
mOldCount = count;
if (count == 0) {
mToBeUpdated = null;
cursor.close();
return;
}
String rowPath;
int rowSize;
// Responding to inserts
if (count > oldCount) {
cursor.moveToLast();
rowPath = cursor.getString(0);
if (rowPath == null) {
cursor.close();
return;
}
rowSize = cursor.getInt(1);
if (rowSize <= 0) {
// On 4.2.2, .insert is called with an empty image with 0 size,
// and when the actual image is saved, .update is called with correct size,
// so we wait for .update with that name.
mToBeUpdated = rowPath;
cursor.close();
return;
}
cursor.close();
addScreenshot(rowPath);
return;
}
// Reponding to possible updates, from the last to the first
String toBeUpdated = mToBeUpdated;
if ((toBeUpdated != null) && (count == oldCount)) {
for (cursor.moveToLast(); count-- > 0; cursor.moveToPrevious()) {
rowPath = cursor.getString(0);
if (!toBeUpdated.equals(rowPath)) {
continue;
}
rowSize = cursor.getInt(1);
if (rowSize <= 0) { // Not updated
cursor.close();
return;
}
mToBeUpdated = null;
cursor.close();
addScreenshot(rowPath);
return;
}
mToBeUpdated = null; // The screenshot is gone from the MediaStore
}
cursor.close();
}
Bitmap notificationBitmap(int name, String packageName) {
Bitmap source = BitmapFactory.decodeFile(
ScreenshotName.folderPath(mAccount.mSteamID, packageName) +
ScreenshotName.nameToString(name) + ScreenshotName.THUMB_SUFFIX);
if (source == null) {
return null;
}
int height = source.getHeight();
int width = source.getWidth();
if ((height == 0) || (width == 0)) {
source.recycle();
return null;
}
int size = (int)(64.0F * mResources.getDisplayMetrics().density);
if ((height > size) || (width > size)) {
Bitmap thumb;
if (height > width) {
height = (int)(height / (width / (float)(size)));
if (height == 0) {
height = 1;
}
width = size;
thumb = Bitmap.createScaledBitmap(source, size, height, true);
} else {
width = (int)(width / (height / (float)(size)));
if (width == 0) {
width = 1;
}
height = size;
thumb = Bitmap.createScaledBitmap(source, width, size, true);
}
source.recycle();
if (thumb == null) {
return null;
}
source = thumb;
}
Bitmap target = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0.25F);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(source, (size - width) / 2.0F, (size - height) / 2.0F, paint);
source.recycle();
canvas.drawColor(0x40ffffff);
return target;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
stopForeground(true);
if (mCursor != null) {
mCursor.unregisterContentObserver(mObserver);
mCursor.close();
}
}
@SuppressWarnings("deprecation")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
if ((extras == null) || !extras.containsKey(EXTRA_ACCOUNT)) {
stopSelf();
return START_NOT_STICKY;
}
mAccount = extras.getParcelable(EXTRA_ACCOUNT);
mContentResolver = getContentResolver();
mCursor = queryMediaStore();
if (mCursor == null) {
stopSelf();
return START_NOT_STICKY;
}
mOldCount = mCursor.getCount();
mActivityManager = (ActivityManager)(getSystemService(ACTIVITY_SERVICE));
mApplicationContext = getApplicationContext();
mNotificationManager = (NotificationManager)(getSystemService(NOTIFICATION_SERVICE));
mPackageManager = getPackageManager();
Resources resources = getResources();
mResources = resources;
mObserver = new TakeContentObserver(this);
mCursor.registerContentObserver(mObserver);
Intent screenshotsIntent = new Intent(mApplicationContext, ScreenshotsActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra(ScreenshotsActivity.EXTRASTATE_ACCOUNT, mAccount);
Notification.Builder builder = new Notification.Builder(this)
.setContentIntent(PendingIntent.getActivity(mApplicationContext, 0,
screenshotsIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setContentTitle(resources.getString(R.string.notification_take, mAccount.mName))
.setContentText(resources.getString(R.string.notification_take_info))
.setSmallIcon(R.drawable.ic_stat_notify)
.setOngoing(true)
.setWhen(0L);
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notification = builder.build();
} else {
notification = builder.getNotification();
}
startForeground(NOTIFICATION_TAKE, notification);
return START_NOT_STICKY;
}
Cursor queryMediaStore() {
return mContentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.SIZE},
MediaStore.Images.ImageColumns.TITLE + " LIKE 'Screenshot\\_____-__-__-__-__-__.__g' ESCAPE '\\'",
null,
MediaStore.Images.ImageColumns.DATE_ADDED + " ASC");
}
}
class TakeContentObserver extends ContentObserver {
TakeService mService;
public TakeContentObserver(TakeService service) {
super(new Handler());
mService = service;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
mService.mediaStoreChanged();
}
}