com.geecko.QuickLyric.fragment.LocalLyricsFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.geecko.QuickLyric.fragment.LocalLyricsFragment.java

Source

/*
 * *
 *  * This file is part of QuickLyric
 *  * Copyright  2017 QuickLyric SPRL
 *  *
 *  * QuickLyric 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.
 *  *
 *  * QuickLyric 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 QuickLyric.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.geecko.QuickLyric.fragment;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.app.ActivityManager;
import android.app.ListFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v4.util.LongSparseArray;
import android.support.v4.view.VelocityTrackerCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.geecko.QuickLyric.App;
import com.geecko.QuickLyric.MainActivity;
import com.geecko.QuickLyric.R;
import com.geecko.QuickLyric.adapter.DrawerAdapter;
import com.geecko.QuickLyric.adapter.LocalAdapter;
import com.geecko.QuickLyric.lyrics.Lyrics;
import com.geecko.QuickLyric.services.BatchDownloaderService;
import com.geecko.QuickLyric.tasks.DBContentLister;
import com.geecko.QuickLyric.tasks.WriteToDatabaseTask;
import com.geecko.QuickLyric.utils.AnimatorActionListener;
import com.geecko.QuickLyric.utils.PermissionsChecker;
import com.geecko.QuickLyric.utils.Spotify;
import com.geecko.QuickLyric.view.AnimatedExpandableListView;
import com.geecko.QuickLyric.view.AnimatedExpandableListView.DummyView;
import com.geecko.QuickLyric.view.BackgroundContainer;
import com.squareup.leakcanary.RefWatcher;

import java.util.Arrays;

public class LocalLyricsFragment extends ListFragment {

    public static final int REQUEST_CODE = 0;
    public boolean showTransitionAnim = true;
    public boolean isActiveFragment = false;
    private AnimatedExpandableListView megaListView;
    private ProgressBar progressBar;
    private BackgroundContainer mBackgroundContainer;
    private boolean mSwiping;

    private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
        float mDownX;
        private int mSwipeSlop = -1;
        private boolean mItemPressed;
        private VelocityTracker mVelocityTracker = null;

        @Override
        public boolean onTouch(final View v, MotionEvent event) {
            int index = event.getActionIndex();
            int pointerId = event.getPointerId(index);

            if (mSwipeSlop < 0) {
                mSwipeSlop = ViewConfiguration.get(getActivity()).getScaledTouchSlop();
            }
            final int groupPosition = v instanceof DummyView
                    ? ((LocalAdapter.ChildViewHolder) ((DummyView) v).views.get(0).getTag()).groupPosition
                    : ((LocalAdapter.ChildViewHolder) v.getTag()).groupPosition;
            int childPosition = 90;

            for (int c = 0; c < megaListView.getChildCount(); c++)
                if (megaListView.getChildAt(c) == v) {
                    childPosition = c;
                    break;
                }

            // v.onTouchEvent(event);

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mItemPressed) {
                    // Multi-item swipes not handled
                    return false;
                }
                mItemPressed = true;
                mDownX = event.getX();
                if (mVelocityTracker == null) {
                    // Retrieve a new VelocityTracker object to watch the velocity of a motion.
                    mVelocityTracker = VelocityTracker.obtain();
                } else {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker.clear();
                }
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_CANCEL:
                v.setAlpha(1);
                v.setTranslationX(0);
                if (((LocalAdapter) megaListView.getExpandableListAdapter()).getGroup(groupPosition).length <= 1) {
                    View groupView = megaListView.getChildAt(childPosition - 1);
                    if (groupView != null && groupView.getTag() instanceof LocalAdapter.GroupViewHolder) {
                        groupView.setTranslationX(0);
                        groupView.setAlpha(1);
                    }
                }
                mItemPressed = false;
                mVelocityTracker.recycle();
                mVelocityTracker = null;
                break;
            case MotionEvent.ACTION_MOVE: {
                mVelocityTracker.addMovement(event);
                float x = event.getX() + v.getTranslationX();
                float deltaX = x - mDownX;
                float deltaXAbs = Math.abs(deltaX);
                if (!mSwiping) {
                    if (deltaXAbs > mSwipeSlop) {
                        mSwiping = true;
                        getListView().requestDisallowInterceptTouchEvent(true);
                        int diff = 0;
                        if (((LocalAdapter) megaListView.getExpandableListAdapter())
                                .getGroup(groupPosition).length <= 1) {
                            View groupView = megaListView.getChildAt(childPosition - 1);
                            if (groupView != null)
                                diff = groupView.getHeight();
                        }
                        mBackgroundContainer.showBackground(v.getTop() - diff, v.getHeight() + diff);
                    }
                }
                if (mSwiping) {
                    v.setTranslationX((x - mDownX));
                    v.setAlpha(1 - deltaXAbs / v.getWidth());
                    if (((LocalAdapter) megaListView.getExpandableListAdapter())
                            .getGroup(groupPosition).length <= 1) {
                        View groupView = megaListView.getChildAt(childPosition - 1);
                        if (groupView != null && groupView.getTag() instanceof LocalAdapter.GroupViewHolder) {
                            groupView.setTranslationX((x - mDownX));
                            groupView.setAlpha(1 - deltaXAbs / v.getWidth());
                        }
                    }
                }
            }
                break;
            case MotionEvent.ACTION_UP: {
                // User let go - figure out whether to animate the view out, or back into place
                if (mSwiping) {
                    final float x = event.getX() + v.getTranslationX();
                    float deltaX = x - mDownX;
                    final float deltaXAbs = Math.abs(deltaX);
                    float fractionCovered;
                    float endX;
                    float endAlpha;
                    final boolean remove;
                    mVelocityTracker.computeCurrentVelocity(1000);
                    float velocityX = Math.abs(VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId));
                    if (velocityX > 700 || deltaXAbs > v.getWidth() / 4) {
                        fractionCovered = deltaXAbs / v.getWidth();
                        endX = deltaX < 0 ? -v.getWidth() : v.getWidth();
                        endAlpha = 0;
                        remove = true;
                    } else {
                        // Not far enough - animate it back
                        fractionCovered = 1 - (deltaXAbs / v.getWidth());
                        endX = 0;
                        endAlpha = 1;
                        remove = false;
                    }
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                    int SWIPE_DURATION = 600;
                    long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
                    getListView().setEnabled(false);
                    v.animate().setDuration(Math.abs(duration)).alpha(endAlpha).translationX(endX)
                            .setListener(new AnimatorActionListener(new Runnable() {
                                @Override
                                public void run() {
                                    if (remove) {
                                        animateRemoval(v);
                                    } else {
                                        mBackgroundContainer.hideBackground();
                                        getListView().setEnabled(true);
                                    }
                                }
                            }, AnimatorActionListener.ActionType.END));
                    if (((LocalAdapter) megaListView.getExpandableListAdapter())
                            .getGroup(groupPosition).length <= 1) {
                        View groupView = megaListView.getChildAt(childPosition - 1);
                        if (groupView != null && groupView.getTag() instanceof LocalAdapter.GroupViewHolder)
                            groupView.animate().setDuration(Math.abs(duration)).alpha(endAlpha).translationX(endX);
                    }
                } else {
                    megaListView.performItemClick(v, megaListView.getPositionForView(v), 0L);
                }
            }
                mSwiping = false;
                mItemPressed = false;
                break;
            default:
                return false;
            }
            return true;
        }

        private void animateRemoval(View viewToRemove) {
            mBackgroundContainer.hideBackground();
            // Delete the item from the adapter

            LocalAdapter.ChildViewHolder childViewHolder = viewToRemove instanceof DummyView
                    ? (LocalAdapter.ChildViewHolder) ((DummyView) viewToRemove).views.get(0).getTag()
                    : (LocalAdapter.ChildViewHolder) viewToRemove.getTag();
            new WriteToDatabaseTask(LocalLyricsFragment.this).execute(LocalLyricsFragment.this, null,
                    childViewHolder.lyrics);
        }
    };

    public void animateUndo(Lyrics lyrics) {
        new WriteToDatabaseTask(LocalLyricsFragment.this).execute(LocalLyricsFragment.this, null, lyrics);
    }

    public LongSparseArray<Integer> collectTopPositions() {
        final LongSparseArray<Integer> itemIdTopMap = new LongSparseArray<>();
        int firstVisiblePosition = megaListView.getFirstVisiblePosition();
        for (int i = 0; i < megaListView.getChildCount(); ++i) {
            View child = megaListView.getChildAt(i);
            int position = firstVisiblePosition + i;
            long itemId = megaListView.getAdapter().getItemId(position);
            itemIdTopMap.put(itemId, child.getTop());
        }
        return itemIdTopMap;
    }

    public void addObserver(final LongSparseArray<Integer> itemIdTopMap) {
        final boolean[] firstAnimation = { true };
        final ViewTreeObserver observer = megaListView.getViewTreeObserver();
        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                observer.removeOnPreDrawListener(this);
                firstAnimation[0] = true;
                int firstVisiblePosition = megaListView.getFirstVisiblePosition();
                for (int i = 0; i < megaListView.getChildCount(); ++i) {
                    final View child = megaListView.getChildAt(i);
                    int position = firstVisiblePosition + i;
                    long itemId = getListView().getAdapter().getItemId(position);
                    Integer formerTop = itemIdTopMap.get(itemId);
                    int newTop = child.getTop();
                    if (formerTop != null) {
                        if (formerTop != newTop) {
                            int delta = formerTop - newTop;
                            child.setTranslationY(delta);
                            int MOVE_DURATION = 500;
                            child.animate().setDuration(MOVE_DURATION).translationY(0);
                            if (firstAnimation[0]) {
                                child.animate().setListener(new AnimatorActionListener(new Runnable() {
                                    public void run() {
                                        mSwiping = false;
                                        getListView().setEnabled(true);
                                    }
                                }, AnimatorActionListener.ActionType.END));
                                firstAnimation[0] = false;
                            }
                        }
                    } else {
                        // Animate new views along with the others. The catch is that they did not
                        // exist in the start state, so we must calculate their starting position
                        // based on neighboring views.
                        int childHeight = child.getHeight() + megaListView.getDividerHeight();
                        boolean isFurthest = true;
                        for (int j = 0; j < itemIdTopMap.size(); j++) {
                            Integer top = itemIdTopMap.valueAt(j);
                            if (top - childHeight > newTop) {
                                isFurthest = false;
                                break;
                            }
                        }
                        formerTop = newTop + (i > 0 ? childHeight : -childHeight);
                        int delta = formerTop - newTop;
                        int MOVE_DURATION = 500;
                        if (isFurthest) {
                            child.setTranslationY(delta);
                            child.animate().setDuration(MOVE_DURATION).translationY(0);
                        } else {
                            int translationX = formerTop > childHeight ? child.getWidth() : 0;
                            child.setTranslationX(translationX);
                            if (translationX == 0)
                                child.setTranslationY(formerTop - newTop);
                            child.animate().setDuration(MOVE_DURATION).translationX(0).translationY(0);
                        }
                        if (firstAnimation[0]) {
                            child.animate().setListener(new AnimatorActionListener(new Runnable() {
                                public void run() {
                                    getListView().setEnabled(true);
                                    mSwiping = false;
                                }
                            }, AnimatorActionListener.ActionType.END));
                            firstAnimation[0] = false;
                        }
                    }
                }
                if (firstAnimation[0]) {
                    mSwiping = false;
                    getListView().setEnabled(true);
                    firstAnimation[0] = false;
                }
                itemIdTopMap.clear();
                return true;
            }
        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        setRetainInstance(true);
        setHasOptionsMenu(true);
        View layout = inflater.inflate(R.layout.local_listview, container, false);
        megaListView = (AnimatedExpandableListView) layout.findViewById(android.R.id.list);
        mBackgroundContainer = (BackgroundContainer) layout.findViewById(R.id.listViewBackground);
        progressBar = (ProgressBar) layout.findViewById(R.id.list_progress);
        return layout;
    }

    @Override
    public void onActivityCreated(Bundle onSavedInstanceState) {
        super.onActivityCreated(onSavedInstanceState);
        if (megaListView != null) {
            View fragmentView = getView();
            TypedValue typedValue = new TypedValue();
            getActivity().getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
            if (fragmentView != null)
                fragmentView.setBackgroundColor(typedValue.data);
            megaListView.setDividerHeight(0);
            megaListView.setFastScrollEnabled(true);
            megaListView.setDrawSelectorOnTop(true);
        }
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        final MainActivity mainActivity = ((MainActivity) this.getActivity());
        super.onViewCreated(view, savedInstanceState);
        if (this.isHidden())
            return;

        DrawerAdapter drawerAdapter = ((DrawerAdapter) ((ListView) mainActivity.findViewById(R.id.drawer_list))
                .getAdapter());
        if (drawerAdapter.getSelectedItem() != 1) {
            drawerAdapter.setSelectedItem(1);
            drawerAdapter.notifyDataSetChanged();
        }

        if (!megaListView.hasOnGroupClickListener())
            megaListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

                @Override
                public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                    final ImageView indicator = (ImageView) v.findViewById(R.id.group_indicator);
                    RotateAnimation anim;
                    if (megaListView.isGroupExpanded(groupPosition)) {
                        megaListView.collapseGroupWithAnimation(groupPosition);
                        if (indicator != null) {
                            anim = new RotateAnimation(180f, 360f, indicator.getWidth() / 2,
                                    indicator.getHeight() / 2);
                            anim.setInterpolator(new DecelerateInterpolator(3));
                            anim.setDuration(500);
                            anim.setFillAfter(true);
                            indicator.startAnimation(anim);
                        }
                    } else {
                        megaListView.expandGroupWithAnimation(groupPosition);
                        if (indicator != null) {
                            anim = new RotateAnimation(0f, 180f, indicator.getWidth() / 2,
                                    indicator.getHeight() / 2);
                            anim.setInterpolator(new DecelerateInterpolator(2));
                            anim.setDuration(500);
                            anim.setFillAfter(true);
                            indicator.startAnimation(anim);
                        }
                    }
                    return true;
                }
            });

        if (!megaListView.hasOnChildClickListener())
            megaListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
                @Override
                public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition,
                        long id) {
                    if (mSwiping) {
                        mSwiping = false;
                        return false;
                    }
                    final MainActivity mainActivity = (MainActivity) getActivity();
                    megaListView.setOnChildClickListener(null); // prevents bug on double tap
                    mainActivity.updateLyricsFragment(R.animator.slide_out_start, R.animator.slide_in_start, true,
                            ((LocalAdapter) megaListView.getExpandableListAdapter()).getChild(groupPosition,
                                    childPosition));
                    return true;
                }
            });

        this.isActiveFragment = true;
        new DBContentLister(this).execute();
    }

    public void update(final String[] results) {
        if (getView() == null)
            return;
        int index = megaListView.getFirstVisiblePosition();
        View v = megaListView.getChildAt(0);
        int top = (v == null) ? 0 : (v.getTop() - megaListView.getPaddingTop());

        megaListView.setAdapter(new LocalAdapter(getActivity(), results, mTouchListener, megaListView));
        megaListView
                .setEmptyView(((ViewGroup) getView().findViewById(R.id.local_empty_database_textview).getParent()));
        getListView().setSelectionFromTop(index, top);
        setListShown(true);
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden)
            this.onViewCreated(getView(), null);
        else
            this.isActiveFragment = false;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        MainActivity mainActivity = (MainActivity) this.getActivity();
        ActionBar actionBar = (mainActivity).getSupportActionBar();
        CollapsingToolbarLayout toolbarLayout = (CollapsingToolbarLayout) mainActivity
                .findViewById(R.id.toolbar_layout);
        if (mainActivity.focusOnFragment) // focus is on Fragment
        {
            if (actionBar.getTitle() == null || !actionBar.getTitle().equals(this.getString(R.string.local_title)))
                toolbarLayout.setTitle(getString(R.string.local_title));
            inflater.inflate(R.menu.local, menu);
        } else
            menu.clear();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_scan:
            if (!isMyServiceRunning(getActivity()))
                showScanDialog();
            else
                Toast.makeText(getActivity(), getString(R.string.dl_progress, 9, 9).replaceAll("9", ""),
                        Toast.LENGTH_LONG).show();
        }
        return false;
    }

    public void showScanDialog() {
        final String[] values = getResources().getStringArray(R.array.URI_values);
        CharSequence[] items = Arrays.copyOfRange(getResources().getStringArray(R.array.URI_labels), 0,
                values.length);
        AlertDialog.Builder choiceBuilder = new AlertDialog.Builder(getActivity());
        AlertDialog choiceDialog = choiceBuilder.setTitle(R.string.content_providers_title)
                .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(final DialogInterface choiceDialog, int i) {
                        if (values[i].equals("Spotify")) {
                            AlertDialog.Builder spotifyChoiceBuilder = new AlertDialog.Builder(getActivity());
                            spotifyChoiceBuilder.setSingleChoiceItems(R.array.spotify_options, -1,
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                            case 1:
                                                Spotify.getPlaylistTracks(getActivity());
                                                dialog.dismiss();
                                                choiceDialog.dismiss();
                                                break;
                                            case 0:
                                                Spotify.getUserTracks(getActivity());
                                                dialog.dismiss();
                                                choiceDialog.dismiss();
                                                break;
                                            }
                                        }
                                    }).show();
                            return;
                        } else {
                            if (!PermissionsChecker.requestPermission(getActivity(),
                                    "android.permission.READ_EXTERNAL_STORAGE", 0,
                                    LocalLyricsFragment.REQUEST_CODE))
                                return;
                        }
                        final Uri contentProvider = Uri.parse(values[i]);
                        String[] projection = new String[] { "artist", "title" };
                        String selection = "artist IS NOT NULL AND artist <> '<unknown>'";
                        Cursor countCursor = getActivity().getContentResolver().query(contentProvider, projection,
                                selection, null, null);
                        if (countCursor == null || countCursor.getCount() == 0) {
                            choiceDialog.cancel();
                            Toast.makeText(getActivity(), getString(R.string.scan_error_no_content),
                                    Toast.LENGTH_LONG).show();
                            return;
                        }
                        int count = countCursor.getCount();
                        final int time = (int) Math.ceil(count / 500f);
                        countCursor.close();
                        choiceDialog.dismiss();
                        String prompt = getResources().getQuantityString(R.plurals.scan_dialog, count);
                        AlertDialog.Builder confirmDialog = new AlertDialog.Builder(getActivity());
                        confirmDialog.setTitle(R.string.warning).setMessage(String.format(prompt, count, time))
                                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        Intent scanInfo = new Intent(getActivity(), BatchDownloaderService.class);
                                        scanInfo.putExtra("uri", contentProvider);
                                        getActivity().startService(scanInfo);
                                    }
                                }).setNegativeButton(android.R.string.cancel, null).create().show();
                    }
                }).create();
        choiceDialog.show();
    }

    private boolean isMyServiceRunning(Context context) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (BatchDownloaderService.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        Log.i("Service not", "running");
        return false;
    }

    public void setListShown(final boolean visible) {
        final Animation fadeIn = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in);
        final Animation fadeOut = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
        Animation.AnimationListener listener = new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                progressBar.setVisibility(visible ? View.GONE : View.VISIBLE);
                megaListView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        };
        fadeIn.setAnimationListener(listener);
        fadeOut.setAnimationListener(listener);
        progressBar.startAnimation(visible ? fadeOut : fadeIn);
        megaListView.startAnimation(visible ? fadeIn : fadeOut);
    }

    public ExpandableListAdapter getExpandableListAdapter() {
        return megaListView.getExpandableListAdapter();
    }

    public AnimatedExpandableListView getMegaListView() {
        return megaListView;
    }

    @Override
    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
        Animator anim = null;
        if (showTransitionAnim) {
            if (nextAnim != 0)
                anim = AnimatorInflater.loadAnimator(getActivity(), nextAnim);
            showTransitionAnim = false;
        } else
            anim = AnimatorInflater.loadAnimator(getActivity(), R.animator.none);
        return anim;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(getActivity());
        refWatcher.watch(this);
    }
}