Android Open Source - Billy Songs Fragment






From Project

Back to project page Billy.

License

The source code is released under:

GNU General Public License

If you think the Android project Billy listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.vibin.billy;
/*from w  w w .j a  v  a 2 s .c  om*/
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

import com.android.volley.Cache;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.StringRequest;
import com.crashlytics.android.Crashlytics;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

/**
 * Heart of the app
 * Fetches songs' data and puts that in a Listview. This goes inside a Viewpager.
 */
public class SongsFragment extends ListFragment implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener {
    ArrayList<ProcessingTask.BillyData> mData, mDataLite;
    String[] billySong, billyArtist, result;
    String jsonMdata, cacheData = "";
    LinearLayout spinner;
    View v;
    boolean spinnerVisible, pulltorefresh, isHot100;
    CustomBaseAdapter customBaseAdapter;
    CustomDatabaseAdapter customDatabaseAdapter;
    SwingBottomInAnimationAdapter swingBottomInAnimationAdapter;
    RequestQueue req;
    BillyApplication billyapp;
    ProcessingTask ft;
    String uri, searchparam, table_name, rssurl;
    ImageLoader imgload;
    SwipeRefreshLayout swipelayout;
    int mIndex, billySize, onlyOnce, position;
    final long ANIMATION_DELAY = 200;
    final long ANIMATION_DURATION = 350;

    private String tag = SongsFragment.class.getSimpleName(); // Tag is not final, because it's dynamic

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

        position = getArguments().getInt("position");
        tag = tag.substring(0, tag.length() - 1) + Integer.toString(position);
        billyapp = (BillyApplication) getActivity().getApplication();
        imgload = billyapp.getImageLoader();
        req = billyapp.getRequestQueue();

        rssurl = getResources().getStringArray(R.array.url)[position];
        table_name = getResources().getStringArray(R.array.table)[position];

        /**
         * Number of elements in Hot100 chart is 100
         * RnB 15, rest all 20
         */

        if (table_name.equals("MostPopular")) {
            isHot100 = true;
            billySize = 100;
        } else if (table_name.equals("RnB")) {
            billySize = 15;
        } else {
            billySize = 20;
        }

        billySong = new String[billySize];
        billyArtist = new String[billySize];
        ft = new ProcessingTask(billySize, getActivity());
        mData = new ArrayList<ProcessingTask.BillyData>(billySize);
        mDataLite = new ArrayList<ProcessingTask.BillyData>(billyapp.getMinBillySize(billySize));
        initializeArraylistitems();

        customDatabaseAdapter = new CustomDatabaseAdapter(getActivity());
        setHasOptionsMenu(true);

        /**
         * Populate mData beforehand if there's no connection
         */
        if (!billyapp.isConnected()) {
            Log.d(tag, "No internet connection");
            jsonMdata = customDatabaseAdapter.getArrayList(table_name);
            if (jsonMdata != null) {
                Gson gson = new Gson();
                mData = gson.fromJson(jsonMdata, new TypeToken<ArrayList<ProcessingTask.BillyData>>() {
                }.getType());
            }
        }

        /**
         * This has to only be in OnCreate and not OnCreateView. Because OnCreateView gets called everytime
         * you switch between Fragments inside a ViewPager.
         */
        if (savedInstanceState == null && billyapp.isConnected()) {
            try {
                performRequests();
            } catch (UnsupportedEncodingException e) {
                Log.d(tag, e.toString());
            }
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(tag, "oncreateview");
        v = inflater.inflate(R.layout.fragment_songs, container, false);

        if (jsonMdata == null && !billyapp.isConnected()) {
            nothingToShow();
        }

        customBaseAdapter = new CustomBaseAdapter(getActivity(), mData, imgload);
        spinner = (LinearLayout) v.findViewById(R.id.spinner);

        /**
         * Show Spinner or ListView (both hidden by default)
         */

        if (spinnerVisible) {
            spinner.setVisibility(View.VISIBLE);
        } else {
            v.findViewById(android.R.id.list).setVisibility(View.VISIBLE);
        }
        mIndex = 0;
        swipelayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_container);
        swipelayout.setOnRefreshListener(this);
        swipelayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                R.color.billy,
                android.R.color.holo_orange_light,
                R.color.green);
        swipelayout.setSize(SwipeRefreshLayout.LARGE);

        /**
         * Restore instance, on Orientation change
         * if instance values are null, spawn requests again
         */

        if (savedInstanceState != null) {
            Log.d(tag, "savedInstanceState is not null");

            mData = savedInstanceState.getParcelableArrayList("MDATA");
            mDataLite = savedInstanceState.getParcelableArrayList("MDATALITE");
            billySong = savedInstanceState.getStringArray("BILLYSONG");
            billyArtist = savedInstanceState.getStringArray("BILLYARTIST");

            ft.setBillySong(billySong);
            ft.setBillyArtist(billyArtist);
            if (!checkArrayList(mData)) {
                Log.d(tag, "mData or its objects are null");
//                initializeArraylistitems();
                try {
                    performRequests();
                } catch (UnsupportedEncodingException e) {
                    Log.d(tag, e.toString());
                }
            } else {
                Log.d(tag, "All songs have loaded, mData size is " + mData.size());
                updateList();
            }
        } else if (!billyapp.isConnected()) {
            Log.d(tag, "Oncreateview + no network, mData size is " + mData.size());
            updateList();
        }
        return v;
    }

    /**
     * When DB is empty and User isn't connected to Internet
     * Show an Alert Dialog and exit app
     *
     * Make sure to call {@link android.content.DialogInterface#cancel()}, before calling {@link android.app.Activity#finish()}
     */

    private void nothingToShow() {
        final Context c = getActivity();
        v.findViewById(android.R.id.list).setVisibility(View.GONE);
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(c);
        alertDialogBuilder.setTitle("No connection");
        alertDialogBuilder
                .setMessage("Please connect to Internet.")
                .setCancelable(false)
                .setPositiveButton("Okay", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                        ((Activity) c).finish();
                    }
                });

        AlertDialog alertDialog = alertDialogBuilder.create();
        if (!(getActivity()).isFinishing()) {
            alertDialog.show();
        }
    }

    /**
     * Dynamically initialize {@link com.vibin.billy.BillyApplication#getMinBillySize(int)} number of objects in ArrayList
     * Call this before loading additional data into ListView
     * <p/>
     * Create a lighter version of {@link #mData}, by shallow-copying it to mDataLite
     */

    private void initializeArraylistitems() {
        if (mData.size() == billyapp.getMinBillySize(billySize)) {
            Log.d(tag, "mDataLite is same as mData");
            mDataLite = new ArrayList<ProcessingTask.BillyData>(mData);
            Log.d(tag, "intitialize, size of mDataLite is " + mDataLite.size() + " size of mData is " + mData.size());
        }
        int i = 0;
        while (i < billyapp.getMinBillySize(billySize)) {
            mData.add(new ProcessingTask.BillyData());
            i++;
        }
        Log.d(tag, "new size of mData is " + mData.size() + " and new size of mDataLite is " + mDataLite.size());
    }

    /**
     * Spawn Billboard request
     * Make sure to get cached version of Billboard response before making a new request, for comparing later
     */
    private void performRequests() throws UnsupportedEncodingException {
        CustomStringRequest stringreq = new CustomStringRequest(Request.Method.GET, rssurl, billyComplete(), billyError());
        spinnerVisible = true;
        Cache.Entry entry = req.getCache().get(rssurl);
        if (entry != null) {
            if (entry.data != null) {
                cacheData = new String(entry.data, "UTF-8");
                Log.d(tag, "cache length is " + entry.data.length + " softtl is " + entry.softTtl + " ttl is " + entry.ttl + " " + entry.refreshNeeded());
            }
            //Log.d(tag, " " + entry.toString() + " " + entry.isExpired() + " " + entry.refreshNeeded());
        }
        stringreq.setTag(this);

        req.add(stringreq);
        Log.d(tag,"performrequests");
    }


    public void updateList() {
        customBaseAdapter.updateArrayList(mData);
        customBaseAdapter.notifyDataSetChanged();
    }

    /**
     * Set adapter to list
     * If current screen is Hot 100, then attach a Button as footer view to ListView which loads additional data
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setListAdapter(customBaseAdapter);

        // Assign the ListView to the AnimationAdapter and vice versa
        swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(customBaseAdapter, ANIMATION_DELAY, ANIMATION_DURATION);
        swingBottomInAnimationAdapter.setAbsListView(getListView());
        getListView().setAdapter(swingBottomInAnimationAdapter);
        swingBottomInAnimationAdapter.setShouldAnimate(false);
        getListView().setOnItemClickListener(this);

        if (isHot100) {
            final LinearLayout layout = new LinearLayout(getActivity());
            layout.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT));
            layout.setGravity(Gravity.CENTER_HORIZONTAL);
            final Button loadMore = new Button(getActivity());
            loadMore.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            loadMore.setText(billyapp.getString(R.string.loadmore));
            layout.addView(loadMore);
            getListView().addFooterView(layout);

            /**
             * mData keeps increasing till {@value billySize} elements (100, in case of Hot100)
             * Footer view removed when mData hits billySize - getMinBillySize(billySize) elements
             */
            loadMore.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (billyapp.isConnected()) {
                        Log.d(tag, "Size is " + mData.size());
                        if (mData.size() >= billySize - billyapp.getMinBillySize(billySize)) {
                            getListView().removeFooterView(loadMore);
                            loadMore.setVisibility(View.GONE); // just a bit precautionary
                        }
                        if (mData.size() < billySize) {
                            initializeArraylistitems();
                            customBaseAdapter.updateArrayList(mData);
                            customBaseAdapter.notifyDataSetChanged();

                            for (int i = 0; i < billyapp.getMinBillySize(billySize); i++) {
                                callitunes(mData.size() - billyapp.getMinBillySize(billySize) + i, false);
                            }
                        }
                    } else {
                        Toast.makeText(getActivity(), billyapp.getString(R.string.nointernet), Toast.LENGTH_LONG).show();
                    }

                }
            });
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        getActivity().overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
        swingBottomInAnimationAdapter.setShouldAnimate(false);
    }

    @Override
    public void onResume() {
        super.onResume();
        PreferenceManager.getDefaultSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(myPrefListener);
    }

    /**
     * Save mData to Bundle, to reuse on rotate
     * If connected to internet, write mDataLite to database, but only once per instance
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putParcelableArrayList("MDATA", mData);
        outState.putStringArray("BILLYSONG", billySong);
        outState.putStringArray("BILLYARTIST", billyArtist);
        if (mDataLite != null) {
            outState.putParcelableArrayList("MDATALITE", mDataLite);
        }

        if (mData.size() > billyapp.getMinBillySize(billySize)) {
            boolean b = saveToDB(mDataLite);
            Log.d(tag, "mDataLite is saved to DB? " + b + " size is " + mDataLite.size() + " and mData size is " + mData.size());
        } else {
            boolean b = saveToDB(mData);
            Log.d(tag, "mData is saved to DB? " + b + " size is " + mData.size());
        }
        super.onSaveInstanceState(outState);
    }

    private boolean saveToDB(ArrayList<ProcessingTask.BillyData> list) {
        if (onlyOnce == 0 && billyapp.isConnected()) {
            if (checkArrayList(list)) {
                Gson gson = new Gson();
                String jsonMdata = gson.toJson(list);
                long yolo = customDatabaseAdapter.insertArrayList(jsonMdata, table_name);
                onlyOnce++;
                if (yolo != -1) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Response listener for Billboard request
     * If response matches cached response, make no new requests, otherwise do.
     */
    private Response.Listener<String> billyComplete() {
        return new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(tag, "response length is " + response.length());
                spinnerVisible = false;
                spinner.setVisibility(View.GONE);
                v.findViewById(android.R.id.list).setVisibility(View.VISIBLE);
                try {
                    String jsonMdata = customDatabaseAdapter.getArrayList(table_name);
                    if (pulltorefresh) {
                        Log.d(tag, "Pull to refresh");
                        mIndex = 0;
                        handleXML(response, true);
                        pulltorefresh = false;
                    } else if (jsonMdata == null) {
                        Log.d(tag, "DB empty. Requests made.");
                        handleXML(response, true);
                    } else if (!response.equals(cacheData) && !cacheData.isEmpty()) {
                        Log.d(tag, "New data available. Requests made.");
                        handleXML(response, true);
                    } else {
                        handleXML(response, false);
                        Log.d(tag, "Cache and response are equal, no requests made");
                        Gson gson = new Gson();
                        mData = gson.fromJson(jsonMdata, new TypeToken<ArrayList<ProcessingTask.BillyData>>() {
                        }.getType());
                        customBaseAdapter.updateArrayList(mData);
                        customBaseAdapter.notifyDataSetChanged();
                    }
                } catch (IOException e) {
                    Log.d(tag, e.toString());
                } catch (XmlPullParserException e) {
                    Log.d(tag, e.toString());
                } catch (NullPointerException e) {
                    Log.d(tag, e.toString());
                }
            }
        };
    }

    private Response.ErrorListener billyError() {
        return new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.d(tag, volleyError.toString());
            }
        };
    }

    /**
     * Parses XML, populates {@code billySong, billyArtist}, spawns requests to iTunes
     *
     * @param response A String containing XML
     */
    private void handleXML(String response, boolean doItunesRequests) throws IOException, XmlPullParserException {
        ft.parseBillboard(response);
        billySong = ft.getBillySong();
        billyArtist = ft.getBillyArtist();
        if (doItunesRequests) {
            while (mIndex < billyapp.getMinBillySize(billySize)) {
                callitunes(mIndex, false);
                mIndex++;
            }
        }
    }

    /**
     * Spawn an iTunes request with song and artist Strings
     */
    private void callitunes(int i, boolean invalidateCache) {
        try {
            if(isAdded()) {
                searchparam = ft.paramEncode(billyArtist[i]) + "+" + ft.paramEncode(billySong[i]);
                uri = getResources().getString(R.string.itunes, searchparam);
                Log.d(tag, uri);
                JsonObjectRequest jsonreq = new JsonObjectRequest(Request.Method.GET, uri, null, itunesComplete(), itunesError());
                jsonreq.setTag(this);
                if (invalidateCache) {
                    req.getCache().invalidate(jsonreq.getCacheKey(), true);
                }
                req.add(jsonreq);
            }
        } catch (NullPointerException e) {
            Log.d(tag,e.toString());
            onRefresh(); // Simulate a pull to refresh
        }
    }

    /**
     * Parses JSONObject, populates {@code mData}, notifies the BaseAdapter
     */
    private Response.Listener<JSONObject> itunesComplete() {
        return new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject jsonObject) {
                try {
                    result = ft.parseItunes(jsonObject);

                    if (result != null) {
                        try {
                            // Log.d(tag, "Match is" +Integer.parseInt(result[4]));
                            mData.get(Integer.parseInt(result[4])).setItunes(result[0], result[1], result[2], result[3]);
                            customBaseAdapter.updateArrayList(mData);
                            customBaseAdapter.notifyDataSetChanged();
                        } catch (NullPointerException e) {
                            Log.d(tag, e.toString());
                        }
                    } else {
                        Crashlytics.log(Log.ERROR, tag, "Result object from iTunes is null for this track. " + jsonObject);
                    }
                } catch (JSONException e) {
                    Log.d(tag, e + "");
                }
            }
        };
    }

    private Response.ErrorListener itunesError() {
        return new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.d(tag, volleyError.toString());
            }
        };
    }

    /**
     * Send all the meta-data to DetailView, as an intent
     */

    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
        try {
            if (billyapp.isConnected()) {
                ProcessingTask.BillyData billyData = mData.get(i);

                Intent myintent = new Intent(getActivity(), DetailView.class);
                if (billyData.artwork != null) {
                    myintent.putExtra("song", billyData.song);
                    myintent.putExtra("album", billyData.album);
                    myintent.putExtra("artist", billyData.artist);
                    myintent.putExtra("artwork", billyData.artwork);
                    myintent.putExtra("index", i);
                    startActivity(myintent);
                } else {
                    //Toast.makeText(getActivity(), "Tap the card to refresh", Toast.LENGTH_LONG).show();
                    callitunes(i, true);
                    customBaseAdapter.notifyDataSetChanged();
                }
            } else {
                Toast.makeText(getActivity(), billyapp.getString(R.string.nointernet), Toast.LENGTH_LONG).show();
            }
        } catch (NullPointerException e) {
            Log.d(tag, e.toString());
            pulltorefresh = true;
            try {
                performRequests(); // mData is null, so perform all requests again and construct it
            } catch (UnsupportedEncodingException e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * Pull to refresh, with animation. Using Google's SwipeRefreshLayout.
     * Invalidates cache
     */

    @Override
    public void onRefresh() {
        if (billyapp.isConnected()) {
            pulltorefresh = true;
            swingBottomInAnimationAdapter.reset();
            CustomStringRequest stringreq = new CustomStringRequest(Request.Method.GET, rssurl, billyComplete(), billyError());
            req.getCache().invalidate(stringreq.getCacheKey(),true);
            stringreq.setTag(this);
            req.add(stringreq);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    swipelayout.setRefreshing(false);
                }
            }, 2500);
        } else {
            Toast.makeText(getActivity(), billyapp.getString(R.string.nointernet), Toast.LENGTH_LONG).show();
            swipelayout.setRefreshing(false);
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.refresh, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.refresh:
                if (billyapp.isConnected()) {
                    onRefresh();
                    swipelayout.setRefreshing(true);
                    return true;
                } else {
                    Toast.makeText(getActivity(), billyapp.getString(R.string.nointernet), Toast.LENGTH_LONG).show();
                    return false;
                }
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Cancel all on-going requests if screen is rotated, or fragment gets destroyed
     *
     * Note that FragmentStatePagerAdapter kills every fragment once you leave it (unlike other PagerAdapters)
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        req.cancelAll(this);
    }

    /**
     * Check if an ArrayList has null values
     *
     * @return true, if it doesn't have null values
     */
    private boolean checkArrayList(ArrayList<ProcessingTask.BillyData> mData) {
        int i = 0;
        try {
            while (i < mData.size()) {
                if (mData.get(i) == null) {
                    return false;
                } else if (mData.get(i).artwork.isEmpty()) {
                    return false;
                }
                i++;
            }
        } catch (NullPointerException e) {
            return false;
        }
        return true;
    }

    /**
     * If compact cards preference is on, reload listview
     * If album art preference is changed, update it in Processing Task
     * spawn all iTunes requests again
     */

    SharedPreferences.OnSharedPreferenceChangeListener myPrefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            if (key.equals("compactCards")) {
                Log.d(tag,"compact cards preference changed");
                ((ListView) v.findViewById(android.R.id.list)).setAdapter(customBaseAdapter);
            } else if (key.equals("albumArtQuality")) {
                if(ft.refreshArtworkUrlResolution()) {
                    Log.i(tag,"album art quality preference changed");
                    mIndex = 0;
                    while (mIndex < billyapp.getMinBillySize(billySize)) {
                        callitunes(mIndex, false);
                        mIndex++;
                    }
                }
            }
        }
    };
}




Java Source Code List

com.vibin.billy.BillyApplication.java
com.vibin.billy.BillyItem.java
com.vibin.billy.BitmapLruCache.java
com.vibin.billy.ChangelogDialog.java
com.vibin.billy.CustomBaseAdapter.java
com.vibin.billy.CustomDatabaseAdapter.java
com.vibin.billy.CustomFragmentAdapter.java
com.vibin.billy.CustomListPreference.java
com.vibin.billy.CustomShareActionProvider.java
com.vibin.billy.CustomStringRequest.java
com.vibin.billy.DetailView.java
com.vibin.billy.LicensesFragment.java
com.vibin.billy.MainActivity.java
com.vibin.billy.MediaControl.java
com.vibin.billy.NotifyingScrollView.java
com.vibin.billy.PPlayerService.java
com.vibin.billy.PlayerService.java
com.vibin.billy.ProcessingTask.java
com.vibin.billy.ReorderedListPreference.java
com.vibin.billy.Settings.java
com.vibin.billy.SongsFragment.java
com.vibin.billy.SwingBottomInAnimationAdapter.java
com.vibin.billy.draglistview.DynamicListView.java
com.vibin.billy.draglistview.StableArrayAdapter.java
com.vibin.billy.swipeable.ActivitySwipeDismissListener.java
com.vibin.billy.swipeable.AnimationUtils.java
com.vibin.billy.swipeable.SwipeDismissViewGroup.java
com.vibin.billy.swipeable.SwipeListener.java
com.vibin.billy.swipeable.SwipeableActivity.java
com.vibin.billy.swipeable.WindowDimens.java
com.vibin.billy.swipeable.WindowUtils.java
org.videolan.libvlc.AudioOutput.java
org.videolan.libvlc.EventHandler.java
org.videolan.libvlc.HWDecoderUtil.java
org.videolan.libvlc.IVideoPlayer.java
org.videolan.libvlc.LibVLC.java
org.videolan.libvlc.LibVlcException.java
org.videolan.libvlc.LibVlcUtil.java
org.videolan.libvlc.MediaList.java
org.videolan.libvlc.Media.java
org.videolan.libvlc.TrackInfo.java