Java tutorial
package com.android.msahakyan.fma.fragment; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Bitmap; import android.media.AudioManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import com.android.msahakyan.fma.R; import com.android.msahakyan.fma.application.FmaApplication; import com.android.msahakyan.fma.model.Track; import com.android.msahakyan.fma.network.FmaApiService; import com.android.msahakyan.fma.network.NetworkRequestListener; import com.android.msahakyan.fma.service.MusicDownloaderService; import com.android.msahakyan.fma.service.MusicService; import com.android.msahakyan.fma.util.AppUtils; import com.android.msahakyan.fma.util.BitmapUtils; import com.android.msahakyan.fma.util.Item; import com.android.msahakyan.fma.util.ItemLoadedListener; import com.android.msahakyan.fma.util.LoadNeighborTrackListener; import com.android.msahakyan.fma.util.MediaPlayerPreparedListener; import com.android.msahakyan.fma.util.MediaPlayerStateChangeListener; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import com.github.rahatarmanahmed.cpv.CircularProgressView; import java.lang.ref.WeakReference; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.Bind; import butterknife.OnClick; import timber.log.Timber; /** * A simple {@link Fragment} subclass. * Use the {@link TrackDetailFragment#newInstance} factory method to * create an instance of this fragment. */ public class TrackDetailFragment extends BaseItemDetailFragment<Track> implements View.OnClickListener, View.OnTouchListener, ItemLoadedListener, MediaPlayerPreparedListener, MediaPlayerStateChangeListener, LoadNeighborTrackListener { private static final String KEY_ITEM_LIST = "KEY_ITEM_LIST"; private static final String KEY_ITEM_POSITION = "KEY_ITEM_POSITION"; @Inject FmaApiService fmaApiService; @Inject ImageLoader imageLoader; @Bind(R.id.track_image) ImageView mTrackImageView; @Bind(R.id.track_detail_image_container) ImageView mTrackImageContainer; @Bind(R.id.track_title) TextView mTitleView; @Bind(R.id.button_play_pause) ImageView mButtonPlayPause; @Bind(R.id.button_previous) ImageView mButtonPrevious; @Bind(R.id.button_next) ImageView mButtonNext; @Bind(R.id.seek_bar) SeekBar mProgress; @Bind(R.id.progress_passed) TextView mProgressPassed; @Bind(R.id.song_time) TextView mSongTime; @Bind(R.id.button_volume) ImageView mButtonVolume; @Bind(R.id.progress_detail_view) CircularProgressView mProgressView; @Bind(R.id.license) ImageView mLicense; private MusicService mMusicService; private final Handler handler; private Intent mPlayIntent; private boolean mIsBound = false; private int mDurationInMillis; private boolean mMute; private boolean mPaused; private boolean mPrepared; private List<Track> mTracks; private int mListPosition; private boolean mPlaybackPaused; private NetworkRequestListener<Item> mNetworkRequestListener; public TrackDetailFragment() { handler = new Handler(); } /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param tracks The list of tracks in the current {@link com.android.msahakyan.fma.model.Album} * @return A new instance of fragment AlbumDetailFragment. */ public static TrackDetailFragment newInstance(ArrayList<Item> tracks, int position) { TrackDetailFragment fragment = new TrackDetailFragment(); Bundle args = new Bundle(); args.putParcelable(KEY_ITEM_PARCEL, tracks.get(position)); args.putParcelableArrayList(KEY_ITEM_LIST, tracks); args.putInt(KEY_ITEM_POSITION, position); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); mTracks = args.getParcelableArrayList(KEY_ITEM_LIST); mListPosition = args.getInt(KEY_ITEM_POSITION); mNetworkRequestListener = new NetworkRequestListener<Item>() { @Override public void onSuccess(@Nullable Item response, int statusCode) { if (response != null && statusCode == HttpURLConnection.HTTP_OK) { Timber.d("Received response for track details: " + response); setItem((Track) response); showBasicView(); onItemLoaded(item); } } @Override public void onError(int statusCode, String errorMessage) { Timber.w(errorMessage); showErrorView(); } }; } @Override protected void showBasicView() { showContentView(); mTitleView.setText(item.getTitle()); imageLoader.get(item.getImage(), new ImageLoader.ImageListener() { @Override public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) { if (response != null) { Bitmap btm = response.getBitmap(); if (btm != null && mTrackImageView != null) { mTrackImageView.setImageBitmap(btm); item.setImageBitmap(btm); BitmapWorkerTask task = new BitmapWorkerTask(mTrackImageContainer); task.execute(btm); } } } @Override public void onErrorResponse(VolleyError error) { Timber.w("Can't load image bitmap url: " + item.getImage()); } }); } private boolean hasPrevious() { return mListPosition > 0; } private boolean hasNext() { return mListPosition < mTracks.size() - 1; } @Override public void onResume() { super.onResume(); if (mPaused) { mPaused = false; } } @Override public void onDestroy() { activity.unbindService(mServiceConnection); mMusicService = null; super.onDestroy(); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { MusicService.MusicBinder binder = (MusicService.MusicBinder) service; mMusicService = binder.getService(); mIsBound = true; } @Override public void onServiceDisconnected(ComponentName name) { mIsBound = false; } }; public int getCurrentPosition() { if (mMusicService != null && mIsBound) return mMusicService.getPosition(); else return 0; } public int getDuration() { if (mMusicService != null && mIsBound && mPrepared) return mMusicService.getDuration(); else return 0; } public boolean isPlaying() { return mMusicService != null && mIsBound && mMusicService.isPlaying(); } public void pause() { mPlaybackPaused = true; mMusicService.pausePlayer(); } public void seekTo(int position) { mMusicService.seek(position); } public void start() { mMusicService.startPlayer(); } @Override public void onStart() { super.onStart(); if (mPlayIntent == null) { mPlayIntent = new Intent(activity, MusicService.class); activity.bindService(mPlayIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } } @Override public void onActivityCreated(@android.support.annotation.Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.hide(); } FmaApplication.get(activity).getApplicationComponent().inject(this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_track_detail, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); showContentView(); init(); } @Override public void refresh() { super.refresh(); fmaApiService.getTrackById(mNetworkRequestListener, item.getId()); } private void init() { mButtonPlayPause.setOnClickListener(this); mProgress.setMax(99); // It means 100% .0-99 mProgress.setPadding(0, 0, 0, 0); mProgress.setProgressDrawable(ContextCompat.getDrawable(getContext(), R.drawable.progress_bar)); mProgress.setOnTouchListener(this); mSongTime.setText(item.getDuration()); updatePlayerPreviousNextButtonsUI(); } private void updatePlayerPreviousNextButtonsUI() { mButtonPrevious.setEnabled(hasPrevious()); mButtonNext.setEnabled(hasNext()); } /** * Updates the SeekBar primary progress by current song playing position */ private void updateProgress() { if (!mPrepared) { return; } if (mProgress != null) { int durationInMillis = getDuration(); int timePercentage = durationInMillis != 0 ? getCurrentPosition() * 100 / durationInMillis : 0; mProgress.setProgress(timePercentage); // This gives a percentage of playing song length" mProgressPassed.setText(AppUtils.durationConverter(timePercentage * durationInMillis / 100000)); } Runnable progressUpdater = this::updateProgress; handler.postDelayed(progressUpdater, 1000); } @OnClick(R.id.button_play_pause) public void onClick(View v) { if (!mPrepared) { return; } if (isPlaying()) { pause(); mButtonPlayPause.setImageResource(R.drawable.ic_av_play); } else { start(); mButtonPlayPause.setImageResource(R.drawable.ic_av_pause); } } @OnClick(R.id.button_download) public void onDownloadButtonClick() { Intent intent = new Intent(activity, MusicDownloaderService.class); intent.putExtra(MusicDownloaderService.KEY_TRACK_URL, item.getFileUrl()); intent.putExtra(MusicDownloaderService.KEY_TRACK_NAME, item.getTitle()); activity.startService(intent); } @OnClick(R.id.license) public void onLicenseViewClick() { if (item.getLicenseUrl() == null) { String trackTitle = item.getTitle(); Timber.e(getString(R.string.empty_license_url, trackTitle)); Toast.makeText(activity, getString(R.string.empty_license_url, trackTitle), Toast.LENGTH_SHORT).show(); return; } Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse((item.getLicenseUrl()))); startActivity(browserIntent); } @OnClick(R.id.button_previous) public void onPreviousButtonClick() { if (!hasPrevious()) { return; } pause(); if (mTracks != null) { mListPosition--; setItem(mTracks.get(mListPosition)); } updatePlayerPreviousNextButtonsUI(); mProgressView.startAnimation(); mProgressView.setVisibility(View.VISIBLE); refresh(); } @OnClick(R.id.button_next) public void onNextButtonClick() { if (!hasNext()) { Toast.makeText(activity, getString(R.string.no_more_tracks_to_load, item.getAlbumTitle()), Toast.LENGTH_SHORT).show(); return; } if (mTracks != null) { mListPosition++; setItem(mTracks.get(mListPosition)); } updatePlayerPreviousNextButtonsUI(); mProgressView.startAnimation(); mProgressView.setVisibility(View.VISIBLE); refresh(); } @Override public boolean onTouch(View v, MotionEvent event) { if (v.getId() == R.id.seek_bar) { // SeekBar onTouch event handler. Seeks MediaPlayer to seekBar primary progress position if (isPlaying()) { SeekBar seekBar = (SeekBar) v; int playPositionInMillis = (mDurationInMillis / 100) * seekBar.getProgress(); seekTo(playPositionInMillis); } } return false; } @Override public void onPause() { super.onPause(); mPaused = true; } @OnClick(R.id.button_volume) public void updateVolumeMode() { mMute = !mMute; mButtonVolume.setActivated(mMute); mute(mMute); } private void mute(boolean mute) { AudioManager audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!audioManager.isStreamMute(AudioManager.STREAM_MUSIC)) { audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); } } else { audioManager.setStreamMute(AudioManager.STREAM_MUSIC, mute); } } @Override public void onItemLoaded(Item item) { Track track = (Track) item; track.setImageBitmap(this.item.getImageBitmap()); mMusicService.setTrack(track); mMusicService.playTrack(); mSongTime.setText(track.getDuration()); mProgressView.startAnimation(); mProgressView.setVisibility(View.VISIBLE); mLicense.bringToFront(); setListeners(); mPlaybackPaused = !mPlaybackPaused; } private void setListeners() { mMusicService.setPreparedListener(this); mMusicService.setStateChangeListener(this); mMusicService.setLoadNeighborTrackListener(this); } @Override public void onPlayerPrepared() { mProgressView.stopAnimation(); mProgressView.setVisibility(View.GONE); mDurationInMillis = getDuration(); // gets the track length in milliseconds mPrepared = true; updatePlayerControlsUI(true); updateProgress(); } @Override public void onPlayerStateChanged(boolean isPlaying) { updatePlayerControlsUI(isPlaying); updateProgress(); } private void updatePlayerControlsUI(boolean isPlaying) { if (isPlaying) { mButtonPlayPause.setImageResource(R.drawable.ic_av_pause); } else { mButtonPlayPause.setImageResource(R.drawable.ic_av_play); } } @Override public void loadNeighborTrack(@AdjacentMode int mode) { switch (mode) { case NEXT: onNextButtonClick(); break; case PREVIOUS: onPreviousButtonClick(); break; } } @Override public void onBeforeDestroyView() { super.onBeforeDestroyView(); ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.show(); } mute(false); // un-mute audio stream } static class BitmapWorkerTask extends AsyncTask<Bitmap, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private Bitmap source; BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<>(imageView); } // Blur image in background. @Override protected Bitmap doInBackground(Bitmap... params) { source = params[0]; return BitmapUtils.fastBlur(source, 0.85f, 100); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } } }