public class ActivityMain extends BasicActivity implements IOverlayable, IToolbarHolder, ISlidingMenuHolder, ILoginManager,
        Toolbar.OnMenuItemClickListener {

    private static final int MIN_NUMBER_FRAGMENTS_IN_STACK = 1;
    private static final float MAX_OVERLAY_LEVEL = 0.8f;
    private static final int FRAGMENT_TRANSACTION_TIME = 300;
    private static final int WELLCOME_SCREEN_TIME = 2000;
    private static final long BACK_DOUBLE_CLICK_DELAY = 2000;

    private static final int RATE_MIN_INSTALL_DAYS = 1;
    private static final int RATE_MIN_INSTALL_TIMES = 7;

    public static boolean isFirstRun = true;

    /**
     * Type of last fragment shown by activity
     */
    public static int lastFragmentType = -1;

    /**
     * Number of last fragment shown by activity (for view pagers)
     */
    public static int lastChildFragmentNumber = -1;

    /**
     * Type of last fragment's child i.e Search (child of main featured fragment)
     */
    public static int lastChildFragmentType = -1;

    private Toolbar toolbar;
    private SlidingMenu slidingMenu;
    private View vOverlay;

    private CallbackManager callbackManager;

    private int[] notificationsActions = {NetworkTasksService.NOTIFICATIONS_SHOW_PODCASTS_SUBSCRIBED};
    private boolean doubleBackToExitPressedOnce;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawable(null);
        setContentView(R.layout.activity_main);

        //Toolbar
        toolbar = (Toolbar) findViewById(R.id.toolbar_main);

        int startedFrom = getIntent().getIntExtra(ActivityPlayer.ACTIVITY_STARTED_FROM, -1);

        if (isFirstRun && !Arrays.asList(notificationsActions).contains(startedFrom)) {
            toolbar.setVisibility(View.GONE);
            final FragmentWellcome fragmentWellcome = new FragmentWellcome();
            showFragment(R.id.fl_window, fragmentWellcome, FragmentWellcome.TAG);
            init();
            new Handler().postDelayed(new Runnable() {
                public void run() {
                    hideFragment(fragmentWellcome);
                    if (!Prefs.getBoolean(FragmentHelp.PREF_HELP_SHOWN, false)) { //!
                        showHelpFragment();
                    } else {
                        toolbar.setVisibility(View.VISIBLE);
                        showFragment(R.id.fl_content, getStartFragment(), FragmentMediaItemsGrid.TAG);
                    }
                }
            }, WELLCOME_SCREEN_TIME);
        } else {
            init();
            toolbar.setVisibility(View.VISIBLE);

            int startedFragmentNumber = ActivityMain.lastChildFragmentNumber;

            if (startedFrom == NetworkTasksService.NOTIFICATIONS_SHOW_PODCASTS_SUBSCRIBED) {
                startedFragmentNumber = 1;
                startedFrom = MediaItemType.PODCAST.ordinal();
                NotificationManager nMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                nMgr.cancel(NetworkTasksService.NEW_EPISODS_NOTIFICATION_ID);
            }

            FragmentMediaItemsGrid fragmentMediaItemsGrid = new FragmentMediaItemsGrid();
            fragmentMediaItemsGrid.setMediaItemType(startedFrom == MediaItemType.PODCAST.ordinal() ? MediaItemType.PODCAST : MediaItemType.RADIO);
            if (startedFragmentNumber >= 0) {
                fragmentMediaItemsGrid.setStartItemNumber(startedFragmentNumber);
            }
            showFragment(R.id.fl_content, fragmentMediaItemsGrid, FragmentMediaItemsGrid.TAG);
            AppRate.with(this)
                    .setInstallDays(RATE_MIN_INSTALL_DAYS)
                    .setLaunchTimes(RATE_MIN_INSTALL_TIMES)
                    .setShowLaterButton(true) // default true
                    .monitor();

            // Show a dialog if meets conditions
            AppRate.showRateDialogIfMeetsConditions(this);
        }
        isFirstRun = false;
    }

    private void init() {
        UpodsApplication.initAllResources();
        vOverlay = findViewById(R.id.vOverlay);

        //Toolbar
        toolbar.inflateMenu(R.menu.menu_activity_main);
        toolbar.setOnMenuItemClickListener(this);
        toolbar.setTitleTextColor(getResources().getColor(R.color.white));

        //Social
        callbackManager = CallbackManager.Factory.create();

        MenuItem searchMenuItem = toolbar.getMenu().findItem(R.id.action_search);
        SearchView searchView = (SearchView) searchMenuItem.getActionView();
        UIHelper.setSearchViewStyle(searchView);
        UIHelper.changeSearchViewTextColor(searchView, Color.WHITE);

        slidingMenu = new SlidingMenu(this, toolbar);
    }

    private void showHelpFragment() {
        FragmentHelp fragmentHelp = new FragmentHelp();
        fragmentHelp.setCloseClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toolbar.setVisibility(View.VISIBLE);
                FragmentMediaItemsGrid fragmentMediaItemsGrid = new FragmentMediaItemsGrid();
                fragmentMediaItemsGrid.setMediaItemType(MediaItemType.RADIO);
                showFragment(R.id.fl_content, fragmentMediaItemsGrid, FragmentMediaItemsGrid.TAG);
            }
        });
        showFragment(R.id.fl_content, fragmentHelp, FragmentHelp.TAG);
    }

    private FragmentMediaItemsGrid getStartFragment() {
        FragmentMediaItemsGrid fragmentMediaItemsGrid = new FragmentMediaItemsGrid();
        String startScreen = SettingsManager.getInstace().getStringSettingValue(SettingsManager.JS_START_SCREEN);
        if (startScreen.equals("rs_subscribed")) {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.RADIO);
            fragmentMediaItemsGrid.setStartItemNumber(1);
        } else if (startScreen.equals("rs_recent")) {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.RADIO);
            fragmentMediaItemsGrid.setStartItemNumber(2);
        } else if (startScreen.equals("podcasts_featured")) {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.PODCAST);
            fragmentMediaItemsGrid.setStartItemNumber(0);
        } else if (startScreen.equals("podcasts_favorites")) {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.PODCAST);
            fragmentMediaItemsGrid.setStartItemNumber(1);
        } else if (startScreen.equals("podcasts_downloaded")) {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.PODCAST);
            fragmentMediaItemsGrid.setStartItemNumber(2);
        } else {
            fragmentMediaItemsGrid.setMediaItemType(MediaItemType.RADIO);
            fragmentMediaItemsGrid.setStartItemNumber(0);
        }
        return fragmentMediaItemsGrid;
    }

    @Override
    public void onBackPressed() {
        if (getFragmentManager().findFragmentByTag(getLatestFragmentTag()) instanceof ICustumziedBackPress) {
            boolean performBackPress = ((ICustumziedBackPress) getFragmentManager().findFragmentByTag(getLatestFragmentTag())).onBackPressed();
            if (!performBackPress) {
                return;
            }
        }

        if (getFragmentManager().getBackStackEntryCount() > MIN_NUMBER_FRAGMENTS_IN_STACK) {
            if (getLatestFragmentTag().equals(FragmentSearch.TAG)) {
                toolbar.getMenu().findItem(R.id.action_search).collapseActionView();
                getFragmentManager().popBackStack();
            } else {
                if (isOverlayShown()) {
                    toggleOverlay();
                }
                getFragmentManager().popBackStack();
            }
        } else {
            if (doubleBackToExitPressedOnce) {
                finish();
                return;
            }

            this.doubleBackToExitPressedOnce = true;
            Toast.makeText(this, R.string.clcik_back_exit, Toast.LENGTH_SHORT).show();

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            }, BACK_DOUBLE_CLICK_DELAY);
        }

    }

    @Override
    public void toggleOverlay() {
        ObjectAnimator alphaAnimation;
        if (isOverlayShown()) {
            alphaAnimation = ObjectAnimator.ofFloat(vOverlay, View.ALPHA, MAX_OVERLAY_LEVEL, 0);
        } else {
            alphaAnimation = ObjectAnimator.ofFloat(vOverlay, View.ALPHA, 0, MAX_OVERLAY_LEVEL);
        }
        alphaAnimation.setDuration(FRAGMENT_TRANSACTION_TIME);
        alphaAnimation.start();
    }

    @Override
    public boolean isOverlayShown() {
        return vOverlay.getAlpha() != 0;
    }

    @Override
    public void setOverlayAlpha(int alphaPercent) {
        vOverlay.getBackground().setAlpha(alphaPercent);
    }

    @Override
    public Toolbar getToolbar() {
        return toolbar;
    }

    @Override
    public SlidingMenu getSlidingMenu() {
        return slidingMenu;
    }

    @Override
    public void setSlidingMenuHeader(String itemName) {
        slidingMenu.getAdapter().setSelectedRow(itemName);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //Facebook analytics
        //AppEventsLogger.activateApp(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //Facebook analytics
        //AppEventsLogger.deactivateApp(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (currentContextMenuData != null && currentContextMenuData instanceof MediaItem) {
            Toast.makeText(this, ((MediaItem) currentContextMenuData).getName(), Toast.LENGTH_SHORT).show();
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        if (contextMenuType == ContextMenuType.PODCAST_MIDDLE_SCREEN) {
            inflater.inflate(R.menu.menu_basic_sceleton, menu);
            menu.add(getString(R.string.about_podcast));
            if (((Podcast) currentContextMenuData).isDownloaded) {
                menu.add(getString(R.string.open_on_disk));
                menu.add(getString(R.string.remove_all_episods));
            }
        } else if (contextMenuType == ContextMenuType.EPISODE_MIDDLE_SCREEN) {
            inflater.inflate(R.menu.menu_basic_sceleton, menu);
            menu.add(getString(R.string.marks_as_played));
            Track track = ((MediaItem.MediaItemBucket) currentContextMenuData).track;
            if (((Episode) track).isDownloaded) {
                menu.add(getString(R.string.delete));
            }
        } else if (contextMenuType == ContextMenuType.PROFILE) {
            inflater.inflate(R.menu.menu_logedin_profile, menu);
        } else {
            inflater.inflate(R.menu.menu_activity_main, menu);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (currentContextMenuData != null && currentContextMenuData instanceof Podcast
                && item.getTitle().equals(getString(R.string.about_podcast))) {
            YandexMetrica.reportEvent(Analytics.MIDDLE_SCREEN_ABOUT_PODCAST);
            ContextMenuHelper.showAboutPodcastDialog((Podcast) currentContextMenuData, this);
        } else if (currentContextMenuData != null && currentContextMenuData instanceof Podcast
                && item.getTitle().equals(getString(R.string.open_on_disk))) {
            YandexMetrica.reportEvent(Analytics.MIDDLE_SCREEN_OPEN_ON_DISK);
            ContextMenuHelper.showPodcastInFolder((MediaItem) currentContextMenuData, this);
        } else if (currentContextMenuData != null && currentContextMenuData instanceof Podcast
                && item.getTitle().equals(getString(R.string.remove_all_episods))) {
            YandexMetrica.reportEvent(Analytics.MIDDLE_REMOVE_ALL_EPISODES);
            ContextMenuHelper.removeAllDonwloadedEpisods(this, (Podcast) currentContextMenuData, onContextItemSelected);
        } else if (currentContextMenuData != null && currentContextMenuData instanceof MediaItem.MediaItemBucket
                && item.getTitle().equals(getString(R.string.delete))) {
            YandexMetrica.reportEvent(Analytics.MIDDLE_REMOVE_EPISODE);
            MediaItem mediaItem = ((MediaItem.MediaItemBucket) currentContextMenuData).mediaItem;
            Track track = ((MediaItem.MediaItemBucket) currentContextMenuData).track;
            ContextMenuHelper.removeDonwloadedTrack(this, track, mediaItem, onContextItemSelected);
        } else if (id == R.id.itemLogout) {
            LoginMaster.getInstance().logout();
            getSlidingMenu().updateHeader(true);
            if (onContextItemSelected != null) {
                onContextItemSelected.operationFinished();
            }
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        callbackManager.onActivityResult(requestCode, resultCode, data);
        if (getFragmentManager().findFragmentByTag(getLatestFragmentTag()) instanceof FragmentProfile) {
            FragmentProfile fragmentProfile = (FragmentProfile) getFragmentManager().findFragmentByTag(getLatestFragmentTag());
            fragmentProfile.onActivityResult(requestCode, resultCode, data);
        }
    }

    @Override
    public CallbackManager getFacebookCallbackManager() {
        return callbackManager;
    }


    @Override
    public boolean onMenuItemClick(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.add_podcast) {
            ContextMenuHelper.showAddMediaDialog(this, MediaItemType.PODCAST);
        } else if (id == R.id.add_radio) {
            ContextMenuHelper.showAddMediaDialog(this, MediaItemType.RADIO);
        }
        return true;
    }

} public class ActivityPlayer extends BasicActivity implements IToolbarHolder, Toolbar.OnMenuItemClickListener {

    public static final String ACTIVITY_STARTED_FROM = "startedFrom";
    public static final String ACTIVITY_STARTED_FROM_IN_DEPTH = "startedFromDepth"; //used if activity was started from depth fragment i.e search

    private MediaItem currentMediaItem;
    private Toolbar toolbar;
    private ActionMenuItemView itemFavorites;
    private FragmentPlayer fragmentPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        toolbar = (Toolbar) findViewById(R.id.toolbar_player);
        toolbar.setTitleTextColor(getResources().getColor(R.color.white));
        toolbar.inflateMenu(R.menu.menu_activity_player);
        toolbar.setOnMenuItemClickListener(this);
        toolbar.setVisibility(View.VISIBLE);
        itemFavorites = (ActionMenuItemView) toolbar.findViewById(R.id.action_favorites_player);

        currentMediaItem = UniversalPlayer.getInstance().getPlayingMediaItem();
        if (currentMediaItem == null) {
            Toast.makeText(this, getString(R.string.error_cant_play), Toast.LENGTH_SHORT).show();
            onBackPressed();
            return;
        }

        if (getFragmentManager().getBackStackEntryCount() == 0) {
            if (currentMediaItem instanceof Podcast && MediaUtils.isVideoUrl(currentMediaItem.getAudeoLink())) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                FragmentVideoPlayer fragmentVideoPlayer = new FragmentVideoPlayer();
                fragmentVideoPlayer.setOnPlayingFailedCallback(MediaUtils.getPlayerFailCallback(this, currentMediaItem));
                showFragment(R.id.fl_window, fragmentVideoPlayer, FragmentVideoPlayer.TAG);
            } else {
                fragmentPlayer = new FragmentPlayer();
                fragmentPlayer.setPlayableItem(currentMediaItem);
                showFragment(R.id.fl_window, fragmentPlayer, FragmentMainFeatured.TAG);
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 1 || getLatestFragmentTag().equals(FragmentPlayer.TAG)) {
            getFragmentManager().popBackStack();
        } else {
            getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            Intent myIntent = new Intent(this, ActivityMain.class);
            if (getIntent().hasExtra(ACTIVITY_STARTED_FROM)) {
                myIntent.putExtra(ACTIVITY_STARTED_FROM, getIntent().getIntExtra(ACTIVITY_STARTED_FROM, -1));
                myIntent.putExtra(ACTIVITY_STARTED_FROM_IN_DEPTH, getIntent().getIntExtra(ACTIVITY_STARTED_FROM_IN_DEPTH, -1));
            }
            startActivity(myIntent);
            overridePendingTransition(R.anim.slide_in_top, R.anim.slide_out_top);
            finish();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public Toolbar getToolbar() {
        return toolbar;
    }


    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        if (contextMenuType == ContextMenuType.PLAYER_SETTINGS) {
            inflater.inflate(R.menu.menu_basic_sceleton, menu);
            if (currentMediaItem instanceof RadioItem) {
                menu.add(getString(R.string.select_stream_quality));
                menu.add(getString(R.string.stream_info));
            } else {
                menu.add(getString(R.string.show_notes));
            }
        }
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        MediaItem mediaItem = UniversalPlayer.getInstance().getPlayingMediaItem();
        if (item.getItemId() == R.id.action_favorites_player) {
            if (mediaItem.isSubscribed) {
                itemFavorites.setIcon(getResources().getDrawable(R.drawable.ic_heart_white_24dp));
                ProfileManager.getInstance().removeSubscribedMediaItem(mediaItem);
                Toast.makeText(this, getString(R.string.removed_from_favorites), Toast.LENGTH_SHORT).show();
                YandexMetrica.reportEvent(Analytics.PLAYER_UNSUBSCRIBE);
            } else {
                itemFavorites.setIcon(getResources().getDrawable(R.drawable.ic_heart_black_24dp));
                ProfileManager.getInstance().addSubscribedMediaItem(mediaItem);
                Toast.makeText(this, getString(R.string.added_to_favorites), Toast.LENGTH_SHORT).show();
                YandexMetrica.reportEvent(Analytics.PLAYER_SUBSCRIBE);
            }
        } else if (item.getItemId() == R.id.action_player_settings) {
            openContextMenu(findViewById(R.id.action_player_settings), ContextMenuType.PLAYER_SETTINGS, null, null);
        }
        return false;
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if (item.getTitle().equals(getString(R.string.select_stream_quality))) {
            YandexMetrica.reportEvent(Analytics.PLAYER_STREAM_QUALITY);
            ContextMenuHelper.selectRadioStreamQuality(this, fragmentPlayer, (RadioItem) currentMediaItem);
        } else if (item.getTitle().equals("Show notes")) {
            DialogFragmentTrackInfo dialogFragmentTrackInfo = new DialogFragmentTrackInfo();
            dialogFragmentTrackInfo.setTrack(((Podcast) UniversalPlayer.getInstance().getPlayingMediaItem()).getSelectedTrack());
            dialogFragmentTrackInfo.enableStream = false;
            showDialogFragment(dialogFragmentTrackInfo);
        } else if (item.getTitle().equals(getString(R.string.stream_info))) {
            ContextMenuHelper.showStreamInfoDialog(this);
            YandexMetrica.reportEvent(Analytics.PLAYER_STREAM_INFO);
        }
        return super.onContextItemSelected(item);
    }

    public static void openWithIntent(Activity activity) {
        //Detect if starting from search -> if yes from which type
        if (FragmentSearch.isActive) {
            if (ActivityMain.lastFragmentType == MediaItemType.PODCAST.ordinal()) {
                ActivityMain.lastChildFragmentType = MediaItemType.PODCAST_SEARCH.ordinal();
            } else if (ActivityMain.lastFragmentType == MediaItemType.RADIO.ordinal()) {
                ActivityMain.lastChildFragmentType = MediaItemType.RADIO_SEARCH.ordinal();
            }
        }

        Intent myIntent = new Intent(activity, ActivityPlayer.class);
        myIntent.putExtra(ActivityPlayer.ACTIVITY_STARTED_FROM, ActivityMain.lastFragmentType);
        myIntent.putExtra(ActivityPlayer.ACTIVITY_STARTED_FROM_IN_DEPTH, ActivityMain.lastChildFragmentType);
        activity.startActivity(myIntent);
        activity.overridePendingTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom);
        activity.finish();
        ActivityMain.lastChildFragmentType = -1;
        ActivityMain.lastChildFragmentType = -1;
    }
} public class BasicActivity extends Activity implements IFragmentsManager, IContextMenuManager {

    private final static String DIALOG_TAG_START = "fr_dialog_";

    //Permissions
    public static int WRITE_EXTERNAL_PERMISSIONS_CODE = 785;
    protected IOperationFinishCallback onPermissionsGranted;

    //For context menus
    protected Object currentContextMenuData;
    protected ContextMenuType contextMenuType;
    protected IOperationFinishCallback onContextItemSelected;
    private boolean isContextItemSelected = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void showFragment(int id, Fragment fragment, String tag, IFragmentsManager.FragmentOpenType openType, IFragmentsManager.FragmentAnimationType animationType) {
        android.app.FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        if (animationType == IFragmentsManager.FragmentAnimationType.BOTTOM_TOP) {
            ft.setCustomAnimations(R.animator.animation_fragment_bototm_top, R.animator.animation_fragment_top_bototm,
                    R.animator.animation_fragment_bototm_top, R.animator.animation_fragment_top_bototm);
        }
        if (openType == IFragmentsManager.FragmentOpenType.OVERLAY) {
            ft.add(id, fragment, tag);
            if (this instanceof IOverlayable) {
                ((IOverlayable) this).toggleOverlay();
            }
        } else {
            ft.replace(id, fragment, tag);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

        if (fragment instanceof IControlStackHistory) {//Decide ether fragment should be added to stack history
            if (((IControlStackHistory) fragment).shouldBeAddedToStack()) {
                ft.addToBackStack(tag);
            }
        } else {
            ft.addToBackStack(tag);
        }

        ft.commitAllowingStateLoss();
    }


    @Override
    public void showFragment(int id, Fragment fragment, String tag) {
        showFragment(id, fragment, tag, IFragmentsManager.FragmentOpenType.REPLACE, IFragmentsManager.FragmentAnimationType.DEFAULT);
    }

    @Override
    public void hideFragment(Fragment fragment) {
        android.app.FragmentManager fm = getFragmentManager();
        try {
            fm.beginTransaction().remove(fragment).commit();
        } catch (Exception e) {
            fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    @Override
    public void showDialogFragment(DialogFragment dialogFragment) {
        long time = Calendar.getInstance().get(Calendar.MILLISECOND);
        String tag = DIALOG_TAG_START + String.valueOf(time);
        dialogFragment.show(getFragmentManager(), tag);
    }

    @Override
    public boolean hasFragment(String tag) {
        return getFragmentManager().findFragmentByTag(tag) != null;
    }

    @Override
    public String getLatestFragmentTag() {
        String tag = "";
        if (getFragmentManager().getBackStackEntryCount() > 0) {
            android.app.FragmentManager.BackStackEntry backEntry = getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount() - 1);
            tag = backEntry.getName();
        }
        return tag;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void openContextMenu(View view, ContextMenuType type, Object dataToPass, IOperationFinishCallback actionFinished) {
        currentContextMenuData = dataToPass;
        contextMenuType = type;
        onContextItemSelected = actionFinished;
        registerForContextMenu(view);
        openContextMenu(view);
    }

    @Override
    public void onContextMenuClosed(Menu menu) {
        if (!isContextItemSelected) {
            currentContextMenuData = null;
            contextMenuType = null;
            onContextItemSelected = null;
        }
        isContextItemSelected = false;
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        isContextItemSelected = true;
        currentContextMenuData = null;
        contextMenuType = null;
        onContextItemSelected = null;
        return super.onContextItemSelected(item);
    }

    public void setPermissionsGrantedCallback(IOperationFinishCallback onPermissionsGranted) {
        this.onPermissionsGranted = onPermissionsGranted;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == WRITE_EXTERNAL_PERMISSIONS_CODE &&
                grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (onPermissionsGranted != null) {
                onPermissionsGranted.operationFinished();
            }
        } else if (requestCode == WRITE_EXTERNAL_PERMISSIONS_CODE) {
            Toast.makeText(this, R.string.permissions_not_granted, Toast.LENGTH_SHORT).show();
        }
        onPermissionsGranted = null;
    }
} public class ProfileManager {

    public static final String JS_SUBSCRIBED_PODCASTS = "subscribedPodcasts";
    public static final String JS_SUBSCRIBED_STATIONS = "subscribedStations";
    public static final String JS_RECENT_STATIONS = "recentStations";

    public static ProfileManager profileManager;
    private IOperationFinishWithDataCallback profileSavedCallback;


    public static class ProfileUpdateEvent {
        public String updateListType;
        public MediaItem mediaItem;
        public boolean isRemoved;

        public ProfileUpdateEvent(String updateListType, MediaItem mediaItem, boolean isRemoved) {
            this.updateListType = updateListType;
            this.mediaItem = mediaItem;
            this.isRemoved = isRemoved;
        }
    }

    private ProfileManager() {

    }

    public synchronized static ProfileManager getInstance() {
        if (profileManager == null) {
            profileManager = new ProfileManager();
        }
        return profileManager;
    }

    public void setProfileSavedCallback(IOperationFinishWithDataCallback profileSavedCallback) {
        this.profileSavedCallback = profileSavedCallback;
    }

    private ArrayList<Long> getMediaListIds(String mediaType, String listType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String[] args1 = {mediaType, listType};
        ArrayList<Long> ids = new ArrayList<>();
        String table = mediaType.equals(MediaListItem.TYPE_RADIO) ? "radio_stations" : "podcasts";
        Cursor cursor = database.rawQuery("SELECT p.id FROM " + table + " as p\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON p.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ?", args1);
        while (cursor.moveToNext()) {
            ids.add(cursor.getLong(cursor.getColumnIndex("id")));
        }
        cursor.close();
        return ids;
    }

    private ArrayList<Podcast> getPodcastsForMediaType(String mediaType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        ArrayList<Long> ids = getMediaListIds(MediaListItem.TYPE_PODCAST,
                mediaType.equals(MediaListItem.DOWNLOADED) ? MediaListItem.SUBSCRIBED : MediaListItem.DOWNLOADED);
        ArrayList<Podcast> podcasts = new ArrayList<>();
        String[] args2 = {MediaListItem.TYPE_PODCAST, mediaType};
        Cursor cursor = database.rawQuery("SELECT p.* FROM podcasts as p\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON p.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ? " +
                "ORDER BY id DESC", args2);
        while (cursor.moveToNext()) {
            Podcast podcast = Podcast.withCursor(cursor);
            if (mediaType.equals(MediaListItem.DOWNLOADED)) {
                podcast.isDownloaded = true;
                if (ids.contains(podcast.id)) {
                    podcast.isSubscribed = true;
                }
            } else if (mediaType.equals(MediaListItem.SUBSCRIBED)) {
                podcast.isSubscribed = true;
                if (ids.contains(podcast.id)) {
                    podcast.isDownloaded = true;
                }
            }
            podcasts.add(podcast);
        }
        cursor.close();

        Podcast.syncWithDb(podcasts);

        return podcasts;
    }

    private ArrayList<RadioItem> getRadioStationsForMediaType(String mediaType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        ArrayList<Long> ids = getMediaListIds(MediaListItem.TYPE_RADIO,
                mediaType.equals(MediaListItem.RECENT) ? MediaListItem.SUBSCRIBED : MediaListItem.RECENT);
        ArrayList<RadioItem> radioItems = new ArrayList<>();
        String[] args2 = {MediaListItem.TYPE_RADIO, mediaType};

        Cursor cursor = database.rawQuery("SELECT r.* FROM radio_stations as r\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON r.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ? " +
                "ORDER BY id DESC", args2);

        while (cursor.moveToNext()) {
            RadioItem radioItem = RadioItem.withCursor(cursor);
            if (mediaType.equals(MediaListItem.RECENT)) {
                radioItem.isRecent = true;
                if (ids.contains(radioItem.id)) {
                    radioItem.isSubscribed = true;
                }
            } else if (mediaType.equals(MediaListItem.SUBSCRIBED)) {
                radioItem.isSubscribed = true;
                if (ids.contains(radioItem.id)) {
                    radioItem.isRecent = true;
                }
            }
            radioItems.add(radioItem);
        }
        cursor.close();
        return radioItems;
    }

    private void addTrack(MediaItem mediaItem, Track track, String listType) {
        if (mediaItem instanceof Podcast && track instanceof Episode) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            boolean isNotifyChanges = false;
            Podcast podcast = (Podcast) mediaItem;
            Episode episode = (Episode) track;

            //1. Save episode/podcast if needed
            if (!podcast.isExistsInDb) {
                podcast.save();
            }
            if (!episode.isExistsInDb) {
                episode.save(podcast.id);
            }

            //2. Add podcast to media_list if needed
            if ((!podcast.isDownloaded && listType.equals(MediaListItem.DOWNLOADED)) ||
                    (!podcast.hasNewEpisodes && listType.equals(MediaListItem.NEW))) {
                ContentValues values = new ContentValues();
                values.put("media_id", mediaItem.id);
                values.put("media_type", MediaListItem.TYPE_PODCAST);
                values.put("list_type", listType);
                database.insert("media_list", null, values);
                isNotifyChanges = true;
            }

            //3. Create podcasts_episodes_rel
            if ((!episode.isDownloaded && listType.equals(MediaListItem.DOWNLOADED)) ||
                    (!episode.isNew && listType.equals(MediaListItem.NEW))) {
                ContentValues values = new ContentValues();
                values.put("podcast_id", podcast.id);
                values.put("episode_id", episode.id);
                values.put("type", listType);
                database.insert("podcasts_episodes_rel", null, values);
            }

            //4. Update objects
            if (listType.equals(MediaListItem.DOWNLOADED)) {
                podcast.isDownloaded = true;
                episode.isDownloaded = true;
                if (isNotifyChanges) {
                    notifyChanges(new ProfileUpdateEvent(MediaListItem.DOWNLOADED, mediaItem, false));
                }
            } else if (listType.equals(MediaListItem.NEW)) {
                podcast.hasNewEpisodes = true;
                episode.isNew = true;
                if (!Podcast.hasEpisodeWithTitle(podcast, episode)) {
                    podcast.getEpisodes().add(episode);
                }
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    notifyChanges(new ProfileUpdateEvent(MediaListItem.NEW, mediaItem, false));
                }
            }
        }
    }

    private void removeTrack(MediaItem mediaItem, Track track, String listType) {
        if (mediaItem instanceof Podcast && track instanceof Episode) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            Podcast podcast = (Podcast) mediaItem;
            Episode episode = (Episode) track;

            //Remove reletionships -> then remove episode
            String args[] = {String.valueOf(podcast.id), String.valueOf(episode.id), listType};
            database.delete("podcasts_episodes_rel", "podcast_id = ? AND episode_id = ? AND type = ?", args);

            if (listType.equals(MediaListItem.DOWNLOADED)) {
                File episodeFile = new File(episode.getAudeoUrl());
                episodeFile.delete();
                episode.isDownloaded = false;
                if (!episode.isNew) {
                    episode.remove();
                }
            } else if (listType.equals(MediaListItem.NEW)) {
                episode.isNew = false;
                if (!episode.isDownloaded) {
                    episode.remove();
                }
            }

            if (listType.equals(MediaListItem.DOWNLOADED) && podcast.getDownloadedEpisodsCount() == 0) {
                String args2[] = {String.valueOf(podcast.id), MediaListItem.TYPE_PODCAST, MediaListItem.DOWNLOADED};
                database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args2);
                podcast.isDownloaded = false;
                notifyChanges(new ProfileUpdateEvent(MediaListItem.DOWNLOADED, mediaItem, true));
            } else if (listType.equals(MediaListItem.NEW) && podcast.getNewEpisodsCount() == 0) {
                podcast.hasNewEpisodes = false;
                String args2[] = {String.valueOf(podcast.id), MediaListItem.TYPE_PODCAST, MediaListItem.NEW};
                database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args2);
            }

            if (listType.equals(MediaListItem.NEW) && Looper.myLooper() == Looper.getMainLooper()) {
                //If we are in main thread and were changes in count of new episodes -> notify provider
                notifyChanges(new ProfileUpdateEvent(MediaListItem.NEW, mediaItem, false));
            }

        }
    }

    public ArrayList<Podcast> getDownloadedPodcasts() {
        return getPodcastsForMediaType(MediaListItem.DOWNLOADED);
    }

    public ArrayList<Podcast> getSubscribedPodcasts() {
        return getPodcastsForMediaType(MediaListItem.SUBSCRIBED);
    }

    public ArrayList<RadioItem> getSubscribedRadioItems() {
        return getRadioStationsForMediaType(MediaListItem.SUBSCRIBED);
    }

    public ArrayList<RadioItem> getRecentRadioItems() {
        return getRadioStationsForMediaType(MediaListItem.RECENT);
    }

    public int getTrackPosition(Track track) {
        int trackPostion = -1;
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {track.getTitle()};
        Cursor cursor = database.rawQuery("SELECT position FROM tracks_positions WHERE track_name = ?", args);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            trackPostion = cursor.getInt(cursor.getColumnIndex("position"));
        }
        return trackPostion;
    }

    public void addSubscribedMediaItem(MediaItem mediaItem) {
        if (!mediaItem.isSubscribed) {
            String mediaType = MediaListItem.TYPE_RADIO;
            if (mediaItem instanceof Podcast) {
                mediaType = MediaListItem.TYPE_PODCAST;
                Podcast podcast = ((Podcast) mediaItem);
                if (!mediaItem.isExistsInDb) {
                    podcast.save();
                }
                Feed.removeFeed(podcast.getFeedUrl());
                Feed.saveAsFeed(podcast.getFeedUrl(), podcast.getEpisodes(), true);
            } else if (mediaItem instanceof RadioItem) {
                if (!mediaItem.isExistsInDb) {
                    ((RadioItem) mediaItem).save();
                }
            }
            ContentValues values = new ContentValues();
            values.put("media_id", mediaItem.id);
            values.put("media_type", mediaType);
            values.put("list_type", MediaListItem.SUBSCRIBED);
            UpodsApplication.getDatabaseManager().getWritableDatabase().insert("media_list", null, values);
            mediaItem.isSubscribed = true;

            if (mediaItem instanceof Podcast && getSubscribedPodcasts().size() == 1) {
                UpodsApplication.setAlarmManagerTasks();
            }
        }
        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, false));
    }

    public void addRecentMediaItem(MediaItem mediaItem) {
        if (mediaItem instanceof RadioItem) {
            if (!((RadioItem) mediaItem).isRecent) {
                if (!mediaItem.isExistsInDb) {
                    ((RadioItem) mediaItem).save();
                }
                ContentValues values = new ContentValues();
                values.put("media_id", mediaItem.id);
                values.put("media_type", MediaListItem.TYPE_RADIO);
                values.put("list_type", MediaListItem.RECENT);
                UpodsApplication.getDatabaseManager().getWritableDatabase().insert("media_list", null, values);
                ((RadioItem) mediaItem).isRecent = true;
                notifyChanges(new ProfileUpdateEvent(MediaListItem.RECENT, mediaItem, false));
            }
        }
    }

    public void addNewTrack(MediaItem mediaItem, Track track) {
        addTrack(mediaItem, track, MediaListItem.NEW);
    }

    public void addDownloadedTrack(MediaItem mediaItem, Track track) {
        addTrack(mediaItem, track, MediaListItem.DOWNLOADED);
    }

    public void saveTrackPosition(Track track, int position) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("track_name", track.getTitle());
        values.put("position", position);
        database.replace("tracks_positions", null, values);
    }

    public void removeNewTrack(MediaItem mediaItem, Track track) {
        removeTrack(mediaItem, track, MediaListItem.NEW);
    }

    public void removeDownloadedTrack(MediaItem mediaItem, Track track) {
        removeTrack(mediaItem, track, MediaListItem.DOWNLOADED);
    }

    public void removeRecentMediaItem(MediaItem mediaItem) {
        if (mediaItem instanceof RadioItem) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            String args[] = {String.valueOf(mediaItem.id), MediaListItem.TYPE_RADIO, MediaListItem.RECENT};
            database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args);
            ((RadioItem) mediaItem).isRecent = false;

            //Remove media item from DB if it doesn't use
            if (!((RadioItem) mediaItem).isSubscribed) {
                String args2[] = {String.valueOf(mediaItem.id)};
                database.delete("radio_stations", "id = ?", args2);
            }
        }
        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, true));
    }

    public void removeSubscribedMediaItem(MediaItem mediaItem) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String type = MediaListItem.TYPE_RADIO;
        if (mediaItem instanceof Podcast) {
            type = MediaListItem.TYPE_PODCAST;
        }
        mediaItem.isSubscribed = false;
        String args[] = {String.valueOf(mediaItem.id), type, MediaListItem.SUBSCRIBED};
        database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args);

        //Remove media item from DB if it doesn't use
        if (mediaItem instanceof RadioItem && !((RadioItem) mediaItem).isRecent) {
            String args2[] = {String.valueOf(mediaItem.id)};
            database.delete("radio_stations", "id = ?", args2);
        } else if (mediaItem instanceof Podcast && !((Podcast) mediaItem).isDownloaded && !((Podcast) mediaItem).hasNewEpisodes) {
            String args2[] = {String.valueOf(mediaItem.id)};
            database.delete("podcasts", "id = ?", args2);
        }

        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, true));
    }

    public void notifyChanges(ProfileUpdateEvent updateEvent) {
        if (profileSavedCallback != null) {
            profileSavedCallback.operationFinished(updateEvent);
        }
    }

    private void initSubscribedPodcasts(JSONArray jSubscribedPodcasts) {
        try {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            ArrayList<Podcast> subscribedPodcasts = new ArrayList<>();
            for (int i = 0; i < jSubscribedPodcasts.length(); i++) {
                Podcast podcast = new Podcast(jSubscribedPodcasts.getJSONObject(i));
                subscribedPodcasts.add(podcast);
            }

            if (subscribedPodcasts.isEmpty()) {
                return;
            }

            Podcast.syncWithDb(subscribedPodcasts);
            for (Podcast podcast : subscribedPodcasts) {
                if (!podcast.isSubscribed) {
                    addSubscribedMediaItem(podcast);
                }
            }
            ArrayList<String> ids = MediaItem.getIds(subscribedPodcasts);
            if (ids.size() == 0) {
                return;
            }
            ArrayList<String> args = new ArrayList<>();
            args.add(MediaListItem.TYPE_PODCAST);
            args.add(MediaListItem.SUBSCRIBED);
            args.addAll(ids);
            database.delete("media_list", "media_type = ? AND list_type = ? AND media_id not in (" + SQLdatabaseManager.makePlaceholders(ids.size()) + ")",
                    args.toArray(new String[args.size()]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initRadioStations(JSONArray jStations, String listType) {
        try {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            ArrayList<RadioItem> radioStations = new ArrayList<>();
            for (int i = 0; i < jStations.length(); i++) {
                RadioItem radioItem = new RadioItem(jStations.getJSONObject(i));
                radioStations.add(radioItem);
            }

            if (radioStations.isEmpty()) {
                return;
            }

            RadioItem.syncWithDb(radioStations);
            for (RadioItem radioItem : radioStations) {
                if (!radioItem.isSubscribed && listType.equals(MediaListItem.SUBSCRIBED)) {
                    addSubscribedMediaItem(radioItem);
                } else if (!radioItem.isRecent && listType.equals(MediaListItem.RECENT)) {
                    addRecentMediaItem(radioItem);
                }
            }
            ArrayList<String> ids = MediaItem.getIds(radioStations);

            if (ids.size() == 0) {
                return;
            }

            ArrayList<String> args = new ArrayList<>();
            args.add(MediaListItem.TYPE_RADIO);
            args.add(listType);
            args.addAll(ids);
            database.delete("media_list", "media_type = ? AND list_type = ? AND media_id not in (" + SQLdatabaseManager.makePlaceholders(ids.size()) + ")",
                    args.toArray(new String[args.size()]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void readFromJson(JSONObject rootProfile) {
        try {
            //Read from JSON -> syncWithDB -> save all which not exists -> remove all which not in JSON
            // -> UI will be notified inside init... functions

            initSubscribedPodcasts(rootProfile.getJSONArray(JS_SUBSCRIBED_PODCASTS));
            initRadioStations(rootProfile.getJSONArray(JS_SUBSCRIBED_STATIONS), MediaListItem.SUBSCRIBED);
            initRadioStations(rootProfile.getJSONArray(JS_RECENT_STATIONS), MediaListItem.RECENT);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    public JSONObject getAsJson() {
        JSONObject rootProfile = new JSONObject();
        try {
            rootProfile.put(JS_RECENT_STATIONS, RadioItem.toJsonArray(getRecentRadioItems()));
            rootProfile.put(JS_SUBSCRIBED_STATIONS, RadioItem.toJsonArray(getSubscribedRadioItems()));
            rootProfile.put(JS_SUBSCRIBED_PODCASTS, Podcast.toJsonArray(getSubscribedPodcasts()));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return rootProfile;
    }

} public class DownloadMaster {

    public static final String PODCASTS_DOWNLOAD_DIRECTORY = "/airtune";

    private static final String DM_LOG = "DownloadMaster";
    private static final long PRGRESS_UPDATE_RATE = 400;
    private static DownloadMaster downloadMaster;
    private ArrayList<DownloadTask> allTasks;
    private TimerTask progressUpdateTask;

    public static class DownloadTask {
        public MediaItem mediaItem;
        public String filePath;
        public Track track;
        public long downloadId;
        public double lastProgress; //Last known progress for this task
        public IUIProgressUpdater progressUpdater;
        public IContentLoadListener contentLoadListener;

        public DownloadTask() {
            this.downloadId = 0;
            this.filePath = "";
            this.mediaItem = null;
            this.track = null;
            this.progressUpdater = null;
            this.contentLoadListener = null;
            this.lastProgress = 0;
        }
    }

    private DownloadMaster() {
        this.allTasks = new ArrayList<>();
    }

    public static synchronized DownloadMaster getInstance() {
        if (downloadMaster == null) {
            downloadMaster = new DownloadMaster();
        }
        return downloadMaster;
    }

    public void download(DownloadTask task) {
        //TODO better path
        DownloadManager downloadManager = (DownloadManager) UpodsApplication.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
        Uri episodUri = Uri.parse(task.track.getAudeoUrl());
        String trackName = GlobalUtils.getCleanFileName(task.track.getTitle()) + ".mp3";
        String mediaItemName = GlobalUtils.getCleanFileName(task.mediaItem.getName());
        String finalPath = PODCASTS_DOWNLOAD_DIRECTORY + "/" + mediaItemName + "/" + trackName;
        finalPath = Environment.getExternalStorageDirectory() + finalPath;
        Request request = new Request(episodUri);
        request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
        request.setTitle(task.track.getTitle());
        request.setDescription(task.track.getSubTitle());
        request.setDestinationUri(Uri.fromFile(new File(finalPath)));
        task.downloadId = downloadManager.enqueue(request);
        task.filePath = finalPath;
        allTasks.add(task);
        Logger.printInfo(DM_LOG, "Starting download episod " + trackName + " to " + finalPath);
        runProgressUpdater();
    }

    private void runProgressUpdater() {
        if (progressUpdateTask == null && allTasks.size() > 0) {
            progressUpdateTask = new TimerTask() {
                @Override
                public void run() {
                    if (allTasks.size() == 0) {
                        progressUpdateTask.cancel();
                        progressUpdateTask = null;
                    } else {
                        for (DownloadTask task : allTasks) {
                            if (task.progressUpdater != null) {
                                task.progressUpdater.updateProgressUI(getDownloadProgress(task.track.getTitle()));
                            }
                        }
                    }
                }
            };
            new Timer().scheduleAtFixedRate(progressUpdateTask, 0, PRGRESS_UPDATE_RATE);
        }
    }

    public DownloadTask getTaskByName(String itemName) {
        for (DownloadTask task : allTasks) {
            if (GlobalUtils.safeTitleEquals(task.track.getTitle(), itemName)) {
                return task;
            }
        }
        return null;
    }

    public DownloadTask getTaskById(long downloadId) {
        for (DownloadTask task : allTasks) {
            if (task.downloadId == downloadId) {
                return task;
            }
        }
        return null;
    }

    private double getDownloadProgress(String itemName) {
        DownloadTask task = getTaskByName(itemName);
        double progress = 0.0;
        if (task != null) {
            long taskId = task.downloadId;
            DownloadManager downloadManager = (DownloadManager) UpodsApplication.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(taskId);
            Cursor c = downloadManager.query(query);

            if (c.moveToFirst()) {
                int sizeIndex = c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
                int downloadedIndex = c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
                long size = c.getInt(sizeIndex);
                long downloaded = c.getInt(downloadedIndex);
                if (size != -1) progress = downloaded * 100.0 / size;
            }
            c.close();
            task.lastProgress = progress;
        }
        return progress;
    }

    public boolean isItemDownloading(String itemName) {
        return getTaskByName(itemName) != null;
    }

    /**
     * Removes all UI updatets
     */
    public void cleanProgressInterfaces() {
        for (DownloadTask task : allTasks) {
            task.progressUpdater = null;
        }
    }

    public void cancelDownload(String itemName) {
        DownloadManager downloadManager = (DownloadManager) UpodsApplication.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadTask task = getTaskByName(itemName);
        task.progressUpdater = null;
        downloadManager.remove(task.downloadId);
        allTasks.remove(task);
        Logger.printInfo(DM_LOG, "Task for item name: " + itemName + " canceled ");
    }

    /**
     * Should be called when DownloadManager finishes to download item
     */
    public void markDownloadTaskFinished(long downloadId) {
        DownloadManager downloadManager = (DownloadManager) UpodsApplication.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadId);
        Cursor c = downloadManager.query(query);
        if (c.moveToFirst()) {
            DownloadTask task = getTaskById(downloadId);
            int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
            if (task != null) {
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    if (task.track instanceof Episode) {
                        ((Episode) task.track).setPathOnDisk(task.filePath);
                    }
                    ProfileManager.getInstance().addDownloadedTrack(task.mediaItem, task.track);
                    task.contentLoadListener.onContentLoaded();
                }
                allTasks.remove(task);
            }
        }
        c.close();
    }
} public class UniversalPlayer implements MediaPlayer.EventListener {


    private static final long RECONNECT_RATE = 5000;

    public enum State {PLAYING, PAUSED, END_REACHED}

    public static final String INTENT_ACTION_PLAY = "com.chickenkiller.upods2.player.PLAY";
    public static final String INTENT_ACTION_PAUSE = "com.chickenkiller.upods2.player.PAUSE";
    public static final String INTENT_ACTION_FORWARD = "com.chickenkiller.upods2.player.FORWARD";
    public static final String INTENT_ACTION_BACKWARD = "com.chickenkiller.upods2.player.BACKWARD";
    private static final String PLAYER_LOG = "UniversalPlayer";

    private static UniversalPlayer universalPlayer;

    private LibVLC mLibVLC = null;
    private MediaPlayer mediaPlayer = null;

    private IOperationFinishCallback onAutonomicTrackChangeCallback;
    private IOperationFinishCallback onPlayingFailedCallback;
    private IPlayerStateListener playerStateListener;

    private MediaItem mediaItem;
    private PlayerNotificationPanel notificationPanel;

    private TimerTask autoReconector;

    public boolean isLinkReadyForPlaying;
    public boolean isPrepaired;
    public boolean isInErrorState;

    private UniversalPlayer() {
    }

    public static synchronized UniversalPlayer getInstance() {
        if (universalPlayer == null) {
            universalPlayer = new UniversalPlayer();
        }
        return universalPlayer;
    }

    /**
     * Checks if given type of media item is supported, if yes copies it and save instance in player.
     *
     * @param mediaItem
     */
    public void setMediaItem(MediaItem mediaItem) {
        if (isCurrentMediaItem(mediaItem)) {
            return;
        }
        YandexMetrica.reportEvent(Analytics.PLAY_PREFIX + mediaItem.getName());
        if (mediaItem instanceof RadioItem) {
            this.mediaItem = new RadioItem((RadioItem) mediaItem);
        } else if (mediaItem instanceof Podcast) {
            this.mediaItem = new Podcast((Podcast) mediaItem);
        } else {
            throw new RuntimeException("Unsupported type of MediaItem");
        }
    }

    /**
     * Checks if given type of media item is supported, if yes copies it and save instance in player.
     *
     * @param needReset - if true will reset the player before changing the mediaItem
     * @param mediaItem
     */
    public void setMediaItem(MediaItem mediaItem, boolean needReset) {
        if (needReset && !isCurrentMediaItem(mediaItem)) {
            resetPlayer();
        }
        setMediaItem(mediaItem);
    }

    public void setOnPlayingFailedCallback(IOperationFinishCallback onPlayingFailedCallback) {
        this.onPlayingFailedCallback = onPlayingFailedCallback;
    }

    public void setPlayerStateListener(IPlayerStateListener playerStateListener) {
        this.playerStateListener = playerStateListener;
    }

    public void setOnAutonomicTrackChangeCallback(IOperationFinishCallback onAutonomicTrackChangeCallback) {
        this.onAutonomicTrackChangeCallback = onAutonomicTrackChangeCallback;
    }

    /**
     * Main prepare method. Prepares media item to be played.
     */
    public void prepare() {
        if (mediaItem == null) {
            throw new RuntimeException("MediaItem is not set. Call setMediaItem before prepare.");
        }

        try {
            isInErrorState = false;

            if (!isLinkReadyForPlaying && !prepareLinkForPlaying()) {
                return;
            }

            String audeoLink = mediaItem.getAudeoLink();

            if (audeoLink.isEmpty()) {
                throw new IllegalArgumentException("Link for playing is empty");
            }

            Logger.printInfo(PLAYER_LOG, "Trying to play: " + audeoLink);
            if (mLibVLC == null) {
                mLibVLC = new LibVLC();
            }
            if (mediaPlayer == null || mediaPlayer.isReleased()) {
                mediaPlayer = new MediaPlayer(mLibVLC);
                mediaPlayer.setEventListener(this);
            }

            isPrepaired = false;

            if (mediaItem instanceof RadioItem || URLUtil.isValidUrl(audeoLink)) {
                Media m = new Media(mLibVLC, Uri.parse(audeoLink));
                mediaPlayer.setMedia(m);
                mediaPlayer.play();
                Logger.printInfo(PLAYER_LOG, "Play called for URL");
            } else {
                Media m = new Media(mLibVLC, audeoLink);
                mediaPlayer.setMedia(m);
                mediaPlayer.play();
                isPrepaired = true;
                Logger.printInfo(PLAYER_LOG, "Play called for local file");
            }

            Handler mainHandler = new Handler(UpodsApplication.getContext().getMainLooper());

            Runnable myRunnable = new Runnable() {
                @Override
                public void run() {
                    if (notificationPanel != null) {
                        notificationPanel.notificationCancel();
                    }
                    notificationPanel = new DefaultNotificationPanel(UpodsApplication.getContext(), mediaItem);
                    notificationPanel.updateNotificationStatus(State.PLAYING);
                }
            };
            mainHandler.post(myRunnable);

        } catch (Exception e) {
            Logger.printInfo(PLAYER_LOG, "Failed to call play...");
            threwError();
            e.printStackTrace();
        }

    }

    /**
     * Call it in the start of prepare  to manage different media formats
     * and broken links. It will try to select working link and  convert format to
     * mp3 if it is not supported.
     *
     * @return true - if prepare should continues regulary, false if it will call atomatycly by callback
     */
    private boolean prepareLinkForPlaying() {
        if (mediaItem instanceof RadioItem) {
            ((RadioItem) mediaItem).fixAudeoLinks(new IOperationFinishCallback() {
                @Override
                public void operationFinished() { //Succeed to fetch valid URL
                    isLinkReadyForPlaying = true;
                    prepare();
                }
            }, new IOperationFinishCallback() {
                @Override
                public void operationFinished() {  //Failed to fetch valid URL
                    isLinkReadyForPlaying = true;
                    threwError();
                }
            });
            return false;
        }
        isLinkReadyForPlaying = true;
        return true;
    }

    public void start() {
        if (mediaPlayer != null) {
            mediaPlayer.play();
        }
    }

    public void pause() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        }
    }

    public void toggle() {
        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                pause();
            } else {
                start();
            }
        }
    }

    public boolean isPlaying() {
        if (mediaPlayer != null) {
            return mediaPlayer.isPlaying();
        }
        return false;
    }


    /**
     * Restarts player in the soft way (didn't release all resurces and callbacks)
     */
    public void softRestart() {
        if (mediaPlayer != null) {
            resetPlayer();
            prepare();
        }
    }

    public void releasePlayer() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
            mediaItem = null;
        }
        isPrepaired = false;
        isLinkReadyForPlaying = false;
        isInErrorState = false;
        removeListeners();
        NotificationManager nMgr = (NotificationManager) UpodsApplication.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
        nMgr.cancelAll();
    }

    public void resetPlayer() {
        isLinkReadyForPlaying = false;
        isPrepaired = false;
        isInErrorState = false;
        if (mediaPlayer != null) {
            mediaPlayer.stop();
        }
        if (notificationPanel != null) {
            notificationPanel.notificationCancel();
        }
    }

    public void removeListeners() {
        playerStateListener = null;
        onAutonomicTrackChangeCallback = null;
        onPlayingFailedCallback = null;
    }


    public MediaItem getPlayingMediaItem() {
        return mediaItem;
    }


    public boolean isCurrentMediaItem(MediaItem mediaItem) {
        if (this.mediaItem == null) {
            return false;
        }
        if (this.mediaItem instanceof Podcast && mediaItem instanceof Podcast) {
            return GlobalUtils.safeTitleEquals(((Podcast) this.mediaItem).getSelectedTrack().getTitle(),
                    ((Podcast) mediaItem).getSelectedTrack().getTitle());
        }

        return GlobalUtils.safeTitleEquals(this.mediaItem.getName(), mediaItem.getName());

    }

    public boolean isCurrentTrack(Track track) {
        if (this.mediaItem == null || !(this.mediaItem instanceof Podcast)) {
            return false;
        }
        return ((Podcast) this.mediaItem).getSelectedTrack().getTitle().equals(track.getTitle());
    }

    public void seekTo(int ms) {
        if (mediaPlayer != null && ms >= 0) {
            mediaPlayer.setTime(ms);
        }
    }

    public void changeTrackToDirection(Direction direction) {
        if (universalPlayer.getPlayingMediaItem() instanceof Podcast) {
            changeTrackToDirectionTrackable(direction);
        } else if (universalPlayer.getPlayingMediaItem() instanceof RadioItem) {
            changeTrackToDirectionRadio(direction);
        }
        if (onAutonomicTrackChangeCallback != null) {
            onAutonomicTrackChangeCallback.operationFinished();
        }
    }

    private void changeTrackToDirectionTrackable(Direction direction) {
        Track currentTrack = ((Podcast) mediaItem).getSelectedTrack();
        ArrayList<? extends Track> tracks = ((Podcast) mediaItem).getTracks();
        for (int i = 0; i < tracks.size(); i++) {
            if (currentTrack.getTitle().equals(tracks.get(i).getTitle())) {
                int changeTo = MediaUtils.calculateNextTrackNumber(direction, i, tracks.size() - 1);
                ((Podcast) mediaItem).selectTrack(tracks.get(changeTo));
                resetPlayer();
                setMediaItem(mediaItem);
                prepare();
                break;
            }
        }
    }

    private void changeTrackToDirectionRadio(Direction direction) {
        List<? extends MediaItem> mediaItems = ProfileManager.getInstance().getRecentRadioItems();
        for (int i = 0; i < mediaItems.size(); i++) {
            MediaItem iPlayableMediaItem = mediaItems.get(i);
            if (iPlayableMediaItem.getName().equals(mediaItem.getName())) {
                int changeTo = MediaUtils.calculateNextTrackNumber(direction, i, mediaItems.size() - 1);
                resetPlayer();
                setMediaItem(mediaItems.get(changeTo));
                prepare();
                break;
            }
        }
    }

    public int getCurrentPosition() {
        return mediaPlayer != null ? (int) mediaPlayer.getTime() : 0;
    }

    private void threwError() {
        isInErrorState = true;
        if (onPlayingFailedCallback != null) {
            onPlayingFailedCallback.operationFinished();
        }
    }


    private void runReconnectTask() {
        if (autoReconector == null) {
            autoReconector = new TimerTask() {
                @Override
                public void run() {
                    if (GlobalUtils.isInternetConnected()) {
                        Logger.printInfo(PLAYER_LOG, "Reconnector -> got internet connection -> restarting player...");
                        softRestart();
                        autoReconector.cancel();
                        autoReconector = null;
                    } else {
                        Logger.printInfo(PLAYER_LOG, "Reconnector -> no internet connection -> will try again later...");
                    }
                }
            };
        }
        new Timer().scheduleAtFixedRate(autoReconector, 0, RECONNECT_RATE);
    }

    @Override
    public void onEvent(MediaPlayer.Event event) {
        if (!isPrepaired && mediaItem instanceof Podcast) {//Try to continue for last position
            int lastPosition = ProfileManager.getInstance().getTrackPosition(((Podcast) mediaItem).getSelectedTrack());
            if (lastPosition > 0) {
                seekTo(lastPosition);
            }
        }
        if (event.type == MediaPlayer.Event.Playing) {
            isPrepaired = true;
            if (playerStateListener != null) {
                playerStateListener.onStateChanged(State.PLAYING);

                //To make sure notification panel is in correct state
                if (notificationPanel != null && notificationPanel.getCurrentState() != State.PLAYING) {
                    notificationPanel.updateNotificationStatus(State.PLAYING);
                }
            }
        } else if (event.type == MediaPlayer.Event.Paused) {
            isPrepaired = true;
            if (playerStateListener != null) {
                playerStateListener.onStateChanged(State.PAUSED);

                //To make sure notification panel is in correct state
                if (notificationPanel != null && notificationPanel.getCurrentState() != State.PAUSED) {
                    notificationPanel.updateNotificationStatus(State.PAUSED);
                }
            }
        } else if (event.type == MediaPlayer.Event.EndReached) {
            if (mediaPlayer.getLength() == 0) {
                threwError();
                if (playerStateListener != null) {
                    playerStateListener.onStateChanged(State.END_REACHED);
                }
            } /*else {
                changeTrackToDirection(Direction.RIGHT);
            }*/
        } else if (event.type == MediaPlayer.Event.EncounteredError) {
            if (!GlobalUtils.isInternetConnected()) {
                runReconnectTask();
            }
        }
        //Logger.printInfo(PLAYER_LOG, "VLC event" + String.valueOf(event.type));
    }


} public class DialogFragmentTrackInfo extends DialogFragment {

    public static final String TAG = "df_track_info";
    public boolean enableStream = true;

    private View.OnClickListener streamClickListener;
    private Track track;


    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View mainView = inflater.inflate(R.layout.dialog_fragment_track_info, null);
        WebView wbSummary = (WebView) mainView.findViewById(R.id.wbTrackInfo);
        wbSummary.getSettings().setJavaScriptEnabled(true);
        wbSummary.loadDataWithBaseURL(
                "file://" + Environment.getExternalStorageDirectory(),
                track.getInfo(), "text/html", "utf-8", "");
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(track.getTitle())
                .setPositiveButton(R.string.ok,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.dismiss();
                            }
                        });
        if (enableStream) {
            builder.setNegativeButton(R.string.stream,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            streamClickListener.onClick(null);
                        }
                    }).setView(mainView);
        }
        return builder.create();
    }

    public void setTrack(Track track) {
        this.track = track;
    }

    public void setStreamClickListener(View.OnClickListener streamClickListener) {
        this.streamClickListener = streamClickListener;
    }
} public interface IContentLoadListener {

    void onContentLoaded();
} public interface IContextMenuManager {

    /**
     * @param view       - which shows context menu
     * @param type       - type of context menu to show
     * @param dataToPass - data which will be used for actions inside menu
     */
    void openContextMenu(View view, ContextMenuType type, Object dataToPass, IOperationFinishCallback contextItemClicked);
} public interface IFragmentsManager {

    enum FragmentOpenType {REPLACE, OVERLAY}

    enum FragmentAnimationType {DEFAULT, BOTTOM_TOP}


    void showFragment(int id, Fragment fragment, String tag, FragmentOpenType openType, FragmentAnimationType animationType);

    void showFragment(int id, Fragment fragment, String tag);

    void hideFragment(Fragment fragment);

    void showDialogFragment(DialogFragment dialogFragment);

    boolean hasFragment(String tag);

    String getLatestFragmentTag();
} public interface IOperationFinishCallback {

    void operationFinished();
} public interface IUIProgressUpdater {

    void updateProgressUI(double progress);
} public class Episode extends Track {
    private static final String TABLE = "episodes";
    private static String EPISODE_LOG = "EPISODE";
    private String summary;
    private String length;
    private String duration;
    private String date;
    private String pathOnDisk;

    //Not in DB
    public boolean isNew;
    public boolean isDownloaded;

    public Episode() {
        super();
        this.summary = "";
        this.length = "";
        this.duration = "";
        this.date = "";
        this.pathOnDisk = "";
        this.isNew = false;
        this.isDownloaded = false;
    }

    public Episode(JSONObject jsonItem) {
        this();
        try {
            this.title = jsonItem.has("title") ? jsonItem.getString("title") : "";
            this.title = this.title.replace("\n", "").trim();
            this.summary = jsonItem.has("summary") ? jsonItem.getString("summary") : "";
            this.length = jsonItem.has("length") ? jsonItem.getString("length") : "";
            this.duration = jsonItem.has("duration") ? jsonItem.getString("duration") : "";
            this.date = jsonItem.has("date") ? jsonItem.getString("date") : "";
            this.pathOnDisk = jsonItem.has("pathOnDisk") ? jsonItem.getString("pathOnDisk") : "";
            this.audeoUrl = jsonItem.has("audeoUrl") ? jsonItem.getString("audeoUrl") : "";
        } catch (JSONException e) {
            Logger.printError(EPISODE_LOG, "Can't parse episod from json");
            e.printStackTrace();
        }
    }

    public long save(long podcastId) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("podcast_id", podcastId);
        values.put("title", title);
        values.put("summary", summary);
        values.put("length", length);
        values.put("duration", duration);
        values.put("date", date);
        values.put("pathOnDisk", pathOnDisk);

        id = database.insert(TABLE, null, values);
        isExistsInDb = true;

        return id;
    }

    public JSONObject toJson() {
        JSONObject jsonEpisod = new JSONObject();
        try {
            jsonEpisod.put("title", this.title);
            jsonEpisod.put("summary", this.summary);
            jsonEpisod.put("length", this.length);
            jsonEpisod.put("duration", this.duration);
            jsonEpisod.put("date", this.date);
            jsonEpisod.put("pathOnDisk", this.pathOnDisk);
            jsonEpisod.put("audeoUrl", this.audeoUrl);
        } catch (JSONException e) {
            Logger.printError(EPISODE_LOG, "Can't save episod to json");
            e.printStackTrace();
        }
        return jsonEpisod;
    }

    public static JSONArray toJSONArray(ArrayList<Episode> episodes, boolean onlySavedInDb) {
        JSONArray episodesJson = new JSONArray();
        for (Episode episode : episodes) {
            if (onlySavedInDb && (episode.isDownloaded || episode.isNew)) {
                episodesJson.put(episode.toJson());
            } else if (!onlySavedInDb) {
                episodesJson.put(episode.toJson());
            }
        }
        return episodesJson;
    }

    public String getDuration() {
        return duration;
    }

    public void setDuration(String duration) {
        if (duration.matches("^[0-9]{3,5}$")) {//Time was given in seconds
            duration = MediaUtils.formatMsToTimeString(Integer.valueOf(duration) * 1000);
        }
        this.duration = duration;
    }

    public String getLength() {
        return length;
    }

    public void setLength(String length) {
        this.length = length;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public void setPathOnDisk(String pathOnDisk) {
        this.pathOnDisk = pathOnDisk;
    }

    public void remove() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String args[] = {String.valueOf(id)};
        database.delete("episodes", "id = ?", args);
        isDownloaded = false;
        isNew = false;
        isExistsInDb = false;
        id = -1;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public String getSubTitle() {
        String size;
        try {
            size = Formatter.formatShortFileSize(UpodsApplication.getContext(), Long.valueOf(length));
        } catch (NumberFormatException e) {
            size = length;
        }
        return duration + " / " + size;
    }

    @Override
    public String getInfo() {
        return summary;
    }

    @Override
    public String getAudeoUrl() {
        if (pathOnDisk != null && !pathOnDisk.isEmpty()) {
            return pathOnDisk;
        }
        return audeoUrl;
    }

    @Override
    public String getDate() {
        return this.date;
    }

    public static void saveList(long podcastId, ArrayList<Episode> episodes) {
        if (episodes.size() > 0) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            database.beginTransaction();
            for (Episode episode : episodes) {
                if (episode.isDownloaded || episode.isNew) {
                    episode.save(podcastId);
                }
            }
            database.endTransaction();
        }
    }

    public static ArrayList<Episode> withPodcastId(long podcastId) {
        ArrayList<Episode> allEpisodes = new ArrayList<>();
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {String.valueOf(podcastId)};
        Cursor cursor = database.rawQuery("SELECT e.*, per.type FROM podcasts_episodes_rel as per\n" +
                "LEFT JOIN episodes  as e \n" +
                "ON e.id = per.episode_id WHERE per.podcast_id = ?", args);
        while (cursor.moveToNext()) {
            Episode episode = Episode.getEpisodByTitle(allEpisodes, cursor.getString(cursor.getColumnIndex("title")));
            if (episode == null) {
                episode = new Episode();
                episode.isExistsInDb = true;
                episode.id = cursor.getLong(cursor.getColumnIndex("id"));
                episode.title = cursor.getString(cursor.getColumnIndex("title"));
                episode.summary = cursor.getString(cursor.getColumnIndex("summary"));
                episode.length = cursor.getString(cursor.getColumnIndex("length"));
                episode.duration = cursor.getString(cursor.getColumnIndex("duration"));
                episode.date = cursor.getString(cursor.getColumnIndex("date"));
                episode.pathOnDisk = cursor.getString(cursor.getColumnIndex("pathOnDisk"));
            }

            if (cursor.getString(cursor.getColumnIndex("type")).equals(MediaListItem.DOWNLOADED)) {
                episode.isDownloaded = true;
            } else if (cursor.getString(cursor.getColumnIndex("type")).equals(MediaListItem.NEW)) {
                episode.isNew = true;
            }

            allEpisodes.add(episode);
        }
        cursor.close();
        return allEpisodes;
    }

    public static boolean hasEpisodWithTitle(ArrayList<Episode> episodes, Episode episodeToCheck) {
        for (Episode episode : episodes) {
            if (GlobalUtils.safeTitleEquals(episode.getTitle(), episodeToCheck.getTitle())) {
                return true;
            }
        }
        return false;
    }

    public static Episode getEpisodByTitle(ArrayList<Episode> episodes, String titleToCheck) {
        for (Episode episode : episodes) {
            if (GlobalUtils.safeTitleEquals(episode.getTitle(), (titleToCheck))) {
                return episode;
            }
        }
        return null;
    }

    public static void syncDbWithLatestEpisodes(Podcast podcast, ArrayList<Episode> currentEpisodes) {
        ArrayList<Episode> episodesInDb = withPodcastId(podcast.id);
        for (Episode episode : episodesInDb) {
            Episode episodeInCurrentFeed = Episode.getEpisodByTitle(currentEpisodes, episode.getTitle());
            if (episodeInCurrentFeed == null) {
                ProfileManager.getInstance().removeNewTrack(podcast, episode);
            }
        }
    }

} public abstract class MediaItem extends SQLModel implements IMediaItemView {

    /**
     * Created by Alon Zilberman on 10/23/15.
     * Use it to transfer MediaItems and tracks in one object
     */
    public static class MediaItemBucket {
        public MediaItem mediaItem;
        public Track track;
    }

    protected String name;
    protected String coverImageUrl;
    protected float score;


    public boolean isSubscribed;

    public MediaItem() {
    }

    public String getCoverImageUrl() {
        return coverImageUrl;
    }

    public String getName() {
        return name;
    }

    public String getSubHeader() {
        return "";
    }

    public String getBottomHeader() {
        return "";
    }

    public boolean hasTracks() {
        return false;
    }

    public String getDescription() {
        return "";
    }

    public String getAudeoLink() {
        return null;
    }

    public String getBitrate() {
        return "";
    }


    public void syncWithDB(){

    }

    public void syncWithMediaItem(MediaItem updatedMediaItem) {
        this.id = updatedMediaItem.id;
        this.isSubscribed = updatedMediaItem.isSubscribed;
        this.isExistsInDb = updatedMediaItem.isExistsInDb;
    }

    public static boolean hasMediaItemWithName(ArrayList<? extends MediaItem> mediaItems, MediaItem mediaItemToCheck) {
        for (MediaItem mediaItem : mediaItems) {
            if (mediaItem.getName().equals(mediaItemToCheck.getName())) {
                return true;
            }
        }
        return false;
    }

    public static MediaItem getMediaItemByName(ArrayList<? extends MediaItem> mediaItems, MediaItem mediaItemTarget) {
        return getMediaItemByName(mediaItems, mediaItemTarget.getName());
    }

    public static MediaItem getMediaItemByName(ArrayList<? extends MediaItem> mediaItems, String targetName) {
        for (MediaItem mediaItem : mediaItems) {
            if (GlobalUtils.safeTitleEquals(mediaItem.getName(), targetName)) {
                return mediaItem;
            }
        }
        return null;
    }

    public static ArrayList<String> getIds(ArrayList<? extends MediaItem> mediaItems) {
        ArrayList<String> ids = new ArrayList<>();
        for (MediaItem mediaItem : mediaItems) {
            ids.add(String.valueOf(mediaItem.id));
        }
        return ids;
    }

    public float getScore(){
        return score;
    }
} public class Podcast extends MediaItem {
    public static final String TABLE = "podcasts";
    private static String PODCAST_LOG = "PODCAST";
    protected String name;
    protected String censoredName;
    protected String artistName;
    protected String feedUrl;
    protected String releaseDate;
    protected String explicitness;
    protected String trackCount;
    protected String country;
    protected String genre;
    protected String description;

    protected ArrayList<Episode> episodes;

    public boolean isDownloaded;
    public boolean hasNewEpisodes;

    public Podcast() {
        super();
        this.episodes = new ArrayList<>();
        this.censoredName = "";
        this.artistName = "";
        this.feedUrl = "";
        this.releaseDate = "";
        this.explicitness = "";
        this.trackCount = "0";
        this.genre = "";
        this.description = "";
        this.score = 4.5f;
    }

    public Podcast(String name, String feedUrl) {
        this();
        this.name = name.replace("\n", "").trim();
        this.feedUrl = feedUrl;
    }


    public Podcast(JSONObject jsonItem) {
        this();
        try {
            this.description = jsonItem.has("description") ? jsonItem.getString("description") : "";

            if (jsonItem.has("episodes")) {
                JSONArray jepisodes = jsonItem.getJSONArray("episodes");
                for (int i = 0; i < jepisodes.length(); i++) {
                    episodes.add(new Episode(jepisodes.getJSONObject(i)));
                }
            }

            if (jsonItem.has("kind")) { //Itunes
                this.id = jsonItem.has("trackId") ? jsonItem.getInt("trackId") : 0;
                this.name = jsonItem.has("collectionName") ? jsonItem.getString("collectionName") : "";
                this.name = name.replace("\n", "").trim();
                this.censoredName = jsonItem.has("collectionCensoredName") ? jsonItem.getString("collectionCensoredName") : "";
                this.artistName = jsonItem.has("artistName") ? jsonItem.getString("artistName") : "";
                this.feedUrl = jsonItem.has("feedUrl") ? jsonItem.getString("feedUrl") : "";
                this.coverImageUrl = jsonItem.has("artworkUrl600") ? jsonItem.getString("artworkUrl600") : "";
                if (this.coverImageUrl.isEmpty()) {
                    this.coverImageUrl = jsonItem.has("artworkUrl100") ? jsonItem.getString("artworkUrl100") : "";
                }
                if (this.coverImageUrl.isEmpty()) {
                    this.coverImageUrl = jsonItem.has("artworkUrl60") ? jsonItem.getString("artworkUrl60") : "";
                }
                this.country = jsonItem.has("country") ? jsonItem.getString("country") : "";
                this.releaseDate = jsonItem.has("releaseDate") ? jsonItem.getString("releaseDate") : "";
                this.explicitness = jsonItem.has("collectionExplicitness") ? jsonItem.getString("collectionExplicitness") : "";
                this.trackCount = jsonItem.has("trackCount") ? jsonItem.getString("trackCount") : "";
                this.genre = jsonItem.has("primaryGenreName") ? jsonItem.getString("primaryGenreName") : "";
                this.score = 4.5f;
            } else {//Our backend
                this.id = jsonItem.has("id") ? jsonItem.getInt("id") : 0;
                this.name = jsonItem.has("name") ? jsonItem.getString("name") : "";
                this.name = name.replace("\n", "").trim();
                this.censoredName = jsonItem.has("censored_name") ? jsonItem.getString("censored_name") : "";
                this.artistName = jsonItem.has("artist_name") ? jsonItem.getString("artist_name") : "";
                this.feedUrl = jsonItem.has("feed_url") ? jsonItem.getString("feed_url") : "";
                this.coverImageUrl = jsonItem.has("image_url") ? jsonItem.getString("image_url") : "";
                this.country = jsonItem.has("country") ? jsonItem.getString("country") : "";
                this.releaseDate = jsonItem.has("release_date") ? jsonItem.getString("release_date") : "";
                this.explicitness = jsonItem.has("explicitness") ? jsonItem.getString("explicitness") : "";
                this.trackCount = jsonItem.has("track_count") ? jsonItem.getString("track_count") : "";
                this.score = jsonItem.has("score") ? (float) jsonItem.getDouble("score") : 4.5f;
                if (jsonItem.has("genres") && jsonItem.getJSONArray("genres").length() > 0) {
                    this.genre = jsonItem.getJSONArray("genres").getString(0);
                }
                if (jsonItem.has("episodes")) {
                    JSONArray jsonEpisodes = jsonItem.getJSONArray("episodes");
                    for (int i = 0; i < jsonEpisodes.length(); i++) {
                        this.episodes.add(new Episode(jsonEpisodes.getJSONObject(i)));
                    }
                }
            }
            this.name = this.name.replace("\n", "").trim();
        } catch (Exception e) {
            Logger.printError(PODCAST_LOG, "Can't parse podcast from json");
            e.printStackTrace();
        }
    }

    public Podcast(Podcast podcast) {
        this.name = podcast.getName().replace("\n", "").trim();
        this.censoredName = podcast.getCensoredName();
        this.artistName = podcast.getArtistName();
        this.feedUrl = podcast.getFeedUrl();
        this.coverImageUrl = podcast.getCoverImageUrl();
        this.releaseDate = podcast.getReleaseDate();
        this.explicitness = podcast.getExplicitness();
        this.trackCount = podcast.getTrackCount();
        this.country = podcast.getCountry();
        this.genre = podcast.getGenre();
        this.id = podcast.id;
        this.isExistsInDb = podcast.isExistsInDb;
        this.isSubscribed = podcast.isSubscribed;
        this.isDownloaded = podcast.isDownloaded;
        this.hasNewEpisodes = podcast.hasNewEpisodes;
        this.score = podcast.score;
        this.episodes = new ArrayList<Episode>(podcast.episodes);
    }


    public void setName(String name) {
        this.name = name;
    }

    public String getCensoredName() {
        return censoredName;
    }

    public String getFeedUrl() {
        return feedUrl;
    }

    public String getArtistName() {
        return artistName;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public String getTrackCount() {
        return trackCount;
    }

    public void setTrackCount(String trackCount) {
        this.trackCount = trackCount;
    }

    public String getExplicitness() {
        return explicitness;
    }

    public String getCountry() {
        return country;
    }

    public String getGenre() {
        return genre;
    }

    public ArrayList<Episode> getEpisodes() {
        return episodes;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setTracks(ArrayList<? extends Track> tracks) {
        if (this.episodes != null) {
            this.episodes.clear();
        }
        this.episodes = (ArrayList<Episode>) tracks;
    }

    public ArrayList<? extends Track> getTracks() {
        return this.episodes;
    }

    public String getTracksFeed() {
        return this.feedUrl;
    }

    public Track getSelectedTrack() {
        for (Episode episode : episodes) {
            if (episode.isSelected) {
                return episode;
            }
        }
        return null;
    }

    public int getNewEpisodsCount() {
        int count = 0;
        for (Episode episode : episodes) {
            if (episode.isNew) {
                count++;
            }
        }
        return count;
    }

    public int getDownloadedEpisodsCount() {
        int count = 0;
        for (Episode episode : episodes) {
            if (episode.isDownloaded) {
                count++;
            }
        }
        return count;
    }

    public String getDownloadedDirectory() {
        for (Episode episode : episodes) {
            if (episode.isDownloaded) {
                File podcastFolder = new File(episode.getAudeoUrl());
                return podcastFolder.getParentFile().getPath();
            }
        }
        return null;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getCoverImageUrl() {
        return coverImageUrl != null && coverImageUrl.isEmpty() ? null : coverImageUrl;
    }

    @Override
    public String getSubHeader() {
        return this.artistName;
    }

    @Override
    public String getBottomHeader() {
        return this.country;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public String getAudeoLink() {
        for (Episode episode : episodes) {
            if (episode.isSelected) {
                return episode.getAudeoUrl();
            }
        }
        return episodes.get(0).getAudeoUrl();
    }

    @Override
    public String getBitrate() {
        return "";
    }

    @Override
    public boolean hasTracks() {
        return true;
    }

    public long save() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("censored_name", censoredName);
        values.put("artist_name", artistName);
        values.put("description", description);
        values.put("feed_url", feedUrl);
        values.put("cover_image_url", coverImageUrl);
        values.put("release_date", releaseDate);
        values.put("explicitness", explicitness);
        values.put("country", country);
        values.put("genre", genre);
        values.put("track_count", trackCount);
        values.put("score", score);

        id = database.insert(TABLE, null, values);
        isExistsInDb = true;

        return id;
    }

    public JSONObject toJSON() {
        JSONObject podcast = new JSONObject();
        try {
            podcast.put("id", this.id);
            podcast.put("name", this.name);
            podcast.put("censored_name", this.censoredName);
            podcast.put("artist_name", this.artistName);
            podcast.put("feed_url", this.feedUrl);
            podcast.put("image_url", this.coverImageUrl);
            podcast.put("country", this.country);
            podcast.put("release_date", this.releaseDate);
            podcast.put("explicitness", this.explicitness);
            podcast.put("track_count", this.trackCount);
            podcast.put("genre", this.genre);
            podcast.put("description", this.description);
            podcast.put("score", this.score);
            //podcast.put("episodes", Episode.toJSONArray(this.episodes, true));
        } catch (JSONException e) {
            Logger.printError(PODCAST_LOG, "Can't convert podcast to json");
            e.printStackTrace();
        }
        return podcast;
    }

    public void selectTrack(Track track) {
        for (Episode episode : episodes) {
            episode.isSelected = episode.equals(track) ? true : false;
        }
    }

    public static boolean isExistsWithName(String name) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String[] args = {name};
        Cursor cursor = database.rawQuery("SELECT * from podcasts where name = ? LIMIT 1", args);
        cursor.moveToFirst();
        return cursor.getCount() > 0;
    }

    public static JSONArray toJsonArray(ArrayList<Podcast> podcasts) {
        JSONArray jsonPodcasts = new JSONArray();
        for (Podcast podcast : podcasts) {
            jsonPodcasts.put(podcast.toJSON());
        }
        return jsonPodcasts;
    }

    public static Podcast withCursor(Cursor cursor) {
        Podcast podcast = new Podcast();
        podcast.isExistsInDb = true;
        podcast.id = cursor.getLong(cursor.getColumnIndex("id"));
        podcast.name = cursor.getString(cursor.getColumnIndex("name"));
        podcast.censoredName = cursor.getString(cursor.getColumnIndex("censored_name"));
        podcast.artistName = cursor.getString(cursor.getColumnIndex("artist_name"));
        podcast.description = cursor.getString(cursor.getColumnIndex("description"));
        podcast.feedUrl = cursor.getString(cursor.getColumnIndex("feed_url"));
        podcast.coverImageUrl = cursor.getString(cursor.getColumnIndex("cover_image_url"));
        podcast.releaseDate = cursor.getString(cursor.getColumnIndex("release_date"));
        podcast.explicitness = cursor.getString(cursor.getColumnIndex("explicitness"));
        podcast.country = cursor.getString(cursor.getColumnIndex("country"));
        podcast.genre = cursor.getString(cursor.getColumnIndex("genre"));
        podcast.trackCount = cursor.getString(cursor.getColumnIndex("track_count"));
        podcast.score = cursor.getFloat(cursor.getColumnIndex("score"));

        podcast.episodes.addAll(Episode.withPodcastId(podcast.id));

        return podcast;
    }

    public static ArrayList<Podcast> withJsonArray(JSONArray jsonPodcastsItems) {
        ArrayList<Podcast> items = new ArrayList<Podcast>();
        try {
            for (int i = 0; i < jsonPodcastsItems.length(); i++) {
                JSONObject podcastItem = (JSONObject) jsonPodcastsItems.get(i);
                items.add(new Podcast(podcastItem));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return items;
    }

    @Override
    public void syncWithMediaItem(MediaItem updatedMediaItem) {
        super.syncWithMediaItem(updatedMediaItem);
        Podcast updatedPodcast = (Podcast) updatedMediaItem;
        this.hasNewEpisodes = updatedPodcast.hasNewEpisodes;
        this.isDownloaded = updatedPodcast.isDownloaded;
        this.episodes.clear();
        this.episodes.addAll(updatedPodcast.episodes);
    }

    @Override
    public void syncWithDB() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String args[] = {String.valueOf(id), MediaListItem.TYPE_PODCAST};
        Cursor cursor = database.rawQuery("SELECT * FROM media_list WHERE media_id = ? AND media_type = ?", args);

        this.isDownloaded = false;
        this.isSubscribed = false;
        this.hasNewEpisodes = false;
        while (cursor.moveToNext()) {
            String listType = cursor.getString(cursor.getColumnIndex("list_type"));
            if (listType.equals(MediaListItem.NEW)) {
                this.hasNewEpisodes = true;
            } else if (listType.equals(MediaListItem.SUBSCRIBED)) {
                this.isSubscribed = true;
            } else if (listType.equals(MediaListItem.DOWNLOADED)) {
                this.isDownloaded = true;
            }
        }
        cursor.close();

        ArrayList<Episode> existingEpisodes = Episode.withPodcastId(id);
        episodes.clear();

        for (Episode episode : existingEpisodes) {
            Episode podcastEpisode = Episode.getEpisodByTitle(episodes, episode.getTitle());
            if (podcastEpisode == null) {
                episodes.add(episode);
            } else {
                podcastEpisode.isDownloaded = episode.isDownloaded;
                podcastEpisode.isNew = episode.isNew;
            }
        }

    }

    public static void syncWithDb(ArrayList<Podcast> podcasts) {
        ArrayList<MediaListItem> listItems = MediaListItem.withMediaType(MediaListItem.TYPE_PODCAST);
        for (MediaListItem listItem : listItems) {
            Podcast podcast = (Podcast) MediaItem.getMediaItemByName(podcasts, listItem.mediaItemName);
            if (podcast != null) {
                podcast.id = listItem.id;
                podcast.isExistsInDb = true;
                if (listItem.listType.equals(MediaListItem.SUBSCRIBED)) {
                    podcast.isSubscribed = true;
                } else if (listItem.listType.equals(MediaListItem.DOWNLOADED)) {
                    podcast.isDownloaded = true;
                } else if (listItem.listType.equals(MediaListItem.NEW)) {
                    podcast.hasNewEpisodes = true;
                }
                ArrayList<Episode> existingEpisodes = Episode.withPodcastId(podcast.id);
                for (Episode episode : existingEpisodes) {
                    Episode podcastEpisode = Episode.getEpisodByTitle(podcast.episodes, episode.getTitle());
                    if (podcastEpisode == null) {
                        podcast.episodes.add(episode);
                    } else {
                        podcastEpisode.isDownloaded = episode.isDownloaded;
                        podcastEpisode.isNew = episode.isNew;
                    }
                }
            }
        }
    }


    public static void syncNewEpisodes(ArrayList<Podcast> podcasts) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {MediaListItem.TYPE_PODCAST, MediaListItem.NEW};
        Cursor cursor = database.rawQuery("SELECT p.id, p.name, m.list_type FROM podcasts as p " +
                "LEFT JOIN media_list as m ON p.id = m.media_id " +
                "WHERE m.media_type =  ? AND m.list_type =  ?", args);
        while (cursor.moveToNext()) {
            long id = cursor.getLong(cursor.getColumnIndex("id"));
            for (Podcast podcast : podcasts) {
                if (podcast.id == id) {
                    podcast.hasNewEpisodes = true;
                    ArrayList<Episode> existingEpisodes = Episode.withPodcastId(podcast.id);
                    podcast.getEpisodes().addAll(existingEpisodes);
                }
            }
        }
        cursor.close();
    }

    public static boolean hasEpisodeWithTitle(Podcast podcast, Episode episodeToCheck) {
        for (Episode episode : podcast.getEpisodes()) {
            if (GlobalUtils.safeTitleEquals(episode.getTitle(), episodeToCheck.getTitle())) {
                return true;
            }
        }
        return false;
    }
} public abstract class Track extends SQLModel {

    protected String title;
    protected String audeoUrl;

    protected boolean isSelected;

    public Track() {
        this.title = "";
        this.audeoUrl = "";
        this.isSelected = false;
    }

    public abstract String getDuration();

    public abstract String getDate();

    public abstract String getTitle();

    public abstract String getAudeoUrl();

    public abstract String getSubTitle();

    public abstract String getInfo();

    public void setTitle(String title) {
        this.title = title;
    }

    public void setAudeoUrl(String audeoUrl) {
        this.audeoUrl = audeoUrl;
    }
} public class Analytics {


    //Sliding menu
    public static final String SLIDING_MENU_SETTINGS_CLICK = "sliding_menu_settings_click";
    public static final String SLIDING_MENU_PODCASTS_CLICK = "sliding_menu_podcasts_click";
    public static final String SLIDING_MENU_RADIO_CLICK = "sliding_menu_radio_click";
    public static final String SLIDING_MENU_PROFILE_CLICK = "sliding_profile_click";
    public static final String SLIDING_MENU_HELP_CLICK = "sliding_menu_help_click";
    public static final String SLIDING_MENU_RATE_CLICK = "sliding_menu_rate_click";
    public static final String SLIDING_MENU_ABOUT_CLICK = "sliding_about_click";

    //Main screen
    public static final String FEATURED_BUTTON_MORE = "featured_btn_more_click";

    //Middle screen
    public static final String MIDDLE_SCREEN_UNSUBSCRIBE = "middle_screen_subscribe";
    public static final String MIDDLE_SCREEN_SUBSCRIBE = "middle_screen_unsubscribe";
    public static final String MIDDLE_SCREEN_PLAY_RADIO = "middle_screen_play_radio";
    public static final String MIDDLE_SCREEN_PLAY_EPISODE = "middle_screen_play_episode";
    public static final String MIDDLE_SCREEN_DOWNLOAD_EPISODE = "middle_screen_download_episode";
    public static final String MIDDLE_SCREEN_CANCELL_DOWNLOAD_EPISODE = "middle_screen_cancel_download";
    public static final String MIDDLE_SCREEN_ABOUT_PODCAST = "middle_screen_about_podcast";
    public static final String MIDDLE_SCREEN_OPEN_ON_DISK = "middle_screen_open_disk";
    public static final String MIDDLE_REMOVE_EPISODE = "middle_screen_remove_episode";
    public static final String MIDDLE_REMOVE_ALL_EPISODES = "middle_screen_rempve_all";

    //Playlist
    public static final String PLAYLIST_TOGGLE = "playlist_toggle";
    public static final String PLAYLIST_ITEM_CLICK = "playlist_item_click";

    //Player
    public static final String PLAYER_STREAM_QUALITY = "player_stream_quality";
    public static final String PLAYER_STREAM_INFO = "player_stream_info";
    public static final String PLAYER_SUBSCRIBE = "player_subscribe";
    public static final String PLAYER_UNSUBSCRIBE = "player_unsubscribe";

    public static String PLAY_PREFIX = "play_";
} public class GlobalUtils {

    public static boolean safeTitleEquals(String title1, String title2) {
        if (title1 == null || title2 == null) {
            return false;
        }
        //replaceAll("[^a-z,A-Z,0-9,.,+,-, ,!,?,_,:,;,=]", "");
        title1 = title1.trim().replace("'","");
        title2 = title2.trim().replace("'","");;
        return title1.equals(title2);
    }

    public static String getCurrentDateTimeUS() {
        DateFormat df = new SimpleDateFormat("d MMM, HH:mm", Locale.US);
        String date = df.format(Calendar.getInstance().getTime());
        Logger.printInfo("Sync date", date);
        return date;
    }

    public static String parserDateToMonth(String date) {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss Z", Locale.US);
            Date inputDate = dateFormat.parse(date);
            dateFormat = new SimpleDateFormat("MMM\ndd", Locale.US);
            date = dateFormat.format(inputDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    public static String getCleanFileName(String str) {
        str = str.toLowerCase();
        return str.replaceAll("[^a-zA-Z0-9]+", "_");
    }

    public static boolean deleteDirectory(File path) {
        if (path.exists()) {
            File[] files = path.listFiles();
            if (files == null) {
                return true;
            }
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                } else {
                    files[i].delete();
                }
            }
        }
        return (path.delete());
    }

    public static boolean isInternetConnected() {
        ConnectivityManager cm = (ConnectivityManager) UpodsApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo() != null;
    }

    /**
     * Returns a list with all links contained in the input
     */
    public static List<String> extractUrls(String text) {
        String lines[] = text.split("\\s+");
        List<String> containedUrls = new ArrayList<String>();
        String urlRegex = "((https?|ftp|gopher|telnet|file):((//)|(\\\\))+[\\w\\d:#@%/;$()~_?\\+-=\\\\\\.&]*)";
        Pattern pattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE);

        for (String line : lines) {
            Matcher urlMatcher = pattern.matcher(line);
            while (urlMatcher.find()) {
                containedUrls.add(line.substring(urlMatcher.start(0), urlMatcher.end(0)));
            }
        }
        return containedUrls;
    }

    public static boolean isUrlReachable(String urlToCheck) {
        try {
            URL url = new URL(urlToCheck);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(1000);
            int code = connection.getResponseCode();
            if (code == 200) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void rateApp(Context context) {
        Uri uri = Uri.parse("market://details?id=" + context.getPackageName());
        Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);

        goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        try {
            context.startActivity(goToMarket);
        } catch (ActivityNotFoundException e) {
            context.startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse("http://play.google.com/store/apps/details?id=" + context.getPackageName())));
        }
    }

    public static boolean isPackageInstalled(String packagename) {
        PackageManager pm = UpodsApplication.getContext().getPackageManager();
        try {
            pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    public static String upperCase(String str) {
        StringBuilder rackingSystemSb = new StringBuilder(str.toLowerCase());
        rackingSystemSb.setCharAt(0, Character.toUpperCase(rackingSystemSb.charAt(0)));
        str = rackingSystemSb.toString();
        return str;
    }

} public enum ContextMenuType {

    CARD_DEFAULT, EPISODE_MIDDLE_SCREEN, PODCAST_MIDDLE_SCREEN, PLAYER_SETTINGS, PROFILE
} public enum MediaItemType {

    DEFAULT, PODCAST, RADIO, PODCAST_DOWNLOADED, RADIO_SUBSCRIBED, RADIO_RECENT, PODCAST_FAVORITE, RADIO_SEARCH, PODCAST_SEARCH
}
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.chickenkiller.upods2.R;
import com.chickenkiller.upods2.activity.ActivityMain;
import com.chickenkiller.upods2.activity.ActivityPlayer;
import com.chickenkiller.upods2.activity.BasicActivity;
import com.chickenkiller.upods2.controllers.app.ProfileManager;
import com.chickenkiller.upods2.controllers.internet.DownloadMaster;
import com.chickenkiller.upods2.controllers.player.UniversalPlayer;
import com.chickenkiller.upods2.dialogs.DialogFragmentTrackInfo;
import com.chickenkiller.upods2.interfaces.IContentLoadListener;
import com.chickenkiller.upods2.interfaces.IContextMenuManager;
import com.chickenkiller.upods2.interfaces.IFragmentsManager;
import com.chickenkiller.upods2.interfaces.IOperationFinishCallback;
import com.chickenkiller.upods2.interfaces.IUIProgressUpdater;
import com.chickenkiller.upods2.models.Episode;
import com.chickenkiller.upods2.models.MediaItem;
import com.chickenkiller.upods2.models.Podcast;
import com.chickenkiller.upods2.models.Track;
import com.chickenkiller.upods2.utils.Analytics;
import com.chickenkiller.upods2.utils.GlobalUtils;
import com.chickenkiller.upods2.utils.enums.ContextMenuType;
import com.chickenkiller.upods2.utils.enums.MediaItemType;
import com.yandex.metrica.YandexMetrica;
import java.util.ArrayList;
import java.util.List;
import at.grabner.circleprogress.CircleProgressView;

            //Mark new episodes
            if (iPlayableMediaItem instanceof Podcast && ((Episode) currentTrack).isNew) {
                viewHolderTrack.tvTitle.setText(Html.fromHtml("<b>" + currentTrack.getTitle() + "</b>"));
            } else {
                viewHolderTrack.tvTitle.setText(currentTrack.getTitle());
            }
            viewHolderTrack.tvSubTitle.setText(currentTrack.getSubTitle());
            viewHolderTrack.tvDate.setText(GlobalUtils.parserDateToMonth(currentTrack.getDate()));
            viewHolderTrack.setClickListner(getShowInfoClick(fragmentsManager, currentTrack, position));
            viewHolderTrack.setLongClickListner(getTrackLongClickListener(currentTrack));
            initDownloadBtn(holder, currentTrack, position);
            holder.itemView.setTag(currentTrack);
        }
    }

    /**
     * Handles logic of download button and progress bar. Including all click listener and progress updates.
     *
     * @param holder
     * @param currentTrack
     * @param position
     */
    private void initDownloadBtn(final RecyclerView.ViewHolder holder, final Track currentTrack, final int position) {
        if (currentTrack instanceof Episode && ((Episode) currentTrack).isDownloaded) {//Already downloaded item
            ((ViewHolderTrack) holder).btnDownload.setVisibility(View.VISIBLE);
            ((ViewHolderTrack) holder).cvDownloadProgress.setVisibility(View.INVISIBLE);
            ((ViewHolderTrack) holder).btnDownload.setText(mContext.getString(R.string.play));
            ((ViewHolderTrack) holder).btnDownload.setOnClickListener(getPlayClickListener(position));
            //Play audeo
        } else if (DownloadMaster.getInstance().isItemDownloading(currentTrack.getTitle())) {//still in downloading progress
            ((ViewHolderTrack) holder).btnDownload.setVisibility(View.INVISIBLE);
            ((ViewHolderTrack) holder).cvDownloadProgress.setVisibility(View.VISIBLE);
            DownloadMaster.DownloadTask task = DownloadMaster.getInstance().getTaskByName(currentTrack.getTitle());
            if (task != null) {
                ((ViewHolderTrack) holder).cvDownloadProgress.setValue((float) task.lastProgress);
                task.progressUpdater = getDownloadProgressUpdater(holder);
                task.contentLoadListener = getContentLoadListener(holder, position);
            }
        } else {//normal state
            ((ViewHolderTrack) holder).cvDownloadProgress.setVisibility(View.INVISIBLE);
            ((ViewHolderTrack) holder).btnDownload.setVisibility(View.VISIBLE);
            ((ViewHolderTrack) holder).btnDownload.setText(mContext.getString(R.string.download));
            ((ViewHolderTrack) holder).btnDownload.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    final IOperationFinishCallback startDownload = new IOperationFinishCallback() {
                        @Override
                        public void operationFinished() {
                            DownloadMaster.DownloadTask downloadTask = new DownloadMaster.DownloadTask();
                            downloadTask.mediaItem = iPlayableMediaItem;
                            downloadTask.track = currentTrack;
                            downloadTask.progressUpdater = getDownloadProgressUpdater(holder);
                            downloadTask.contentLoadListener = getContentLoadListener(holder, position);
                            DownloadMaster.getInstance().download(downloadTask);
                            if (holder != null) {
                                ((ViewHolderTrack) holder).btnDownload.setVisibility(View.INVISIBLE);
                                ((ViewHolderTrack) holder).cvDownloadProgress.setVisibility(View.VISIBLE);
                            }
                            YandexMetrica.reportEvent(Analytics.MIDDLE_SCREEN_DOWNLOAD_EPISODE);
                        }
                    };
                    int hasPermissions = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                            && hasPermissions != PackageManager.PERMISSION_GRANTED) {
                        new MaterialDialog.Builder(mContext)
                                .title(R.string.write_external_permissions)
                                .content(R.string.write_external_description)
                                .positiveText(R.string.ok)
                                .onAny(new MaterialDialog.SingleButtonCallback() {
                                    @TargetApi(Build.VERSION_CODES.M)
                                    @Override
                                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                                        ((BasicActivity) mContext).setPermissionsGrantedCallback(startDownload);
                                        ((BasicActivity) mContext).requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                                ActivityMain.WRITE_EXTERNAL_PERMISSIONS_CODE);
                                    }
                                }).build().show();
                    } else {
                        startDownload.operationFinished();
                    }
                }
            });
        }
        ((ViewHolderTrack) holder).cvDownloadProgress.setOnClickListener(getDownloadCancellListener(holder, currentTrack));
    }

    private IUIProgressUpdater getDownloadProgressUpdater(final RecyclerView.ViewHolder holder) {
        return new IUIProgressUpdater() {
            @Override
            public void updateProgressUI(double progress) {
                if (((ViewHolderTrack) holder).cvDownloadProgress != null) {
                    ((ViewHolderTrack) holder).cvDownloadProgress.setValue((float) progress);
                }
            }
        };
    }

    private IContentLoadListener getContentLoadListener(final RecyclerView.ViewHolder holder, final int position) {
        return new IContentLoadListener() {
            @Override
            public void onContentLoaded() {
                ((ViewHolderTrack) holder).btnDownload.setText(mContext.getString(R.string.play));
                ((ViewHolderTrack) holder).btnDownload.setOnClickListener(getPlayClickListener(position));
                ((ViewHolderTrack) holder).btnDownload.setVisibility(View.VISIBLE);
                ((ViewHolderTrack) holder).cvDownloadProgress.setVisibility(View.GONE);
            }
        };
    }

    private View.OnClickListener getPlayClickListener(final int position) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                DownloadMaster.getInstance().cleanProgressInterfaces();
                YandexMetrica.reportEvent(Analytics.MIDDLE_SCREEN_PLAY_EPISODE);
                Track track = tracks.get(position);
                if (iPlayableMediaItem instanceof Podcast) {
                    ((Podcast) iPlayableMediaItem).selectTrack(track);
                }
                ProfileManager.getInstance().removeNewTrack(iPlayableMediaItem, track);