/* * Created by Angel Leon (@gubatron), Alden Torres (aldenml) * Copyright (c) 2011, 2012, FrostWire(TM). All rights reserved. * * 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/>. */ package com.bt.download.android.gui.services; import java.io.File; import java.util.concurrent.ExecutorService; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.os.Environment; import android.telephony.TelephonyManager; import android.util.Log; import com.bt.download.android.core.ConfigurationManager; import com.bt.download.android.core.Constants; import com.bt.download.android.core.player.CoreMediaPlayer; import com.bt.download.android.gui.Librarian; import com.bt.download.android.gui.NetworkManager; import com.bt.download.android.gui.UniversalScanner; import com.bt.download.android.gui.transfers.TransferManager; import com.bt.download.android.util.SystemUtils; /** * Receives and controls messages from the external world. Depending on the * status it attempts to control what happens with the Engine. * * @author gubatron * @author aldenml * */ public class EngineBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "FW.EngineBroadcastReceiver"; private boolean wasPlaying; public EngineBroadcastReceiver() { } @Override public void onReceive(Context context, Intent intent) { try { String action = intent.getAction(); if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { handleMediaMounted(context, intent); if (Engine.instance().isDisconnected()) { Engine.instance().getThreadPool().execute(new Runnable() { @Override public void run() { Engine.instance().startServices(); } }); } } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) { handleMediaUnmounted(context, intent); } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { handleActionPhoneStateChanged(intent); } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { Librarian.instance().syncMediaStore(); } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED) { handleDisconnectedNetwork(networkInfo); } else if (networkInfo.getDetailedState() == DetailedState.CONNECTED) { handleConnectedNetwork(networkInfo); } } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { if (Engine.instance().getMediaPlayer().isPlaying()) { Engine.instance().getMediaPlayer().togglePause(); } } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)) { Librarian.instance().syncApplicationsProvider(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { // no sure about this case } } catch (Throwable e) { Log.e(TAG, "Error processing broadcast message", e); } } private void handleActionPhoneStateChanged(Intent intent) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); String msg = "Phone state changed to " + state; Log.v(TAG, msg); if (TelephonyManager.EXTRA_STATE_RINGING.equals(state) || TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state) || TelephonyManager.EXTRA_STATE_IDLE.equals(state)) { CoreMediaPlayer mediaPlayer = Engine.instance().getMediaPlayer(); if (mediaPlayer.isPlaying()) { wasPlaying = true; mediaPlayer.togglePause(); } else if (wasPlaying && TelephonyManager.EXTRA_STATE_IDLE.equals(state)) { mediaPlayer.seekTo(Math.max(0, mediaPlayer.getPosition() - 2000)); wasPlaying = false; mediaPlayer.togglePause(); } } } private void handleDisconnectedNetwork(NetworkInfo networkInfo) { Log.v(TAG, "Disconnected from network (" + networkInfo.getTypeName() + ")"); Engine.instance().getThreadPool().execute(new Runnable() { @Override public void run() { Engine.instance().stopServices(true); } }); } private void handleConnectedNetwork(NetworkInfo networkInfo) { if (NetworkManager.instance().isDataUp()) { boolean useTorrentsOnMobileData = ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_NETWORK_USE_MOBILE_DATA); // "Boolean Master", just for fun. // Let a <= "mobile up", // b <= "use torrents on mobile" // // In English: // is mobile data up and not user torrents on mobile? then abort else start services. // // In Boolean: // if (a && !b) then return; else start services. // // since early 'return' statements are a source of evil, I'll use boolean algebra... // so that we can instead just start services under the right conditions. // // negating "a && !b" I get... // ^(a && !b) => ^a || b // // In English: // if not mobile up or use torrents on mobile data then start services. (no else needed) // // mobile up means only mobile data is up and wifi is down. if (!NetworkManager.instance().isDataMobileUp() || useTorrentsOnMobileData) { Log.v(TAG, "Connected to " + networkInfo.getTypeName()); if (Engine.instance().isDisconnected()) { // avoid ANR error inside a broadcast receiver Engine.instance().getThreadPool().execute(new Runnable() { @Override public void run() { Engine.instance().startServices(); if (!ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_TORRENT_SEED_FINISHED_TORRENTS) || (!NetworkManager.instance().isDataWIFIUp() && ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_TORRENT_SEED_FINISHED_TORRENTS_WIFI_ONLY))) { TransferManager.instance().stopSeedingTorrents(); } } }); } } } } private void handleMediaMounted(final Context context, Intent intent) { try { String path = intent.getDataString().replace("file://", ""); if (!SystemUtils.isPrimaryExternalPath(new File(path))) { Intent i = new Intent(Constants.ACTION_NOTIFY_SDCARD_MOUNTED); context.sendBroadcast(i); if (SystemUtils.hasKitKat()) { final File privateDir = new File(path + File.separator + "Android" + File.separator + "data" + File.separator + context.getPackageName() + File.separator + "files" + File.separator + "FrostWire"); if (privateDir.exists() && privateDir.isDirectory()) { Thread t = new Thread(new Runnable() { @Override public void run() { new UniversalScanner(context).scanDir(privateDir); } }); t.setName("Private MediaScanning"); t.setDaemon(true); t.start(); } } } } catch (Throwable e) { e.printStackTrace(); } } /** * make sure the current save location will be the primary external if * the media being unmounted is the sd card. * @param context * @param intent */ private void handleMediaUnmounted(Context context, Intent intent) { String path = intent.getDataString().replace("file://", ""); if (!SystemUtils.isPrimaryExternalPath(new File(path)) && SystemUtils.isPrimaryExternalStorageMounted()) { File primaryExternal = Environment.getExternalStorageDirectory(); ConfigurationManager.instance().setStoragePath(primaryExternal.getAbsolutePath()); } } }