Java tutorial
/* * Copyright (C) 2017 Team Gateship-One * (Hendrik Borghorst & Frederik Luetkes) * * The AUTHORS.md file contains a detailed contributors list: * <https://github.com/gateship-one/malp/blob/master/AUTHORS.md> * * 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.gateshipone.malp.application.views; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.AttributeSet; import android.util.Log; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupMenu; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.ViewSwitcher; import org.gateshipone.malp.R; import org.gateshipone.malp.application.activities.FanartActivity; import org.gateshipone.malp.application.artworkdatabase.ArtworkManager; import org.gateshipone.malp.application.callbacks.OnSaveDialogListener; import org.gateshipone.malp.application.callbacks.TextDialogCallback; import org.gateshipone.malp.application.fragments.TextDialog; import org.gateshipone.malp.application.fragments.serverfragments.ChoosePlaylistDialog; import org.gateshipone.malp.application.utils.CoverBitmapLoader; import org.gateshipone.malp.application.utils.FormatHelper; import org.gateshipone.malp.application.utils.ThemeUtils; import org.gateshipone.malp.application.utils.VolumeButtonLongClickListener; import org.gateshipone.malp.mpdservice.handlers.MPDConnectionStateChangeHandler; import org.gateshipone.malp.mpdservice.handlers.MPDStatusChangeHandler; import org.gateshipone.malp.mpdservice.handlers.serverhandler.MPDCommandHandler; import org.gateshipone.malp.mpdservice.handlers.serverhandler.MPDQueryHandler; import org.gateshipone.malp.mpdservice.handlers.serverhandler.MPDStateMonitoringHandler;; import org.gateshipone.malp.mpdservice.mpdprotocol.mpdobjects.MPDAlbum; import org.gateshipone.malp.mpdservice.mpdprotocol.mpdobjects.MPDArtist; import org.gateshipone.malp.mpdservice.mpdprotocol.mpdobjects.MPDCurrentStatus; import org.gateshipone.malp.mpdservice.mpdprotocol.mpdobjects.MPDTrack; import java.util.Locale; public class NowPlayingView extends RelativeLayout implements PopupMenu.OnMenuItemClickListener, ArtworkManager.onNewAlbumImageListener, ArtworkManager.onNewArtistImageListener, SharedPreferences.OnSharedPreferenceChangeListener { private final ViewDragHelper mDragHelper; private ServerStatusListener mStateListener; private ServerConnectionListener mConnectionStateListener; /** * Upper view part which is dragged up & down */ private View mHeaderView; /** * Main view of draggable part */ private View mMainView; private LinearLayout mDraggedUpButtons; private LinearLayout mDraggedDownButtons; /** * Absolute pixel position of upper layout bound */ private int mTopPosition; /** * relative dragposition */ private float mDragOffset; /** * Height of non-draggable part. * (Layout height - draggable part) */ private int mDragRange; /** * Flag whether the views switches between album cover and artist image */ private boolean mShowArtistImage = false; /** * Main cover imageview */ private AlbumArtistView mCoverImage; /** * Small cover image, part of the draggable header */ private ImageView mTopCoverImage; /** * View that contains the playlist ListVIew */ private CurrentPlaylistView mPlaylistView; /** * ViewSwitcher used for switching between the main cover image and the playlist */ private ViewSwitcher mViewSwitcher; /** * Asynchronous loader for coverimages for TrackItems. */ private CoverBitmapLoader mCoverLoader = null; /** * Observer for information about the state of the draggable part of this view. * This is probably the Activity of which this view is part of. * (Used for smooth statusbar transition and state resuming) */ private NowPlayingDragStatusReceiver mDragStatusReceiver = null; /** * Top buttons in the draggable header part. */ private ImageButton mTopPlayPauseButton; private ImageButton mTopPlaylistButton; private ImageButton mTopMenuButton; /** * Buttons in the bottom part of the view */ private ImageButton mBottomRepeatButton; private ImageButton mBottomPreviousButton; private ImageButton mBottomPlayPauseButton; private ImageButton mBottomStopButton; private ImageButton mBottomNextButton; private ImageButton mBottomRandomButton; /** * Seekbar used for seeking and informing the user of the current playback position. */ private SeekBar mPositionSeekbar; /** * Seekbar used for volume control of host */ private SeekBar mVolumeSeekbar; private ImageView mVolumeIcon; private ImageView mVolumeIconButtons; private TextView mVolumeText; private ImageButton mVolumeMinus; private ImageButton mVolumePlus; private LinearLayout mHeaderTextLayout; private LinearLayout mVolumeSeekbarLayout; private LinearLayout mVolumeButtonLayout; /** * Various textviews for track information */ private TextView mTrackName; private TextView mTrackAdditionalInfo; private TextView mElapsedTime; private TextView mDuration; private TextView mTrackNo; private TextView mPlaylistNo; private TextView mBitrate; private TextView mAudioProperties; private TextView mTrackURI; private MPDCurrentStatus mLastStatus; private MPDTrack mLastTrack; private boolean mUseEnglishWikipedia; public NowPlayingView(Context context) { this(context, null, 0); } public NowPlayingView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NowPlayingView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mDragHelper = ViewDragHelper.create(this, 1f, new BottomDragCallbackHelper()); mStateListener = new ServerStatusListener(); mConnectionStateListener = new ServerConnectionListener(); mLastStatus = new MPDCurrentStatus(); mLastTrack = new MPDTrack(""); } /** * Maximizes this view with an animation. */ public void maximize() { smoothSlideTo(0f); } /** * Minimizes the view with an animation. */ public void minimize() { smoothSlideTo(1f); } /** * Slides the view to the given position. * * @param slideOffset 0.0 - 1.0 (0.0 is dragged down, 1.0 is dragged up) * @return If the move was successful */ boolean smoothSlideTo(float slideOffset) { final int topBound = getPaddingTop(); int y = (int) (topBound + slideOffset * mDragRange); if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) { ViewCompat.postInvalidateOnAnimation(this); return true; } return false; } /** * Set the position of the draggable view to the given offset. This is done without an animation. * Can be used to resume a certain state of the view (e.g. on resuming an activity) * * @param offset Offset to position the view to from 0.0 - 1.0 (0.0 dragged up, 1.0 dragged down) */ public void setDragOffset(float offset) { if (offset > 1.0f || offset < 0.0f) { mDragOffset = 1.0f; } mDragOffset = offset; invalidate(); requestLayout(); // Set inverse alpha values for smooth layout transition. // Visibility still needs to be set otherwise parts of the buttons // are not clickable. mDraggedDownButtons.setAlpha(mDragOffset); mDraggedUpButtons.setAlpha(1.0f - mDragOffset); // Calculate the margin to smoothly resize text field LayoutParams layoutParams = (LayoutParams) mHeaderTextLayout.getLayoutParams(); layoutParams.setMarginEnd((int) (mTopPlaylistButton.getWidth() * (1.0 - mDragOffset))); mHeaderTextLayout.setLayoutParams(layoutParams); // Notify the observers about the change if (mDragStatusReceiver != null) { mDragStatusReceiver.onDragPositionChanged(offset); } if (mDragOffset == 0.0f) { // top mDraggedDownButtons.setVisibility(INVISIBLE); mDraggedUpButtons.setVisibility(VISIBLE); mCoverImage.setVisibility(VISIBLE); if (mDragStatusReceiver != null) { mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_UP); } } else { // bottom mDraggedDownButtons.setVisibility(VISIBLE); mDraggedUpButtons.setVisibility(INVISIBLE); mCoverImage.setVisibility(INVISIBLE); if (mDragStatusReceiver != null) { mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_DOWN); } } } /** * Menu click listener. This method gets called when the user selects an item of the popup menu (right top corner). * * @param item MenuItem that was clicked. * @return Returns true if the item was handled by this method. False otherwise. */ @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_clear_playlist: final AlertDialog.Builder removeListBuilder = new AlertDialog.Builder(getContext()); removeListBuilder.setTitle(getContext().getString(R.string.action_delete_playlist)); removeListBuilder.setMessage(getContext().getString(R.string.dialog_message_delete_current_playlist)); removeListBuilder.setPositiveButton(R.string.dialog_action_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MPDQueryHandler.clearPlaylist(); } }); removeListBuilder.setNegativeButton(R.string.dialog_action_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); removeListBuilder.create().show(); break; case R.id.action_save_playlist: OnSaveDialogListener plDialogCallback = new OnSaveDialogListener() { @Override public void onSaveObject(final String title) { AlertDialog.Builder overWriteBuilder = new AlertDialog.Builder(getContext()); overWriteBuilder.setTitle(getContext().getString(R.string.action_overwrite_playlist)); overWriteBuilder.setMessage( getContext().getString(R.string.dialog_message_overwrite_playlist) + ' ' + title + '?'); overWriteBuilder.setPositiveButton(R.string.dialog_action_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MPDQueryHandler.removePlaylist(title); MPDQueryHandler.savePlaylist(title); } }); overWriteBuilder.setNegativeButton(R.string.dialog_action_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); overWriteBuilder.create().show(); } @Override public void onCreateNewObject() { // open dialog in order to save the current playlist as a playlist in the mediastore TextDialog textDialog = new TextDialog(); Bundle args = new Bundle(); args.putString(TextDialog.EXTRA_DIALOG_TITLE, getResources().getString(R.string.dialog_save_playlist)); args.putString(TextDialog.EXTRA_DIALOG_TEXT, getResources().getString(R.string.default_playlist_title)); textDialog.setCallback(new TextDialogCallback() { @Override public void onFinished(String text) { MPDQueryHandler.savePlaylist(text); } }); textDialog.setArguments(args); textDialog.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "SavePLTextDialog"); } }; // open dialog in order to save the current playlist as a playlist in the mediastore ChoosePlaylistDialog choosePlaylistDialog = new ChoosePlaylistDialog(); Bundle args = new Bundle(); args.putBoolean(ChoosePlaylistDialog.EXTRA_SHOW_NEW_ENTRY, true); choosePlaylistDialog.setCallback(plDialogCallback); choosePlaylistDialog.setArguments(args); choosePlaylistDialog.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "ChoosePlaylistDialog"); break; case R.id.action_add_url: TextDialog addURLDialog = new TextDialog(); addURLDialog.setCallback(new TextDialogCallback() { @Override public void onFinished(String text) { MPDQueryHandler.addPath(text); } }); Bundle textDialogArgs = new Bundle(); textDialogArgs.putString(TextDialog.EXTRA_DIALOG_TEXT, "http://..."); textDialogArgs.putString(TextDialog.EXTRA_DIALOG_TITLE, getResources().getString(R.string.action_add_url)); addURLDialog.setArguments(textDialogArgs); addURLDialog.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "AddURLDialog"); break; case R.id.action_jump_to_current: mPlaylistView.jumpToCurrentSong(); break; case R.id.action_toggle_single_mode: if (null != mLastStatus) { if (mLastStatus.getSinglePlayback() == 0) { MPDCommandHandler.setSingle(true); } else { MPDCommandHandler.setSingle(false); } } break; case R.id.action_toggle_consume_mode: if (null != mLastStatus) { if (mLastStatus.getConsume() == 0) { MPDCommandHandler.setConsume(true); } else { MPDCommandHandler.setConsume(false); } } break; case R.id.action_open_fanart: Intent intent = new Intent(getContext(), FanartActivity.class); getContext().startActivity(intent); return true; case R.id.action_wikipedia_album: Intent albumIntent = new Intent(Intent.ACTION_VIEW); //albumIntent.setData(Uri.parse("https://" + Locale.getDefault().getLanguage() + ".wikipedia.org/wiki/index.php?search=" + mLastTrack.getTrackAlbum() + "&title=Special:Search&go=Go")); if (mUseEnglishWikipedia) { albumIntent.setData(Uri.parse("https://en.wikipedia.org/wiki/" + mLastTrack.getTrackAlbum())); } else { albumIntent.setData(Uri.parse("https://" + Locale.getDefault().getLanguage() + ".wikipedia.org/wiki/" + mLastTrack.getTrackAlbum())); } getContext().startActivity(albumIntent); return true; case R.id.action_wikipedia_artist: Intent artistIntent = new Intent(Intent.ACTION_VIEW); //artistIntent.setData(Uri.parse("https://" + Locale.getDefault().getLanguage() + ".wikipedia.org/wiki/index.php?search=" + mLastTrack.getTrackAlbumArtist() + "&title=Special:Search&go=Go")); if (mUseEnglishWikipedia) { artistIntent.setData(Uri.parse("https://en.wikipedia.org/wiki/" + mLastTrack.getTrackArtist())); } else { artistIntent.setData(Uri.parse("https://" + Locale.getDefault().getLanguage() + ".wikipedia.org/wiki/" + mLastTrack.getTrackArtist())); } getContext().startActivity(artistIntent); return true; default: return false; } return false; } @Override public void newAlbumImage(MPDAlbum album) { if (mLastTrack.getTrackAlbum().equals(album.getName())) { mCoverLoader.getImage(mLastTrack, true); } } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(getContext().getString(R.string.pref_volume_controls_key))) { setVolumeControlSetting(); } else if (key.equals(getContext().getString(R.string.pref_use_english_wikipedia_key))) { mUseEnglishWikipedia = sharedPreferences.getBoolean(key, getContext().getResources().getBoolean(R.bool.pref_use_english_wikipedia_default)); } else if (key.equals(getContext().getString(R.string.pref_show_npv_artist_image_key))) { mShowArtistImage = sharedPreferences.getBoolean(key, getContext().getResources().getBoolean(R.bool.pref_show_npv_artist_image_default)); // Show artist image if artwork is requested if (mShowArtistImage) { mCoverLoader.getArtistImage(mLastTrack, true); } else { // Hide artist image mCoverImage.clearArtistImage(); } } } private void setVolumeControlSetting() { SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); String volumeControlView = sharedPref.getString(getContext().getString(R.string.pref_volume_controls_key), getContext().getString(R.string.pref_volume_control_view_default)); LinearLayout volLayout = (LinearLayout) findViewById(R.id.volume_control_layout); if (volumeControlView.equals(getContext().getString(R.string.pref_volume_control_view_off_key))) { if (volLayout != null) { volLayout.setVisibility(GONE); } mVolumeSeekbarLayout.setVisibility(GONE); mVolumeButtonLayout.setVisibility(GONE); } else if (volumeControlView .equals(getContext().getString(R.string.pref_volume_control_view_seekbar_key))) { if (volLayout != null) { volLayout.setVisibility(VISIBLE); } mVolumeSeekbarLayout.setVisibility(VISIBLE); mVolumeButtonLayout.setVisibility(GONE); } else if (volumeControlView .equals(getContext().getString(R.string.pref_volume_control_view_buttons_key))) { if (volLayout != null) { volLayout.setVisibility(VISIBLE); } mVolumeSeekbarLayout.setVisibility(GONE); mVolumeButtonLayout.setVisibility(VISIBLE); } } @Override public void newArtistImage(MPDArtist artist) { if (mShowArtistImage && mLastTrack.getTrackArtist().equals(artist.getArtistName())) { mCoverLoader.getArtistImage(artist, false); } } /** * Observer class for changes of the drag status. */ private class BottomDragCallbackHelper extends ViewDragHelper.Callback { /** * Checks if a given child view should act as part of the drag. This is only true for the header * element of this View-class. * * @param child Child that was touched by the user * @param pointerId Id of the pointer used for touching the view. * @return True if the view should be allowed to be used as dragging part, false otheriwse. */ @Override public boolean tryCaptureView(View child, int pointerId) { return child == mHeaderView; } /** * Called if the position of the draggable view is changed. This rerequests the layout of the view. * * @param changedView The view that was changed. * @param left Left position of the view (should stay constant in this case) * @param top Top position of the view * @param dx Dimension of the width * @param dy Dimension of the height */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // Save the heighest top position of this view. mTopPosition = top; // Calculate the new drag offset mDragOffset = (float) top / mDragRange; // Relayout this view requestLayout(); // Set inverse alpha values for smooth layout transition. // Visibility still needs to be set otherwise parts of the buttons // are not clickable. mDraggedDownButtons.setAlpha(mDragOffset); mDraggedUpButtons.setAlpha(1.0f - mDragOffset); // Calculate the margin to smoothly resize text field LayoutParams layoutParams = (LayoutParams) mHeaderTextLayout.getLayoutParams(); layoutParams.setMarginEnd((int) (mTopPlaylistButton.getWidth() * (1.0 - mDragOffset))); mHeaderTextLayout.setLayoutParams(layoutParams); if (mDragStatusReceiver != null) { mDragStatusReceiver.onDragPositionChanged(mDragOffset); } } /** * Called if the user lifts the finger(release the view) with a velocity * * @param releasedChild View that was released * @param xvel x position of the view * @param yvel y position of the view */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { int top = getPaddingTop(); if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) { top += mDragRange; } // Snap the view to top/bottom position mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top); invalidate(); } /** * Returns the range within a view is allowed to be dragged. * * @param child Child to get the dragrange for * @return Dragging range */ @Override public int getViewVerticalDragRange(View child) { return mDragRange; } /** * Clamps (limits) the view during dragging to the top or bottom(plus header height) * * @param child Child that is being dragged * @param top Top position of the dragged view * @param dy Delta value of the height * @return The limited height value (or valid position inside the clamped range). */ @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); int bottomBound = getHeight() - mHeaderView.getHeight() - mHeaderView.getPaddingBottom(); final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } /** * Called when the drag state changed. Informs observers that it is either dragged up or down. * Also sets the visibility of button groups in the header * * @param state New drag state */ @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); // Check if the new state is the idle state. If then notify the observer (if one is registered) if (state == ViewDragHelper.STATE_IDLE) { // Enable scrolling of the text views mTrackName.setSelected(true); mTrackAdditionalInfo.setSelected(true); if (mDragOffset == 0.0f) { // Called when dragged up mDraggedDownButtons.setVisibility(INVISIBLE); mDraggedUpButtons.setVisibility(VISIBLE); if (mDragStatusReceiver != null) { mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_UP); } } else { // Called when dragged down mDraggedDownButtons.setVisibility(VISIBLE); mDraggedUpButtons.setVisibility(INVISIBLE); mCoverImage.setVisibility(INVISIBLE); if (mDragStatusReceiver != null) { mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_DOWN); } } } else if (state == ViewDragHelper.STATE_DRAGGING) { /* * Show both layouts to enable a smooth transition via * alpha values of the layouts. */ mDraggedDownButtons.setVisibility(VISIBLE); mDraggedUpButtons.setVisibility(VISIBLE); mCoverImage.setVisibility(VISIBLE); // report the change of the view if (mDragStatusReceiver != null) { // Disable scrolling of the text views mTrackName.setSelected(false); mTrackAdditionalInfo.setSelected(false); mDragStatusReceiver.onStartDrag(); if (mViewSwitcher.getCurrentView() == mPlaylistView && mDragOffset == 1.0f) { mPlaylistView.jumpToCurrentSong(); } } } } } /** * Informs the dragHelper about a scroll movement. */ @Override public void computeScroll() { // Continues the movement of the View Drag Helper and sets the invalidation for this View // if the animation is not finished and needs continuation if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } /** * Handles touch inputs to some views, to make sure, the ViewDragHelper is called. * * @param ev Touch input event * @return True if handled by this view or false otherwise */ @Override public boolean onTouchEvent(MotionEvent ev) { // Call the drag helper mDragHelper.processTouchEvent(ev); // Get the position of the new touch event final float x = ev.getX(); final float y = ev.getY(); // Check if the position lies in the bounding box of the header view (which is draggable) boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y); // Check if drag is handled by the helper, or the header or mainview. If not notify the system that input is not yet handled. return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mMainView, (int) x, (int) y); } /** * Checks if an input to coordinates lay within a View * * @param view View to check with * @param x x value of the input * @param y y value of the input * @return */ private boolean isViewHit(View view, int x, int y) { int[] viewLocation = new int[2]; view.getLocationOnScreen(viewLocation); int[] parentLocation = new int[2]; this.getLocationOnScreen(parentLocation); int screenX = parentLocation[0] + x; int screenY = parentLocation[1] + y; return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() && screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight(); } /** * Asks the ViewGroup about the size of all its children and paddings around. * * @param widthMeasureSpec The width requirements for this view * @param heightMeasureSpec The height requirements for this view */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // FIXME check why super.onMeasure(widthMeasureSpec, heightMeasureSpec); causes // problems with scrolling header view. measureChildren(widthMeasureSpec, heightMeasureSpec); int maxWidth = MeasureSpec.getSize(widthMeasureSpec); int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); ViewGroup.LayoutParams imageParams = mCoverImage.getLayoutParams(); imageParams.height = mViewSwitcher.getHeight(); mCoverImage.setLayoutParams(imageParams); mCoverImage.requestLayout(); // Calculate the margin to smoothly resize text field LayoutParams layoutParams = (LayoutParams) mHeaderTextLayout.getLayoutParams(); layoutParams.setMarginEnd((int) (mTopPlaylistButton.getMeasuredHeight() * (1.0 - mDragOffset))); mHeaderTextLayout.setLayoutParams(layoutParams); } /** * Called after the layout inflater is finished. * Sets all global view variables to the ones inflated. */ @Override protected void onFinishInflate() { super.onFinishInflate(); // Get both main views (header and bottom part) mHeaderView = findViewById(R.id.now_playing_headerLayout); mMainView = findViewById(R.id.now_playing_bodyLayout); // header buttons mTopPlayPauseButton = (ImageButton) findViewById(R.id.now_playing_topPlayPauseButton); mTopPlaylistButton = (ImageButton) findViewById(R.id.now_playing_topPlaylistButton); mTopMenuButton = (ImageButton) findViewById(R.id.now_playing_topMenuButton); // bottom buttons mBottomRepeatButton = (ImageButton) findViewById(R.id.now_playing_bottomRepeatButton); mBottomPreviousButton = (ImageButton) findViewById(R.id.now_playing_bottomPreviousButton); mBottomPlayPauseButton = (ImageButton) findViewById(R.id.now_playing_bottomPlayPauseButton); mBottomStopButton = (ImageButton) findViewById(R.id.now_playing_bottomStopButton); mBottomNextButton = (ImageButton) findViewById(R.id.now_playing_bottomNextButton); mBottomRandomButton = (ImageButton) findViewById(R.id.now_playing_bottomRandomButton); // Main cover image mCoverImage = (AlbumArtistView) findViewById(R.id.now_playing_cover); // Small header cover image mTopCoverImage = (ImageView) findViewById(R.id.now_playing_topCover); // View with the ListView of the playlist mPlaylistView = (CurrentPlaylistView) findViewById(R.id.now_playing_playlist); // view switcher for cover and playlist view mViewSwitcher = (ViewSwitcher) findViewById(R.id.now_playing_view_switcher); // Button container for the buttons shown if dragged up mDraggedUpButtons = (LinearLayout) findViewById(R.id.now_playing_layout_dragged_up); // Button container for the buttons shown if dragged down mDraggedDownButtons = (LinearLayout) findViewById(R.id.now_playing_layout_dragged_down); // textviews mTrackName = (TextView) findViewById(R.id.now_playing_trackName); // For marquee scrolling the TextView need selected == true mTrackName.setSelected(true); mTrackAdditionalInfo = (TextView) findViewById(R.id.now_playing_track_additional_info); // For marquee scrolling the TextView need selected == true mTrackAdditionalInfo.setSelected(true); mTrackNo = (TextView) findViewById(R.id.now_playing_text_track_no); mPlaylistNo = (TextView) findViewById(R.id.now_playing_text_playlist_no); mBitrate = (TextView) findViewById(R.id.now_playing_text_bitrate); mAudioProperties = (TextView) findViewById(R.id.now_playing_text_audio_properties); mTrackURI = (TextView) findViewById(R.id.now_playing_text_track_uri); // Textviews directly under the seekbar mElapsedTime = (TextView) findViewById(R.id.now_playing_elapsedTime); mDuration = (TextView) findViewById(R.id.now_playing_duration); mHeaderTextLayout = (LinearLayout) findViewById(R.id.now_playing_header_textLayout); // seekbar (position) mPositionSeekbar = (SeekBar) findViewById(R.id.now_playing_seekBar); mPositionSeekbar.setOnSeekBarChangeListener(new PositionSeekbarListener()); mVolumeSeekbar = (SeekBar) findViewById(R.id.volume_seekbar); mVolumeIcon = (ImageView) findViewById(R.id.volume_icon); mVolumeIcon.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { MPDCommandHandler.setVolume(0); } }); mVolumeSeekbar.setMax(100); mVolumeSeekbar.setOnSeekBarChangeListener(new VolumeSeekBarListener()); /* Volume control buttons */ mVolumeIconButtons = (ImageView) findViewById(R.id.volume_icon_buttons); mVolumeIconButtons.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { MPDCommandHandler.setVolume(0); } }); mVolumeText = (TextView) findViewById(R.id.volume_button_text); mVolumeMinus = (ImageButton) findViewById(R.id.volume_button_minus); mVolumeMinus.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { MPDCommandHandler.decreaseVolume(); } }); mVolumePlus = (ImageButton) findViewById(R.id.volume_button_plus); mVolumePlus.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { MPDCommandHandler.increaseVolume(); } }); /* Create two listeners that start a repeating timer task to repeat the volume plus/minus action */ VolumeButtonLongClickListener plusListener = new VolumeButtonLongClickListener( VolumeButtonLongClickListener.LISTENER_ACTION.VOLUME_UP); VolumeButtonLongClickListener minusListener = new VolumeButtonLongClickListener( VolumeButtonLongClickListener.LISTENER_ACTION.VOLUME_DOWN); /* Set the listener to the plus/minus button */ mVolumeMinus.setOnLongClickListener(minusListener); mVolumeMinus.setOnTouchListener(minusListener); mVolumePlus.setOnLongClickListener(plusListener); mVolumePlus.setOnTouchListener(plusListener); mVolumeSeekbarLayout = (LinearLayout) findViewById(R.id.volume_seekbar_layout); mVolumeButtonLayout = (LinearLayout) findViewById(R.id.volume_button_layout); // set dragging part default to bottom mDragOffset = 1.0f; mDraggedUpButtons.setVisibility(INVISIBLE); mDraggedDownButtons.setVisibility(VISIBLE); mDraggedUpButtons.setAlpha(0.0f); // add listener to top playpause button mTopPlayPauseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { MPDCommandHandler.togglePause(); } }); // Add listeners to top playlist button mTopPlaylistButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // get color for playlist button int color; if (mViewSwitcher.getCurrentView() != mPlaylistView) { color = ThemeUtils.getThemeColor(getContext(), R.attr.colorAccent); } else { color = ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent); } // tint the button mTopPlaylistButton.setImageTintList(ColorStateList.valueOf(color)); // toggle between cover and playlistview mViewSwitcher.showNext(); // report the change of the view if (mDragStatusReceiver != null) { // set view status if (mViewSwitcher.getDisplayedChild() == 0) { // cover image is shown mDragStatusReceiver .onSwitchedViews(NowPlayingDragStatusReceiver.VIEW_SWITCHER_STATUS.COVER_VIEW); } else { // playlist view is shown mDragStatusReceiver .onSwitchedViews(NowPlayingDragStatusReceiver.VIEW_SWITCHER_STATUS.PLAYLIST_VIEW); mPlaylistView.jumpToCurrentSong(); } } } }); // Add listener to top menu button mTopMenuButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showAdditionalOptionsMenu(v); } }); // Add listener to bottom repeat button mBottomRepeatButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { if (null != mLastStatus) { if (mLastStatus.getRepeat() == 0) { MPDCommandHandler.setRepeat(true); } else { MPDCommandHandler.setRepeat(false); } } } }); // Add listener to bottom previous button mBottomPreviousButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { MPDCommandHandler.previousSong(); } }); // Add listener to bottom playpause button mBottomPlayPauseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { MPDCommandHandler.togglePause(); } }); mBottomStopButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { MPDCommandHandler.stop(); } }); // Add listener to bottom next button mBottomNextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { MPDCommandHandler.nextSong(); } }); // Add listener to bottom random button mBottomRandomButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { if (null != mLastStatus) { if (mLastStatus.getRandom() == 0) { MPDCommandHandler.setRandom(true); } else { MPDCommandHandler.setRandom(false); } } } }); mCoverImage.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getContext(), FanartActivity.class); getContext().startActivity(intent); } }); mCoverImage.setVisibility(INVISIBLE); mCoverLoader = new CoverBitmapLoader(getContext(), new CoverReceiverClass()); } /** * Called to open the popup menu on the top right corner. * * @param v */ private void showAdditionalOptionsMenu(View v) { PopupMenu menu = new PopupMenu(getContext(), v); // Inflate the menu from a menu xml file menu.inflate(R.menu.popup_menu_nowplaying); // Set the main NowPlayingView as a listener (directly implements callback) menu.setOnMenuItemClickListener(this); // Set the checked menu item state if a MPDCurrentStatus is available if (null != mLastStatus) { MenuItem singlePlaybackItem = menu.getMenu().findItem(R.id.action_toggle_single_mode); singlePlaybackItem.setChecked(mLastStatus.getSinglePlayback() == 1); MenuItem consumeItem = menu.getMenu().findItem(R.id.action_toggle_consume_mode); consumeItem.setChecked(mLastStatus.getConsume() == 1); } // Check if the current view is the cover or the playlist. If it is the playlist hide its actions. // If the viewswitcher only has one child the dual pane layout is used if (mViewSwitcher.getDisplayedChild() == 0 && (mViewSwitcher.getChildCount() > 1)) { menu.getMenu().setGroupEnabled(R.id.group_playlist_actions, false); menu.getMenu().setGroupVisible(R.id.group_playlist_actions, false); } // Open the menu itself menu.show(); } /** * Called when a layout is requested from the graphics system. * * @param changed If the layout is changed (size, ...) * @param l Left position * @param t Top position * @param r Right position * @param b Bottom position */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // Calculate the maximal range that the view is allowed to be dragged mDragRange = (getMeasuredHeight() - mHeaderView.getMeasuredHeight()); // New temporary top position, to fix the view at top or bottom later if state is idle. int newTop = mTopPosition; // fix height at top or bottom if state idle if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) { newTop = (int) (mDragRange * mDragOffset); } // Request the upper part of the NowPlayingView (header) mHeaderView.layout(0, newTop, r, newTop + mHeaderView.getMeasuredHeight()); // Request the lower part of the NowPlayingView (main part) mMainView.layout(0, newTop + mHeaderView.getMeasuredHeight(), r, newTop + b); } /** * Stop the refresh timer when the view is not visible to the user anymore. * Unregister the receiver for NowPlayingInformation intends, not needed anylonger. */ public void onPause() { // Unregister listener MPDStateMonitoringHandler.unregisterStatusListener(mStateListener); MPDStateMonitoringHandler.unregisterConnectionStateListener(mConnectionStateListener); mPlaylistView.onPause(); ArtworkManager.getInstance(getContext().getApplicationContext()).unregisterOnNewAlbumImageListener(this); ArtworkManager.getInstance(getContext().getApplicationContext()).unregisterOnNewArtistImageListener(this); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); sharedPref.unregisterOnSharedPreferenceChangeListener(this); } /** * Resumes refreshing operation because the view is visible to the user again. * Also registers to the NowPlayingInformation intends again. */ public void onResume() { // get the playbackservice, when the connection is successfully established the timer gets restarted // Reenable scrolling views after resuming if (mTrackName != null) { mTrackName.setSelected(true); } if (mTrackAdditionalInfo != null) { mTrackAdditionalInfo.setSelected(true); } invalidate(); // Register with MPDStateMonitoring system MPDStateMonitoringHandler.registerStatusListener(mStateListener); MPDStateMonitoringHandler.registerConnectionStateListener(mConnectionStateListener); mPlaylistView.onResume(); ArtworkManager.getInstance(getContext().getApplicationContext()).registerOnNewAlbumImageListener(this); ArtworkManager.getInstance(getContext().getApplicationContext()).registerOnNewArtistImageListener(this); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); sharedPref.registerOnSharedPreferenceChangeListener(this); setVolumeControlSetting(); mUseEnglishWikipedia = sharedPref.getBoolean( getContext().getString(R.string.pref_use_english_wikipedia_key), getContext().getResources().getBoolean(R.bool.pref_use_english_wikipedia_default)); mShowArtistImage = sharedPref.getBoolean(getContext().getString(R.string.pref_show_npv_artist_image_key), getContext().getResources().getBoolean(R.bool.pref_show_npv_artist_image_default)); } private void updateMPDStatus(MPDCurrentStatus status) { MPDCurrentStatus.MPD_PLAYBACK_STATE state = status.getPlaybackState(); // update play buttons switch (state) { case MPD_PLAYING: mTopPlayPauseButton.setImageResource(R.drawable.ic_pause_48dp); mBottomPlayPauseButton.setImageResource(R.drawable.ic_pause_circle_fill_48dp); break; case MPD_PAUSING: case MPD_STOPPED: mTopPlayPauseButton.setImageResource(R.drawable.ic_play_arrow_48dp); mBottomPlayPauseButton.setImageResource(R.drawable.ic_play_circle_fill_48dp); break; } // update repeat button // FIXME with single playback switch (status.getRepeat()) { case 0: mBottomRepeatButton.setImageResource(R.drawable.ic_repeat_24dp); mBottomRepeatButton.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent))); break; case 1: mBottomRepeatButton.setImageResource(R.drawable.ic_repeat_24dp); mBottomRepeatButton.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), android.R.attr.colorAccent))); break; } // update random button switch (status.getRandom()) { case 0: mBottomRandomButton.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent))); break; case 1: mBottomRandomButton.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), android.R.attr.colorAccent))); break; } // Update position seekbar & textviews mPositionSeekbar.setMax(status.getTrackLength()); mPositionSeekbar.setProgress(status.getElapsedTime()); mElapsedTime.setText(FormatHelper.formatTracktimeFromS(status.getElapsedTime())); mDuration.setText(FormatHelper.formatTracktimeFromS(status.getTrackLength())); // Update volume seekbar int volume = status.getVolume(); mVolumeSeekbar.setProgress(volume); if (volume >= 70) { mVolumeIcon.setImageResource(R.drawable.ic_volume_high_black_48dp); mVolumeIconButtons.setImageResource(R.drawable.ic_volume_high_black_48dp); } else if (volume >= 30 && volume < 70) { mVolumeIcon.setImageResource(R.drawable.ic_volume_medium_black_48dp); mVolumeIconButtons.setImageResource(R.drawable.ic_volume_medium_black_48dp); } else if (volume > 0 && volume < 30) { mVolumeIcon.setImageResource(R.drawable.ic_volume_low_black_48dp); mVolumeIconButtons.setImageResource(R.drawable.ic_volume_low_black_48dp); } else { mVolumeIcon.setImageResource(R.drawable.ic_volume_mute_black_48dp); mVolumeIconButtons.setImageResource(R.drawable.ic_volume_mute_black_48dp); } mVolumeIcon.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent))); mVolumeIconButtons.setImageTintList( ColorStateList.valueOf(ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent))); mVolumeText.setText(String.valueOf(volume) + '%'); mPlaylistNo.setText(String.valueOf(status.getCurrentSongIndex() + 1) + getResources().getString(R.string.track_number_album_count_separator) + String.valueOf(status.getPlaylistLength())); mLastStatus = status; mBitrate.setText(status.getBitrate() + getResources().getString(R.string.bitrate_unit_kilo_bits)); // Set audio properties string String properties = status.getSamplerate() + getResources().getString(R.string.samplerate_unit_hertz) + ' '; // Check for fancy new formats here (dsd, float = f) String sampleFormat = status.getBitDepth(); if (sampleFormat.equals("8") || sampleFormat.equals("16") || sampleFormat.equals("24") || sampleFormat.equals("32")) { properties += status.getBitDepth() + getResources().getString(R.string.bitcount_unit) + ' '; } else if (sampleFormat.equals("f")) { properties += "float "; } else { properties += status.getBitDepth() + ' '; } properties += status.getChannelCount() + getResources().getString(R.string.channel_count_unit); mAudioProperties.setText(properties); } private void updateMPDCurrentTrack(MPDTrack track) { if (track.getTrackTitle().isEmpty()) { mTrackName.setText(FormatHelper.getFilenameFromPath(track.getPath())); } else { mTrackName.setText(track.getTrackTitle()); } if (!track.getTrackArtist().isEmpty() && !track.getTrackAlbum().isEmpty()) { mTrackAdditionalInfo.setText(track.getTrackArtist() + getResources().getString(R.string.track_item_separator) + track.getTrackAlbum()); } else if (track.getTrackArtist().isEmpty() && !track.getTrackAlbum().isEmpty()) { mTrackAdditionalInfo.setText(track.getTrackAlbum()); } else if (track.getTrackAlbum().isEmpty() && !track.getTrackArtist().isEmpty()) { mTrackAdditionalInfo.setText(track.getTrackArtist()); } else { mTrackAdditionalInfo.setText(track.getPath()); } if (null == mLastTrack || !track.getTrackAlbum().equals(mLastTrack.getTrackAlbum())) { // get tint color int tintColor = ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_background_primary); Drawable drawable = getResources().getDrawable(R.drawable.cover_placeholder, null); drawable = DrawableCompat.wrap(drawable); DrawableCompat.setTint(drawable, tintColor); // Show the placeholder image until the cover fetch process finishes mCoverImage.clearAlbumImage(); tintColor = ThemeUtils.getThemeColor(getContext(), R.attr.malp_color_text_accent); drawable = getResources().getDrawable(R.drawable.cover_placeholder_128dp, null); drawable = DrawableCompat.wrap(drawable); DrawableCompat.setTint(drawable, tintColor); // The same for the small header image mTopCoverImage.setImageDrawable(drawable); // Start the cover loader mCoverLoader.getImage(track, true); } if (mShowArtistImage && (null == mLastTrack || !track.getTrackArtist().equals(mLastTrack.getTrackArtist()))) { mCoverImage.clearArtistImage(); mCoverLoader.getArtistImage(track, true); } // Calculate the margin to avoid cut off textviews RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mHeaderTextLayout .getLayoutParams(); layoutParams.setMarginEnd((int) (mTopPlaylistButton.getWidth() * (1.0 - mDragOffset))); mHeaderTextLayout.setLayoutParams(layoutParams); mTrackURI.setText(track.getPath()); if (track.getAlbumTrackCount() != 0) { mTrackNo.setText(String.valueOf(track.getTrackNumber()) + getResources().getString(R.string.track_number_album_count_separator) + String.valueOf(track.getAlbumTrackCount())); } else { mTrackNo.setText(String.valueOf(track.getTrackNumber())); } mLastTrack = track; } /** * Can be used to register an observer to this view, that is notified when a change of the dragstatus,offset happens. * * @param receiver Observer to register, only one observer at a time is possible. */ public void registerDragStatusReceiver(NowPlayingDragStatusReceiver receiver) { mDragStatusReceiver = receiver; // Initial status notification if (mDragStatusReceiver != null) { // set drag status if (mDragOffset == 0.0f) { // top mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_UP); } else { // bottom mDragStatusReceiver.onStatusChanged(NowPlayingDragStatusReceiver.DRAG_STATUS.DRAGGED_DOWN); } // set view status if (mViewSwitcher.getDisplayedChild() == 0) { // cover image is shown mDragStatusReceiver.onSwitchedViews(NowPlayingDragStatusReceiver.VIEW_SWITCHER_STATUS.COVER_VIEW); } else { // playlist view is shown mDragStatusReceiver .onSwitchedViews(NowPlayingDragStatusReceiver.VIEW_SWITCHER_STATUS.PLAYLIST_VIEW); } } } /** * Set the viewswitcher of cover/playlist view to the requested state. * * @param view the view which should be displayed. */ public void setViewSwitcherStatus(NowPlayingDragStatusReceiver.VIEW_SWITCHER_STATUS view) { int color = 0; switch (view) { case COVER_VIEW: // change the view only if the requested view is not displayed mViewSwitcher.setDisplayedChild(0); color = ThemeUtils.getThemeColor(getContext(), android.R.attr.textColor); break; case PLAYLIST_VIEW: // change the view only if the requested view is not displayed mViewSwitcher.setDisplayedChild(1); color = ThemeUtils.getThemeColor(getContext(), R.attr.colorAccent); break; } // tint the button according to the requested view mTopPlaylistButton.setImageTintList(ColorStateList.valueOf(color)); } /** * Public interface used by observers to be notified about a change in drag state or drag position. */ public interface NowPlayingDragStatusReceiver { // Possible values for DRAG_STATUS (up,down) enum DRAG_STATUS { DRAGGED_UP, DRAGGED_DOWN } // Possible values for the view in the viewswitcher (cover, playlist) enum VIEW_SWITCHER_STATUS { COVER_VIEW, PLAYLIST_VIEW } // Called when the whole view is either completely dragged up or down void onStatusChanged(DRAG_STATUS status); // Called continuously during dragging. void onDragPositionChanged(float pos); // Called when the view switcher switches between cover and playlist view void onSwitchedViews(VIEW_SWITCHER_STATUS view); // Called when the user starts the drag void onStartDrag(); } private class ServerStatusListener extends MPDStatusChangeHandler { @Override protected void onNewStatusReady(MPDCurrentStatus status) { updateMPDStatus(status); } @Override protected void onNewTrackReady(MPDTrack track) { updateMPDCurrentTrack(track); } } private class ServerConnectionListener extends MPDConnectionStateChangeHandler { @Override public void onConnected() { updateMPDStatus(MPDStateMonitoringHandler.getLastStatus()); } @Override public void onDisconnected() { updateMPDStatus(new MPDCurrentStatus()); updateMPDCurrentTrack(new MPDTrack("")); } } private class PositionSeekbarListener implements SeekBar.OnSeekBarChangeListener { /** * Called if the user drags the seekbar to a new position or the seekbar is altered from * outside. Just do some seeking, if the action is done by the user. * * @param seekBar Seekbar of which the progress was changed. * @param progress The new position of the seekbar. * @param fromUser If the action was initiated by the user. */ @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { // FIXME Check if it is better to just update if user releases the seekbar // (network stress) MPDCommandHandler.seekSeconds(progress); } } /** * Called if the user starts moving the seekbar. We do not handle this for now. * * @param seekBar SeekBar that is used for dragging. */ @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } /** * Called if the user ends moving the seekbar. We do not handle this for now. * * @param seekBar SeekBar that is used for dragging. */ @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } } private class VolumeSeekBarListener implements SeekBar.OnSeekBarChangeListener { /** * Called if the user drags the seekbar to a new position or the seekbar is altered from * outside. Just do some seeking, if the action is done by the user. * * @param seekBar Seekbar of which the progress was changed. * @param progress The new position of the seekbar. * @param fromUser If the action was initiated by the user. */ @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { MPDCommandHandler.setVolume(progress); if (progress >= 70) { mVolumeIcon.setImageResource(R.drawable.ic_volume_high_black_48dp); } else if (progress >= 30 && progress < 70) { mVolumeIcon.setImageResource(R.drawable.ic_volume_medium_black_48dp); } else if (progress > 0 && progress < 30) { mVolumeIcon.setImageResource(R.drawable.ic_volume_low_black_48dp); } else { mVolumeIcon.setImageResource(R.drawable.ic_volume_mute_black_48dp); } } } /** * Called if the user starts moving the seekbar. We do not handle this for now. * * @param seekBar SeekBar that is used for dragging. */ @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } /** * Called if the user ends moving the seekbar. We do not handle this for now. * * @param seekBar SeekBar that is used for dragging. */ @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } } /** * Private class that handles when the CoverGenerator finishes its fetching of cover images. */ private class CoverReceiverClass implements CoverBitmapLoader.CoverBitmapListener { /** * Called when a bitmap is created * * @param bm Bitmap ready for use in the UI */ @Override public void receiveBitmap(final Bitmap bm, final CoverBitmapLoader.IMAGE_TYPE type) { if (bm != null) { Activity activity = (Activity) getContext(); if (activity != null) { // Run on the UI thread of the activity because we are modifying gui elements. activity.runOnUiThread(new Runnable() { @Override public void run() { if (type == CoverBitmapLoader.IMAGE_TYPE.ALBUM_IMAGE) { // Set the main cover image mCoverImage.setAlbumImage(bm); // Set the small header image mTopCoverImage.setImageBitmap(bm); } else if (type == CoverBitmapLoader.IMAGE_TYPE.ARTIST_IMAGE) { mCoverImage.setArtistImage(bm); } } }); } } } } }