package com.eyeem.theroll.service;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.location.Address;
import android.location.Geocoder;
import android.media.ExifInterface;
import android.os.IBinder;
import android.provider.MediaStore;
import com.eyeem.theroll.model.Photo;
import com.eyeem.theroll.storage.PhotoStorage;
import com.eyeem.theroll.utils.Log;
import net.jakobnielsen.imagga.client.APIClientConfig;
import net.jakobnielsen.imagga.color.bean.ColorResult;
import net.jakobnielsen.imagga.color.bean.ExtendedColor;
import net.jakobnielsen.imagga.color.client.ColorAPIClient;
import net.jakobnielsen.imagga.upload.client.UploadClient;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: vishna
* Date: 4/6/13
* Time: 12:06 PM
* To change this template use File | Settings | File Templates.
*/
public class Scanner extends Service {
public final static String ACTION_SCAN = "scan_files";
public PhotoStorage storage;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
storage = PhotoStorage.getInstance();
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
public void handleCommand(Intent intent) {
if (intent != null && ACTION_SCAN.equals(intent.getAction())) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
scanPhotos();
PhotoStorage.all.save();
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
}
private void scanPhotos() {
// TODO scan photos here
Log.i(this, "scanPhotos");
ContentResolver cr = getContentResolver();
final String[] columns = {
//MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.TITLE, MediaStore.Images.Media.DATE_TAKEN,
//MediaStore.Images.Media.SIZE, MediaStore.Images.Media.LATITUDE, MediaStore.Images.Media.LONGITUDE, "width", "height"
};
Cursor cursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
cursor.moveToPosition(-1);
int processedCount = 0;
while (cursor.moveToNext()) {
String id = String.valueOf(cursor.getInt((cursor.getColumnIndex(MediaStore.Images.Media._ID))));
String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
if (PhotoStorage.getInstance().contains(id)) {
// already have this
// FIXME clear storage on model update
continue;
}
Photo photo = process(filePath, id);
Log.i(this,"filePath: "+photo.filePath);
/*String width = cursor.getString(cursor.getColumnIndex("width"));
String height = cursor.getString(cursor.getColumnIndex("height"));
if(width!=null)
photo.width = Integer.parseInt(width);
if(height!=null)
photo.height = Integer.parseInt(height);*/
if (photo != null) {
PhotoStorage.all.add(photo);
}
if (processedCount > 0 && processedCount % 20 == 0) {
// save photos every 20 pics
PhotoStorage.all.save();
}
processedCount++;
if (processedCount > 150) {
// TMP
break;
}
}
cursor.close();
}
private Photo process(String filePath, String id) {
Log.i(this, "scanPhotos process("+filePath+")");
Photo photo = new Photo();
photo.id = id;
photo.filePath = filePath;
try {
//GET basic metadata (lat, lng, width, height, timestamp)
ExifInterface newExif=null;
newExif = new ExifInterface(filePath);
if(newExif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) != null && newExif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE) != null){
try{
photo.lat = ExifLatLong(newExif.getAttribute(ExifInterface.TAG_GPS_LATITUDE),newExif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
photo.lon = ExifLatLong(newExif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE),newExif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
Geocoder gcd = new Geocoder(this, Locale.getDefault());
List<Address> addresses = gcd.getFromLocation(photo.lat, photo.lon, 1);
if (addresses.size() > 0){
photo.city = addresses.get(0).getLocality();
photo.country = addresses.get(0).getCountryName();
}
getTimeOfDay(newExif.getAttribute(ExifInterface.TAG_DATETIME), photo);
if(photo.width==0){
photo.width = Integer.parseInt(newExif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH));
}
if(photo.height==0){
photo.height = Integer.parseInt(newExif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH));
}
}catch (Exception e) {
Log.e(this, "process() error", e);
}
}
//GET inferred data (day, time of day, city, country)
} catch (Throwable e){
}
try {
photo.colors = colorQuery(filePath);
} catch (Throwable e) {
//Log.e(this, "scanPhotos colorQuery("+filePath+") error", e);
}
printPhoto(photo);
return photo;
}
APIClientConfig IMAGA_CONFIG = new APIClientConfig("acc_b6c25fc6", "7c0765b6ce790023f06cfa18a7b47790", "78.128.78.162");
private ArrayList<String> colorQuery(String filePath) throws IOException {
ColorAPIClient client = new ColorAPIClient(IMAGA_CONFIG);
UploadClient uploadClient = new UploadClient(IMAGA_CONFIG);
List<ColorResult> colorResults = client.colorsByUploadCode(uploadClient.uploadForProcessing(new File(filePath)));
ArrayList<String> colorsInt = new ArrayList<String>();
for (ColorResult res : colorResults) {
for (ExtendedColor color : res.getInfo().getImageColors()) {
Log.i(this, "scanPhotos closest parent: "+color.getClosestPaletteColorParent()+" w/ percent:"+color.getPercent());
if(color.getPercent()>35.0){
if(!color.getClosestPaletteColorParent().equals("black") && !color.getClosestPaletteColorParent().equals("grey") && !color.getClosestPaletteColorParent().equals("white")){
//TODO: this might actually be adding the same parent twice based on the child.
colorsInt.add(color.getClosestPaletteColorParent());
Log.i(this, "scanPhotos adding color: "+color.getClosestPaletteColorParent());
}
}
}
}
return colorsInt;
}
public float ExifLatLong(String value, String ref) throws Exception {
Float result = null;
String[] DMS = value.split(",", 3);
String[] stringD = DMS[0].split("/", 2);
Double D0 = Double.valueOf(stringD[0]);
Double D1 = Double.valueOf(stringD[1]);
Double FloatD = D0 / D1;
String[] stringM = DMS[1].split("/", 2);
Double M0 = Double.valueOf(stringM[0]);
Double M1 = Double.valueOf(stringM[1]);
Double FloatM = M0 / M1;
String[] stringS = DMS[2].split("/", 2);
Double S0 = Double.valueOf(stringS[0]);
Double S1 = Double.valueOf(stringS[1]);
Double FloatS = S0 / S1;
result = new Float(FloatD + (FloatM / 60) + (FloatS / 3600));
if (ref.equals("N") || ref.equals("E")) {
return result;
} else {
return 0 - result;
}
}
public void getTimeOfDay(String dateTime,Photo photo){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
Date convertedDate = new Date();
try {
convertedDate = dateFormat.parse(dateTime);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
photo.timestamp = convertedDate.getTime();
dateFormat.applyPattern("EEEE");
photo.day = dateFormat.format(convertedDate);
dateFormat.applyPattern("HH");
int hour = Integer.parseInt(dateFormat.format(convertedDate));
if(hour >=0 && hour <= 6)
photo.timeOfDay = "Night";
else if(hour >6 && hour < 12)
photo.timeOfDay = "Morning";
else if(hour >=12 && hour < 18)
photo.timeOfDay = "Afternoon";
else
photo.timeOfDay = "Evening";
}
public void printPhoto(Photo p){
Log.i(this,"scanPhoto width:"+p.width);
Log.i(this,"scanPhoto height:"+p.height);
Log.i(this,"scanPhoto lat:"+p.lat);
Log.i(this,"scanPhoto lon:"+p.lon);
Log.i(this,"scanPhoto city:"+p.city);
Log.i(this,"scanPhoto country:"+p.country);
Log.i(this,"scanPhoto time:"+p.timeOfDay);
Log.i(this,"scanPhoto day:"+p.day);
}
}