BookmarkService.java :  » Client » beelicious » net » bitquill » delicious » Android Open Source

Android Open Source » Client » beelicious 
beelicious » net » bitquill » delicious » BookmarkService.java
/**
 * Copyright 2009 Spiros Papadimitriou <spapadim@cs.cmu.edu>
 * 
 * This file is part of Bitlicious.
 * 
 * Bitlicious 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.
 * 
 * Bitlicious 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 Bitlicious.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.bitquill.delicious;

import net.bitquill.delicious.api.Bookmark;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.Log;

/**
 * Simple service for background tag cache refresh and background bookmark submission. 
 */
public class BookmarkService extends Service {
    private static final String TAG = BookmarkService.class.getSimpleName();
    
    private NotificationManager mNotificationManager;
    private ConnectivityManager mConnectivityManager;
    private TelephonyManager mTelephonyManager;
    
    private boolean mSyncActive;
    
    /**
     * Cancel repeating tag sync alarms; won't actually cancel an ongoing sync.
     * @param context
     */
    public static void syncCancel (Context context) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
        Intent intent = new Intent(context, BookmarkService.class);
        intent.setAction(DeliciousApp.ACTION_SYNC_TAGS);
        alarmManager.cancel(PendingIntent.getService(context, 0, intent, 0));
    }
    
    /**
     * Register an alarm for periodic tag syncing in the background.  Will clear any
     * existing alarms and register a new inexact alarm with the given interval.
     * @param context
     * @param interval
     */
    public static void syncSchedule (Context context, long interval) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
        
        // If we set firstWake equal to current time plus interval (instead of plus a second),
        // the alarm may never trigger if syncSchedule (which is called on app startup)
        // is invoked more often than the interval.
        // For inexact repeating alarms, firstWake is the time *before* which the alarm
        // is guaranteed to not be triggered, but the alarm may actuall be triggered 
        // with a delay of up to a whole interval after the requested firstWake.
        // Therefore, setting firstWake to a second in the future does not mean the alarm
        // will be triggered at that time; it just means that we want it to trigger in the
        // next inexact time bucket; this is sort of a hack, but it seems to work...
        long firstWake = System.currentTimeMillis() + DateUtils.SECOND_IN_MILLIS;
        
        Intent intent = new Intent(context, BookmarkService.class);
        intent.setAction(DeliciousApp.ACTION_SYNC_TAGS);
        PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
        alarmManager.cancel(pending); // Make sure existing alarms are cleared
        alarmManager.setInexactRepeating(AlarmManager.RTC, 
                firstWake, interval, pending);
    }
    
    /**
     * Register or cancel alarm for periodic tag sync, depending on preference value.
     * @param context
     */
    public static void syncUpdate (Context context) {
        SharedPreferences settings = 
            context.getSharedPreferences(DeliciousApp.PREFS_NAME, MODE_PRIVATE);
        int intervalInHours = Integer.parseInt(settings.getString(SettingsActivity.PREF_TAG_BG_SYNC, "24"));
        if (intervalInHours > 0) {
            syncSchedule(context, DateUtils.HOUR_IN_MILLIS * intervalInHours);
        } else {
            syncCancel(context);
        }
    }
    
    /**
     * Initiate a tag sync in the background.
     * @param context
     * @param force Set to true if sync desired even on 2G networks.
     */
    public static void actionSyncTags (Context context, boolean force) {
        Intent intent = new Intent(context, BookmarkService.class);
        intent.setAction(DeliciousApp.ACTION_SYNC_TAGS);
        if (force) {
            intent.putExtra(DeliciousApp.EXTRA_FORCE_SYNC, true);
        }
        context.startService(intent);
    }
    
    /**
     * Submit a bookmark to Delicious in the background.
     * @param context
     * @param bookmark
     * @param shared
     */
    public static void actionSubmitBookmark (Context context, Bookmark bookmark, boolean shared) {
        Intent intent = new Intent(context, BookmarkService.class);
        intent.setAction(DeliciousApp.ACTION_SUBMIT_BOOKMARK);
        intent.putExtra(DeliciousApp.EXTRA_BOOKMARK, bookmark);
        intent.putExtra(DeliciousApp.EXTRA_SHARED, shared);
        context.startService(intent);
    }
    
  @Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return null;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
    mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    mSyncActive = false;
  }

  @Override
  public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    String action = intent.getAction();
    if (DeliciousApp.ACTION_SYNC_TAGS.equals(action)) {
        boolean forceSync = intent.hasExtra(DeliciousApp.EXTRA_FORCE_SYNC) && 
            intent.getBooleanExtra(DeliciousApp.EXTRA_FORCE_SYNC, false);
        syncTags(!forceSync);
    } else if (DeliciousApp.ACTION_SUBMIT_BOOKMARK.equals(action)) {
        Bookmark bookmark = intent.getParcelableExtra(DeliciousApp.EXTRA_BOOKMARK);
        boolean shared = intent.getBooleanExtra(DeliciousApp.EXTRA_SHARED, true);
        submitBookmark(bookmark, shared);
    }
  }

  /**
   * Determine whether the network conditions and settings are suitable
   * for an update; based on the "Coding for Life" presentation
   * at GoogleIO 2009.
   * 
   * @return True if conditions/settings are suitable for background update.
   */
  private boolean canSync (boolean requireHighSpeed) {
      NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
      // Skip if no connection or background data is disabled
      if (info == null ||
              !mConnectivityManager.getBackgroundDataSetting()) {
          return false;
      }
      // Skip if roaming
      int netType = info.getType();
      if (netType == ConnectivityManager.TYPE_MOBILE
              && mTelephonyManager.isNetworkRoaming()) {
          return false;
      }
      
      // Read user setting
      SharedPreferences settings = getSharedPreferences(DeliciousApp.PREFS_NAME, 
              Context.MODE_PRIVATE);
      boolean syncOn2G = settings.getBoolean(SettingsActivity.PREF_SYNC_ON_2G, false);

      // Check if connection speed is appropriate
        int netSubtype = info.getSubtype();
      if (syncOn2G || !requireHighSpeed) {
          return info.isConnected();
      } else if (netType == ConnectivityManager.TYPE_WIFI) {
          return info.isConnected();
      } else if (netType == ConnectivityManager.TYPE_MOBILE
              && netSubtype == TelephonyManager.NETWORK_TYPE_UMTS
              && !mTelephonyManager.isNetworkRoaming()) {
          return info.isConnected();
      } else {
          return false;
      }
  }
  
  /**
   * Start sync in background, if possible and appropriate.
   */
  private void syncTags (boolean requireHighSpeed) {
      Log.d(TAG, "syncTags()");
      if (mSyncActive) {
          return;
      }
      Log.d(TAG, "Sync not active");
    final DeliciousApp app = DeliciousApp.getInstance();
    
    // If not possible or not appropriate, don't sync
    if (!app.mDeliciousClient.hasCredentials() || !canSync(requireHighSpeed)) {
      return;
    }

    Thread syncThread = new Thread() {
      @Override
      public void run () {
        TagsCache.syncTags(app);
        mSyncActive = false;
        mNotificationManager.cancel(R.drawable.stat_notify_sync);
      }
    };

    Notification notification = new Notification(R.drawable.stat_notify_sync, null, System.currentTimeMillis());
    notification.setLatestEventInfo(this, 
            getText(R.string.notify_sync_title), getText(R.string.notify_sync_text), 
            makeTagListPendingIntent());
    notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
    mNotificationManager.notify(R.drawable.stat_notify_sync, notification);  // Use drawable ID as unique notification ID

    mSyncActive = true;
    syncThread.start();
  }

  private void submitBookmark(final Bookmark bookmark, final boolean shared) {
      final DeliciousApp app = DeliciousApp.getInstance();
      if (!app.mDeliciousClient.hasCredentials()) {
          // FIXME popup error notification instead; although this should not happen??
          return;
      }

      Thread addBookmarkThread = new Thread() {
            @Override
            public void run () {
                boolean success = app.mDeliciousClient.addBookmark(bookmark, true, shared);
                mNotificationManager.cancel(R.drawable.stat_notify_submit);
                if (!success) {
                    Notification notification = new Notification(R.drawable.stat_notify_error, null, System.currentTimeMillis());
                    notification.setLatestEventInfo(BookmarkService.this, 
                            getText(R.string.notify_error_title), getText(R.string.notify_error_text), 
                            makeBookmarkPendingIntent(bookmark, shared, PendingIntent.FLAG_ONE_SHOT));
                    notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_NO_CLEAR;
                    mNotificationManager.notify(R.drawable.stat_notify_error, notification); // FIXME notification id
                }
            }
        };

        Notification notification = new Notification(R.drawable.stat_notify_submit, null, System.currentTimeMillis());
        notification.setLatestEventInfo(this, 
                getText(R.string.notify_submit_title), getText(R.string.notify_submit_text),
                makeBookmarkPendingIntent(bookmark, shared, PendingIntent.FLAG_UPDATE_CURRENT));
        notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
        mNotificationManager.notify(R.drawable.stat_notify_submit, notification);

        addBookmarkThread.start();
  }
  
  private PendingIntent makeBookmarkPendingIntent (Bookmark bookmark, boolean shared, int flags) {
      Intent intent = new Intent(this, BookmarkActivity.class);
      intent.setAction(Intent.ACTION_INSERT);
      intent.putExtra(DeliciousApp.EXTRA_BOOKMARK, bookmark);
      intent.putExtra(DeliciousApp.EXTRA_SHARED, shared);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      return PendingIntent.getActivity(this, 0, intent, flags);
  }
  
  private PendingIntent makeTagListPendingIntent () {
      Intent intent = new Intent(this, TagListActivity.class);
      intent.setAction(Intent.ACTION_VIEW); // XXX
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  }
  
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.