/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.feeds; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.annotation.Nullable; import android.support.v4.net.ConnectivityManagerCompat; import android.util.Log; import com.keepsafe.switchboard.SwitchBoard; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.feeds.action.FeedAction; import org.mozilla.gecko.feeds.action.CheckForUpdatesAction; import org.mozilla.gecko.feeds.action.EnrollSubscriptionsAction; import org.mozilla.gecko.feeds.action.SetupAlarmsAction; import org.mozilla.gecko.feeds.action.SubscribeToFeedAction; import org.mozilla.gecko.feeds.action.WithdrawSubscriptionsAction; import org.mozilla.gecko.preferences.GeckoPreferences; import org.mozilla.gecko.Experiments; /** * Background service for subscribing to and checking website feeds to notify the user about updates. */ public class FeedService extends IntentService { private static final String LOGTAG = "GeckoFeedService"; public static final String ACTION_SETUP = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.SETUP"; public static final String ACTION_SUBSCRIBE = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.SUBSCRIBE"; public static final String ACTION_CHECK = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.CHECK"; public static final String ACTION_ENROLL = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.ENROLL"; public static final String ACTION_WITHDRAW = AppConstants.ANDROID_PACKAGE_NAME + ".FEEDS.WITHDRAW"; public static void setup(Context context) { Intent intent = new Intent(context, FeedService.class); intent.setAction(ACTION_SETUP); context.startService(intent); } public static void subscribe(Context context, String feedUrl) { Intent intent = new Intent(context, FeedService.class); intent.setAction(ACTION_SUBSCRIBE); intent.putExtra(SubscribeToFeedAction.EXTRA_FEED_URL, feedUrl); context.startService(intent); } public FeedService() { super(LOGTAG); } private BrowserDB browserDB; @Override public void onCreate() { super.onCreate(); browserDB = BrowserDB.from(this); } @Override protected void onHandleIntent(Intent intent) { try { if (intent == null) { return; } Log.d(LOGTAG, "Service started with action: " + intent.getAction()); if (!isInExperiment(this)) { Log.d(LOGTAG, "Not in content notifications experiment. Skipping."); return; } FeedAction action = createActionForIntent(intent); if (action == null) { Log.d(LOGTAG, "No action to process"); return; } if (action.requiresPreferenceEnabled() && !isPreferenceEnabled()) { Log.d(LOGTAG, "Preference is disabled. Skipping."); return; } if (action.requiresNetwork() && !isConnectedToUnmeteredNetwork()) { // For now just skip if we are not connected or the network is metered. We do not want // to use precious mobile traffic. Log.d(LOGTAG, "Not connected to a network or network is metered. Skipping."); return; } action.perform(browserDB, intent); } finally { FeedAlarmReceiver.completeWakefulIntent(intent); } Log.d(LOGTAG, "Done."); } @Nullable private FeedAction createActionForIntent(Intent intent) { final Context context = getApplicationContext(); switch (intent.getAction()) { case ACTION_SETUP: return new SetupAlarmsAction(context); case ACTION_SUBSCRIBE: return new SubscribeToFeedAction(context); case ACTION_CHECK: return new CheckForUpdatesAction(context); case ACTION_ENROLL: return new EnrollSubscriptionsAction(context); case ACTION_WITHDRAW: return new WithdrawSubscriptionsAction(context); default: throw new AssertionError("Unknown action: " + intent.getAction()); } } private boolean isConnectedToUnmeteredNetwork() { ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); if (networkInfo == null || !networkInfo.isConnected()) { return false; } return !ConnectivityManagerCompat.isActiveNetworkMetered(manager); } public static boolean isInExperiment(Context context) { return SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_12HRS) || SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_5PM) || SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_8AM); } public static String getEnabledExperiment(Context context) { String experiment = null; if (SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_12HRS)) { experiment = Experiments.CONTENT_NOTIFICATIONS_12HRS; } else if (SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_8AM)) { experiment = Experiments.CONTENT_NOTIFICATIONS_8AM; } else if (SwitchBoard.isInExperiment(context, Experiments.CONTENT_NOTIFICATIONS_5PM)) { experiment = Experiments.CONTENT_NOTIFICATIONS_5PM; } return experiment; } private boolean isPreferenceEnabled() { return GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_NOTIFICATIONS_CONTENT, true); } }