src/main/java/ch/citux/td/data/model/TwitchChunks.java
public class TwitchChunks extends TwitchBase {

    private List<TwitchChunk> live;

    public List<TwitchChunk> getLive() {
        return live;
    }

    public void setLive(List<TwitchChunk> live) {
        this.live = live;
    }
}
src/main/java/ch/citux/td/data/worker/TDBasicCallback.java
public abstract class TDBasicCallback<Result> implements TDCallback<Result> {

    private Object caller;

    protected TDBasicCallback(Object caller) {
        this.caller = caller;
    }

    @Override
    public void startLoading() {
        if (caller instanceof TDListFragment) {
            ((TDListFragment) caller).startLoading();
        }
    }

    @Override
    public void stopLoading() {
        if (caller instanceof TDListFragment) {
            ((TDListFragment) caller).stopLoading();
        }
    }

    @Override
    public void onError(String title, String message) {
        if (caller instanceof TDListFragment) {
            ((TDListFragment) caller).onError(title, message);
        }
    }
}
src/main/java/net/chilicat/m3u8/ParseException.java
public class ParseException extends Exception {
    private final String line;
    private final int lineNumber;

    public ParseException(String line, int lineNumber, Throwable cause) {
        super(cause);
        this.line = line;
        this.lineNumber = lineNumber;
    }

    public ParseException(String line, int lineNumber, String message) {
        super(message);
        this.line = line;
        this.lineNumber = lineNumber;
    }

    public String getLine() {
        return line;
    }

    public int getLineNumber() {
        return lineNumber;
    }

    @Override
    public String getMessage() {
        return "Error at line " + getLineNumber() + ": " + getLine() + "\n" + super.getMessage();    //To change body of overridden methods use File | Settings | File Templates.
    }
}
src/main/java/ch/citux/td/data/model/TwitchBroadcast.java
public class TwitchBroadcast extends TwitchBase {

    private String api_id;
    private String increment_view_count_url;
    private String path;
    private String broadcaster_software;
    private String channel;
    private String preview_small;
    private String preview;
    private TwitchChunks chunks;
    private long duration;
    private long start_offset;
    private long end_offset;
    private long play_offset;
    private long vod_ad_frequency;
    private long vod_ad_length;

    public String getApi_id() {
        return api_id;
    }

    public void setApi_id(String api_id) {
        this.api_id = api_id;
    }

    public String getIncrement_view_count_url() {
        return increment_view_count_url;
    }

    public void setIncrement_view_count_url(String increment_view_count_url) {
        this.increment_view_count_url = increment_view_count_url;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getBroadcaster_software() {
        return broadcaster_software;
    }

    public void setBroadcaster_software(String broadcaster_software) {
        this.broadcaster_software = broadcaster_software;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }

    public String getPreview_small() {
        return preview_small;
    }

    public void setPreview_small(String preview_small) {
        this.preview_small = preview_small;
    }

    public String getPreview() {
        return preview;
    }

    public void setPreview(String preview) {
        this.preview = preview;
    }

    public TwitchChunks getChunks() {
        return chunks;
    }

    public void setChunks(TwitchChunks chunks) {
        this.chunks = chunks;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public long getStart_offset() {
        return start_offset;
    }

    public void setStart_offset(long start_offset) {
        this.start_offset = start_offset;
    }

    public long getEnd_offset() {
        return end_offset;
    }

    public void setEnd_offset(long end_offset) {
        this.end_offset = end_offset;
    }

    public long getPlay_offset() {
        return play_offset;
    }

    public void setPlay_offset(long play_offset) {
        this.play_offset = play_offset;
    }

    public long getVod_ad_frequency() {
        return vod_ad_frequency;
    }

    public void setVod_ad_frequency(long vod_ad_frequency) {
        this.vod_ad_frequency = vod_ad_frequency;
    }

    public long getVod_ad_length() {
        return vod_ad_length;
    }

    public void setVod_ad_length(long vod_ad_length) {
        this.vod_ad_length = vod_ad_length;
    }
}
src/main/java/ch/citux/td/data/model/TwitchVideo.java
public class TwitchVideo extends TwitchBase {

    private String _id;
    private String title;
    private String description;
    private long broadcast_id;
    private Date recorded_at;
    private String game;
    private long length;
    private String preview;
    private String url;
    private long views;
    private TwitchChannel channel;

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getTitle() {
        return title;
    }

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

    public String getDescription() {
        return description;
    }

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

    public long getBroadcast_id() {
        return broadcast_id;
    }

    public void setBroadcast_id(long broadcast_id) {
        this.broadcast_id = broadcast_id;
    }

    public Date getRecorded_at() {
        return recorded_at;
    }

    public void setRecorded_at(Date recorded_at) {
        this.recorded_at = recorded_at;
    }

    public String getGame() {
        return game;
    }

    public void setGame(String game) {
        this.game = game;
    }

    public long getLength() {
        return length;
    }

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

    public String getPreview() {
        return preview;
    }

    public void setPreview(String preview) {
        this.preview = preview;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public long getViews() {
        return views;
    }

    public void setViews(long views) {
        this.views = views;
    }

    public TwitchChannel getChannel() {
        return channel;
    }

    public void setChannel(TwitchChannel channel) {
        this.channel = channel;
    }
}
src/main/java/ch/citux/td/ui/dialogs/ErrorDialogFragment.java
public class ErrorDialogFragment extends DialogFragment {

    private static final String FRAGMENT_TAG = "ErrorDialogFragment";
    private static final String BUNDLE_TITLE = "title";
    private static final String BUNDLE_MESSAGE = "message";
    private DialogInterface.OnClickListener mOnClickListener;
    private OnCancelListener mOnCancelListener;

    private static ErrorDialogFragment newInstance(String title, String message,
                                                   DialogInterface.OnClickListener onClickListener, OnCancelListener onCancelListener) {
        ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
        dialogFragment.mOnClickListener = onClickListener;

        Bundle args = new Bundle();
        args.putString(BUNDLE_TITLE, title);
        args.putString(BUNDLE_MESSAGE, message);
        dialogFragment.setArguments(args);

        return dialogFragment;
    }

    public static void dismiss(FragmentActivity activity) {
        FragmentManager fragmentManager = activity.getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        Fragment prev = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
        if (prev != null) {
            fragmentTransaction.remove(prev);
        }
        fragmentTransaction.commit();
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle args = getArguments();
        AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
        b.setTitle(args.getString(BUNDLE_TITLE));
        b.setMessage(args.getString(BUNDLE_MESSAGE));
        setCancelable(true);
        b.setNeutralButton(getActivity().getString(android.R.string.ok), mOnClickListener);
        return b.create();
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        if (mOnCancelListener != null) {
            mOnCancelListener.onCancel();
        }
    }

    public interface OnCancelListener {
        public void onCancel();
    }

    public static class ErrorDialogFragmentBuilder {
        private FragmentActivity mActivity;
        private String mTitle;
        private String mMessage;
        private DialogInterface.OnClickListener mOnClickListener;
        private OnCancelListener mOnCancelListener;

        public ErrorDialogFragmentBuilder(FragmentActivity activity) {
            if (activity != null) {
                mActivity = activity;
                mTitle = activity.getString(R.string.dialog_error_title);
            }
        }

        public ErrorDialogFragmentBuilder setTitle(int resId) {
            mTitle = mActivity.getString(resId);
            return this;
        }

        public ErrorDialogFragmentBuilder setTitle(String text) {
            mTitle = text;
            return this;
        }

        public ErrorDialogFragmentBuilder setMessage(int resId) {
            mMessage = mActivity.getString(resId);
            return this;
        }

        public ErrorDialogFragmentBuilder setMessage(String text) {
            mMessage = text;
            return this;
        }

        public ErrorDialogFragmentBuilder setOnClickListener(DialogInterface.OnClickListener onClickListener) {
            mOnClickListener = onClickListener;
            return this;
        }

        public ErrorDialogFragmentBuilder setOnCancelListener(OnCancelListener onCancelListener) {
            mOnCancelListener = onCancelListener;
            return this;
        }

        public void show() {
            FragmentManager fragmentManager = mActivity.getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            Fragment prev = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
            if (prev != null) {
                fragmentTransaction.remove(prev);
            }
            fragmentTransaction.addToBackStack(null);

            ErrorDialogFragment.newInstance(mTitle, mMessage, mOnClickListener, mOnCancelListener)
                    .show(fragmentManager, FRAGMENT_TAG);
        }
    }
}
src/main/java/ch/citux/td/ui/TDActivity.java
public class TDActivity extends ActionBarActivity implements TDCallback<TwitchChannel>, View.OnFocusChangeListener, AdapterView.OnItemClickListener {

    private FavoritesFragment favoritesFragment;
    private GameOverviewFragment gameOverviewFragment;
    private GameStreamsFragment gameStreamsFragment;
    private ChannelFragment channelFragment;
    private SearchFragment searchFragment;
    private VideoFragment videoFragment;
    private SettingsFragment settingsFragment;

    private ActionBarDrawerToggle toggle;
    private MenuItem refreshItem;
    private MenuItem searchItem;
    private View refreshView;
    private String username;
    private Toast toast;
    private boolean isLoading;
    private boolean hasUsername;

    @InjectView(R.id.user) View user;
    @InjectView(R.id.toolbar) Toolbar toolbar;
    @InjectView(R.id.imgUser) ImageView imgUser;
    @InjectView(R.id.lblUser) TextView lblUser;
    @InjectView(R.id.lblNoUser) TextView lblNoUser;
    @Optional @InjectView(R.id.drawerLayout) DrawerLayout drawerLayout;

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

        setContentView(R.layout.main);
        ButterKnife.inject(this);

        Context contextThemeWrapper = new ContextThemeWrapper(this, R.style.Theme_TD_Light);
        LayoutInflater inflater = LayoutInflater.from(this).cloneInContext(contextThemeWrapper);
        refreshView = inflater.inflate(R.layout.action_refresh, null);

        initNavigation();
        updateUser();

        if (favoritesFragment == null) {
            Bundle args = new Bundle();
            args.putBoolean(TDConfig.SETTINGS_CHANNEL_NAME, hasUsername);

            favoritesFragment = new FavoritesFragment();
            favoritesFragment.setArgs(args);

            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.content, favoritesFragment)
                    .commit();
        } else {
            replaceFragment(favoritesFragment);
        }
    }

    private void initNavigation() {
        toolbar.setLogo(R.drawable.twitch_logo_white);
        toolbar.setLogoDescription(R.string.app_name);
        setSupportActionBar(toolbar);

        if (drawerLayout != null) {
            toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_open, R.string.navigation_close);
            toggle.setDrawerIndicatorEnabled(true);
            drawerLayout.setDrawerListener(toggle);
        }

        ListView lstNav = (ListView) findViewById(R.id.lstNav);
        lstNav.setAdapter(new ArrayAdapter<>(this, R.layout.list_item_navigation, getResources().getStringArray(R.array.navigation)));
        lstNav.setOnItemClickListener(this);
    }

    public void updateUser() {
        username = PreferenceManager.getDefaultSharedPreferences(this).getString(TDConfig.SETTINGS_CHANNEL_NAME, "");
        hasUsername = !username.equals("");

        if (hasUsername) {
            user.setVisibility(View.VISIBLE);
            lblUser.setText(username);
            lblNoUser.setVisibility(View.GONE);

            TDTaskManager.executeTask(this);
        } else {
            user.setVisibility(View.GONE);
            lblNoUser.setVisibility(View.VISIBLE);
            imgUser.setImageResource(R.drawable.default_channel_logo_medium);
        }
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        if (drawerLayout != null) {
            toggle.syncState();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        refreshData();
    }

    private void replaceFragment(Fragment fragment) {
        replaceFragment(fragment, true);
    }

    private void replaceFragment(Fragment fragment, boolean backstack) {
        if (fragment != null && !fragment.isAdded()) {
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.content, fragment);
            if (backstack) {
                transaction.addToBackStack(null);
            }
            transaction.commitAllowingStateLoss();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);

            if (searchFragment == null) {
                searchFragment = new SearchFragment();
            }
            searchFragment.setQuery(query);
            searchFragment.loadData();

            replaceFragment(searchFragment);
            Log.d(this, query);
        }
    }

    @Override
    public void onPause() {
        TDTaskManager.cancelAllTasks();
        super.onPause();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);

        refreshItem = menu.findItem(R.id.menu_refresh);
        searchItem = menu.findItem(R.id.menu_search);

        // Associate searchable configuration with the SearchView
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setOnQueryTextFocusChangeListener(this);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_refresh:
                if (!isLoading) {
                    refreshData();
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public void showChannel(TwitchChannel channel) {
        if (channelFragment == null) {
            channelFragment = new ChannelFragment();
        }

        if (channelFragment.isAdded()) {
            channelFragment.updateChannel(channel);
        } else {
            Bundle arguments = new Bundle();
            arguments.putSerializable(ChannelFragment.CHANNEL, channel);
            channelFragment.setArgs(arguments);
            replaceFragment(channelFragment);
        }
    }

    public void showVideo(Bundle args) {
        if (videoFragment == null) {
            videoFragment = new VideoFragment();
        }
        videoFragment.setArgs(args);

        if (videoFragment.isAdded()) {
            videoFragment.playVideo();
        } else {
            replaceFragment(videoFragment);
        }
        MenuItemCompat.collapseActionView(searchItem);
    }

    public void showPlaylist(TwitchBroadcast broadcast) {
        if (channelFragment.isAdded()) {
            channelFragment.showPlaylist(broadcast);
        }
    }

    public void showStreams(Bundle args) {
        if (gameStreamsFragment == null) {
            gameStreamsFragment = new GameStreamsFragment();
        }
        gameStreamsFragment.setArgs(args);
        replaceFragment(gameStreamsFragment);
    }

    public void startLoading() {
        isLoading = true;
        if (refreshItem != null) {
            MenuItemCompat.setActionView(refreshItem, refreshView);
        }
    }

    public void stopLoading() {
        isLoading = false;
        if (refreshItem != null) {
            MenuItemCompat.setActionView(refreshItem, null);
        }
    }

    @Override
    public TwitchChannel startRequest() {
        return TDServiceImpl.getInstance().getChannel(username);
    }

    @Override
    public void onResponse(TwitchChannel response) {
        if (response != null) {
            Picasso.with(this)
                    .load(response.getLogo().getUrl(TwitchLogo.Size.MEDIUM))
                    .placeholder(R.drawable.default_channel_logo_medium)
                    .into(imgUser);
        }
    }

    @Override
    public void onError(String title, String message) {
        toast = Toast.makeText(this, title + ": " + message, Toast.LENGTH_SHORT);
        toast.show();
    }

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

    @Override
    public void setTitle(int titleId) {
        super.setTitle(titleId);
        if (toolbar != null) {
            toolbar.setTitle(titleId);
        }
    }

    @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        if (toolbar != null) {
            toolbar.setTitle(title);
        }
    }

    protected void refreshData() {
        if (favoritesFragment != null && favoritesFragment.isAdded()) {
            favoritesFragment.refreshData();
        }
        if (channelFragment != null && channelFragment.isAdded()) {
            channelFragment.refreshData();
        }
        if (searchFragment != null && searchFragment.isAdded()) {
            searchFragment.refreshData();
        }
        if (gameOverviewFragment != null && gameOverviewFragment.isAdded()) {
            gameOverviewFragment.refreshData();
        }
        if (gameStreamsFragment != null && gameStreamsFragment.isAdded()) {
            gameStreamsFragment.refreshData();
        }
    }

    @Override
    public void onFocusChange(View view, boolean queryTextFocused) {
        if (!queryTextFocused) {
            MenuItemCompat.collapseActionView(searchItem);
        }
    }

    @Override
    public void onBackPressed() {
        if (drawerLayout != null && drawerLayout.isDrawerOpen(Gravity.START)) {
            drawerLayout.closeDrawers();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        switch (position) {
            case 0:
                if (favoritesFragment == null) {
                    favoritesFragment = new FavoritesFragment();
                }
                replaceFragment(favoritesFragment);
                break;
            case 1:
                if (gameOverviewFragment == null) {
                    gameOverviewFragment = new GameOverviewFragment();
                }
                replaceFragment(gameOverviewFragment);
                break;
            case 2:
                if (settingsFragment == null) {
                    settingsFragment = new SettingsFragment();
                }
                replaceFragment(settingsFragment);
                break;
        }
        if (drawerLayout != null) {
            drawerLayout.closeDrawers();
        }
    }
}
src/main/java/ch/citux/td/data/model/TwitchChunk.java
public class TwitchChunk extends TwitchBase {

    private String url;
    private String vod_count_url;
    private String upkeep;
    private long length;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getVod_count_url() {
        return vod_count_url;
    }

    public void setVod_count_url(String vod_count_url) {
        this.vod_count_url = vod_count_url;
    }

    public String getUpkeep() {
        return upkeep;
    }

    public void setUpkeep(String upkeep) {
        this.upkeep = upkeep;
    }

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }
}
src/main/java/ch/citux/td/data/model/TwitchStreamQuality.java
public class TwitchStreamQuality extends TwitchBase {

    private String key;
    private String name;
    private int value;

    public TwitchStreamQuality(String key, int value) {
        this(key, value, key);
    }

    public TwitchStreamQuality(String key, int value, String name) {
        this.key = key;
        this.value = value;
        this.name = name;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
src/main/java/ch/citux/td/ui/adapter/PlaylistAdapter.java
public class PlaylistAdapter extends BaseAdapter {

    private LayoutInflater inflater;
    private List<TwitchChunk> videos;
    private ArrayList<Integer> played;

    public PlaylistAdapter(Context context, List<TwitchChunk> videos) {
        super();
        this.inflater = LayoutInflater.from(context);
        this.videos = videos;
        this.played = new ArrayList<Integer>(videos.size());
    }

    public void setVideos(List<TwitchChunk> videos) {
        this.videos = videos;
        if (videos != null && videos.size() > 0) {
            notifyDataSetChanged();
        } else {
            notifyDataSetInvalidated();
        }
    }

    public void setPlayed(int position) {
        played.add(position);
    }

    @Override
    public int getCount() {
        return videos.size();
    }

    @Override
    public TwitchChunk getItem(int position) {
        if (position < videos.size()) {
            return videos.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TwitchChunk video = getItem(position);
        ViewHolder holder;
        if (convertView == null || convertView.getTag() == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.list_item_playlist, parent, false);
            if (convertView != null) {
                ButterKnife.inject(holder, convertView);
            }
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.lblPartIndex.setText(String.valueOf(position + 1));
        holder.lblPartCount.setText(String.valueOf(getCount()));
        //TODO holder.lblTitle.setText(video.());
        holder.chkPlayed.setChecked(played.contains(position));
        return convertView;
    }

    class ViewHolder {
        @InjectView(R.id.lblPartIndex) TextView lblPartIndex;
        @InjectView(R.id.lblPartCount) TextView lblPartCount;
        @InjectView(R.id.lblTitle) TextView lblTitle;
        @InjectView(R.id.chkPlayed) CheckBox chkPlayed;
    }
}
src/main/java/ch/citux/td/ui/fragments/VideoFragment.java
public class VideoFragment extends TDFragment {//implements MediaPlayer.OnPreparedListener {

    public static final String PLAYLIST = "playlist";
    public static final String TITLE = "title";
    public static final String URL = "url";

    @InjectView(android.R.id.empty) EmptyView emptyView;
    //    @InjectView(R.id.videoView) VideoView videoView;
    @InjectView(R.id.player) View player;
    @InjectView(R.id.chat) View chat;

    @Override
    protected int onCreateView() {
        return R.layout.video;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        super.setRetainInstance(true);

//        if (((TDActivity) getActivity()).isTablet()) {
//            emptyView.setImage(R.drawable.ic_glitchicon_white);
//        }

//        MediaController mediaController = new MediaController(getActivity());
//        videoView.setMediaController(mediaController);
//        videoView.setVideoLayout(VideoView.VIDEO_LAYOUT_SCALE, 1);
//        videoView.setOnPreparedListener(this);
//
//        onOrientationChange(getResources().getConfiguration().orientation);
//
//        playVideo();
    }

    @Override
    public void loadData() {

    }

    @Override
    public void onPause() {
        super.onPause();

        getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getSupportActionBar().show();
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

//        onOrientationChange(newConfig.orientation);
    }

    private void onOrientationChange(int orientation) {
//        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
//            getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//            getSupportActionBar().hide();
//            videoView.setVideoLayout(VideoView.VIDEO_LAYOUT_STRETCH, 1.77f);
//            chat.setVisibility(View.GONE);
//        } else {
//            getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//            getSupportActionBar().show();
//            videoView.setVideoLayout(VideoView.VIDEO_LAYOUT_SCALE, 1.77f);
//            chat.setVisibility(View.VISIBLE);
//        }
    }

    public void playVideo() {

//        getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC);
//        if (getArguments() != null && (!getArguments().containsKey(URL) || !getArguments().containsKey(CHUNKS))) {
//            getSupportActionBar().setTitle(getArguments().getString(TITLE));
//
//            if (getArguments().containsKey(URL)) {
//                videoView.setVideoURI(Uri.parse(getArguments().getString(URL)));
//            }
//            if (getArguments().containsKey(CHUNKS)) {
//                videoView.setVideoPlaylist(getArguments().getStringArray(CHUNKS));
//            }
//            videoView.start();
//        }
    }

    @Override
    public Object startRequest() {
        return null;
    }

    @Override
    public void onResponse(Object response) {

    }

//    @Override
//    public void onPrepared(MediaPlayer mp) {
//        emptyView.setVisibility(View.GONE);
//        player.setVisibility(View.VISIBLE);
//    }
}
src/main/java/ch/citux/td/ui/fragments/TDBase.java
public interface TDBase {

    public void loadData();

    public void startActivity(Intent intent);

    public SharedPreferences getDefaultSharedPreferences();

    public boolean isAdded();

    public TDActivity getTDActivity();

    public void setArgs(Bundle args);

    public Bundle getArgs();
}
src/main/java/net/chilicat/m3u8/Element.java
public interface Element {

    public String getTitle();


    public int getDuration();

    /**
     * URI to media or playlist.
     *
     * @return the URI.
     */
    public URI getURI();

    /**
     * Media can be encrypted.
     *
     * @return true if media encrypted.
     */
    public boolean isEncrypted();

    /**
     * Element can be another playlist.
     *
     * @return true if element a playlist.
     */
    public boolean isPlayList();

    /**
     * Element is a media file.
     *
     * @return true if element a media file and not a playlist.
     */
    public boolean isMedia();

    /**
     * If media is encryped than will this method return a info object.
     *
     * @return the info object or null if media not encrypted.
     */
    public EncryptionInfo getEncryptionInfo();

    /**
     * If element a playlist than this method will return a PlaylistInfo object.
     *
     * @return a info object or null in case of element is not a playlist.
     */
    public PlaylistInfo getPlayListInfo();

    /**
     * The program date.
     *
     * @return -1 in case of program date is not set.
     */
    public long getProgramDate();

}
src/main/java/net/chilicat/m3u8/Playlist.java
public final class Playlist implements Iterable<Element> {

    public static Playlist parse(Readable readable) throws ParseException {
        if (readable == null) {
            throw new NullPointerException("playlist");
        }
        return PlaylistParser.create(PlaylistType.M3U8).parse(readable);
    }

    public static Playlist parse(String playlist) throws ParseException {
        if (playlist == null) {
            throw new NullPointerException("playlist");
        }
        return parse(new StringReader(playlist));
    }

    public static Playlist parse(InputStream playlist) throws ParseException {
        if (playlist == null) {
            throw new NullPointerException("playlist");
        }
        return parse(new InputStreamReader(playlist));
    }

    public static Playlist parse(ReadableByteChannel playlist) throws ParseException {
        if (playlist == null) {
            throw new NullPointerException("playlist");
        }
        return parse(makeReadable(playlist));
    }

    private static Readable makeReadable(ReadableByteChannel source) {
        if (source == null) {
            throw new NullPointerException("source");
        }


        return Channels.newReader(source,
                java.nio.charset.Charset.defaultCharset().name());
    }

    private final List<Element> elements;
    private final boolean endSet;
    private final int targetDuration;
    private int mediaSequenceNumber;

    Playlist(List<Element> elements, boolean endSet, int targetDuration, int mediaSequenceNumber) {
        if (elements == null) {
            throw new NullPointerException("elements");
        }
        this.targetDuration = targetDuration;
        this.elements = elements;
        this.endSet = endSet;
        this.mediaSequenceNumber = mediaSequenceNumber;
    }

    public int getTargetDuration() {
        return targetDuration;
    }

    public Iterator<Element> iterator() {
        return elements.iterator();
    }

    public List<Element> getElements() {
        return elements;
    }

    public boolean isEndSet() {
        return endSet;
    }

    public int getMediaSequenceNumber() {
        return mediaSequenceNumber;
    }

    @Override
    public String toString() {
        return "PlayListImpl{" +
                "elements=" + elements +
                ", endSet=" + endSet +
                ", targetDuration=" + targetDuration +
                ", mediaSequenceNumber=" + mediaSequenceNumber +
                '}';
    }
}
src/main/java/ch/citux/td/data/model/TwitchAccessToken.java
public class TwitchAccessToken extends TwitchBase {

    private String token;
    private String sig;
    private String p;
    private boolean mobile_restricted;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getSig() {
        return sig;
    }

    public void setSig(String sig) {
        this.sig = sig;
    }

    public String getP() {
        return p;
    }

    public void setP(String p) {
        this.p = p;
    }

    public boolean isMobile_restricted() {
        return mobile_restricted;
    }

    public void setMobile_restricted(boolean mobile_restricted) {
        this.mobile_restricted = mobile_restricted;
    }
}
src/main/java/ch/citux/td/config/TDConfig.java
public class TDConfig {

    public static final String SETTINGS_CHANNEL_NAME = "channel_name";
    public static final String SETTINGS_LICENSE_DIALOG = "license_dialog";
    public static final String SETTINGS_STREAM_QUALITY = "stream_quality";

    public static final String CAST_APPLICATION_ID = "D25E1BBE";
    public static final String KRAKEN_CLIENT_ID = "sx9d3whtcht2lhrtd6yjia8rv019wxi";

    public static final String UTF_8 = "UTF-8";

    public static final String MIME_FLV = "video/x-flv";
    public static final String MIME_TWITCH = "application/vnd.twitchtv.v3+json";

    public static final String URL_API_TWITCH_KRAKEN_BASE = "https://api.twitch.tv/kraken";
    public static final String URL_API_TWITCH_API_BASE = "https://api.twitch.tv/api";
    public static final String URL_API_USHER_BASE = "http://usher.twitch.tv";

}
src/main/java/ch/citux/td/data/service/TDServiceImpl.java
public class TDServiceImpl implements TwitchAPI, TwitchKraken, TwitchUsher, RestAdapter.Log {

    private static final String TAG = "TDService";

    private static TDServiceImpl instance;

    private TwitchAPI twitchAPI;
    private TwitchUsher twitchUsher;
    private TwitchKraken twitchKraken;

    private TDServiceImpl() {
        RestAdapter.Builder builder = new RestAdapter.Builder()
                .setLog(this)
                .setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
                .setConverter(new JacksonConverter());

        RestAdapter apiAdapter = builder
                .setEndpoint(TDConfig.URL_API_TWITCH_API_BASE)
                .build();
        RestAdapter usherAdapter = builder
                .setEndpoint(TDConfig.URL_API_USHER_BASE)
                .build();
        RestAdapter krakenAdapter = builder
                .setEndpoint(TDConfig.URL_API_TWITCH_KRAKEN_BASE)
                .setRequestInterceptor(new KrakenRequestInterceptor())
                .build();

        twitchAPI = apiAdapter.create(TwitchAPI.class);
        twitchUsher = usherAdapter.create(TwitchUsher.class);
        twitchKraken = krakenAdapter.create(TwitchKraken.class);
    }

    public static TDServiceImpl getInstance() {
        if (instance == null) {
            instance = new TDServiceImpl();
        }
        return instance;
    }

    @Override
    public void log(String message) {
        Log.d(TAG, message);
    }

    private class KrakenRequestInterceptor implements RequestInterceptor {
        @Override
        public void intercept(RequestInterceptor.RequestFacade request) {
            request.addHeader("Accept", TDConfig.MIME_TWITCH);
            request.addHeader("Client-ID", TDConfig.KRAKEN_CLIENT_ID);
        }
    }

    @Override
    public TwitchFollows getFollows(String username, int offset) {
        return twitchKraken.getFollows(username, offset);
    }

    @Override
    public TwitchChannel getChannel(String channel) {
        return twitchKraken.getChannel(channel);
    }

    @Override
    public TwitchStream getStream(String channel) {
        return twitchKraken.getStream(channel);
    }

    @Override
    public TwitchStream getStreams(String game, int offset) {
        return twitchKraken.getStreams(game, offset);
    }

    @Override
    public TwitchVideos getVideos(String channel, int offset) {
        return twitchKraken.getVideos(channel, offset);
    }

    @Override
    public TwitchStream searchStreams(String query, int offset) {
        return twitchKraken.searchStreams(query, offset);
    }

    @Override
    public TwitchChannels searchChannels(String query, int offset) {
        return twitchKraken.searchChannels(query, offset);
    }

    @Override
    public TwitchGames getTopGames(int limit, int offset) {
        return twitchKraken.getTopGames(limit, offset);
    }

    //TwitchAPI
    @Override
    public TwitchAccessToken getChannelToken(String channel) {
        return twitchAPI.getChannelToken(channel);
    }

    @Override
    public TwitchAccessToken getVodToken(@Path("videoId") String videoId) {
        return twitchAPI.getVodToken(videoId);
    }

    @Override
    public TwitchBroadcast getVideoPlaylist(@Path("id") String id) {
        return twitchAPI.getVideoPlaylist(id);
    }

    //TwitchUsher
    @Override
    public Response getChannelPlaylist(String channel, String p, String token, String sig) {
        return twitchUsher.getChannelPlaylist(channel, p, token, sig);
    }

    @Override
    public Response getVodPlaylist(@Path("videoId") String videoId, @Query("p") String p, @Query("token") String token, @Query("sig") String sig) {
        return twitchUsher.getVodPlaylist(videoId, p, token, sig);
    }
}
src/main/java/ch/citux/td/data/model/TwitchPlayList.java
public class TwitchPlayList extends TwitchBase {

    public static final TwitchStreamQuality QUALITY_MOBILE = new TwitchStreamQuality("mobile", 1);
    public static final TwitchStreamQuality QUALITY_LOW = new TwitchStreamQuality("low", 2);
    public static final TwitchStreamQuality QUALITY_MEDIUM = new TwitchStreamQuality("medium", 3);
    public static final TwitchStreamQuality QUALITY_HIGH = new TwitchStreamQuality("high", 4);
    public static final TwitchStreamQuality QUALITY_CHUNKED = new TwitchStreamQuality("chunked", 5, "source");

    public static final TwitchStreamQuality[] SUPPORTED_QUALITIES = {
            QUALITY_MOBILE,
            QUALITY_LOW,
            QUALITY_MEDIUM,
            QUALITY_HIGH,
            QUALITY_CHUNKED
    };

    private HashMap<TwitchStreamQuality, String> streams;

    public static TwitchStreamQuality parseQuality(String name) {
        for (TwitchStreamQuality quality : SUPPORTED_QUALITIES) {
            if (quality.getName().equalsIgnoreCase(name) || quality.getKey().equalsIgnoreCase(name)) {
                return quality;
            }
        }
        return null;
    }

    public String getStream(TwitchStreamQuality quality) {
        String url = streams.get(quality);
        if (StringUtils.isEmpty(url)) {
            url = getBestStream();
        }
        return url;
    }

    public String getBestStream() {
        TwitchStreamQuality best = new TwitchStreamQuality("", -1);

        for (TwitchStreamQuality quality : streams.keySet()) {
            if (best.getValue() < quality.getValue()) {
                best = quality;
            }
        }

        if (best.getValue() > -1) {
            return getStream(best);
        }
        return null;
    }

    public HashMap<TwitchStreamQuality, String> getStreams() {
        return streams;
    }

    public void setStreams(HashMap<TwitchStreamQuality, String> streams) {
        this.streams = streams;
    }
}


Question: import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import net.chilicat.m3u8.Element;
import net.chilicat.m3u8.ParseException;
import net.chilicat.m3u8.Playlist;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import ch.citux.td.R;
import ch.citux.td.config.TDConfig;
import ch.citux.td.data.model.TwitchAccessToken;
import ch.citux.td.data.model.TwitchBroadcast;
import ch.citux.td.data.model.TwitchChunk;
import ch.citux.td.data.model.TwitchChunks;
import ch.citux.td.data.model.TwitchPlayList;
import ch.citux.td.data.model.TwitchStreamQuality;
import ch.citux.td.data.model.TwitchVideo;
import ch.citux.td.data.service.TDServiceImpl;
import ch.citux.td.data.worker.TDBasicCallback;
import ch.citux.td.ui.TDActivity;
import ch.citux.td.ui.adapter.PlaylistAdapter;
import ch.citux.td.ui.dialogs.ErrorDialogFragment;
import ch.citux.td.ui.fragments.TDBase;
import ch.citux.td.ui.fragments.VideoFragment;
import retrofit.client.Response;
/*
 * Copyright 2013-2014 Paul Stöhr
 *
 * This file is part of TD.
 *
 * TD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ch.citux.td.util;





public class VideoPlayer {

    private static final String TAG = "VideoPlayer";

    public static void playVideo(TDActivity activity, TwitchBroadcast broadcast) {
        Log.d(TAG, "Playing Playlist'" + broadcast.getChannel() + "' with " + broadcast.getChunks().getLive().size() + " parts");

        if (useInternPlayer(activity)) { //built-in Player
            Bundle args = new Bundle();
            args.putString(VideoFragment.TITLE, broadcast.getChannel());
            args.putStringArray(VideoFragment.PLAYLIST, getPlaylistUrls(broadcast));
            playVideoIntern(activity, args);
        } else {
            activity.showPlaylist(broadcast);
        }
    }

    private static String[] getPlaylistUrls(TwitchBroadcast broadcast) {
        if (broadcast != null && broadcast.getChunks() != null && broadcast.getChunks().getLive() != null) {
            List<TwitchChunk> videos = broadcast.getChunks().getLive();
            String[] urls = new String[videos.size()];
            for (int i = 0; i < videos.size(); i++) {
                urls[i] = videos.get(i).getUrl();
            }
            return urls;
        }
        return null;
    }

    public static void playVideo(TDActivity activity, String title, String url) {
        if (activity != null) {
            Log.d(TAG, "Playing '" + title + "' from " + url);

            if (useInternPlayer(activity)) { //built-in Player
                Log.d(TAG, "internal");
                Bundle args = new Bundle();
                args.putString(VideoFragment.TITLE, title);
                args.putString(VideoFragment.URL, url);
                playVideoIntern(activity, args);
            } else { //external Player
                Log.d(TAG, "external");

                Intent intent = new Intent(Intent.ACTION_VIEW);
Answer:                 intent.setDataAndType(Uri.parse(url), TDConfig.MIME_FLV);