/*
* Created by Angel Leon (@gubatron), Alden Torres (aldenml)
* Copyright (c) 2011-2014, FrostWire(R). 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.activities;
import static com.andrew.apollo.utils.MusicUtils.mService;
import com.andrew.apollo.IApolloService;
import com.andrew.apollo.utils.MusicUtils;
import com.andrew.apollo.utils.MusicUtils.ServiceToken;
import com.bt.download.android.R;
import com.bt.download.android.core.ConfigurationManager;
import com.bt.download.android.core.Constants;
import com.bt.download.android.gui.PeerManager;
import com.bt.download.android.gui.SoftwareUpdater;
import com.bt.download.android.gui.activities.internal.MainController;
import com.bt.download.android.gui.activities.internal.MainMenuAdapter;
import com.bt.download.android.gui.dialogs.YesNoDialog;
import com.bt.download.android.gui.fragments.AboutFragment;
import com.bt.download.android.gui.fragments.BrowsePeerFragment;
import com.bt.download.android.gui.fragments.BrowsePeersDisabledFragment;
import com.bt.download.android.gui.fragments.BrowsePeersFragment;
import com.bt.download.android.gui.fragments.MainFragment;
import com.bt.download.android.gui.fragments.SearchFragment;
import com.bt.download.android.gui.fragments.TransfersFragment;
import com.bt.download.android.gui.fragments.TransfersFragment.TransferStatus;
import com.bt.download.android.gui.services.Engine;
import com.bt.download.android.gui.transfers.TransferManager;
import com.bt.download.android.gui.util.UIUtils;
import com.bt.download.android.gui.views.AbstractActivity;
import com.bt.download.android.gui.views.AbstractDialog;
import com.bt.download.android.gui.views.AbstractDialog.OnDialogClickListener;
import com.bt.download.android.gui.views.PlayerMenuItemView;
import com.bt.download.android.gui.views.TimerObserver;
import com.bt.download.android.gui.views.TimerService;
import com.bt.download.android.gui.views.TimerSubscription;
import com.frostwire.logging.Logger;
import com.frostwire.util.Ref;
import com.umeng.analytics.MobclickAgent;
import com.wandoujia.ads.sdk.Ads;
import com.wandoujia.ads.sdk.loader.Fetcher;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.SimpleDrawerListener;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.RelativeLayout;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Stack;
/**
*
* @author gubatron
* @author aldenml
*
*/
public class MainActivity extends AbstractActivity implements OnDialogClickListener, ServiceConnection {
private static final Logger LOG = Logger.getLogger(MainActivity.class);
private static final String FRAGMENTS_STACK_KEY = "fragments_stack";
private static final String CURRENT_FRAGMENT_KEY = "current_fragment";
private static final String DUR_TOKEN_KEY = "dur_token";
private static final String APPIA_STARTED_KEY = "appia_started";
private static final String LAST_BACK_DIALOG_ID = "last_back_dialog";
private static final String SHUTDOWN_DIALOG_ID = "shutdown_dialog";
private static boolean firstTime = true;
private MainController controller;
private DrawerLayout drawerLayout;
private ActionBarDrawerToggle drawerToggle;
private View leftDrawer;
private ListView listMenu;
private SearchFragment search;
private BrowsePeerFragment library;
private TransfersFragment transfers;
private BrowsePeersFragment peers;
private BrowsePeersDisabledFragment peersDisabled;
private AboutFragment about;
private Fragment currentFragment;
private final Stack<Integer> fragmentsStack;
private PlayerMenuItemView playerItem;
private TimerSubscription playerSubscription;
private BroadcastReceiver mainBroadcastReceiver;
public MainActivity() {
super(R.layout.activity_main);
this.controller = new MainController(this);
this.fragmentsStack = new Stack<Integer>();
}
public void showMyFiles() {
controller.showMyFiles();
}
public void switchFragment(int itemId) {
controller.switchFragment(itemId);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (!(getCurrentFragment() instanceof SearchFragment)) {
controller.switchFragment(R.id.menu_main_search);
}
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
toggleDrawer();
} else {
return super.onKeyDown(keyCode, event);
}
return true;
}
@Override
public void onBackPressed() {
if (fragmentsStack.size() > 1) {
try {
fragmentsStack.pop();
int id = fragmentsStack.peek();
Fragment fragment = getFragmentManager().findFragmentById(id);
switchContent(fragment, false);
} catch (Throwable e) {
// don't break the app
handleLastBackPressed();
}
} else {
handleLastBackPressed();
}
syncSlideMenu();
updateHeader(getCurrentFragment());
}
private boolean isShutdown(Intent intent) {
if (intent == null) {
return false;
}
if (intent.getBooleanExtra("shutdown-" + ConfigurationManager.instance().getUUIDString(), false)) {
finish();
Engine.instance().shutdown();
return true;
}
return false;
}
@Override
protected void initComponents(Bundle savedInstanceState) {
if (isShutdown(getIntent())) {
return;
}
drawerLayout = findView(R.id.drawer_layout);
drawerLayout.setDrawerListener(new SimpleDrawerListener() {
@Override
public void onDrawerStateChanged(int newState) {
refreshPlayerItem();
syncSlideMenu();
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
}
@Override
public void onDrawerOpened(View drawerView) {
}
@Override
public void onDrawerClosed(View drawerView) {
}
});
leftDrawer = findView(R.id.activity_main_left_drawer);
listMenu = findView(R.id.left_drawer);
playerItem = findView(R.id.slidemenu_player_menuitem);
playerItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
controller.launchPlayerActivity();
}
});
setupFragments();
setupMenuItems();
setupInitialFragment(savedInstanceState);
playerSubscription = TimerService.subscribe((TimerObserver) findView(R.id.activity_main_player_notifier), 1);
onNewIntent(getIntent());
setupActionBar();
setupDrawer();
//PlaybackService.get(this);
}
@Override
protected void onNewIntent(Intent intent) {
if (isShutdown(intent)) {
return;
}
String action = intent.getAction();
//onResumeFragments();
if (action != null && action.equals(Constants.ACTION_SHOW_TRANSFERS)) {
if (Ads.isLoaded(Fetcher.AdFormat.interstitial, Constants.TAG_INTERSTITIAL_WIDGET)) {
Ads.showAppWidget(this, null, Constants.TAG_INTERSTITIAL_WIDGET,
Ads.ShowMode.FULL_SCREEN);
}
controller.showTransfers(TransferStatus.ALL);
} else if (action != null && action.equals(Constants.ACTION_OPEN_TORRENT_URL)) {
//Open a Torrent from a URL or from a local file :), say from Astro File Manager.
/**
* TODO: Ask @aldenml the best way to plug in NewTransferDialog.
* I've refactored this dialog so that it is forced (no matter if the setting
* to not show it again has been used) and when that happens the checkbox is hidden.
*
* However that dialog requires some data about the download, data which is not
* obtained until we have instantiated the Torrent object.
*
* I'm thinking that we can either:
* a) Pass a parameter to the transfer manager, but this would probably
* not be cool since the transfer manager (I think) should work independently from
* the UI thread.
*
* b) Pass a "listener" to the transfer manager, once the transfer manager has the torrent
* it can notify us and wait for the user to decide wether or not to continue with the transfer
*
* c) Forget about showing that dialog, and just start the download, the user can cancel it.
*/
//Show me the transfer tab
Intent i = new Intent(this, MainActivity.class);
i.setAction(Constants.ACTION_SHOW_TRANSFERS);
i.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);
//go!
TransferManager.instance().downloadTorrent(intent.getDataString());
}
// When another application wants to "Share" a file and has chosen FrostWire to do so.
// We make the file "Shared" so it's visible for other FrostWire devices on the local network.
else if (action != null && (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE))) {
controller.handleSendAction(intent);
}
if (intent.hasExtra(Constants.EXTRA_DOWNLOAD_COMPLETE_NOTIFICATION)) {
controller.showTransfers(TransferStatus.COMPLETED);
TransferManager.instance().clearDownloadsToReview();
try {
((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(Constants.NOTIFICATION_DOWNLOAD_TRANSFER_FINISHED);
Bundle extras = intent.getExtras();
if (extras.containsKey(Constants.EXTRA_DOWNLOAD_COMPLETE_PATH)) {
File file = new File(extras.getString(Constants.EXTRA_DOWNLOAD_COMPLETE_PATH));
if (file.isFile()) {
UIUtils.openFile(this, file.getAbsoluteFile());
}
}
} catch (Throwable e) {
LOG.warn("Error handling download complete notification", e);
}
}
}
@Override
protected void onResume() {
super.onResume();
refreshPeersFragment();
mainResume();
registerMainBroadcastReceiver();
MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
if (mainBroadcastReceiver != null) {
unregisterReceiver(mainBroadcastReceiver);
}
MobclickAgent.onPause(this);
}
private void registerMainBroadcastReceiver() {
mainBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Constants.ACTION_NOTIFY_SDCARD_MOUNTED.equals(intent.getAction())) {
onNotifySdCardMounted();
}
}
};
IntentFilter bf = new IntentFilter(Constants.ACTION_NOTIFY_SDCARD_MOUNTED);
registerReceiver(mainBroadcastReceiver, bf);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveLastFragment(outState);
saveFragmentsStack(outState);
}
private ServiceToken mToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mToken = MusicUtils.bindToService(this, this);
try {
Ads.init(MainActivity.this, Constants.ADS_APP_ID, Constants.ADS_SECRET_KEY);
} catch (Exception e) {
e.printStackTrace();
}
Ads.preLoad(this, Fetcher.AdFormat.interstitial, Constants.TAG_INTERSTITIAL_WIDGET);
Ads.preLoad(this, Fetcher.AdFormat.banner, Constants.TAG_BANNER);
}
@Override
public void onStart() {
if (currentFragment instanceof TransfersFragment && transfers.adBanner != null) {
transfers.adBanner.startAutoScroll();
}
if (currentFragment instanceof SearchFragment && search.adBanner != null) {
search.adBanner.startAutoScroll();
}
super.onStart();
}
@Override
public void onStop() {
if (transfers.adBanner != null) {
transfers.adBanner.stopAutoScroll();
}
if (search.adBanner != null) {
search.adBanner.stopAutoScroll();
}
super.onStop();
}
private void onNotifySdCardMounted() {
transfers.initStorageRelatedRichNotifications(null);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (playerSubscription != null) {
playerSubscription.unsubscribe();
}
//avoid memory leaks when the device is tilted and the menu gets recreated.
SoftwareUpdater.instance().removeConfigurationUpdateListener(this);
if (playerItem != null) {
playerItem.unbindDrawables();
}
if (mToken != null) {
MusicUtils.unbindFromService(mToken);
mToken = null;
}
}
private void saveLastFragment(Bundle outState) {
Fragment fragment = getCurrentFragment();
if (fragment != null) {
getFragmentManager().putFragment(outState, CURRENT_FRAGMENT_KEY, fragment);
}
}
private void mainResume() {
syncSlideMenu();
if (firstTime) {
firstTime = false;
Engine.instance().startServices(); // it's necessary for the first time after wizard
}
}
private void toggleDrawer() {
if (drawerLayout.isDrawerOpen(leftDrawer)) {
drawerLayout.closeDrawer(leftDrawer);
} else {
drawerLayout.openDrawer(leftDrawer);
}
updateHeader(getCurrentFragment());
}
private Fragment getWifiSharingFragment() {
return Engine.instance().isStarted() && ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_NETWORK_USE_UPNP) ? peers : peersDisabled;
}
private void handleLastBackPressed() {
YesNoDialog dlg = YesNoDialog.newInstance(LAST_BACK_DIALOG_ID, R.string.minimize_frostwire, R.string.are_you_sure_you_wanna_leave);
dlg.show(getFragmentManager());
}
private void handleShutdownRequest() {
YesNoDialog dlg = YesNoDialog.newInstance(SHUTDOWN_DIALOG_ID, R.string.app_shutdown_dlg_title, R.string.app_shutdown_dlg_message);
dlg.show(getFragmentManager());
}
@Override
public void onDialogClick(String tag, int which) {
if (tag.equals(LAST_BACK_DIALOG_ID) && which == AbstractDialog.BUTTON_POSITIVE) {
finish();
} else if (tag.equals(SHUTDOWN_DIALOG_ID) && which == AbstractDialog.BUTTON_POSITIVE) {
controller.shutdown();
}
}
private void syncSlideMenu() {
Fragment fragment = getCurrentFragment();
if (fragment instanceof SearchFragment) {
setSelectedItem(R.id.menu_main_search);
} else if (fragment instanceof BrowsePeerFragment) {
setSelectedItem(R.id.menu_main_library);
} else if (fragment instanceof TransfersFragment) {
setSelectedItem(R.id.menu_main_transfers);
} else if (fragment instanceof BrowsePeersFragment || fragment instanceof BrowsePeersDisabledFragment) {
setSelectedItem(R.id.menu_main_peers);
} else if (fragment instanceof AboutFragment) {
setSelectedItem(R.id.menu_main_about);
}
updateHeader(getCurrentFragment());
}
private void setSelectedItem(int id) {
try {
int position = 0;
MainMenuAdapter adapter = (MainMenuAdapter) listMenu.getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
if (adapter.getItemId(i) == id) {
position = i;
}
}
listMenu.setItemChecked(position, true);
} catch (Throwable e) { // protecting from weird android UI engine issues
LOG.warn("Error setting slide menu item selected", e);
}
}
private void refreshPlayerItem() {
if (playerItem != null) {
playerItem.refresh();
}
}
private void setupMenuItems() {
listMenu.setAdapter(new MainMenuAdapter(this));
listMenu.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
controller.closeSlideMenu();
try {
if (id == R.id.menu_main_preferences) {
controller.showPreferences();
} else if (id == R.id.menu_main_shutdown) {
handleShutdownRequest();
} else {
listMenu.setItemChecked(position, true);
controller.switchFragment((int) id);
}
} catch (Throwable e) { // protecting from weird android UI engine issues
LOG.error("Error clicking slide menu item", e);
}
}
});
}
private void setupFragments() {
search = (SearchFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_search);
library = (BrowsePeerFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_browse_peer);
transfers = (TransfersFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_transfers);
peers = (BrowsePeersFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_browse_peers);
peersDisabled = (BrowsePeersDisabledFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_browse_peers_disabled);
about = (AboutFragment) getFragmentManager().findFragmentById(R.id.activity_main_fragment_about);
hideFragments(getFragmentManager().beginTransaction()).commit();
library.setPeer(PeerManager.instance().getLocalPeer());
}
private FragmentTransaction hideFragments(FragmentTransaction ts) {
return ts.hide(search).hide(library).hide(transfers).hide(peers).hide(peersDisabled).hide(about);
}
private void setupInitialFragment(Bundle savedInstanceState) {
Fragment fragment = null;
if (savedInstanceState != null) {
fragment = getFragmentManager().getFragment(savedInstanceState, CURRENT_FRAGMENT_KEY);
restoreFragmentsStack(savedInstanceState);
}
if (fragment == null) {
fragment = search;
setSelectedItem(R.id.menu_main_search);
}
switchContent(fragment);
}
private void saveFragmentsStack(Bundle outState) {
int[] stack = new int[fragmentsStack.size()];
for (int i = 0; i < stack.length; i++) {
stack[i] = fragmentsStack.get(i);
}
outState.putIntArray(FRAGMENTS_STACK_KEY, stack);
}
private void restoreFragmentsStack(Bundle savedInstanceState) {
try {
int[] stack = savedInstanceState.getIntArray(FRAGMENTS_STACK_KEY);
for (int id : stack) {
fragmentsStack.push(id);
}
} catch (Throwable e) {
// silent recovering, stack is't not really important
}
}
private void updateHeader(Fragment fragment) {
try {
RelativeLayout placeholder = (RelativeLayout) getActionBar().getCustomView();//findView(R.id.activity_main_layout_header_placeholder);
if (placeholder != null && placeholder.getChildCount() > 0) {
placeholder.removeAllViews();
}
if (fragment instanceof MainFragment) {
View header = ((MainFragment) fragment).getHeader(this);
if (placeholder != null && header != null) {
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
placeholder.addView(header, params);
}
}
} catch (Throwable e) {
LOG.error("Error updating main header", e);
}
}
private void refreshPeersFragment() {
Fragment fragment = getCurrentFragment();
if (fragment instanceof BrowsePeersFragment || fragment instanceof BrowsePeersDisabledFragment) {
controller.switchFragment(R.id.menu_main_peers);
}
PeerManager.instance().updateLocalPeer();
}
private void switchContent(Fragment fragment, boolean addToStack) {
hideFragments(getFragmentManager().beginTransaction()).show(fragment).commitAllowingStateLoss();
if (addToStack && (fragmentsStack.isEmpty() || fragmentsStack.peek() != fragment.getId())) {
fragmentsStack.push(fragment.getId());
}
if (fragment instanceof TransfersFragment) {
if (transfers.adBanner != null) {
transfers.adBanner.startAutoScroll();
}
} else {
if (transfers.adBanner != null) {
transfers.adBanner.stopAutoScroll();
}
}
if (fragment instanceof SearchFragment) {
if (search.adBanner != null) {
search.adBanner.startAutoScroll();
}
} else {
if (search.adBanner != null) {
search.adBanner.stopAutoScroll();
}
}
currentFragment = fragment;
updateHeader(fragment);
}
/*
* The following methods are only public to be able to use them from another package(internal).
*/
public Fragment getFragmentByMenuId(int id) {
switch (id) {
case R.id.menu_main_search:
return search;
case R.id.menu_main_library:
return library;
case R.id.menu_main_transfers:
return transfers;
case R.id.menu_main_peers:
return getWifiSharingFragment();
case R.id.menu_main_about:
return about;
default:
return null;
}
}
public void switchContent(Fragment fragment) {
switchContent(fragment, true);
}
public Fragment getCurrentFragment() {
return currentFragment;
}
public void closeSlideMenu() {
drawerLayout.closeDrawer(leftDrawer);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
private void setupActionBar() {
ActionBar bar = getActionBar();
bar.setCustomView(R.layout.view_custom_actionbar);
bar.setDisplayShowCustomEnabled(true);
bar.setDisplayHomeAsUpEnabled(true);
bar.setHomeButtonEnabled(true);
}
private void setupDrawer() {
drawerToggle = new MenuDrawerToggle(this, drawerLayout);
drawerLayout.setDrawerListener(drawerToggle);
}
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
mService = IApolloService.Stub.asInterface(service);
}
/**
* {@inheritDoc}
*/
@Override
public void onServiceDisconnected(final ComponentName name) {
mService = null;
}
private static final class MenuDrawerToggle extends ActionBarDrawerToggle {
private final WeakReference<MainActivity> activityRef;
public MenuDrawerToggle(MainActivity activity, DrawerLayout drawerLayout) {
super(activity, drawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
// aldenml: even if the parent class hold a strong reference, I decided to keep a weak one
this.activityRef = Ref.weak(activity);
}
@Override
public void onDrawerClosed(View view) {
if (Ref.alive(activityRef)) {
activityRef.get().invalidateOptionsMenu();
}
}
@Override
public void onDrawerOpened(View drawerView) {
if (Ref.alive(activityRef)) {
UIUtils.hideKeyboardFromActivity(activityRef.get());
activityRef.get().invalidateOptionsMenu();
}
}
@Override
public void onDrawerStateChanged(int newState) {
if (Ref.alive(activityRef)) {
MainActivity activity = activityRef.get();
activity.refreshPlayerItem();
activity.syncSlideMenu();
}
}
}
public void performYTSearch(String ytUrl) {
SearchFragment searchFragment = (SearchFragment) getFragmentByMenuId(R.id.menu_main_search);
searchFragment.performYTSearch(ytUrl);
switchContent(searchFragment);
}
}