/* * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.apps.iosched.service; import android.app.Service; import android.content.Intent; import android.os.*; import com.google.android.apps.iosched.Config; import com.google.android.apps.iosched.gcm.ServerUtilities; import com.google.android.apps.iosched.sync.SyncHelper; import com.google.android.apps.iosched.util.AccountUtils; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.turbomanage.httpclient.AsyncCallback; import com.turbomanage.httpclient.HttpResponse; import com.turbomanage.httpclient.ParameterMap; import com.turbomanage.httpclient.android.AndroidHttpClient; import static com.google.android.apps.iosched.util.LogUtils.*; /** * Background {@link android.app.Service} that adds or removes sessions from your calendar via the * Conference API. * * @see com.google.android.apps.iosched.sync.SyncHelper */ public class ScheduleUpdaterService extends Service { private static final String TAG = makeLogTag(ScheduleUpdaterService.class); public static final String EXTRA_SESSION_ID = "com.google.android.apps.iosched.extra.SESSION_ID"; public static final String EXTRA_IN_SCHEDULE = "com.google.android.apps.iosched.extra.IN_SCHEDULE"; private static final int SCHEDULE_UPDATE_DELAY_MILLIS = 5000; private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private final LinkedList<Intent> mScheduleUpdates = new LinkedList<Intent>(); // Handler pattern copied from IntentService private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { processPendingScheduleUpdates(); int numRemainingUpdates; synchronized (mScheduleUpdates) { numRemainingUpdates = mScheduleUpdates.size(); } if (numRemainingUpdates == 0) { notifyGcmDevices(); stopSelf(); } else { // More updates were added since the current pending set was processed. Reschedule // another pass. removeMessages(0); sendEmptyMessageDelayed(0, SCHEDULE_UPDATE_DELAY_MILLIS); } } } // private static class NotifyGcmDevicesTask extends AsyncTask<String, Void, Void> { // // @Override // protected Void doInBackground(String... params) { // BasicHttp // String gPlusID = params[0]; // Uri uri = new Uri(Config.GCM_SERVER_URL + "/send/" + gPlusID + "/sync_user"); // connection = (HttpURLConnection) url.openConnection(); // connection.setDoOutput(true); // connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // connection.setRequestMethod("POST"); // // request = new OutputStreamWriter(connection.getOutputStream()); // request.write(parameters); // request.flush(); // request.close(); // // } // } private void notifyGcmDevices() { String plusID = AccountUtils.getPlusProfileId(getApplicationContext()); if (plusID != null) { LOGI(TAG, "Sending device sync notification"); AndroidHttpClient httpClient = new AndroidHttpClient(Config.GCM_SERVER_URL); httpClient.setMaxRetries(1); ParameterMap params = httpClient.newParams() .add("key", Config.GCM_API_KEY) .add("squelch", ServerUtilities.getGcmId(this)); String path = "/send/" + plusID + "/sync_user"; httpClient.post(path, params, new AsyncCallback() { @Override public void onComplete(HttpResponse httpResponse) { LOGI(TAG, "Device sync notification sent"); } @Override public void onError(Exception e) { LOGW(TAG, "Device sync notification failed", e); } }); } else { LOGI(TAG, "No gPlusID, skipping device sync notification"); } } public ScheduleUpdaterService() { } @Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread(ScheduleUpdaterService.class.getSimpleName()); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // When receiving a new intent, delay the schedule until 5 seconds from now. mServiceHandler.removeMessages(0); mServiceHandler.sendEmptyMessageDelayed(0, SCHEDULE_UPDATE_DELAY_MILLIS); // Remove pending updates involving this session ID. String sessionId = intent.getStringExtra(EXTRA_SESSION_ID); Iterator<Intent> updatesIterator = mScheduleUpdates.iterator(); while (updatesIterator.hasNext()) { Intent existingIntent = updatesIterator.next(); if (sessionId.equals(existingIntent.getStringExtra(EXTRA_SESSION_ID))) { updatesIterator.remove(); } } // Queue this schedule update. synchronized (mScheduleUpdates) { mScheduleUpdates.add(intent); } return START_REDELIVER_INTENT; } @Override public void onDestroy() { mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) { return null; } void processPendingScheduleUpdates() { try { // Operate on a local copy of the schedule update list so as not to block // the main thread adding to this list List<Intent> scheduleUpdates = new ArrayList<Intent>(); synchronized (mScheduleUpdates) { scheduleUpdates.addAll(mScheduleUpdates); mScheduleUpdates.clear(); } SyncHelper syncHelper = new SyncHelper(this); for (Intent updateIntent : scheduleUpdates) { String sessionId = updateIntent.getStringExtra(EXTRA_SESSION_ID); boolean inSchedule = updateIntent.getBooleanExtra(EXTRA_IN_SCHEDULE, false); LOGI(TAG, "addOrRemoveSessionFromSchedule:" + " sessionId=" + sessionId + " inSchedule=" + inSchedule); syncHelper.addOrRemoveSessionFromSchedule(this, sessionId, inSchedule); } } catch (IOException e) { // TODO: do something useful here, like revert the changes locally in the // content provider to maintain client/server sync LOGE(TAG, "Error processing schedule update", e); } } }