org.dharmaseed.android.PlayTalkActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.dharmaseed.android.PlayTalkActivity.java

Source

/*
 *     Dharmaseed Android app
 *     Copyright (C) 2016  Brett Bethke
 *
 *     This program 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 org.dharmaseed.android;

import android.Manifest;
import android.app.DialogFragment;
import android.content.pm.PackageManager;
import android.graphics.drawable.Animatable;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class PlayTalkActivity extends AppCompatActivity
        implements SeekBar.OnSeekBarChangeListener, DeleteTalkFragment.DeleteTalkListener {

    TalkPlayerFragment talkPlayerFragment;
    int talkID;
    DBManager dbManager;
    boolean userDraggingSeekBar;
    int userSeekBarPosition;

    private static Talk talk;

    private static final String LOG_TAG = "PlayTalkActivity";

    // request code for writing external storage (the number is arbitrary)
    private static final int PERMISSIONS_WRITE_EXTERNAL_STORAGE = 9087;

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

        // Turn on action bar up/home button
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        // Get the ID of the talk to display
        Intent i = getIntent();
        talkID = (int) i.getLongExtra(NavigationActivity.TALK_DETAIL_EXTRA, 0);

        dbManager = DBManager.getInstance(this);

        // only hit the DB again if we know the talk is different than the one
        // we have saved.
        // for example, if the user selects a talk, exits, and re-opens it, no need
        // to hit the DB again, since we already have that talk saved
        if (talk == null || talk.getId() != talkID) {
            Cursor cursor = getCursor();
            if (cursor.moveToFirst()) {
                // convert DB result to an object
                talk = new Talk(cursor);
                talk.setId(talkID);
            } else {
                Log.e(LOG_TAG, "Could not look up talk, id=" + talkID);
                cursor.close();
                return;
            }
            cursor.close();
        } // else we already have the talk, just re-draw the page

        // Set the talk title
        TextView titleView = (TextView) findViewById(R.id.play_talk_talk_title);
        titleView.setText(talk.getTitle());

        // Set the teacher name
        TextView teacherView = (TextView) findViewById(R.id.play_talk_teacher);
        teacherView.setText(talk.getTeacherName());

        // Set the center name
        TextView centerView = (TextView) findViewById(R.id.play_talk_center);
        centerView.setText(talk.getCenterName());

        // Set the talk description
        TextView descriptionView = (TextView) findViewById(R.id.play_talk_talk_description);
        descriptionView.setText(talk.getDescription());

        // Set teacher photo
        String photoFilename = talk.getPhotoFileName();
        ImageView photoView = (ImageView) findViewById(R.id.play_talk_teacher_photo);
        Log.i(LOG_TAG, "photoFilename: " + photoFilename);
        try {
            FileInputStream photo = openFileInput(photoFilename);
            photoView.setImageBitmap(BitmapFactory.decodeStream(photo));
        } catch (FileNotFoundException e) {
            Drawable icon = ContextCompat.getDrawable(this, R.drawable.dharmaseed_icon);
            photoView.setImageDrawable(icon);
        }

        // set the image of the download button based on whether the talk is
        // downloaded or not
        toggleDownloadImage();

        // Set date
        TextView dateView = (TextView) findViewById(R.id.play_talk_date);
        String recDate = talk.getDate();
        SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            dateView.setText(DateFormat.getDateInstance().format(parser.parse(recDate)));
        } catch (ParseException e) {
            dateView.setText("");
            Log.w(LOG_TAG, "Could not parse talk date for talk ID " + talkID);
        }

        // Get/create a persistent fragment to manage the MediaPlayer instance
        FragmentManager fm = getSupportFragmentManager();
        talkPlayerFragment = (TalkPlayerFragment) fm.findFragmentByTag("talkPlayerFragment");
        if (talkPlayerFragment == null) {
            // add the fragment
            talkPlayerFragment = new TalkPlayerFragment();
            fm.beginTransaction().add(talkPlayerFragment, "talkPlayerFragment").commit();
        } else if (talkPlayerFragment.getMediaPlayer().isPlaying()) {
            setPPButton("ic_media_pause");
        }

        // Set the talk duration
        final TextView durationView = (TextView) findViewById(R.id.play_talk_talk_duration);
        double duration = talk.getDurationInMinutes();
        String durationStr = DateUtils.formatElapsedTime((long) (duration * 60));
        durationView.setText(durationStr);

        // Start a handler to periodically update the seek bar and talk time
        final SeekBar seekBar = (SeekBar) findViewById(R.id.play_talk_seek_bar);
        seekBar.setMax((int) (duration * 60 * 1000));
        userDraggingSeekBar = false;
        userSeekBarPosition = 0;
        seekBar.setOnSeekBarChangeListener(this);
        final Handler handler = new Handler();
        final MediaPlayer mediaPlayer = talkPlayerFragment.getMediaPlayer();
        handler.post(new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(this, 1000);
                if (talkPlayerFragment.getMediaPrepared() && !userDraggingSeekBar) {
                    try {
                        int pos = mediaPlayer.getCurrentPosition();
                        int mpDuration = mediaPlayer.getDuration();
                        seekBar.setMax(mpDuration);
                        seekBar.setProgress(pos);
                        String posStr = DateUtils.formatElapsedTime(pos / 1000);
                        String mpDurStr = DateUtils.formatElapsedTime(mpDuration / 1000);
                        durationView.setText(posStr + "/" + mpDurStr);
                    } catch (IllegalStateException e) {
                    }
                }
            }
        });
    }

    private Cursor getCursor() {
        SQLiteDatabase db = dbManager.getReadableDatabase();
        String query = String.format(
                "SELECT %s, %s.%s, %s, %s, %s, %s, %s, %s, %s, %s.%s AS teacher_name, %s.%s AS center_name, "
                        + "%s.%s FROM %s, %s, %s WHERE %s.%s=%s.%s AND %s.%s=%s.%s AND %s.%s=%s",
                DBManager.C.Talk.TITLE, DBManager.C.Talk.TABLE_NAME, DBManager.C.Talk.DESCRIPTION,
                DBManager.C.Talk.AUDIO_URL, DBManager.C.Talk.DURATION_IN_MINUTES, DBManager.C.Talk.RECORDING_DATE,
                DBManager.C.Talk.UPDATE_DATE, DBManager.C.Talk.RETREAT_ID, DBManager.C.Talk.FILE_PATH,
                DBManager.C.Talk.TEACHER_ID, DBManager.C.Teacher.TABLE_NAME, DBManager.C.Teacher.NAME,
                DBManager.C.Center.TABLE_NAME, DBManager.C.Center.NAME,

                DBManager.C.Teacher.TABLE_NAME, DBManager.C.Teacher.ID,

                // FROM
                DBManager.C.Talk.TABLE_NAME, DBManager.C.Teacher.TABLE_NAME, DBManager.C.Center.TABLE_NAME,

                // WHERE
                DBManager.C.Talk.TABLE_NAME, DBManager.C.Talk.TEACHER_ID, DBManager.C.Teacher.TABLE_NAME,
                DBManager.C.Teacher.ID,

                DBManager.C.Talk.TABLE_NAME, DBManager.C.Talk.VENUE_ID, DBManager.C.Center.TABLE_NAME,
                DBManager.C.Center.ID,

                DBManager.C.Talk.TABLE_NAME, DBManager.C.Talk.ID, talkID);

        return db.rawQuery(query, null);
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.play_talk, menu);

        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
        MenuItem star = menu.findItem(R.id.play_talk_action_toggle_starred);
        if (dbManager.isStarred(DBManager.C.TalkStars.TABLE_NAME, talkID)) {
            star.setIcon(ContextCompat.getDrawable(this,
                    getResources().getIdentifier("btn_star_big_on", "drawable", "android")));
        } else {
            star.setIcon(ContextCompat.getDrawable(this,
                    getResources().getIdentifier("btn_star_big_off", "drawable", "android")));
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        switch (id) {
        case R.id.play_talk_action_toggle_starred:
            if (dbManager.isStarred(DBManager.C.TalkStars.TABLE_NAME, talkID)) {
                dbManager.removeStar(DBManager.C.TalkStars.TABLE_NAME, talkID);
                item.setIcon(getResources().getIdentifier("btn_star_big_off", "drawable", "android"));
            } else {
                dbManager.addStar(DBManager.C.TalkStars.TABLE_NAME, talkID);
                item.setIcon(getResources().getIdentifier("btn_star_big_on", "drawable", "android"));
            }

            // Notify main activity to update its data
            LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("updateDisplayedData"));

            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void setPPButton(String drawableName) {
        ImageButton playButton = (ImageButton) findViewById(R.id.activity_play_talk_play_button);
        playButton.setImageDrawable(
                ContextCompat.getDrawable(this, getResources().getIdentifier(drawableName, "drawable", "android")));
    }

    public void playTalkButtonClicked(View view) {
        Log.d(LOG_TAG, "button pressed");
        MediaPlayer mediaPlayer = talkPlayerFragment.getMediaPlayer();
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            setPPButton("ic_media_play");
        } else if (talkPlayerFragment.getMediaPrepared()) {
            mediaPlayer.start();
            setPPButton("ic_media_pause");
        } else {
            try {
                if (talk.isDownloaded()) {
                    Log.d(LOG_TAG, "Playing from " + talk.getPath());
                    mediaPlayer.setDataSource(talk.getPath());
                } else {
                    mediaPlayer.setDataSource(talk.getAudioUrl());
                }
                mediaPlayer.prepareAsync();
            } catch (Exception e) {
                Log.e(LOG_TAG, e.toString());
            }
        }
    }

    public void fastForwardButtonClicked(View view) {
        MediaPlayer mediaPlayer = talkPlayerFragment.getMediaPlayer();
        int currentPosition = mediaPlayer.getCurrentPosition();
        int newPosition = Math.min(currentPosition + 15000, mediaPlayer.getDuration());
        mediaPlayer.seekTo(newPosition);
    }

    public void rewindButtonClicked(View view) {
        MediaPlayer mediaPlayer = talkPlayerFragment.getMediaPlayer();
        int currentPosition = mediaPlayer.getCurrentPosition();
        int newPosition = Math.max(currentPosition - 15000, 0);
        mediaPlayer.seekTo(newPosition);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (fromUser) {
            userSeekBarPosition = progress;
            String posStr = DateUtils.formatElapsedTime(progress / 1000);
            String mpDurStr = DateUtils.formatElapsedTime(seekBar.getMax() / 1000);
            TextView durationView = (TextView) findViewById(R.id.play_talk_talk_duration);
            durationView.setText(posStr + "/" + mpDurStr);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        userDraggingSeekBar = true;
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        userDraggingSeekBar = false;
        talkPlayerFragment.setUserSeekBarPosition(userSeekBarPosition);
        MediaPlayer mediaPlayer = talkPlayerFragment.getMediaPlayer();
        try {
            mediaPlayer.seekTo(userSeekBarPosition);
        } catch (IllegalStateException e) {
        }
    }

    /**
     * Download the talk if we have permission. If we don't have permission, request it
     */
    private void downloadTalk() {
        int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // requestPermissions will ask the user for permission asynchronously
            // and will call onRequestPermissionsResult() with the results
            ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
                    PERMISSIONS_WRITE_EXTERNAL_STORAGE);
        } else if (permission == PackageManager.PERMISSION_GRANTED) {
            new DownloadTalkTask().execute(talk);
        } else {
            // should never happen
            Log.w(LOG_TAG, "Permission was " + permission);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
        switch (requestCode) {
        case PERMISSIONS_WRITE_EXTERNAL_STORAGE:
            // we asked for and received permission
            if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
                new DownloadTalkTask().execute(talk);
            }
            // if we didn't receive permission, don't do anything
            return;
        }
    }

    public void toggleDownloadImage() {
        ImageButton downloadButton = (ImageButton) findViewById(R.id.download_button);
        if (talk.isDownloaded()) {
            Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_check_circle_green_24dp);
            downloadButton.setImageDrawable(icon);
            downloadButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    onDeleteTalkClicked(view);
                }
            });
        } else {
            Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_file_download_green_24dp);
            downloadButton.setImageDrawable(icon);
            downloadButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    onDownloadButtonClicked(view);
                }
            });
        }
    }

    public void onDownloadButtonClicked(View view) {
        Log.d(LOG_TAG, "Downloading talk " + talk.getId());
        downloadTalk();
    }

    public void onDeleteTalkClicked(View view) {
        DialogFragment dialog = new DeleteTalkFragment();
        dialog.show(getFragmentManager(), "DeleteTalkFragment");
    }

    /**
     * Share a link to the dharmaseed website for this talk
     */
    public void onShareButtonClicked(View view) {
        String url = String.format("https://dharmaseed.org/teacher/%d/talk/%d/", talk.getTeacherId(), talk.getId());
        Log.d(LOG_TAG, "Sharing link to " + url);

        Intent share = new Intent(android.content.Intent.ACTION_SEND);
        share.setType("text/plain");
        share.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);

        // Add data to the intent, the receiving app will decide
        // what to do with it.
        share.putExtra(Intent.EXTRA_SUBJECT, talk.getTitle());
        share.putExtra(Intent.EXTRA_TEXT, url);

        startActivity(Intent.createChooser(share, "Share talk"));
    }

    /**
     * Deletes the talk from the FS and removes the path from the DB row
     */
    public void deleteTalk() {
        if (!TalkManager.delete(talk)) {
            showToast("Unable to delete '" + talk.getTitle() + "'.", Toast.LENGTH_SHORT);
        } else {
            dbManager.removeDownload(talk);
            showToast("Deleted '" + talk.getTitle() + "'.", Toast.LENGTH_SHORT);
            toggleDownloadImage();
            Log.d(LOG_TAG, "Deleted talk " + talk.getId());
        }
    }

    public void startLoadingAnim() {
        ImageButton downloadButton = (ImageButton) findViewById(R.id.download_button);
        // override the current listener so the user can't accidentally download the talk
        // twice
        downloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // do nothing
            }
        });
        Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_downloading);
        downloadButton.setImageDrawable(icon);
        if (icon instanceof Animatable)
            ((Animatable) icon).start();
    }

    public void stopLoadingAnim() {
        Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_downloading);
        if (icon instanceof Animatable)
            ((Animatable) icon).stop();
    }

    public void showToast(String message, int length) {
        Toast.makeText(getApplicationContext(), message, length).show();
    }

    @Override
    public void onDeleteTalkPositiveClick(DialogFragment dialogFragment) {
        deleteTalk();
    }

    @Override
    public void onDeleteTalkNegativeClick(DialogFragment dialogFragment) {
        // do nothing
    }

    class DownloadTalkTask extends AsyncTask<Talk, Integer, Long> {

        private static final String LOG_TAG = "DownloadTalkTask";
        private Talk talk;

        public DownloadTalkTask() {
        }

        @Override
        protected void onPreExecute() {
            startLoadingAnim();
        }

        @Override
        protected Long doInBackground(Talk... talks) {
            long totalSize = 0;
            if (talks.length > 0) {
                // only download the first one if we get passed multiple
                this.talk = talks[0];
                totalSize = TalkManager.download(this.talk);
            }
            return totalSize;
        }

        @Override
        protected void onPostExecute(Long size) {
            if (size > 0) {
                if (dbManager.addDownload(this.talk)) {
                    showToast("'" + talk.getTitle() + "' downloaded.", Toast.LENGTH_SHORT);
                } else {
                    // remove talk from fs because we couldn't update the DB
                    deleteTalk();
                    Log.d(LOG_TAG, "failed to update db with talk path. deleting talk");
                }
            } else {
                showToast("Failed to download '" + talk.getTitle() + "'.", Toast.LENGTH_SHORT);
            }
            stopLoadingAnim();
            toggleDownloadImage();
        }
    }
}