Project: be.norio.twunch.android
/**
 * Copyright 2012 Norio bvba 
 * 
 * This program is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 *  
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 *  
 * You should have received a copy of the GNU General Public License 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
 
/*
 * Based on http://code.google.com/p/iosched/source/browse/android/src/com/google/android/apps/iosched/service/SyncService.java 
 */
 
 
package be.norio.twunch.android.service; 
 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.zip.GZIPInputStream; 
 
import org.apache.http.Header; 
import org.apache.http.HeaderElement; 
import org.apache.http.HttpEntity; 
import org.apache.http.HttpRequest; 
import org.apache.http.HttpRequestInterceptor; 
import org.apache.http.HttpResponse; 
import org.apache.http.HttpResponseInterceptor; 
import org.apache.http.client.HttpClient; 
import org.apache.http.entity.HttpEntityWrapper; 
import org.apache.http.impl.client.DefaultHttpClient; 
import org.apache.http.params.BasicHttpParams; 
import org.apache.http.params.HttpConnectionParams; 
import org.apache.http.params.HttpParams; 
import org.apache.http.params.HttpProtocolParams; 
import org.apache.http.protocol.HttpContext; 
 
import android.app.IntentService; 
import android.app.Service; 
import android.content.ContentResolver; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.PackageInfo; 
import android.content.pm.PackageManager; 
import android.content.pm.PackageManager.NameNotFoundException; 
import android.content.res.Resources; 
import android.os.Bundle; 
import android.os.ResultReceiver; 
import android.text.format.DateUtils; 
import android.util.Log; 
import be.norio.twunch.android.io.TwunchesHandler; 
import be.norio.twunch.android.util.PrefsUtils; 
 
import com.google.android.apps.iosched.io.RemoteExecutor; 
 
/**
 * Background {@link Service} that synchronizes data living in 
 * {@link ScheduleProvider}. Reads data from both local {@link Resources} and 
 * from remote sources, such as a spreadsheet. 
 */
 
public class SyncService extends IntentService { 
 private static final String TAG = SyncService.class.getSimpleName(); 
 
 private final static String URL = "http://twunch.be/events.xml?when=future"
 
 public static final String EXTRA_STATUS_RECEIVER = "be.norio.twunch.android.extra.STATUS_RECEIVER"
 
 public static final int STATUS_RUNNING = 0x1
 public static final int STATUS_ERROR = 0x2
 public static final int STATUS_FINISHED = 0x3
 
 private static final int SECOND_IN_MILLIS = (int) DateUtils.SECOND_IN_MILLIS; 
 
 private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"
 private static final String ENCODING_GZIP = "gzip"
 
 private RemoteExecutor mRemoteExecutor; 
 
 public SyncService() { 
  super(TAG); 
 
 
 @Override 
 public void onCreate() { 
  super.onCreate(); 
 
  final HttpClient httpClient = getHttpClient(this); 
  final ContentResolver resolver = getContentResolver(); 
 
  mRemoteExecutor = new RemoteExecutor(httpClient, resolver); 
 
 
 @Override 
 protected void onHandleIntent(Intent intent) { 
  Log.d(TAG, "onHandleIntent(intent=" + intent.toString() + ")"); 
 
  final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_STATUS_RECEIVER); 
  if (receiver != null
   receiver.send(STATUS_RUNNING, Bundle.EMPTY); 
 
  try { 
   final long startRemote = System.currentTimeMillis(); 
   mRemoteExecutor.executeGet(URL, new TwunchesHandler()); 
   Log.d(TAG, "Remote sync took " + (System.currentTimeMillis() - startRemote) + "ms"); 
   PrefsUtils.setLastUpdate(startRemote); 
  catch (Exception e) { 
   Log.e(TAG, "Problem while syncing", e); 
 
   if (receiver != null) { 
    // Pass back error to surface listener 
    final Bundle bundle = new Bundle(); 
    bundle.putString(Intent.EXTRA_TEXT, e.toString()); 
    receiver.send(STATUS_ERROR, bundle); 
   
  
 
  // Announce success to any surface listener 
  Log.d(TAG, "sync finished"); 
  if (receiver != null
   receiver.send(STATUS_FINISHED, Bundle.EMPTY); 
 
 
 /**
  * Generate and return a {@link HttpClient} configured for general use, 
  * including setting an application-specific user-agent string. 
  */
 
 public static HttpClient getHttpClient(Context context) { 
  final HttpParams params = new BasicHttpParams(); 
 
  // Use generous timeouts for slow mobile networks 
  HttpConnectionParams.setConnectionTimeout(params, 20 * SECOND_IN_MILLIS); 
  HttpConnectionParams.setSoTimeout(params, 20 * SECOND_IN_MILLIS); 
 
  HttpConnectionParams.setSocketBufferSize(params, 8192); 
  HttpProtocolParams.setUserAgent(params, buildUserAgent(context)); 
 
  final DefaultHttpClient client = new DefaultHttpClient(params); 
 
  client.addRequestInterceptor(new HttpRequestInterceptor() { 
   public void process(HttpRequest request, HttpContext context) { 
    // Add header to accept gzip content 
    if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) { 
     request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP); 
    
   
  }); 
 
  client.addResponseInterceptor(new HttpResponseInterceptor() { 
   public void process(HttpResponse response, HttpContext context) { 
    // Inflate any responses compressed with gzip 
    final HttpEntity entity = response.getEntity(); 
    final Header encoding = entity.getContentEncoding(); 
    if (encoding != null) { 
     for (HeaderElement element : encoding.getElements()) { 
      if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) { 
       response.setEntity(new InflatingEntity(response.getEntity())); 
       break
      
     
    
   
  }); 
 
  return client; 
 
 
 /**
  * Build and return a user-agent string that can identify this application to 
  * remote servers. Contains the package name and version code. 
  */
 
 private static String buildUserAgent(Context context) { 
  try { 
   final PackageManager manager = context.getPackageManager(); 
   final PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); 
 
   // Some APIs require "(gzip)" in the user-agent string. 
   return info.packageName + "/" + info.versionName + " (" + info.versionCode + ") (gzip)"
  catch (NameNotFoundException e) { 
   return null
  
 
 
 /**
  * Simple {@link HttpEntityWrapper} that inflates the wrapped 
  * {@link HttpEntity} by passing it through {@link GZIPInputStream}. 
  */
 
 private static class InflatingEntity extends HttpEntityWrapper { 
  public InflatingEntity(HttpEntity wrapped) { 
   super(wrapped); 
  
 
  @Override 
  public InputStream getContent() throws IOException { 
   return new GZIPInputStream(wrappedEntity.getContent()); 
  
 
  @Override 
  public long getContentLength() { 
   return -1
  
 
 
}