berict/Tapad

View on GitHub
app/src/main/java/com/bedrock/padder/helper/SoundHelper.java

Summary

Maintainability
F
4 days
Test Coverage
package com.bedrock.padder.helper;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.SoundPool;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.bedrock.padder.R;
import com.bedrock.padder.activity.MainActivity;
import com.bedrock.padder.model.preferences.Preferences;
import com.bedrock.padder.model.preset.Preset;
import com.bedrock.padder.model.sound.Deck;
import com.bedrock.padder.model.sound.GesturePad;
import com.bedrock.padder.model.sound.Pad;
import com.bedrock.padder.model.sound.Sound;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import static com.bedrock.padder.activity.MainActivity.PAD_PATTERN;
import static com.bedrock.padder.activity.MainActivity.currentPreset;
import static com.bedrock.padder.activity.MainActivity.isPresetLoading;
import static com.bedrock.padder.activity.MainActivity.setPadPattern;
import static com.bedrock.padder.helper.WindowHelper.getViewFromId;
import static com.bedrock.padder.model.tutorial.TimingListener.broadcast;

public class SoundHelper {

    private static Preset previousPreset = null;

    private SoundPool sp = new SoundPool(16, AudioManager.STREAM_MUSIC, 0);

    private MediaMetadataRetriever mmr = new MediaMetadataRetriever();

    private Activity activity;

    private int buttonId[] = {
            R.id.btn00,
            R.id.tgl1,
            R.id.tgl2,
            R.id.tgl3,
            R.id.tgl4,
            R.id.btn11,
            R.id.btn12,
            R.id.btn13,
            R.id.btn14,
            R.id.btn21,
            R.id.btn22,
            R.id.btn23,
            R.id.btn24,
            R.id.btn31,
            R.id.btn32,
            R.id.btn33,
            R.id.btn34,
            R.id.btn41,
            R.id.btn42,
            R.id.btn43,
            R.id.btn44
    };

    private int padPatternToggleId[] = {
            R.id.tgl5,
            R.id.tgl6,
            R.id.tgl7,
            R.id.tgl8,
    };

    private AdMobHelper ad = new AdMobHelper();
    private AnimateHelper anim = new AnimateHelper();
    private WindowHelper window = new WindowHelper();

    private AsyncTask unload = null;
    private AsyncTask load = null;

    private ProgressBar progress;

    private int progressCount;
    private int presetSoundCount;

    private Deck decks[];

    private int color = 0;
    private int colorDef = 0;

    private int intervalPixel;
    private int intervalCount;

    public SoundPool getSoundPool() {
        return sp;
    }

    public void setDecks(int color, int colorDef, Activity activity) {
        Preferences preferences = new Preferences(activity);
        this.color = preferences.getColor();
        this.colorDef = colorDef;
        decks = new Deck[]{
                new Deck(new Pad[17], null, activity.findViewById(buttonId[1]), color, colorDef, activity),
                new Deck(new Pad[17], null, activity.findViewById(buttonId[2]), color, colorDef, activity),
                new Deck(new Pad[17], null, activity.findViewById(buttonId[3]), color, colorDef, activity),
                new Deck(new Pad[17], null, activity.findViewById(buttonId[4]), color, colorDef, activity),
        };

        for (int i = 1; i <= 4; i++) {
            final int index = i - 1;
            window.setOnClick(buttonId[i], new Runnable() {
                @Override
                public void run() {
                    // set onTouch events
                    if (decks[index].isSelected()) {
                        // was already selected
                        select(-1);
                    } else {
                        select(index);
                    }
                }
            }, activity);
        }

        for (int i = 0; i < 4; i++) {
            final int index = i + 1;
            window.setOnClick(padPatternToggleId[i], new Runnable() {
                @Override
                public void run() {
                    // set pad pattern ids
                    if (index == PAD_PATTERN) {
                        // was already selected
                        selectPattern(-1);
                    } else {
                        selectPattern(index);
                    }
                }
            }, activity);
        }
    }

    private void select(int index) {
        Log.d("SH", "Index selected " + index);
        // index starts from 0
        if (index == -1) {
            // disable all
            for (Deck deck : decks) {
                deck.setUnselected();
                deck.setSelected(false);
            }
        } else {
            for (int i = 0; i < decks.length; i++) {
                if (i == index) {
                    // selected
                    broadcast(i + 1);
                    decks[i].setSelected(true);
                    for (Pad pad : decks[i].getPads()) {
                        // update looping pads
                        pad.update();
                    }
                } else {
                    decks[i].setSelected(false);
                }
            }
        }
    }

    private void selectPattern(int index) {
        Log.d("SH", "Index selected " + index);
        // index starts from 0
        if (index == -1) {
            // disable all
            setPadPattern(0);
            for (int i = 1; i <= 4; i++) {
                getViewFromId("tgl" + String.valueOf(i + 4), activity).setBackgroundColor(activity.getResources().getColor(R.color.grey));
            }
        } else {
            setPadPattern(index);
            for (int i = 1; i <= 4; i++) {
                if (i == index) {
                    // selected
                    getViewFromId("tgl" + String.valueOf(i + 4), activity).setBackgroundColor(activity.getResources().getColor(R.color.colorAccent));
                } else {
                    getViewFromId("tgl" + String.valueOf(i + 4), activity).setBackgroundColor(activity.getResources().getColor(R.color.grey));
                }
            }
        }
    }

    public void clear() {
        // clear buttons
        for (Deck deck : decks) {
            for (Pad pad : deck.getPads()) {
                if (pad != null) {
                    pad.stop();
                }
            }
        }
    }

    public void loadColor(int color) {
        if (decks != null && currentPreset != null) {
            for (Deck deck : decks) {
                for (Pad pad : deck.getPads()) {
                    if (pad != null) {
                        pad.setColor(color);
                    }
                }
            }
        }
    }

    public void load(Preset preset, int color, int colorDef, Activity activity) {
        // set the previous preset
        this.color = color;
        this.colorDef = colorDef;
        previousPreset = currentPreset;
        currentPreset = preset;
        this.activity = activity;

        unload = new Unload().execute();

        if (!(currentPreset.getInAppTutorialAvailable() ||
                currentPreset.getAbout().getTutorialAvailable())) {
            // no tutorial available
            activity.findViewById(R.id.toolbar_tutorial).setVisibility(View.GONE);
        } else {
            activity.findViewById(R.id.toolbar_tutorial).setVisibility(View.VISIBLE);
        }
    }

    public void cancelLoad() {
        try {
            unload.cancel(true);
            Log.i("TAG", "Unloading canceled");
        } catch (NullPointerException e) {
            Log.e("NPE", "Unload AsyncTask is null");
        }
        try {
            load.cancel(true);
            Log.i("TAG", "Loading canceled");
        } catch (NullPointerException e) {
            Log.e("NPE", "Loading AsyncTask is null");
        }
    }

    public void stop() {
        for (Deck deck : decks) {
            deck.stop();
        }
    }

    public void stopAll() {
        sp.autoPause();
        stop();
    }

    private GesturePad getGesturePadFromArray(String soundPaths[],
                                              SoundPool soundPool,
                                              int deck,
                                              View buttonView,
                                              int color, int colorDef,
                                              Activity activity) {
        Sound sounds[] = new Sound[5];
        for (int i = 0; i < 5; i++) {
            if (i < soundPaths.length) {
                // sounds exists
                sounds[i] = new Sound(soundPool, soundPaths[i], mmr);
            } else {
                // no sound gesture
                sounds[i] = new Sound(soundPool, null, mmr);
            }
        }
        return new GesturePad(sounds, deck, buttonView, color, colorDef, activity);
    }

    private void onLoadFinish() {
        // final sampleId
        Log.d("LoadSound", "Loading completed, SoundPool successfully loaded "
                + presetSoundCount
                + " sounds");

        // pause adViewMain after the loading
        ad.pauseAdView(R.id.adView_main, activity);

        ((ImageView) activity.findViewById(R.id.toolbar_tutorial_icon)).setImageResource(R.drawable.ic_tutorial_white);

        anim.fadeOut(R.id.progress_bar_layout, 0, 600, activity);
        anim.fadeOut(R.id.adView_main, 0, 600, activity);

        // Load finished, set AsyncTask objects to null
        load = null;
        unload = null;

        revealButtonWithAnimation();

        isPresetLoading = false;
    }

    public void revealButtonWithAnimation() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                window.setVisible(R.id.base, 0, activity);
                buttonRevealAnimation(new Random().nextInt(25));
            }
        }, 600);
    }

    private void buttonRevealAnimation(final int buttonRectIndex) {
        final Rect buttonRects[] = {
                window.getRect(R.id.btn00, activity),
                window.getRect(R.id.tgl1, activity),
                window.getRect(R.id.tgl2, activity),
                window.getRect(R.id.tgl3, activity),
                window.getRect(R.id.tgl4, activity),
                window.getRect(R.id.tgl5, activity),
                window.getRect(R.id.tgl6, activity),
                window.getRect(R.id.tgl7, activity),
                window.getRect(R.id.tgl8, activity),
                window.getRect(R.id.btn11, activity),
                window.getRect(R.id.btn12, activity),
                window.getRect(R.id.btn13, activity),
                window.getRect(R.id.btn14, activity),
                window.getRect(R.id.btn21, activity),
                window.getRect(R.id.btn22, activity),
                window.getRect(R.id.btn23, activity),
                window.getRect(R.id.btn24, activity),
                window.getRect(R.id.btn31, activity),
                window.getRect(R.id.btn32, activity),
                window.getRect(R.id.btn33, activity),
                window.getRect(R.id.btn34, activity),
                window.getRect(R.id.btn41, activity),
                window.getRect(R.id.btn42, activity),
                window.getRect(R.id.btn43, activity),
                window.getRect(R.id.btn44, activity)
        };

        final View buttonViews[] = {
                activity.findViewById(R.id.btn00),
                activity.findViewById(R.id.tgl1),
                activity.findViewById(R.id.tgl2),
                activity.findViewById(R.id.tgl3),
                activity.findViewById(R.id.tgl4),
                activity.findViewById(R.id.tgl5),
                activity.findViewById(R.id.tgl6),
                activity.findViewById(R.id.tgl7),
                activity.findViewById(R.id.tgl8),
                activity.findViewById(R.id.btn11),
                activity.findViewById(R.id.btn12),
                activity.findViewById(R.id.btn13),
                activity.findViewById(R.id.btn14),
                activity.findViewById(R.id.btn21),
                activity.findViewById(R.id.btn22),
                activity.findViewById(R.id.btn23),
                activity.findViewById(R.id.btn24),
                activity.findViewById(R.id.btn31),
                activity.findViewById(R.id.btn32),
                activity.findViewById(R.id.btn33),
                activity.findViewById(R.id.btn34),
                activity.findViewById(R.id.btn41),
                activity.findViewById(R.id.btn42),
                activity.findViewById(R.id.btn43),
                activity.findViewById(R.id.btn44)
        };

        intervalPixel = (int) Math.hypot(window.getWindowWidthPx(activity), window.getWindowWidthPx(activity)) / 40;
        Log.i("intervalPixel", String.valueOf(intervalPixel));
        intervalCount = 0;
        // 40 intervals x 10ms = 400ms animation

        anim.fadeIn(buttonViews[buttonRectIndex], 0, 100, "btn" + String.valueOf(buttonRectIndex) + "In", activity);

        final Handler intervalTimer = new Handler();
        intervalTimer.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (intervalCount <= 40) {
                    for (int i = 0; i < buttonRects.length; i++) {
                        if (buttonRectIndex != i) {
                            // not the view itself
                            if (isAnimationCollides(
                                    buttonRects[buttonRectIndex],
                                    buttonRects[i],
                                    intervalPixel * intervalCount) &&
                                    buttonViews[i].getVisibility() != View.VISIBLE) {
                                // collides, fadeIn
                                anim.fadeIn(buttonViews[i], 0, 50, "btn" + String.valueOf(i) + "In", activity);
                            }
                        }
                    }
                    intervalCount++;
                    intervalTimer.postDelayed(this, 10);
                }
            }
        }, 10);
    }

    private boolean isAnimationCollides(Rect startViewRect, Rect targetViewRect, int distance) {
        double viewDistance =
                Math.hypot(
                        Math.abs(startViewRect.centerX() - targetViewRect.centerX()),
                        Math.abs(startViewRect.centerY() - targetViewRect.centerY())
                );
        // gets view hypothesis
        return viewDistance < distance;
    }

    private class Unload extends AsyncTask<Void, Void, Void> {

        String TAG = "UnLoad";

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // started loading
            isPresetLoading = true;
            presetSoundCount = currentPreset.getSoundCount();
            // set progress
            progressCount = 0;
            progress = ((ProgressBar) activity.findViewById(R.id.progress_bar));
            ad.resumeAdView(R.id.adView_main, activity);
            if (activity.findViewById(R.id.progress_bar_layout).getVisibility() == View.GONE) {
                anim.fadeIn(R.id.progress_bar_layout, 0, 600, "progressIn", activity);
                // request ads
                anim.fadeIn(R.id.adView_main, 0, 600, "adViewIn", activity);
                ad.requestLoadAd(ad.getAdView(R.id.adView_main, activity));
                window.setInvisible(R.id.base, 600, activity);
                progress.setIndeterminate(true);
            }

            // initialize view
            View buttonViews[] = {
                    activity.findViewById(R.id.btn00),
                    activity.findViewById(R.id.tgl1),
                    activity.findViewById(R.id.tgl2),
                    activity.findViewById(R.id.tgl3),
                    activity.findViewById(R.id.tgl4),
                    activity.findViewById(R.id.tgl5),
                    activity.findViewById(R.id.tgl6),
                    activity.findViewById(R.id.tgl7),
                    activity.findViewById(R.id.tgl8),
                    activity.findViewById(R.id.btn11),
                    activity.findViewById(R.id.btn12),
                    activity.findViewById(R.id.btn13),
                    activity.findViewById(R.id.btn14),
                    activity.findViewById(R.id.btn21),
                    activity.findViewById(R.id.btn22),
                    activity.findViewById(R.id.btn23),
                    activity.findViewById(R.id.btn24),
                    activity.findViewById(R.id.btn31),
                    activity.findViewById(R.id.btn32),
                    activity.findViewById(R.id.btn33),
                    activity.findViewById(R.id.btn34),
                    activity.findViewById(R.id.btn41),
                    activity.findViewById(R.id.btn42),
                    activity.findViewById(R.id.btn43),
                    activity.findViewById(R.id.btn44)
            };

            for (View view : buttonViews) {
                view.setVisibility(View.INVISIBLE);
            }
        }

        @Override
        protected Void doInBackground(Void... params) {
            if (previousPreset != null) {
                Log.i(TAG, "Preset \"" + previousPreset.getTag() + "\" unloading");
                for (int i = 0; i < 4; i++) {
                    Log.i(TAG, "Deck " + (i + 1));
                    decks[i].unload();
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            // start loading sound
            Log.d(TAG, "Finished unloading sounds");
            sp.release();
            sp = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
            load = new Load().execute();
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onCancelled(Void aVoid) {
            super.onCancelled(aVoid);
            onLoadFinish();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            onLoadFinish();
        }
    }

    private class Load extends AsyncTask<Void, Void, Void> {

        String TAG = "Load";
        View buttonViews[];
        private int savedSampleId = 0;
        private int savedSampleIdInRunnable = 1;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // started loading sounds
            progress.setIndeterminate(false);
            progress.setMax(presetSoundCount);
            progress.setProgress(0);
            ((ImageView) activity.findViewById(R.id.toolbar_tutorial_icon)).setImageResource(R.drawable.ic_tutorial_disabled_white);

            // initialize view
            buttonViews = new View[]{
                    activity.findViewById(R.id.btn00),
                    activity.findViewById(R.id.btn11),
                    activity.findViewById(R.id.btn12),
                    activity.findViewById(R.id.btn13),
                    activity.findViewById(R.id.btn14),
                    activity.findViewById(R.id.btn21),
                    activity.findViewById(R.id.btn22),
                    activity.findViewById(R.id.btn23),
                    activity.findViewById(R.id.btn24),
                    activity.findViewById(R.id.btn31),
                    activity.findViewById(R.id.btn32),
                    activity.findViewById(R.id.btn33),
                    activity.findViewById(R.id.btn34),
                    activity.findViewById(R.id.btn41),
                    activity.findViewById(R.id.btn42),
                    activity.findViewById(R.id.btn43),
                    activity.findViewById(R.id.btn44)
            };
        }

        @Override
        protected Void doInBackground(Void... params) {
            if (previousPreset != null) {
                Log.i(TAG, "Preset \"" + currentPreset.getTag() + "\" loading");
                for (int i = 0; i < 4; i++) {
                    Log.i(TAG, "  Deck " + (i + 1));
                    // pad loop
                    for (int j = 0; j < 17; j++) {
                        Log.i(TAG, "    Pad " + (j + 1));
                        // pad read from file
                        ArrayList<String> sounds = new ArrayList<>();
                        for (int k = 0; k < 5; k++) {
                            String sound = currentPreset.getSound(i, j, k);
                            if (sound != null) {
                                sounds.add(sound);
                                publishProgress();
                            }
                        }
                        if (sounds.size() == 1) {
                            // only one sound, use sound
                            decks[i].setPad(new Pad(new Sound(sp, sounds.get(0), mmr), i + 1, buttonViews[j],
                                    color, colorDef, activity), j);
                        } else if (sounds.size() > 1) {
                            // gesture pad
                            decks[i].setPad(getGesturePadFromArray(sounds.toArray(new String[5]), sp, i + 1, buttonViews[j],
                                    color, colorDef, activity), j);
                        } else {
                            // no sounds
                            decks[i].setPad(new Pad(new Sound(), i + 1, buttonViews[j],
                                    color, colorDef, activity), j);
                        }
                    }

                    for (int j = 0; j < 4; j++) {
                        String sound = currentPreset.getSound(i, "0" + (j + 1));
                        if (sound != null) {
                            publishProgress();
                            decks[i].setSound(new Sound(sp, sound, mmr));
                        }
                    }
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            progress.setProgress(progressCount++);
        }

        @Override
        protected void onCancelled(Void aVoid) {
            super.onCancelled(aVoid);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
        // needs to be different at first to make changes

        @Override
        protected void onPostExecute(Void aVoid) {
            if (isCancelled()) {
                onLoadFinish();
            } else {
                // loading finish listener
                Log.d(TAG, "sampleId count : " + presetSoundCount);
                progress.setIndeterminate(true);

                sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
                    @Override
                    public void onLoadComplete(SoundPool soundPool, final int sampleId, int status) {
                        Log.d(TAG, "Loading Finished, sampleId : " + sampleId);
                        savedSampleId = sampleId;
                    }
                });

                // Add loaded preset to shortcut
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
                    Intent main = new Intent(activity.getApplicationContext(), MainActivity.class);
                    main.setAction(Intent.ACTION_VIEW);
                    main.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    main.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    main.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    main.putExtra("shortcut", currentPreset.getTag());

                    ShortcutManager shortcutManager = activity.getSystemService(ShortcutManager.class);

                    ShortcutInfo.Builder shortcut = new ShortcutInfo.Builder(activity, currentPreset.getTag())
                            .setShortLabel(currentPreset.getAbout().getSongName())
                            .setLongLabel(currentPreset.getAbout().getSongName() + " - " + currentPreset.getAbout().getSongArtist())
                            .setIntent(main);

                    Bitmap icon;
                    try {
                        icon = BitmapFactory.decodeStream(new FileInputStream(
                                new File(currentPreset.getAbout().getImage(currentPreset))
                        ));

                        shortcut = shortcut.setIcon(Icon.createWithBitmap(icon));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    if (shortcutManager != null) {
                        shortcutManager.addDynamicShortcuts(Arrays.asList(shortcut.build()));
                    }
                }

                final Handler intervalTimer = new Handler();

                intervalTimer.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        // loops while checking the last saved sample id and current one
                        if (savedSampleId == savedSampleIdInRunnable) {
                            // if same, break the loop
                            Log.d(TAG, "Finished loading all sounds");
                            onLoadFinish();
                        } else {
                            // updated
                            savedSampleIdInRunnable = savedSampleId;
                            intervalTimer.postDelayed(this, 100);
                        }
                    }
                }, 100);
            }
        }
    }
}