Java tutorial
/* * Author: Scott Ware <scoot.software@gmail.com> * Copyright (c) 2015 Scott Ware * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.scooter1556.sms.androidtv.fragment; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.support.v17.leanback.app.BackgroundManager; import android.support.v17.leanback.app.PlaybackOverlayFragment; import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ClassPresenterSelector; import android.support.v17.leanback.widget.ControlButtonPresenterSelector; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.PlaybackControlsRow; import android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction; import android.support.v17.leanback.widget.PlaybackControlsRow.SkipNextAction; import android.support.v17.leanback.widget.PlaybackControlsRow.SkipPreviousAction; import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.support.v4.content.ContextCompat; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.widget.PopupMenu; import com.bumptech.glide.Glide; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.scooter1556.sms.androidtv.R; import com.scooter1556.sms.androidtv.presenter.MediaElementPresenter; import com.scooter1556.sms.lib.android.domain.MediaElement; import com.scooter1556.sms.lib.android.service.AudioPlayerService; import com.scooter1556.sms.lib.android.service.RESTService; import java.util.List; public class AudioPlayerFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment implements AudioPlayerService.AudioPlayerListener, PopupMenu.OnMenuItemClickListener { private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_DARK; public static final int ACTION_STOP_ID = 0x7f0f0012; public static final int ACTION_CLEAR_PLAYLIST_ID = 0x7f0f0013; private static final int THUMB_WIDTH = 274; private static final int THUMB_HEIGHT = 274; private static final int DEFAULT_UPDATE_PERIOD = 1000; private ArrayObjectAdapter rowsAdapter; private ArrayObjectAdapter primaryActionsAdapter; private ArrayObjectAdapter secondaryActionsAdapter; private PlaybackControlsRow playbackControlsRow; protected PlayPauseAction playPauseAction; private StopAction stopAction; private SkipNextAction skipNextAction; private SkipPreviousAction skipPreviousAction; private ClearPlaylistAction clearPlaylistAction; private List<MediaElement> playlist; private Handler handler; private Runnable runnable; private BackgroundManager backgroundManager; private Drawable defaultBackground; private DisplayMetrics displayMetrics; private ArrayObjectAdapter listRowAdapter; private ListRow listRow; private MediaElement selectedMediaElement; private ClassPresenterSelector presenterSelector; // Audio Player Service private AudioPlayerService audioPlayerService; private Intent audioPlayerIntent; private boolean audioPlayerBound = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Setup background manager backgroundManager = BackgroundManager.getInstance(getActivity()); backgroundManager.attach(getActivity().getWindow()); // Get default background defaultBackground = getResources().getDrawable(R.drawable.default_background, null); backgroundManager.setDrawable(defaultBackground); // Get screen size displayMetrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); handler = new Handler(); listRowAdapter = new ArrayObjectAdapter(new MediaElementPresenter()); presenterSelector = new ClassPresenterSelector(); rowsAdapter = new ArrayObjectAdapter(presenterSelector); setBackgroundType(BACKGROUND_TYPE); setFadingEnabled(false); setupRows(); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { if (audioPlayerService != null) { if (audioPlayerService.isPlaying()) { audioPlayerService.pause(); } else { audioPlayerService.start(); } } return true; } } return false; } }); } // Connect to the Audio Player service private ServiceConnection audioPlayerConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bindAudioPlayerService(service); } @Override public void onServiceDisconnected(ComponentName name) { audioPlayerBound = false; } }; private void bindAudioPlayerService(IBinder service) { // Get Service AudioPlayerService.AudioPlayerBinder binder = (AudioPlayerService.AudioPlayerBinder) service; audioPlayerService = binder.getService(); audioPlayerService.registerListener(this); audioPlayerBound = true; updatePlaylistItem(); updatePlayListRow(); updatePlaybackState(); } @Override public void onStart() { if (audioPlayerIntent == null) { audioPlayerIntent = new Intent(getActivity(), AudioPlayerService.class); getActivity().bindService(audioPlayerIntent, audioPlayerConnection, Context.BIND_AUTO_CREATE); } super.onStart(); } @Override public void onDestroy() { if (audioPlayerService != null) { audioPlayerService.unregisterListener(this); } if (audioPlayerBound) { getActivity().unbindService(audioPlayerConnection); audioPlayerBound = false; } super.onDestroy(); } private void setupRows() { PlaybackControlsRowPresenter playbackControlsRowPresenter; playbackControlsRowPresenter = new PlaybackControlsRowPresenter(new DescriptionPresenter()); playbackControlsRowPresenter.setOnActionClickedListener(new OnActionClickedListener() { public void onActionClicked(Action action) { if (audioPlayerService == null) { return; } if (action.getId() == playPauseAction.getId()) { if (audioPlayerService.isPlaying()) { audioPlayerService.pause(); } else { audioPlayerService.start(); } } else if (action.getId() == skipNextAction.getId()) { audioPlayerService.playNext(); } else if (action.getId() == skipPreviousAction.getId()) { audioPlayerService.playPrev(); } else if (action.getId() == stopAction.getId()) { audioPlayerService.stop(); } else if (action.getId() == clearPlaylistAction.getId()) { audioPlayerService.clearMediaList(); } } }); presenterSelector.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); playbackControlsRow = new PlaybackControlsRow(new MediaElementHolder()); ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector(); primaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); secondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); playbackControlsRow.setPrimaryActionsAdapter(primaryActionsAdapter); playbackControlsRow.setSecondaryActionsAdapter(secondaryActionsAdapter); playPauseAction = new PlayPauseAction(getActivity()); stopAction = new StopAction((getActivity())); skipNextAction = new PlaybackControlsRow.SkipNextAction(getActivity()); skipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getActivity()); clearPlaylistAction = new ClearPlaylistAction(getActivity()); primaryActionsAdapter.add(skipPreviousAction); primaryActionsAdapter.add(stopAction); primaryActionsAdapter.add(playPauseAction); primaryActionsAdapter.add(skipNextAction); secondaryActionsAdapter.add(clearPlaylistAction); rowsAdapter.add(playbackControlsRow); setAdapter(rowsAdapter); setOnItemViewClickedListener(new ItemViewClickedListener()); } private void resetPlaybackRow() { playbackControlsRow.setTotalTime(0); playbackControlsRow.setCurrentTime(0); playbackControlsRow.setImageDrawable(null); ((MediaElementHolder) playbackControlsRow.getItem()).element = new MediaElement(); rowsAdapter.notifyArrayItemRangeChanged(rowsAdapter.indexOf(playbackControlsRow), 1); backgroundManager.setDrawable(defaultBackground); } protected void startProgressAutomation() { if (handler != null && runnable != null) { handler.removeCallbacks(runnable); } runnable = new Runnable() { @Override public void run() { playbackControlsRow.setCurrentTime((int) (audioPlayerService.getCurrentPosition() * 1000)); handler.postDelayed(this, DEFAULT_UPDATE_PERIOD); } }; handler.postDelayed(runnable, DEFAULT_UPDATE_PERIOD); } protected void stopProgressAutomation() { if (handler != null && runnable != null) { handler.removeCallbacks(runnable); setFadingEnabled(false); } } public void updatePlaylistItem() { if (playbackControlsRow == null || audioPlayerService == null) { return; } MediaElement element = audioPlayerService.getMediaElement(); if (element == null) { resetPlaybackRow(); return; } playbackControlsRow.setTotalTime(element.getDuration() * 1000); ((MediaElementHolder) playbackControlsRow.getItem()).element = element; rowsAdapter.notifyArrayItemRangeChanged(rowsAdapter.indexOf(playbackControlsRow), 1); updateAlbumArt(element.getID()); } private void updateAlbumArt(long id) { // Get cover Glide.with(getActivity()).load( RESTService.getInstance().getConnection().getUrl() + "/image/" + id + "/cover/" + THUMB_HEIGHT) .asBitmap().into(new SimpleTarget<Bitmap>(THUMB_WIDTH, THUMB_HEIGHT) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { playbackControlsRow.setImageBitmap(getActivity(), resource); rowsAdapter.notifyArrayItemRangeChanged(rowsAdapter.indexOf(playbackControlsRow), 1); } }); // Get fanart Glide.with(getActivity()) .load(RESTService.getInstance().getConnection().getUrl() + "/image/" + id + "/fanart/" + displayMetrics.widthPixels) .asBitmap().into(new SimpleTarget<Bitmap>(displayMetrics.widthPixels, displayMetrics.heightPixels) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { backgroundManager.setBitmap(resource); } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { backgroundManager.setDrawable(defaultBackground); } }); } public void updatePlaybackState() { if (playbackControlsRow == null || audioPlayerService == null) { return; } if (audioPlayerService.isPlaying()) { startProgressAutomation(); playPauseAction.setIndex(PlayPauseAction.PAUSE); updatePlaylistItem(); } else { stopProgressAutomation(); playPauseAction.setIndex(PlayPauseAction.PLAY); if (!audioPlayerService.isPaused()) { resetPlaybackRow(); } } rowsAdapter.notifyArrayItemRangeChanged(rowsAdapter.indexOf(playbackControlsRow), 1); } public void updatePlayListRow() { if (audioPlayerService == null) { return; } // Get current play queue playlist = audioPlayerService.getMediaList(); if (playlist == null || playlist.isEmpty()) { // Remove the playlist row if no items are in the playlist rowsAdapter.remove(listRow); listRow = null; resetPlaybackRow(); return; } // Reset playlist row listRowAdapter.clear(); // Populate playlist for (MediaElement element : playlist) { listRowAdapter.add(element); } // Add to interface if (listRow == null) { HeaderItem header = new HeaderItem(0, getString(R.string.audio_player_playlist)); presenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter()); listRow = new ListRow(header, listRowAdapter); rowsAdapter.add(listRow); } else { rowsAdapter.notifyArrayItemRangeChanged(rowsAdapter.indexOf(listRow), 1); } } public void showOptionsMenu(View v) { PopupMenu popup = new PopupMenu(getActivity(), v); // This activity implements OnMenuItemClickListener popup.setOnMenuItemClickListener(this); popup.inflate(R.menu.menu_playlist_item); popup.show(); } private final class ItemViewClickedListener implements OnItemViewClickedListener { @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (item instanceof MediaElement) { selectedMediaElement = (MediaElement) item; showOptionsMenu(itemViewHolder.view); } } } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.play: if (audioPlayerService != null && selectedMediaElement != null) { audioPlayerService.setMediaListPosition(selectedMediaElement); audioPlayerService.play(); } return true; case R.id.remove: if (audioPlayerService != null && selectedMediaElement != null) { audioPlayerService.removeMediaElementFromList(selectedMediaElement.getID()); } return true; default: return false; } } @Override public void PlaybackStateChanged() { updatePlaybackState(); } @Override public void PlaylistPositionChanged() { updatePlaylistItem(); } @Override public void PlaylistChanged() { updatePlayListRow(); } private static final class DescriptionPresenter extends AbstractDetailsDescriptionPresenter { @Override protected void onBindDescription(ViewHolder viewHolder, Object item) { MediaElementHolder elementHolder = ((MediaElementHolder) item); if (elementHolder.element != null) { viewHolder.getTitle().setText(elementHolder.element.getTitle()); viewHolder.getSubtitle().setText(elementHolder.element.getArtist()); } } } private static final class MediaElementHolder { MediaElement element; public MediaElementHolder() { } public MediaElementHolder(MediaElement element) { this.element = element; } } public static class StopAction extends Action { /** * Constructor * @param context Context used for loading resources. */ public StopAction(Context context) { super(ACTION_STOP_ID); setIcon(ContextCompat.getDrawable(context, android.support.v17.leanback.R.drawable.lb_ic_stop)); setLabel1(context.getString(R.string.action_stop)); addKeyCode(KeyEvent.KEYCODE_MEDIA_STOP); } } public static class ClearPlaylistAction extends Action { /** * Constructor * @param context Context used for loading resources. */ public ClearPlaylistAction(Context context) { super(ACTION_CLEAR_PLAYLIST_ID); setIcon(ContextCompat.getDrawable(context, R.drawable.ic_clear)); setLabel1(context.getString(R.string.action_clear_playlist)); } } }