package cn.zadui.reader.service;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.mcsoxford.rss.RSSFeed;
import org.mcsoxford.rss.RSSItem;
import org.mcsoxford.rss.RSSReader;
import org.mcsoxford.rss.RSSReaderException;
import android.app.Service;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import cn.zadui.reader.R;
import cn.zadui.reader.helper.NetHelper;
import cn.zadui.reader.helper.RssHelper;
import cn.zadui.reader.helper.Settings;
import cn.zadui.reader.helper.StorageHelper;
import cn.zadui.reader.provider.ReaderArchive.Archives;
/**
*
* @author David
*
*/
public class DownloadService extends Service {
public static final String TRIGGER="TriggerBy";
static final String[] PROJECTION={Archives._ID,Archives.GUID};
static final String TAG="DownloadService";
public static StateListener listener;
public static boolean isRunning=false;
private StorageHelper storageHelper;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
/*
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
return START_STICKY;
}
*/
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
private void handleCommand(Intent intent){
String trigger=null;
if(intent.getExtras()!=null){
trigger=intent.getExtras().getString(TRIGGER);
if (trigger!=null) Log.d(TAG,"Service lauched by => "+trigger);
}
if(isRunning) return;
int netType=NetHelper.currentNetwork(getBaseContext());
if (netType<0){
if(listener!=null) listener.onStateChanged(ServiceState.ERROR,getString(R.string.no_network_available));
return;
}
storageHelper=new StorageHelper(getPackageName());
(new DownloadThread(netType,trigger)).start();
}
@Override
public void onDestroy(){
storageHelper=null;
super.onDestroy();
}
public enum ServiceState {
WORKING, FINISHED, ERROR, STOP;
}
public interface StateListener{
public void onStateChanged(ServiceState state,String info);
}
/**
* Download zip pkg from remote server and unzip.
* @param item
* @param buffer
* @return true if download and unzip successfully or false if failed
* @throws DownloadException
*/
private boolean handleZipPkg(RSSItem item,byte[] buffer) throws DownloadException{
if (!StorageHelper.isSdcardWritable()) return false;
// Download the pkg.zip and extract it.
int len=0;
long st=System.currentTimeMillis();
Log.d(TAG,"Begin download zip file==>"+String.valueOf(st));
String zipFileName=item.getGuid()+"_"+Settings.getStringPreferenceValue(this, Settings.PRE_IMAGE_QUALITY, Settings.DEF_IMAGE_QUALITY)+".zip";
File targetZip=new File(storageHelper.getArchivesDirInSdcard(),zipFileName);
try {
//Url
String pkgUrl=item.getZipPkgUrl();
pkgUrl=pkgUrl.substring(0,pkgUrl.lastIndexOf('/')+1)+zipFileName;
HttpURLConnection con=NetHelper.buildUrlConnection(item.getZipPkgUrl());
con.connect();
FileOutputStream out=new FileOutputStream(targetZip);
InputStream in=con.getInputStream();
len=0;
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
out.close();
in.close();
con.disconnect();
} catch (IOException e) {
Log.e(TAG,"Downloa zip file error");
e.printStackTrace();
throw new DownloadException(e);
}
long a=(System.currentTimeMillis()-st)/1000;
Log.d(TAG,"Download takes "+String.valueOf(a)+" secondes");
Log.d(TAG,"Finished download zip file, then unzip it");
// Unzip the downloaded pkg.zip file
try {
ZipFile zip=new ZipFile(targetZip);
Enumeration<?> entries = zip.entries();
while(entries.hasMoreElements()){
ZipEntry entry=(ZipEntry)entries.nextElement();
if(entry.isDirectory()){
new File(storageHelper.getArchivesDirInSdcard(),entry.getName()).mkdirs();
continue;
}
BufferedInputStream bis=new BufferedInputStream(zip.getInputStream(entry),8*1024);
File img=new File(storageHelper.getArchivesDirInSdcard(),entry.getName());
Log.d(TAG,"Unzip file => "+img.getPath());
File parent=img.getParentFile();
if(parent!=null && !parent.exists()) parent.mkdirs();
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(img),8*1024);
len=0;
while((len=bis.read(buffer))>0) bos.write(buffer, 0, len);
bos.flush();
bos.close();
bis.close();
Thread.sleep(30);
}
zip.close();
// delete target zip file
targetZip.delete();
} catch (ZipException e) {
e.printStackTrace();
throw new DownloadException(e);
} catch (IOException e) {
e.printStackTrace();
throw new DownloadException(e);
} catch (InterruptedException e) {
e.printStackTrace();
throw new DownloadException(e);
}
return true;
}
/**
* TODO Remove hard code 'thumb96' here.
* @param item
* @param buffer
* @return
* @throws DownloadException
*/
private String downloadThumbnail(RSSItem item,byte[] buffer) throws DownloadException{
File thumb=new File(new File(storageHelper.getArchivesDirInSdcard(),String.valueOf(item.getGuid())),"thumb96");
InputStream in=null;
FileOutputStream out=null;
try {
HttpURLConnection con=NetHelper.buildUrlConnection(item.getThumbUrl());
con.connect();
in=con.getInputStream();
out=new FileOutputStream(thumb);
int len=0;
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
out.close();
in.close();
} catch (IOException e) {
Log.e(TAG,"Download thumb error");
e.printStackTrace();
throw new DownloadException(e);
}
return thumb.getAbsolutePath();
}
/**
* There are two kinds of download action, one is triggered by user click refresh button
* The other is started background.
* @author david
*
*/
private class DownloadThread extends Thread{
private int networkType;
private String trigger;
public DownloadThread(int netType,String trigger){
networkType=netType;
this.trigger=trigger;
}
@Override
public void run(){
Log.i(TAG,"Beggin download process");
isRunning=true;
if (listener!=null) listener.onStateChanged(ServiceState.WORKING,"");
if (networkType!=ConnectivityManager.TYPE_WIFI && Settings.getBooleanPreferenceValue(DownloadService.this, Settings.PRE_WIFI_ONLY, false)){
//TODO use fix text hard code here
if(listener!=null) listener.onStateChanged(ServiceState.ERROR,DownloadService.this.getString(R.string.please_use_wifi));
// UsageCollector.uploadCollectedUsageDate(DownloadService.this.getApplicationContext());
// checkNewVersion();
listener=null;
isRunning=false;
DownloadService.this.stopSelf();
return;
}
Log.d(TAG,"DDDDDDDDDDDDDDDDDDDDDZZZZZZZZZZZZZZZZDD"+trigger);
if (trigger.equals("Activate")){
UsageCollector.nofityInstalled(DownloadService.this.getApplicationContext());
}
// upload collected data to Server
UsageCollector.uploadCollectedUsageDate(DownloadService.this.getApplicationContext());
// upload user comments to server if had
UsageCollector.uploadUserComment(DownloadService.this.getApplicationContext());
// Check new version
NetHelper.checkNewVersion(DownloadService.this.getApplicationContext());
// check archives
String feed_url=NetHelper.webPath("http", "/archives/feed.xml");
RSSReader reader = new RSSReader();
RSSFeed feed;
byte[] buffer=new byte[8*1024];
try {
feed = reader.load(feed_url);
feed.getPubDate();
if (feed.getPubDate().toGMTString().equals(Settings.getLastFeedPubDate(DownloadService.this))){
Log.d(TAG,"No updates of feed xml");
isRunning=false;
if(listener!=null) listener.onStateChanged(ServiceState.FINISHED,DownloadService.this.getString(R.string.no_new_contents));
listener=null;
DownloadService.this.stopSelf();
return;
}
// Fetch all GUIDs of archive
Cursor cursor = DownloadService.this.getContentResolver().query(Archives.CONTENT_URI, PROJECTION, null, null,
Archives.DEFAULT_SORT_ORDER);
HashSet<Long> guids=new HashSet<Long>();
while(cursor.moveToNext()){
guids.add(cursor.getLong(cursor.getColumnIndex(Archives.GUID)));
}
cursor.close();
Log.d(TAG,"Items size is ==> "+String.valueOf(feed.getItems().size()));
// Store items in feeder to db
for (Iterator<RSSItem> iter = feed.getItems().iterator(); iter.hasNext();) {
//Did this item already existed?
RSSItem item=iter.next();
Log.d(TAG,"Item in feed ==>"+item.getTitle());
if (guids.contains(Long.valueOf(item.getGuid()))) continue;
ContentValues cv=RssHelper.feedItemToContentValues(item);
if(handleZipPkg(item,buffer)){
cv.put(Archives.CAHECED, true);
//cv.put(Archives.THUMB_URL, "");
}else{
// download thumb image
String localThumb=downloadThumbnail(item,buffer);
if (localThumb!=null) cv.put(Archives.THUMB_URL, localThumb);
}
Uri mUri = DownloadService.this.getContentResolver().insert(Archives.CONTENT_URI, cv);
Log.d(TAG,"Get a new archive");
}
// Update feed time stamp
Settings.updateLastFeedPubDate(DownloadService.this,feed.getPubDate().toGMTString());
reader.close();
} catch (RSSReaderException e) {
if(listener!=null) listener.onStateChanged(ServiceState.ERROR,e.getMessage());
e.printStackTrace();
} catch(DownloadException de){
if(listener!=null) listener.onStateChanged(ServiceState.ERROR,DownloadService.this.getString(R.string.download_error));
de.printStackTrace();
} catch(Exception ce){
if(listener!=null) listener.onStateChanged(ServiceState.ERROR,ce.getMessage());
ce.printStackTrace();
}
//delete old items
Cursor oldItems=DownloadService.this.getContentResolver().query(Archives.OLD_ARCHIVES_URI, PROJECTION, null, null,
Archives.DEFAULT_SORT_ORDER);
if(oldItems!=null){
while(oldItems.moveToNext()){
long guid=oldItems.getLong(oldItems.getColumnIndex(Archives.GUID));
// Delete item in db
DownloadService.this.getContentResolver().delete(ContentUris.withAppendedId(Archives.ARCHIVE_GUID_URI,guid),null,null);
// Delete folder in sdcard
StorageHelper.deleteDirectory(storageHelper.getArchiveDir(guid));
}
oldItems.close();
}
// update next sync time
Settings.updateSyncJob(DownloadService.this.getBaseContext());
Log.i(TAG, "After update sync job");
isRunning=false;
if(listener!=null) listener.onStateChanged(ServiceState.FINISHED,"");
listener=null;
DownloadService.this.stopSelf();
}
}
}