- git (re)init (git structure was corrupted)
- added shuffler and repeat mode - xxxhdpi icons - typos - new styles - not fully working playing queue [alpha]
|
|
@ -0,0 +1,13 @@
|
|||
package com.kabouzeid.materialmusic;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
61
app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.kabouzeid.materialmusic">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.MaterialMusic">
|
||||
<activity
|
||||
android:name=".ui.activities.MainActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".ui.activities.AlbumDetailActivity">
|
||||
</activity>
|
||||
<activity android:name=".ui.activities.ArtistDetailActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.MusicControllerActivity"
|
||||
android:parentActivityName=".ui.activities.MainActivity">
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.MusicService"
|
||||
android:enabled="true">
|
||||
</service>
|
||||
|
||||
<receiver android:name=".service.MediaButtonIntentReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="com.crashlytics.ApiKey"
|
||||
android:value="b23725bd3d266aa65c5a3dd1816b2f801524a189"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.activities.tageditor.SongTagEditorActivity"
|
||||
android:label="@string/title_activity_tag_editor"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.tageditor.AlbumTagEditorActivity"
|
||||
android:label="@string/title_activity_album_tag_editor">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.samples.apps.iosched.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* To be used with ViewPager to provide a tab indicator component which give constant feedback as to
|
||||
* the user's scroll progress.
|
||||
* <p/>
|
||||
* To use the component, simply add it to your view hierarchy. Then in your
|
||||
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
|
||||
* {@link #setViewPager(android.support.v4.view.ViewPager)} providing it the ViewPager this layout is being used for.
|
||||
* <p/>
|
||||
* The colors can be customized in two ways. The first and simplest is to provide an array of colors
|
||||
* via {@link #setSelectedIndicatorColors(int...)}. The
|
||||
* alternative is via the {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} interface which provides you complete control over
|
||||
* which color is used for any individual position.
|
||||
* <p/>
|
||||
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||
* providing the layout ID of your custom layout.
|
||||
*/
|
||||
public class SlidingTabLayout extends HorizontalScrollView {
|
||||
/**
|
||||
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||
* {@link #setCustomTabColorizer(com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer)}.
|
||||
*/
|
||||
public interface TabColorizer {
|
||||
|
||||
/**
|
||||
* @return return the color of the indicator used when {@code position} is selected.
|
||||
*/
|
||||
int getIndicatorColor(int position);
|
||||
|
||||
}
|
||||
|
||||
private static final int TITLE_OFFSET_DIPS = 24;
|
||||
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||
|
||||
private int mTitleOffset;
|
||||
|
||||
private int mTabViewLayoutId;
|
||||
private int mTabViewTextViewId;
|
||||
private boolean mDistributeEvenly;
|
||||
|
||||
private ViewPager mViewPager;
|
||||
private SparseArray<String> mContentDescriptions = new SparseArray<String>();
|
||||
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
|
||||
|
||||
private final SlidingTabStrip mTabStrip;
|
||||
|
||||
public SlidingTabLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SlidingTabLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
// Disable the Scroll Bar
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
// Make sure that the Tab Strips fills this View
|
||||
setFillViewport(true);
|
||||
|
||||
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
|
||||
|
||||
mTabStrip = new SlidingTabStrip(context);
|
||||
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} to be used.
|
||||
* <p/>
|
||||
* If you only require simple custmisation then you can use
|
||||
* {@link #setSelectedIndicatorColors(int...)} to achieve
|
||||
* similar effects.
|
||||
*/
|
||||
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||
mTabStrip.setCustomTabColorizer(tabColorizer);
|
||||
}
|
||||
|
||||
public void setDistributeEvenly(boolean distributeEvenly) {
|
||||
mDistributeEvenly = distributeEvenly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||
* circular array. Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setSelectedIndicatorColors(int... colors) {
|
||||
mTabStrip.setSelectedIndicatorColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link android.support.v4.view.ViewPager.OnPageChangeListener}. When using {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout} you are
|
||||
* required to set any {@link android.support.v4.view.ViewPager.OnPageChangeListener} through this method. This is so
|
||||
* that the layout can update it's scroll position correctly.
|
||||
*
|
||||
* @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener)
|
||||
*/
|
||||
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||
mViewPagerPageChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param layoutResId Layout id to be inflated
|
||||
* @param textViewId id of the {@link android.widget.TextView} in the inflated view
|
||||
*/
|
||||
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||
mTabViewLayoutId = layoutResId;
|
||||
mTabViewTextViewId = textViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||
* (number of tabs and tab titles) does not change after this call has been made.
|
||||
*/
|
||||
public void setViewPager(ViewPager viewPager) {
|
||||
mTabStrip.removeAllViews();
|
||||
|
||||
mViewPager = viewPager;
|
||||
if (viewPager != null) {
|
||||
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
|
||||
populateTabStrip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||
* {@link #setCustomTabView(int, int)}.
|
||||
*/
|
||||
protected TextView createDefaultTabView(Context context) {
|
||||
TextView textView = new TextView(context);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
|
||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
textView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
|
||||
outValue, true);
|
||||
textView.setBackgroundResource(outValue.resourceId);
|
||||
textView.setAllCaps(true);
|
||||
|
||||
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
|
||||
textView.setPadding(padding, padding, padding, padding);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
private void populateTabStrip() {
|
||||
final PagerAdapter adapter = mViewPager.getAdapter();
|
||||
final OnClickListener tabClickListener = new TabClickListener();
|
||||
|
||||
for (int i = 0; i < adapter.getCount(); i++) {
|
||||
View tabView = null;
|
||||
TextView tabTitleView = null;
|
||||
|
||||
if (mTabViewLayoutId != 0) {
|
||||
// If there is a custom tab view layout id set, try and inflate it
|
||||
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
|
||||
false);
|
||||
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
|
||||
}
|
||||
|
||||
if (tabView == null) {
|
||||
tabView = createDefaultTabView(getContext());
|
||||
}
|
||||
|
||||
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||
tabTitleView = (TextView) tabView;
|
||||
}
|
||||
|
||||
if (mDistributeEvenly) {
|
||||
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
|
||||
lp.width = 0;
|
||||
lp.weight = 1;
|
||||
}
|
||||
|
||||
tabTitleView.setText(adapter.getPageTitle(i));
|
||||
tabView.setOnClickListener(tabClickListener);
|
||||
String desc = mContentDescriptions.get(i, null);
|
||||
if (desc != null) {
|
||||
tabView.setContentDescription(desc);
|
||||
}
|
||||
|
||||
mTabStrip.addView(tabView);
|
||||
if (i == mViewPager.getCurrentItem()) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentDescription(int i, String desc) {
|
||||
mContentDescriptions.put(i, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
if (mViewPager != null) {
|
||||
scrollToTab(mViewPager.getCurrentItem(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToTab(int tabIndex, int positionOffset) {
|
||||
final int tabStripChildCount = mTabStrip.getChildCount();
|
||||
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
View selectedChild = mTabStrip.getChildAt(tabIndex);
|
||||
if (selectedChild != null) {
|
||||
int targetScrollX = selectedChild.getLeft() + positionOffset;
|
||||
|
||||
if (tabIndex > 0 || positionOffset > 0) {
|
||||
// If we're not at the first child and are mid-scroll, make sure we obey the offset
|
||||
targetScrollX -= mTitleOffset;
|
||||
}
|
||||
|
||||
scrollTo(targetScrollX, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||
private int mScrollState;
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
int tabStripChildCount = mTabStrip.getChildCount();
|
||||
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||
|
||||
View selectedTitle = mTabStrip.getChildAt(position);
|
||||
int extraOffset = (selectedTitle != null)
|
||||
? (int) (positionOffset * selectedTitle.getWidth())
|
||||
: 0;
|
||||
scrollToTab(position, extraOffset);
|
||||
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
|
||||
positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
mScrollState = state;
|
||||
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||
mTabStrip.onViewPagerPageChanged(position, 0f);
|
||||
scrollToTab(position, 0);
|
||||
}
|
||||
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||
mTabStrip.getChildAt(i).setSelected(position == i);
|
||||
}
|
||||
if (mViewPagerPageChangeListener != null) {
|
||||
mViewPagerPageChangeListener.onPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TabClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||
if (v == mTabStrip.getChildAt(i)) {
|
||||
mViewPager.setCurrentItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.samples.apps.iosched.ui.widget;
|
||||
|
||||
import android.R;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
class SlidingTabStrip extends LinearLayout {
|
||||
|
||||
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
|
||||
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
|
||||
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||
|
||||
private final int mBottomBorderThickness;
|
||||
private final Paint mBottomBorderPaint;
|
||||
|
||||
private final int mSelectedIndicatorThickness;
|
||||
private final Paint mSelectedIndicatorPaint;
|
||||
|
||||
private final int mDefaultBottomBorderColor;
|
||||
|
||||
private int mSelectedPosition;
|
||||
private float mSelectionOffset;
|
||||
|
||||
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
|
||||
private final SimpleTabColorizer mDefaultTabColorizer;
|
||||
|
||||
SlidingTabStrip(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
SlidingTabStrip(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
|
||||
TypedValue outValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
|
||||
final int themeForegroundColor = outValue.data;
|
||||
|
||||
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
|
||||
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||
|
||||
mDefaultTabColorizer = new SimpleTabColorizer();
|
||||
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
|
||||
|
||||
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||
mBottomBorderPaint = new Paint();
|
||||
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
|
||||
|
||||
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||
mSelectedIndicatorPaint = new Paint();
|
||||
}
|
||||
|
||||
void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
|
||||
mCustomTabColorizer = customTabColorizer;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setSelectedIndicatorColors(int... colors) {
|
||||
// Make sure that the custom colorizer is removed
|
||||
mCustomTabColorizer = null;
|
||||
mDefaultTabColorizer.setIndicatorColors(colors);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||
mSelectedPosition = position;
|
||||
mSelectionOffset = positionOffset;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
final int height = getHeight();
|
||||
final int childCount = getChildCount();
|
||||
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
|
||||
? mCustomTabColorizer
|
||||
: mDefaultTabColorizer;
|
||||
|
||||
// Thick colored underline below the current selection
|
||||
if (childCount > 0) {
|
||||
View selectedTitle = getChildAt(mSelectedPosition);
|
||||
int left = selectedTitle.getLeft();
|
||||
int right = selectedTitle.getRight();
|
||||
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
|
||||
|
||||
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
|
||||
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
|
||||
if (color != nextColor) {
|
||||
color = blendColors(nextColor, color, mSelectionOffset);
|
||||
}
|
||||
|
||||
// Draw the selection partway between the tabs
|
||||
View nextTitle = getChildAt(mSelectedPosition + 1);
|
||||
left = (int) (mSelectionOffset * nextTitle.getLeft() +
|
||||
(1.0f - mSelectionOffset) * left);
|
||||
right = (int) (mSelectionOffset * nextTitle.getRight() +
|
||||
(1.0f - mSelectionOffset) * right);
|
||||
}
|
||||
|
||||
mSelectedIndicatorPaint.setColor(color);
|
||||
|
||||
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
|
||||
height, mSelectedIndicatorPaint);
|
||||
}
|
||||
|
||||
// Thin underline along the entire bottom edge
|
||||
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||
*/
|
||||
private static int setColorAlpha(int color, byte alpha) {
|
||||
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||
*
|
||||
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||
* 0.0 will return {@code color2}.
|
||||
*/
|
||||
private static int blendColors(int color1, int color2, float ratio) {
|
||||
final float inverseRation = 1f - ratio;
|
||||
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||
return Color.rgb((int) r, (int) g, (int) b);
|
||||
}
|
||||
|
||||
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
|
||||
private int[] mIndicatorColors;
|
||||
|
||||
@Override
|
||||
public final int getIndicatorColor(int position) {
|
||||
return mIndicatorColors[position % mIndicatorColors.length];
|
||||
}
|
||||
|
||||
void setIndicatorColors(int... colors) {
|
||||
mIndicatorColors = colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
app/src/main/java/com/kabouzeid/materialmusic/App.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package com.kabouzeid.materialmusic;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.Fragment;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import com.kabouzeid.materialmusic.helper.MusicPlayerRemote;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
|
||||
/**
|
||||
* Created by karim on 25.11.14.
|
||||
*/
|
||||
public class App extends Application {
|
||||
private static final String TAG = App.class.getSimpleName();
|
||||
|
||||
public Fragment[] MainActivityFragments = new Fragment[5];
|
||||
private MusicPlayerRemote playerRemote;
|
||||
private int appTheme;
|
||||
private SharedPreferences defaultSharedPreferences;
|
||||
private RequestQueue requestQueue;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Fabric.with(this, new Crashlytics());
|
||||
ImageLoaderUtil.initImageLoader(this);
|
||||
}
|
||||
|
||||
public MusicPlayerRemote getMusicPlayerRemote() {
|
||||
if (playerRemote == null) {
|
||||
playerRemote = new MusicPlayerRemote(this);
|
||||
playerRemote.restorePreviousState();
|
||||
}
|
||||
return playerRemote;
|
||||
}
|
||||
|
||||
public SharedPreferences getDefaultSharedPreferences() {
|
||||
if (defaultSharedPreferences == null) {
|
||||
defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
}
|
||||
return defaultSharedPreferences;
|
||||
}
|
||||
|
||||
public int getAppTheme() {
|
||||
if (appTheme == 0) {
|
||||
appTheme = getDefaultSharedPreferences().getInt(AppKeys.SP_THEME, R.style.Theme_MaterialMusic);
|
||||
}
|
||||
return appTheme;
|
||||
}
|
||||
|
||||
public void setAppTheme(int appTheme) {
|
||||
this.appTheme = appTheme;
|
||||
defaultSharedPreferences.edit().putInt(AppKeys.SP_THEME, appTheme);
|
||||
}
|
||||
|
||||
public boolean isTablet() {
|
||||
return getResources().getConfiguration().smallestScreenWidthDp >= 600;
|
||||
}
|
||||
|
||||
public boolean isInPortraitMode() {
|
||||
return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
|
||||
public RequestQueue getRequestQueue() {
|
||||
if (requestQueue == null) {
|
||||
requestQueue = Volley.newRequestQueue(this);
|
||||
}
|
||||
return requestQueue;
|
||||
}
|
||||
|
||||
public void addToRequestQueue(Request request) {
|
||||
request.setTag(TAG);
|
||||
getRequestQueue().add(request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.kabouzeid.materialmusic.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.kabouzeid.materialmusic.view.SquareImageView;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.11.14.
|
||||
*/
|
||||
public class AlbumViewGridAdapter extends ArrayAdapter<Album> {
|
||||
public static final String TAG = AlbumViewGridAdapter.class.getSimpleName();
|
||||
private Context context;
|
||||
private boolean usePalette;
|
||||
|
||||
public AlbumViewGridAdapter(Context context, List<Album> objects) {
|
||||
super(context, R.layout.album_tile, objects);
|
||||
this.context = context;
|
||||
usePalette = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final Album album = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.album_tile, parent, false);
|
||||
}
|
||||
final SquareImageView albumArt = (SquareImageView) convertView.findViewById(R.id.album_art);
|
||||
final TextView title = (TextView) convertView.findViewById(R.id.album_title);
|
||||
final TextView artist = (TextView) convertView.findViewById(R.id.album_interpret);
|
||||
final View footer = convertView.findViewById(R.id.footer);
|
||||
|
||||
title.setText(album.title);
|
||||
artist.setText(album.artistName);
|
||||
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(album.id).toString(), albumArt, new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
albumArt.setImageDrawable(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
if (usePalette) {
|
||||
paletteBugFixBlackAndWhite(title, artist, footer);
|
||||
}
|
||||
albumArt.setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
if (usePalette) {
|
||||
applyPalette(loadedImage, title, artist, footer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private void applyPalette(Bitmap bitmap, final TextView title, final TextView artist, final View footer) {
|
||||
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
|
||||
@Override
|
||||
public void onGenerated(Palette palette) {
|
||||
final Palette.Swatch vibrantSwatch = palette.getVibrantSwatch();
|
||||
if (vibrantSwatch != null) {
|
||||
title.setTextColor(vibrantSwatch.getTitleTextColor());
|
||||
artist.setTextColor(vibrantSwatch.getTitleTextColor());
|
||||
ViewUtil.animateViewColor(footer, Util.resolveColor(context, R.attr.colorPrimary),
|
||||
vibrantSwatch.getRgb());
|
||||
} else {
|
||||
paletteBugFixBlackAndWhite(title, artist, footer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void paletteBugFixBlackAndWhite(TextView title, TextView artist, View footer) {
|
||||
title.setTextColor(Util.resolveColor(context, R.attr.title_text_color));
|
||||
artist.setTextColor(Util.resolveColor(context, R.attr.caption_text_color));
|
||||
ViewUtil.animateViewColor(footer, Util.resolveColor(context, R.attr.colorPrimary),
|
||||
Util.resolveColor(context, R.attr.colorPrimary));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.kabouzeid.materialmusic.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.lastfm.artist.LastFMArtistThumbnailLoader;
|
||||
import com.kabouzeid.materialmusic.model.Artist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class ArtistViewListAdapter extends ArrayAdapter<Artist> {
|
||||
private Context context;
|
||||
|
||||
|
||||
public ArtistViewListAdapter(Context context, List<Artist> objects) {
|
||||
super(context, R.layout.item_artist_view, objects);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
Artist artist = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_artist_view, parent, false);
|
||||
}
|
||||
final TextView artistName = (TextView) convertView.findViewById(R.id.artist_name);
|
||||
final ImageView artistArt = (ImageView) convertView.findViewById(R.id.artist_image);
|
||||
|
||||
artistName.setText(artist.name);
|
||||
artistArt.setImageResource(R.drawable.default_artist_image);
|
||||
|
||||
final Object tag = artist.name;
|
||||
artistArt.setTag(tag);
|
||||
|
||||
LastFMArtistThumbnailLoader.loadArtistThumbnail(context, artist.name, new LastFMArtistThumbnailLoader.ArtistThumbnailLoaderCallback() {
|
||||
@Override
|
||||
public void onArtistThumbnailLoaded(Bitmap thumbnail) {
|
||||
if (artistArt.getTag().equals(tag)) {
|
||||
if (thumbnail != null) {
|
||||
artistArt.setImageBitmap(thumbnail);
|
||||
} else {
|
||||
artistArt.setImageResource(R.drawable.default_artist_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.kabouzeid.materialmusic.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.model.NavigationDrawerItem;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 23.11.14.
|
||||
*/
|
||||
public class NavigationDrawerItemAdapter extends ArrayAdapter<NavigationDrawerItem> {
|
||||
private int currentChecked = -1;
|
||||
|
||||
public NavigationDrawerItemAdapter(Context context, int resource, List<NavigationDrawerItem> objects) {
|
||||
super(context, resource, objects);
|
||||
}
|
||||
|
||||
public void setChecked(int position) {
|
||||
currentChecked = position;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
NavigationDrawerItem item = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_navigation_drawer, parent, false);
|
||||
}
|
||||
TextView title = (TextView) convertView.findViewById(R.id.title);
|
||||
ImageView icon = (ImageView) convertView.findViewById(R.id.album_art);
|
||||
title.setText(item.title);
|
||||
icon.setImageResource(item.imageRes);
|
||||
if (position == currentChecked) {
|
||||
title.setTextColor(Util.resolveColor(getContext(), R.attr.colorAccent));
|
||||
} else {
|
||||
title.setTextColor(Util.resolveColor(getContext(), R.attr.title_text_color));
|
||||
}
|
||||
View container = convertView.findViewById(R.id.container);
|
||||
container.setActivated(position == currentChecked);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.kabouzeid.materialmusic.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.01.15.
|
||||
*/
|
||||
public class PlayListAdapter extends ArrayAdapter<Song> {
|
||||
private Context context;
|
||||
private App app;
|
||||
|
||||
public PlayListAdapter(Context context, List<Song> playList) {
|
||||
super(context, R.layout.item_playlist, playList);
|
||||
this.context = context;
|
||||
app = (App) context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final Song song = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_playlist, parent, false);
|
||||
}
|
||||
final TextView title = (TextView) convertView.findViewById(R.id.song_title);
|
||||
final ImageView albumArt = (ImageView) convertView.findViewById(R.id.album_art);
|
||||
|
||||
title.setText(song.title);
|
||||
if (app.getMusicPlayerRemote().getCurrentSongId() == song.id) {
|
||||
albumArt.setImageResource(R.drawable.ic_speaker_white_48dp);
|
||||
} else {
|
||||
albumArt.setImageBitmap(null);
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package com.kabouzeid.materialmusic.adapter.songadapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.helper.SongDetailDialogHelper;
|
||||
import com.kabouzeid.materialmusic.loader.SongFileLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.tageditor.SongTagEditorActivity;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 27.11.14.
|
||||
*/
|
||||
public class SongAdapter extends ArrayAdapter<Song> {
|
||||
public static final String TAG = SongAdapter.class.getSimpleName();
|
||||
protected Context context;
|
||||
protected GoToAble goToAble;
|
||||
|
||||
public SongAdapter(Context context, GoToAble goToAble, List<Song> objects) {
|
||||
super(context, R.layout.item_song, objects);
|
||||
this.context = context;
|
||||
this.goToAble = goToAble;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final Song song = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_song, parent, false);
|
||||
}
|
||||
TextView songTitle = (TextView) convertView.findViewById(R.id.song_title);
|
||||
TextView trackNumber = (TextView) convertView.findViewById(R.id.track_number);
|
||||
TextView songDuration = (TextView) convertView.findViewById(R.id.song_duration);
|
||||
ImageView overflowButton = (ImageView) convertView.findViewById(R.id.menu);
|
||||
|
||||
overflowButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
PopupMenu popupMenu = new PopupMenu(context, v);
|
||||
popupMenu.inflate(R.menu.menu_song);
|
||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_tag_editor:
|
||||
Intent intent = new Intent(context, SongTagEditorActivity.class);
|
||||
intent.putExtra(AppKeys.E_ID, song.id);
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
case R.id.action_details:
|
||||
String songFilePath = SongFileLoader.getSongFile(context, song.id);
|
||||
File songFile = new File(songFilePath);
|
||||
SongDetailDialogHelper.getDialog(context, songFile).show();
|
||||
return true;
|
||||
case R.id.action_go_to_album:
|
||||
if (goToAble != null) {
|
||||
goToAble.goToAlbum(song.albumId);
|
||||
}
|
||||
return true;
|
||||
case R.id.action_go_to_artist:
|
||||
if (goToAble != null) {
|
||||
goToAble.goToArtist(song.artistId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
popupMenu.show();
|
||||
}
|
||||
});
|
||||
|
||||
songTitle.setText(song.title);
|
||||
trackNumber.setText(String.valueOf(MusicUtil.getFixedTrackNumber(song.trackNumber)));
|
||||
songDuration.setText(MusicUtil.getReadableDurationString(song.duration));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public static interface GoToAble {
|
||||
public void goToAlbum(int albumId);
|
||||
|
||||
public void goToArtist(int artistId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.kabouzeid.materialmusic.adapter.songadapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.helper.SongDetailDialogHelper;
|
||||
import com.kabouzeid.materialmusic.loader.SongFileLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.tageditor.SongTagEditorActivity;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 27.11.14.
|
||||
*/
|
||||
public class SongViewListAdapter extends SongAdapter {
|
||||
public static final String TAG = SongViewListAdapter.class.getSimpleName();
|
||||
|
||||
public SongViewListAdapter(Context context, SongAdapter.GoToAble goToAble, List<Song> objects) {
|
||||
super(context, goToAble, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final Song song = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_song_view, parent, false);
|
||||
}
|
||||
TextView songTitle = (TextView) convertView.findViewById(R.id.song_title);
|
||||
final ImageView albumArt = (ImageView) convertView.findViewById(R.id.album_art);
|
||||
ImageView overflowButton = (ImageView) convertView.findViewById(R.id.menu);
|
||||
|
||||
overflowButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
PopupMenu popupMenu = new PopupMenu(context, v);
|
||||
popupMenu.inflate(R.menu.menu_song);
|
||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_tag_editor:
|
||||
Intent intent = new Intent(context, SongTagEditorActivity.class);
|
||||
intent.putExtra(AppKeys.E_ID, song.id);
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
case R.id.action_details:
|
||||
String songFilePath = SongFileLoader.getSongFile(context, song.id);
|
||||
File songFile = new File(songFilePath);
|
||||
SongDetailDialogHelper.getDialog(context, songFile).show();
|
||||
return true;
|
||||
case R.id.action_go_to_album:
|
||||
if (goToAble != null) {
|
||||
goToAble.goToAlbum(song.albumId);
|
||||
}
|
||||
return true;
|
||||
case R.id.action_go_to_artist:
|
||||
if (goToAble != null) {
|
||||
goToAble.goToArtist(song.artistId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
popupMenu.show();
|
||||
}
|
||||
});
|
||||
|
||||
songTitle.setText(song.title);
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(song.albumId).toString(), albumArt, new ImageLoaderUtil.defaultAlbumArtOnFailed());
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.kabouzeid.materialmusic.comparator;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Created by karim on 25.11.14.
|
||||
*/
|
||||
public class AlbumAlphabeticComparator implements Comparator<Album> {
|
||||
@Override
|
||||
public int compare(Album lhs, Album rhs) {
|
||||
return lhs.title.trim().compareToIgnoreCase(rhs.title.trim());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.kabouzeid.materialmusic.comparator;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Artist;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class ArtistAlphabeticComparator implements Comparator<Artist> {
|
||||
@Override
|
||||
public int compare(Artist lhs, Artist rhs) {
|
||||
return lhs.name.trim().compareToIgnoreCase(rhs.name.trim());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.kabouzeid.materialmusic.comparator;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Created by karim on 28.12.14.
|
||||
*/
|
||||
public class SongAlphabeticComparator implements Comparator<Song> {
|
||||
@Override
|
||||
public int compare(Song lhs, Song rhs) {
|
||||
return lhs.title.trim().compareToIgnoreCase(rhs.title.trim());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.kabouzeid.materialmusic.comparator;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Created by karim on 25.11.14.
|
||||
*/
|
||||
public class SongTrackNumberComparator implements Comparator<Song> {
|
||||
@Override
|
||||
public int compare(Song lhs, Song rhs) {
|
||||
// 0 gleich
|
||||
// -1 steht über dem anderen
|
||||
// 1 steht unter dem anderen
|
||||
if (lhs.trackNumber == rhs.trackNumber) {
|
||||
return 0;
|
||||
}
|
||||
if (lhs.trackNumber > rhs.trackNumber) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
package com.kabouzeid.materialmusic.helper;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.service.MusicService;
|
||||
import com.kabouzeid.materialmusic.util.InternalStorageUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.11.14.
|
||||
*/
|
||||
public class MusicPlayerRemote implements OnMusicRemoteEventListener {
|
||||
private static final String TAG = MusicPlayerRemote.class.getSimpleName();
|
||||
|
||||
private App app;
|
||||
|
||||
private int position = -1;
|
||||
|
||||
private List<Song> playingQueue;
|
||||
private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners;
|
||||
|
||||
private MusicService musicService;
|
||||
private Intent musicServiceIntent;
|
||||
private boolean musicBound = false;
|
||||
|
||||
public MusicPlayerRemote(Context context) {
|
||||
app = (App) context.getApplicationContext();
|
||||
playingQueue = new ArrayList<>();
|
||||
onMusicRemoteEventListeners = new ArrayList<>();
|
||||
startAndBindService();
|
||||
}
|
||||
|
||||
private void startAndBindService() {
|
||||
if (musicServiceIntent == null) {
|
||||
musicServiceIntent = new Intent(app, MusicService.class);
|
||||
app.bindService(musicServiceIntent, musicConnection, Context.BIND_AUTO_CREATE);
|
||||
app.startService(musicServiceIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private ServiceConnection musicConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
|
||||
musicService = binder.getService();
|
||||
musicService.setPosition(position);
|
||||
musicBound = true;
|
||||
musicService.addOnMusicRemoteEventListener(MusicPlayerRemote.this);
|
||||
setPlayingQueue(playingQueue);
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SERVICE_CONNECTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
musicBound = false;
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SERVICE_DISCONNECTED);
|
||||
}
|
||||
};
|
||||
|
||||
public boolean playSongAt(int position) {
|
||||
if (musicBound) {
|
||||
if (position < getPlayingQueue().size() && position >= 0) {
|
||||
this.position = position;
|
||||
musicService.setPosition(position);
|
||||
musicService.playSong();
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "No song in queue at given index!");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void pauseSong() {
|
||||
if (musicBound) {
|
||||
musicService.pausePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
public void playNextSong() {
|
||||
if (musicBound) {
|
||||
musicService.playNextSong();
|
||||
}
|
||||
}
|
||||
|
||||
public void playPreviousSong() {
|
||||
if (musicBound) {
|
||||
musicService.back();
|
||||
}
|
||||
}
|
||||
|
||||
public void back() {
|
||||
if (musicBound) {
|
||||
musicService.back();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
if (musicBound) {
|
||||
return musicService.isPlaying();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void resumePlaying() {
|
||||
if (musicBound) {
|
||||
musicService.resumePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentSongId() {
|
||||
if (musicBound) {
|
||||
return musicService.getCurrentSongId();
|
||||
}
|
||||
try {
|
||||
return playingQueue.get(position).id;
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
if (musicBound) {
|
||||
position = musicService.getPosition();
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPlayingQueue(List<Song> songs) {
|
||||
playingQueue = songs;
|
||||
if (musicBound) {
|
||||
musicService.setPlayingQueue(playingQueue);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Song> getPlayingQueue() {
|
||||
if (musicBound) {
|
||||
playingQueue = musicService.getPlayingQueue();
|
||||
}
|
||||
return playingQueue;
|
||||
}
|
||||
|
||||
public Song getCurrentSong() {
|
||||
final int position = getPosition();
|
||||
if (position != -1) {
|
||||
return getPlayingQueue().get(position);
|
||||
}
|
||||
return new Song();
|
||||
}
|
||||
|
||||
public int getSongProgressMillis() {
|
||||
if (isPlayerPrepared()) {
|
||||
return musicService.getSongProgressMillis();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getSongDurationMillis() {
|
||||
if (isPlayerPrepared()) {
|
||||
return musicService.getSongDurationMillis();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean isMusicBound() {
|
||||
return musicBound;
|
||||
}
|
||||
|
||||
public void seekTo(int millis) {
|
||||
if (musicBound) {
|
||||
musicService.seekTo(millis);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPlayerPrepared() {
|
||||
if (musicBound) {
|
||||
return musicService.isPlayerPrepared();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void notifyPlayingQueueChanged() {
|
||||
final long currentSongId = getCurrentSongId();
|
||||
}
|
||||
|
||||
public int getRepeatMode() {
|
||||
if (musicBound) {
|
||||
return musicService.getRepeatMode();
|
||||
}
|
||||
return app.getDefaultSharedPreferences().getInt(AppKeys.SP_REPEAT_MODE, 0);
|
||||
}
|
||||
|
||||
public int getShuffleMode() {
|
||||
if (musicBound) {
|
||||
return musicService.getShuffleMode();
|
||||
}
|
||||
return app.getDefaultSharedPreferences().getInt(AppKeys.SP_SHUFFLE_MODE, 0);
|
||||
}
|
||||
|
||||
public boolean cycleRepeatMode() {
|
||||
if (musicBound) {
|
||||
musicService.cycleRepeatMode();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean toggleShuffleMode() {
|
||||
if (musicBound) {
|
||||
musicService.toggleShuffle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicRemoteEvent(MusicRemoteEvent event) {
|
||||
notifyOnMusicRemoteEventListeners(event.getAction());
|
||||
}
|
||||
|
||||
public void addOnMusicRemoteEventListener(OnMusicRemoteEventListener onMusicRemoteEventListener) {
|
||||
onMusicRemoteEventListeners.add(onMusicRemoteEventListener);
|
||||
}
|
||||
|
||||
public void removeOnMusicRemoteEventListener(OnMusicRemoteEventListener onMusicRemoteEventListener) {
|
||||
onMusicRemoteEventListeners.remove(onMusicRemoteEventListener);
|
||||
}
|
||||
|
||||
public void removeAllOnMusicRemoteEventListeners() {
|
||||
onMusicRemoteEventListeners.clear();
|
||||
}
|
||||
|
||||
private void notifyOnMusicRemoteEventListeners(int event) {
|
||||
MusicRemoteEvent musicRemoteEvent = new MusicRemoteEvent(event);
|
||||
for (OnMusicRemoteEventListener listener : onMusicRemoteEventListeners) {
|
||||
listener.onMusicRemoteEvent(musicRemoteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void restorePreviousState() {
|
||||
try {
|
||||
List restoredQueue = (ArrayList<Song>) InternalStorageUtil.readObject(app, AppKeys.IS_PLAYING_QUEUE);
|
||||
int restoredPosition = (int) InternalStorageUtil.readObject(app, AppKeys.IS_POSITION_IN_QUEUE);
|
||||
setPlayingQueue(restoredQueue);
|
||||
position = restoredPosition;
|
||||
if (musicBound) {
|
||||
musicService.setPosition(restoredPosition);
|
||||
}
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STATE_RESTORED);
|
||||
Log.i(TAG, "restored last state");
|
||||
} catch (IOException | ClassNotFoundException | ClassCastException e) {
|
||||
Log.e(TAG, "error while restoring music service state", e);
|
||||
playingQueue = new ArrayList<>();
|
||||
position = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
package com.kabouzeid.materialmusic.helper;
|
||||
|
||||
/**
|
||||
* Created by karim on 27.12.14.
|
||||
*/
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.service.MusicService;
|
||||
import com.kabouzeid.materialmusic.ui.activities.MusicControllerActivity;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
public class NotificationHelper {
|
||||
public static final String TAG = NotificationHelper.class.getSimpleName();
|
||||
public static final int NOTIFICATION_ID = 1337;
|
||||
|
||||
private final MusicService service;
|
||||
|
||||
private final NotificationManager notificationManager;
|
||||
private Notification notification = null;
|
||||
|
||||
private RemoteViews notificationLayout;
|
||||
private RemoteViews notificationLayoutExpanded;
|
||||
|
||||
public NotificationHelper(final MusicService service) {
|
||||
this.service = service;
|
||||
notificationManager = (NotificationManager) service
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
public void buildNotification(Song song, final boolean isPlaying) {
|
||||
notificationLayout = new RemoteViews(service.getPackageName(),
|
||||
R.layout.notification_playing);
|
||||
notificationLayoutExpanded = new RemoteViews(service.getPackageName(),
|
||||
R.layout.notification_playing_expanded);
|
||||
|
||||
setUpCollapsedLayout(song);
|
||||
setUpExpandedLayout(song);
|
||||
setUpPlaybackActions(isPlaying);
|
||||
setUpExpandedPlaybackActions(isPlaying);
|
||||
|
||||
notification = new NotificationCompat.Builder(service)
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setContentIntent(getOpenMusicControllerPendingIntent())
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContent(notificationLayout)
|
||||
.build();
|
||||
notification.bigContentView = notificationLayoutExpanded;
|
||||
|
||||
service.startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
public void killNotification() {
|
||||
service.stopForeground(true);
|
||||
notification = null;
|
||||
}
|
||||
|
||||
public void updatePlayState(final boolean isPlaying) {
|
||||
if (notification == null || notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
if (notificationLayout != null) {
|
||||
notificationLayout.setImageViewResource(R.id.button_toggle_playpause,
|
||||
isPlaying ? R.drawable.ic_pause_white_48dp : R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
if (notificationLayoutExpanded != null) {
|
||||
notificationLayoutExpanded.setImageViewResource(R.id.button_toggle_playpause,
|
||||
isPlaying ? R.drawable.ic_pause_white_48dp : R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
private PendingIntent getOpenMusicControllerPendingIntent() {
|
||||
Intent result = new Intent(service, MusicControllerActivity.class);
|
||||
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(service);
|
||||
taskStackBuilder.addParentStack(MusicControllerActivity.class);
|
||||
taskStackBuilder.addNextIntent(result);
|
||||
return taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private void setUpExpandedPlaybackActions(boolean isPlaying) {
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.button_toggle_playpause,
|
||||
retrievePlaybackActions(1));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.button_next,
|
||||
retrievePlaybackActions(2));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.button_prev,
|
||||
retrievePlaybackActions(3));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.button_quit,
|
||||
retrievePlaybackActions(4));
|
||||
|
||||
notificationLayoutExpanded.setImageViewResource(R.id.button_toggle_playpause,
|
||||
isPlaying ? R.drawable.ic_pause_white_48dp : R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
|
||||
private void setUpPlaybackActions(boolean isPlaying) {
|
||||
notificationLayout.setOnClickPendingIntent(R.id.button_toggle_playpause,
|
||||
retrievePlaybackActions(1));
|
||||
|
||||
notificationLayout.setOnClickPendingIntent(R.id.button_next,
|
||||
retrievePlaybackActions(2));
|
||||
|
||||
notificationLayout.setOnClickPendingIntent(R.id.button_quit,
|
||||
retrievePlaybackActions(4));
|
||||
|
||||
notificationLayout.setImageViewResource(R.id.button_toggle_playpause,
|
||||
isPlaying ? R.drawable.ic_pause_white_48dp : R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
|
||||
private PendingIntent retrievePlaybackActions(final int which) {
|
||||
Intent action;
|
||||
PendingIntent pendingIntent;
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
switch (which) {
|
||||
case 1:
|
||||
action = new Intent(MusicService.ACTION_TOGGLE_PLAYBACK);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 1, action, 0);
|
||||
return pendingIntent;
|
||||
case 2:
|
||||
action = new Intent(MusicService.ACTION_SKIP);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 2, action, 0);
|
||||
return pendingIntent;
|
||||
case 3:
|
||||
action = new Intent(MusicService.ACTION_REWIND);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 3, action, 0);
|
||||
return pendingIntent;
|
||||
case 4:
|
||||
action = new Intent(MusicService.ACTION_QUIT);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 4, action, 0);
|
||||
return pendingIntent;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setUpCollapsedLayout(Song song) {
|
||||
loadAlbumArt(notificationLayout, MusicUtil.getAlbumArtUri(song.albumId).toString());
|
||||
notificationLayout.setTextViewText(R.id.song_title, song.title);
|
||||
notificationLayout.setTextViewText(R.id.song_artist, song.title);
|
||||
}
|
||||
|
||||
private void setUpExpandedLayout(Song song) {
|
||||
loadAlbumArt(notificationLayoutExpanded, MusicUtil.getAlbumArtUri(song.albumId).toString());
|
||||
notificationLayoutExpanded.setTextViewText(R.id.song_title, song.title);
|
||||
notificationLayoutExpanded.setTextViewText(R.id.song_artist, song.artistName);
|
||||
notificationLayoutExpanded.setTextViewText(R.id.album_title, song.albumName);
|
||||
}
|
||||
|
||||
private static void loadAlbumArt(RemoteViews notificationView, String albumArtUri) {
|
||||
Bitmap albumArtBitmap = ImageLoader.getInstance().loadImageSync(albumArtUri);
|
||||
if (albumArtBitmap == null) {
|
||||
notificationView.setImageViewResource(R.id.album_art, R.drawable.default_album_art);
|
||||
} else {
|
||||
notificationView.setImageViewBitmap(R.id.album_art, albumArtBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.kabouzeid.materialmusic.helper;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.PlayListAdapter;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.01.15.
|
||||
*/
|
||||
public class PlayingQueueDialogHelper {
|
||||
public static MaterialDialog getDialog(Context context) {
|
||||
final App app = (App) context.getApplicationContext();
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(context)
|
||||
.title(context.getResources().getString(R.string.label_current_playing_queue))
|
||||
.customView(R.layout.dialog_playlist, false)
|
||||
.positiveText(context.getResources().getString(R.string.close))
|
||||
.negativeText(context.getResources().getString(R.string.save_as_playlist))
|
||||
.callback(new MaterialDialog.ButtonCallback() {
|
||||
@Override
|
||||
public void onPositive(MaterialDialog dialog) {
|
||||
super.onPositive(dialog);
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegative(MaterialDialog dialog) {
|
||||
super.onNegative(dialog);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
final DragSortListView dragSortListView = (DragSortListView) dialog.getCustomView().findViewById(R.id.dragSortListView);
|
||||
final PlayListAdapter playListAdapter = new PlayListAdapter(context, app.getMusicPlayerRemote().getPlayingQueue());
|
||||
dragSortListView.setAdapter(playListAdapter);
|
||||
dragSortListView.setDropListener(new DragSortListView.DropListener() {
|
||||
@Override
|
||||
public void drop(int from, int to) {
|
||||
Song songToMove = app.getMusicPlayerRemote().getPlayingQueue().get(from);
|
||||
app.getMusicPlayerRemote().getPlayingQueue().remove(from);
|
||||
app.getMusicPlayerRemote().getPlayingQueue().add(to, songToMove);
|
||||
playListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.kabouzeid.materialmusic.helper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.01.15.
|
||||
*/
|
||||
public class Shuffler {
|
||||
private static int MAX_HISTORY_SIZE = 250;
|
||||
private List<Integer> order = new ArrayList<>();
|
||||
private int position;
|
||||
private int interval;
|
||||
|
||||
public Shuffler(final int interval) {
|
||||
order = getShuffledOrderList(interval);
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public int nextInt(boolean infinite) {
|
||||
position = position + 1;
|
||||
if (position > order.size() - 1) {
|
||||
if (infinite) {
|
||||
order.addAll(getShuffledOrderList(interval));
|
||||
if (order.size() > Math.max(interval, MAX_HISTORY_SIZE)) {
|
||||
order = order.subList(order.size() / 2 - 1, order.size() - 1);
|
||||
}
|
||||
} else {
|
||||
return order.get(order.size() - 1);
|
||||
}
|
||||
}
|
||||
return order.get(position);
|
||||
}
|
||||
|
||||
public int previousInt() {
|
||||
position = position - 1;
|
||||
if (position < 0) {
|
||||
position = 0;
|
||||
}
|
||||
return order.get(position);
|
||||
}
|
||||
|
||||
private List<Integer> getShuffledOrderList(int interval) {
|
||||
final List<Integer> newList = new ArrayList<>();
|
||||
for (int i = 0; i < interval; i++) {
|
||||
newList.add(i);
|
||||
}
|
||||
Collections.shuffle(newList);
|
||||
return newList;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.kabouzeid.materialmusic.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.AudioHeader;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by karim on 19.01.15.
|
||||
*/
|
||||
public class SongDetailDialogHelper {
|
||||
public static final String TAG = SongDetailDialogHelper.class.getSimpleName();
|
||||
|
||||
public static MaterialDialog getDialog(final Context context, final File songFile) {
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(context)
|
||||
.customView(R.layout.dialog_file_details, true)
|
||||
.title(context.getResources().getString(R.string.label_details))
|
||||
.positiveText(context.getResources().getString(R.string.ok))
|
||||
.callback(new MaterialDialog.ButtonCallback() {
|
||||
@Override
|
||||
public void onPositive(MaterialDialog dialog) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
View dialogView = dialog.getCustomView();
|
||||
final TextView fileName = (TextView) dialogView.findViewById(R.id.file_name);
|
||||
final TextView filePath = (TextView) dialogView.findViewById(R.id.file_path);
|
||||
final TextView fileSize = (TextView) dialogView.findViewById(R.id.file_size);
|
||||
final TextView fileFormat = (TextView) dialogView.findViewById(R.id.file_format);
|
||||
final TextView trackLength = (TextView) dialogView.findViewById(R.id.track_length);
|
||||
final TextView bitRate = (TextView) dialogView.findViewById(R.id.bitrate);
|
||||
final TextView samplingRate = (TextView) dialogView.findViewById(R.id.sampling_rate);
|
||||
|
||||
fileName.setText(makeTextWithTitle(context, R.string.label_file_name, "-"));
|
||||
filePath.setText(makeTextWithTitle(context, R.string.label_file_path, "-"));
|
||||
fileSize.setText(makeTextWithTitle(context, R.string.label_file_size, "-"));
|
||||
fileFormat.setText(makeTextWithTitle(context, R.string.label_file_format, "-"));
|
||||
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, "-"));
|
||||
bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate, "-"));
|
||||
samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate, "-"));
|
||||
|
||||
try {
|
||||
if (songFile != null && songFile.exists()) {
|
||||
AudioFile audioFile = AudioFileIO.read(songFile);
|
||||
AudioHeader audioHeader = audioFile.getAudioHeader();
|
||||
|
||||
fileName.setText(makeTextWithTitle(context, R.string.label_file_name, songFile.getName()));
|
||||
filePath.setText(makeTextWithTitle(context, R.string.label_file_path, songFile.getAbsolutePath()));
|
||||
fileSize.setText(makeTextWithTitle(context, R.string.label_file_size, Util.getFileSizeString(songFile.length())));
|
||||
fileFormat.setText(makeTextWithTitle(context, R.string.label_file_format, audioHeader.getFormat()));
|
||||
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(audioHeader.getTrackLength() * 1000)));
|
||||
bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate, audioHeader.getBitRate() + " kb/s"));
|
||||
samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate, audioHeader.getSampleRate() + " Hz"));
|
||||
}
|
||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
Log.e(TAG, "error while reading the song file", e);
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private static Spanned makeTextWithTitle(Context context, int titleResId, String text) {
|
||||
return Html.fromHtml("<b>" + context.getResources().getString(titleResId) + ": " + "</b>" + text);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.kabouzeid.materialmusic.interfaces;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public interface KabSearchAbleFragment {
|
||||
public void search(String query);
|
||||
|
||||
public void returnToNonSearch();
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.kabouzeid.materialmusic.interfaces;
|
||||
|
||||
/**
|
||||
* Created by karim on 23.12.14.
|
||||
*/
|
||||
public interface KabViewsDisableAble {
|
||||
public void enableViews();
|
||||
|
||||
public void disableViews();
|
||||
|
||||
public boolean areViewsEnabled();
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.kabouzeid.materialmusic.interfaces;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
|
||||
/**
|
||||
* Created by karim on 19.12.14.
|
||||
*/
|
||||
public interface OnMusicRemoteEventListener {
|
||||
public void onMusicRemoteEvent(MusicRemoteEvent event);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.kabouzeid.materialmusic.lastfm;
|
||||
|
||||
/**
|
||||
* Created by karim on 15.01.15.
|
||||
*/
|
||||
public class LastFMUtil {
|
||||
public static String BASE_URL = "ws.audioscrobbler.com";
|
||||
public static String API_KEY = "bd9c6ea4d55ec9ed3af7d276e5ece304";
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.album;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.provider.AlbumJSONStore;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 01.01.15.
|
||||
*/
|
||||
public class LastFMAlbumImageLoader {
|
||||
public static final String TAG = LastFMAlbumImageLoader.class.getSimpleName();
|
||||
|
||||
public static void loadAlbumImage(Context context, String queryAlbum, String queryArtist, AlbumImageLoaderCallback callback) {
|
||||
if (queryAlbum != null) {
|
||||
String albumJSON = AlbumJSONStore.getInstance(context).getAlbumJSON(queryAlbum + queryArtist);
|
||||
if (albumJSON != null) {
|
||||
Log.i(TAG, queryAlbum + " by " + queryArtist + " is in cache.");
|
||||
try {
|
||||
loadAlbumImageFromJSON(new JSONObject(albumJSON), callback);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error while parsing string from cache to JSONObject", e);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, queryAlbum + " is not in cache.");
|
||||
downloadAlbumImage(context, queryAlbum, queryArtist, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAlbumImageFromJSON(JSONObject jsonObject, final AlbumImageLoaderCallback callback) {
|
||||
Log.i(TAG, "Applying album art...");
|
||||
String url = LastFMAlbumInfoUtil.getAlbumImageUrlFromJSON(jsonObject);
|
||||
if (!url.trim().equals("")) {
|
||||
DisplayImageOptions options = new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(false)
|
||||
.postProcessor(new BitmapProcessor() {
|
||||
@Override
|
||||
public Bitmap process(Bitmap bmp) {
|
||||
return Util.getAlbumArtScaledBitmap(bmp, true);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
ImageLoader.getInstance().loadImage(url, options, new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
callback.onAlbumImageLoaded(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
callback.onAlbumImageLoaded(loadedImage, imageUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
callback.onAlbumImageLoaded(null, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onAlbumImageLoaded(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadAlbumImage(final Context context, final String album, final String artist, final AlbumImageLoaderCallback callback) {
|
||||
Log.i(TAG, "Downloading details for " + album);
|
||||
App app = (App) context.getApplicationContext();
|
||||
String albumUrl = LastFMAlbumInfoUtil.getAlbumUrl(album, artist);
|
||||
JsonObjectRequest albumInfoJSONRequest = new JsonObjectRequest(0, albumUrl, null, new Response.Listener<JSONObject>() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
Log.i(TAG, "Download was successful!");
|
||||
LastFMAlbumInfoUtil.saveAlbumJSONDataToCacheAndDisk(context, album, artist, response);
|
||||
loadAlbumImageFromJSON(response, callback);
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Log.e(TAG, "Download failed!", error);
|
||||
callback.onAlbumImageLoaded(null, null);
|
||||
}
|
||||
});
|
||||
app.addToRequestQueue(albumInfoJSONRequest);
|
||||
}
|
||||
|
||||
public static interface AlbumImageLoaderCallback {
|
||||
public void onAlbumImageLoaded(Bitmap albumImage, String uri);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.album;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kabouzeid.materialmusic.lastfm.LastFMUtil;
|
||||
import com.kabouzeid.materialmusic.provider.AlbumJSONStore;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.12.14.
|
||||
*/
|
||||
public class LastFMAlbumInfoUtil {
|
||||
public static final String TAG = LastFMAlbumInfoUtil.class.getSimpleName();
|
||||
|
||||
private static String AUTO_CORRECT = "1";
|
||||
|
||||
public static String getAlbumUrl(String album, String artist) {
|
||||
if (album != null) {
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme("http")
|
||||
.authority(LastFMUtil.BASE_URL)
|
||||
.appendPath("2.0")
|
||||
.appendQueryParameter("method", "album.getinfo")
|
||||
.appendQueryParameter("album", album)
|
||||
.appendQueryParameter("artist", artist)
|
||||
//.appendQueryParameter("lang", "de")
|
||||
.appendQueryParameter("autocorrect", AUTO_CORRECT)
|
||||
.appendQueryParameter("api_key", LastFMUtil.API_KEY)
|
||||
.appendQueryParameter("format", "json");
|
||||
return builder.build().toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String getAlbumNameFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
return rootJSON.getJSONObject("album").getString("name");
|
||||
} catch (JSONException e) {
|
||||
//Log.e(TAG, "Error while getting album name from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAlbumThumbnailUrlFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
JSONArray images = getAlbumImageArrayFromJSON(rootJSON);
|
||||
if (images.length() > 2) {
|
||||
return images.getJSONObject(2).getString("#text");
|
||||
} else if (images.length() > 1) {
|
||||
return images.getJSONObject(1).getString("#text");
|
||||
}
|
||||
return images.getJSONObject(0).getString("#text");
|
||||
} catch (JSONException | NullPointerException e) {
|
||||
//Log.e(TAG, "Error while getting album thumbnail image from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAlbumImageUrlFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
JSONArray images = getAlbumImageArrayFromJSON(rootJSON);
|
||||
return images.getJSONObject(images.length() - 1).getString("#text");
|
||||
} catch (JSONException | NullPointerException e) {
|
||||
//Log.e(TAG, "Error while getting album image from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONArray getAlbumImageArrayFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
return rootJSON.getJSONObject("album").getJSONArray("image");
|
||||
} catch (JSONException e) {
|
||||
//Log.e(TAG, "Error while getting album image array from JSON parameter!", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveAlbumJSONDataToCacheAndDisk(Context context, String album, String artist, JSONObject jsonObject) {
|
||||
Log.i(TAG, "Saving new JSON album data for " + album + "...");
|
||||
AlbumJSONStore.getInstance(context).addAlbumJSON(album + artist, jsonObject.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.artist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.provider.ArtistJSONStore;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 01.01.15.
|
||||
*/
|
||||
public class LastFMArtistBiographyLoader {
|
||||
public static final String TAG = LastFMArtistBiographyLoader.class.getSimpleName();
|
||||
|
||||
public static void loadArtistBio(Context context, String queryArtist, ArtistBioLoaderCallback callback) {
|
||||
if (queryArtist != null) {
|
||||
String artistJSON = ArtistJSONStore.getInstance(context).getArtistJSON(queryArtist);
|
||||
if (artistJSON != null) {
|
||||
Log.i(TAG, queryArtist + " is in cache.");
|
||||
try {
|
||||
JSONObject json = new JSONObject(artistJSON);
|
||||
String bio = LastFMArtistInfoUtil.getArtistBiographyFromJSON(json);
|
||||
callback.onArtistBioLoaded(bio);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error while parsing bio from cache to JSONObject", e);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, queryArtist + " is not in cache.");
|
||||
downloadArtistBio(context, queryArtist, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadArtistBio(final Context context, final String artist, final ArtistBioLoaderCallback callback) {
|
||||
Log.i(TAG, "Downloading details for " + artist);
|
||||
App app = (App) context.getApplicationContext();
|
||||
String artistUrl = LastFMArtistInfoUtil.getArtistUrl(artist);
|
||||
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, null, new Response.Listener<JSONObject>() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
Log.i(TAG, "Download was successful!");
|
||||
LastFMArtistInfoUtil.saveArtistJSONDataToCacheAndDisk(context, artist, response);
|
||||
String bio = LastFMArtistInfoUtil.getArtistBiographyFromJSON(response);
|
||||
callback.onArtistBioLoaded(bio);
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Log.e(TAG, "Download failed!", error);
|
||||
callback.onArtistBioLoaded("");
|
||||
}
|
||||
});
|
||||
app.addToRequestQueue(artistInfoJSONRequest);
|
||||
}
|
||||
|
||||
public static interface ArtistBioLoaderCallback {
|
||||
public void onArtistBioLoaded(String bio);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.artist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.provider.ArtistJSONStore;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 01.01.15.
|
||||
*/
|
||||
public class LastFMArtistImageLoader {
|
||||
public static final String TAG = LastFMArtistImageLoader.class.getSimpleName();
|
||||
|
||||
public static void loadArtistImage(Context context, String queryArtist, ArtistImageLoaderCallback callback) {
|
||||
if (queryArtist != null) {
|
||||
String artistJSON = ArtistJSONStore.getInstance(context).getArtistJSON(queryArtist);
|
||||
if (artistJSON != null) {
|
||||
Log.i(TAG, queryArtist + " is in cache.");
|
||||
try {
|
||||
loadArtistImageFromJSON(new JSONObject(artistJSON), callback);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error while parsing string from cache to JSONObject", e);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, queryArtist + " is not in cache.");
|
||||
downloadArtistImage(context, queryArtist, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadArtistImageFromJSON(JSONObject jsonObject, final ArtistImageLoaderCallback callback) {
|
||||
Log.i(TAG, "Applying artist art...");
|
||||
String url = LastFMArtistInfoUtil.getArtistImageUrlFromJSON(jsonObject);
|
||||
if (!url.trim().equals("")) {
|
||||
ImageLoader.getInstance().loadImage(url, ImageLoaderUtil.getCacheOnDiskOptions(), new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
callback.onArtistImageLoaded(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
callback.onArtistImageLoaded(loadedImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
callback.onArtistImageLoaded(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onArtistImageLoaded(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadArtistImage(final Context context, final String artist, final ArtistImageLoaderCallback callback) {
|
||||
Log.i(TAG, "Downloading details for " + artist);
|
||||
App app = (App) context.getApplicationContext();
|
||||
String artistUrl = LastFMArtistInfoUtil.getArtistUrl(artist);
|
||||
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, null, new Response.Listener<JSONObject>() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
Log.i(TAG, "Download was successful!");
|
||||
LastFMArtistInfoUtil.saveArtistJSONDataToCacheAndDisk(context, artist, response);
|
||||
loadArtistImageFromJSON(response, callback);
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Log.e(TAG, "Download failed!", error);
|
||||
callback.onArtistImageLoaded(null);
|
||||
}
|
||||
});
|
||||
app.addToRequestQueue(artistInfoJSONRequest);
|
||||
}
|
||||
|
||||
public static interface ArtistImageLoaderCallback {
|
||||
public void onArtistImageLoaded(Bitmap artistImage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.artist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kabouzeid.materialmusic.lastfm.LastFMUtil;
|
||||
import com.kabouzeid.materialmusic.provider.ArtistJSONStore;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 24.12.14.
|
||||
*/
|
||||
public class LastFMArtistInfoUtil {
|
||||
public static final String TAG = LastFMArtistInfoUtil.class.getSimpleName();
|
||||
|
||||
private static String AUTO_CORRECT = "1";
|
||||
|
||||
public static String getArtistUrl(String artist) {
|
||||
if (artist != null) {
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme("http")
|
||||
.authority(LastFMUtil.BASE_URL)
|
||||
.appendPath("2.0")
|
||||
.appendQueryParameter("method", "artist.getinfo")
|
||||
.appendQueryParameter("artist", artist)
|
||||
//.appendQueryParameter("lang", "de")
|
||||
.appendQueryParameter("autocorrect", AUTO_CORRECT)
|
||||
.appendQueryParameter("api_key", LastFMUtil.API_KEY)
|
||||
.appendQueryParameter("format", "json");
|
||||
return builder.build().toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String getArtistNameFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
return rootJSON.getJSONObject("artist").getString("name");
|
||||
} catch (JSONException e) {
|
||||
//Log.e(TAG, "Error while getting artist name from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getArtistThumbnailUrlFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
JSONArray images = getArtistImageArrayFromJSON(rootJSON);
|
||||
if (images.length() > 2) {
|
||||
return images.getJSONObject(2).getString("#text");
|
||||
} else if (images.length() > 1) {
|
||||
return images.getJSONObject(1).getString("#text");
|
||||
}
|
||||
return images.getJSONObject(0).getString("#text");
|
||||
} catch (JSONException | NullPointerException e) {
|
||||
//Log.e(TAG, "Error while getting artist thumbnail image from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getArtistImageUrlFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
JSONArray images = getArtistImageArrayFromJSON(rootJSON);
|
||||
return images.getJSONObject(images.length() - 1).getString("#text");
|
||||
} catch (JSONException | NullPointerException e) {
|
||||
//Log.e(TAG, "Error while getting artist image from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONArray getArtistImageArrayFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
return rootJSON.getJSONObject("artist").getJSONArray("image");
|
||||
} catch (JSONException e) {
|
||||
//Log.e(TAG, "Error while getting artist image array from JSON parameter!", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getArtistBiographyFromJSON(JSONObject rootJSON) {
|
||||
try {
|
||||
return rootJSON.getJSONObject("artist").getJSONObject("bio").getString("content");
|
||||
} catch (JSONException e) {
|
||||
//Log.e(TAG, "Error while getting artist biografie from JSON parameter!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveArtistJSONDataToCacheAndDisk(Context context, String artist, JSONObject jsonObject) {
|
||||
Log.i(TAG, "Saving new JSON artist data for " + artist + "...");
|
||||
ArtistJSONStore.getInstance(context).addArtistJSON(artist, jsonObject.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.kabouzeid.materialmusic.lastfm.artist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.JsonObjectRequest;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.provider.ArtistJSONStore;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Created by karim on 01.01.15.
|
||||
*/
|
||||
public class LastFMArtistThumbnailLoader {
|
||||
public static final String TAG = LastFMArtistThumbnailLoader.class.getSimpleName();
|
||||
|
||||
public static void loadArtistThumbnail(Context context, String queryArtist, ArtistThumbnailLoaderCallback callback) {
|
||||
if (queryArtist != null) {
|
||||
String artistJSON = ArtistJSONStore.getInstance(context).getArtistJSON(queryArtist);
|
||||
if (artistJSON != null) {
|
||||
Log.i(TAG, queryArtist + " is in cache.");
|
||||
try {
|
||||
loadArtistThumbnailFromJSON(new JSONObject(artistJSON), callback);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error while parsing string from cache to JSONObject", e);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, queryArtist + " is not in cache.");
|
||||
downloadArtistThumbnail(context, queryArtist, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadArtistThumbnailFromJSON(JSONObject jsonObject, final ArtistThumbnailLoaderCallback callback) {
|
||||
Log.i(TAG, "Applying artist thumbnail...");
|
||||
String url = LastFMArtistInfoUtil.getArtistThumbnailUrlFromJSON(jsonObject);
|
||||
if (!url.trim().equals("")) {
|
||||
ImageLoader.getInstance().loadImage(url, ImageLoaderUtil.getCacheOnDiskOptions(), new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
callback.onArtistThumbnailLoaded(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
callback.onArtistThumbnailLoaded(loadedImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
callback.onArtistThumbnailLoaded(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onArtistThumbnailLoaded(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadArtistThumbnail(final Context context, final String artist, final ArtistThumbnailLoaderCallback callback) {
|
||||
Log.i(TAG, "Downloading details for " + artist);
|
||||
App app = (App) context.getApplicationContext();
|
||||
String artistUrl = LastFMArtistInfoUtil.getArtistUrl(artist);
|
||||
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, null, new Response.Listener<JSONObject>() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
Log.i(TAG, "Download was successful!");
|
||||
LastFMArtistInfoUtil.saveArtistJSONDataToCacheAndDisk(context, artist, response);
|
||||
loadArtistThumbnailFromJSON(response, callback);
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Log.e(TAG, "Download failed!", error);
|
||||
callback.onArtistThumbnailLoaded(null);
|
||||
}
|
||||
});
|
||||
app.addToRequestQueue(artistInfoJSONRequest);
|
||||
}
|
||||
|
||||
public static interface ArtistThumbnailLoaderCallback {
|
||||
public void onArtistThumbnailLoaded(Bitmap thumbnail);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class AlbumLoader {
|
||||
|
||||
public static List<Album> getAllAlbums(Context context) {
|
||||
Cursor cursor = makeAlbumCursor(context);
|
||||
List<Album> albums = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String albumName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
albums.add(album);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
|
||||
public static Album getAlbum(Context context, int albumId) {
|
||||
Cursor cursor = makeAlbumCursor(context);
|
||||
Album album = new Album();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
if (id == albumId) {
|
||||
final String albumName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return album;
|
||||
}
|
||||
|
||||
public static List<Album> getAlbums(Context context, String query) {
|
||||
Cursor cursor = makeAlbumCursor(context);
|
||||
List<Album> albums = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String albumName = cursor.getString(1);
|
||||
if (albumName.trim().toLowerCase().contains(query.trim().toLowerCase())) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
albums.add(album);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
|
||||
private static Cursor makeAlbumCursor(final Context context) {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AlbumColumns.ALBUM,
|
||||
/* 2 */
|
||||
MediaStore.Audio.AlbumColumns.ARTIST,
|
||||
/* 3 */
|
||||
MediaStore.Audio.Media.ARTIST_ID,
|
||||
/* 4 */
|
||||
MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
|
||||
/* 5 */
|
||||
MediaStore.Audio.AlbumColumns.FIRST_YEAR
|
||||
}, null, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class AlbumSongLoader {
|
||||
|
||||
public static List<Song> getAlbumSongList(final Context context, final int albumId) {
|
||||
Cursor cursor = makeAlbumSongCursor(context, albumId);
|
||||
List<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
|
||||
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
songs.add(song);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static final Cursor makeAlbumSongCursor(final Context context, final int albumId) {
|
||||
final StringBuilder selection = new StringBuilder();
|
||||
selection.append(MediaStore.Audio.AudioColumns.IS_MUSIC + "=1");
|
||||
selection.append(" AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''");
|
||||
selection.append(" AND " + MediaStore.Audio.AudioColumns.ALBUM_ID + "=" + albumId);
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AudioColumns.TITLE,
|
||||
/* 2 */
|
||||
MediaStore.Audio.AudioColumns.ARTIST,
|
||||
/* 3 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM,
|
||||
/* 4 */
|
||||
MediaStore.Audio.AudioColumns.DURATION,
|
||||
/* 5 */
|
||||
MediaStore.Audio.AudioColumns.TRACK,
|
||||
/* 6 */
|
||||
MediaStore.Audio.AudioColumns.ARTIST_ID
|
||||
}, selection.toString(), null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 04.01.15.
|
||||
*/
|
||||
public class ArtistAlbumLoader {
|
||||
public static List<Album> getArtistAlbumList(final Context context, final int artistId) {
|
||||
Cursor cursor = makeArtistAlbumCursor(context, artistId);
|
||||
List<Album> albums = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String albumName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
final int year = cursor.getInt(4);
|
||||
|
||||
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
albums.add(album);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
|
||||
public static Cursor makeArtistAlbumCursor(final Context context, final int artistId) {
|
||||
return context.getContentResolver().query(
|
||||
MediaStore.Audio.Artists.Albums.getContentUri("external", artistId), new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AlbumColumns.ALBUM,
|
||||
/* 2 */
|
||||
MediaStore.Audio.AlbumColumns.ARTIST,
|
||||
/* 3 */
|
||||
MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
|
||||
/* 4 */
|
||||
MediaStore.Audio.AlbumColumns.FIRST_YEAR
|
||||
}, null, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Artist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class ArtistLoader {
|
||||
|
||||
public static List<Artist> getAllArtists(Context context) {
|
||||
Cursor cursor = makeArtistCursor(context);
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String artistName = cursor.getString(1);
|
||||
final int albumCount = cursor.getInt(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
|
||||
final Artist artist = new Artist(id, artistName, albumCount, songCount);
|
||||
artists.add(artist);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
|
||||
public static Artist getArtist(Context context, int artistId) {
|
||||
Cursor cursor = makeArtistCursor(context);
|
||||
Artist artist = new Artist();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
if (id == artistId) {
|
||||
final String artistName = cursor.getString(1);
|
||||
final int albumCount = cursor.getInt(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
|
||||
artist = new Artist(id, artistName, albumCount, songCount);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return artist;
|
||||
}
|
||||
|
||||
public static List<Artist> getArtists(Context context, String query) {
|
||||
Cursor cursor = makeArtistCursor(context);
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String artistName = cursor.getString(1);
|
||||
if (artistName.trim().toLowerCase().contains(query.trim().toLowerCase())) {
|
||||
final int id = cursor.getInt(0);
|
||||
final int albumCount = cursor.getInt(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
|
||||
final Artist artist = new Artist(id, artistName, albumCount, songCount);
|
||||
artists.add(artist);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
|
||||
public static final Cursor makeArtistCursor(final Context context) {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.ArtistColumns.ARTIST,
|
||||
/* 2 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_ALBUMS,
|
||||
/* 3 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_TRACKS
|
||||
}, null, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 01.01.15.
|
||||
*/
|
||||
public class ArtistSongLoader {
|
||||
public static List<Song> getArtistSongList(final Context context, final int artistId) {
|
||||
Cursor cursor = makeArtistSongCursor(context, artistId);
|
||||
List<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int albumId = cursor.getInt(6);
|
||||
|
||||
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
songs.add(song);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Cursor makeArtistSongCursor(final Context context, final int artistId) {
|
||||
final StringBuilder selection = new StringBuilder();
|
||||
selection.append(MediaStore.Audio.AudioColumns.IS_MUSIC + "=1");
|
||||
selection.append(" AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''");
|
||||
selection.append(" AND " + MediaStore.Audio.AudioColumns.ARTIST_ID + "=" + artistId);
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AudioColumns.TITLE,
|
||||
/* 2 */
|
||||
MediaStore.Audio.AudioColumns.ARTIST,
|
||||
/* 3 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM,
|
||||
/* 4 */
|
||||
MediaStore.Audio.AudioColumns.DURATION,
|
||||
/* 5 */
|
||||
MediaStore.Audio.AudioColumns.TRACK,
|
||||
/* 6 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID
|
||||
}, selection.toString(), null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 11.01.15.
|
||||
*/
|
||||
public class SongFileLoader {
|
||||
public static final String TAG = SongFileLoader.class.getSimpleName();
|
||||
|
||||
public static List<String> getSongFiles(Context context, List<Integer> queryIds) {
|
||||
Cursor cursor = makeSongFileCursor(context);
|
||||
List<String> songFiles = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
if (queryIds.contains(id)) {
|
||||
songFiles.add(cursor.getString(1));
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return songFiles;
|
||||
}
|
||||
|
||||
public static String getSongFile(Context context, int queryId) {
|
||||
Cursor cursor = makeSongFileCursor(context);
|
||||
String filePath = "";
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
if (id == queryId) {
|
||||
filePath = cursor.getString(1);
|
||||
break;
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public static final Cursor makeSongFileCursor(final Context context) {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AudioColumns.DATA,
|
||||
}, null, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package com.kabouzeid.materialmusic.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class SongLoader {
|
||||
public static List<Song> getAllSongs(Context context) {
|
||||
Cursor cursor = makeAlbumSongCursor(context);
|
||||
List<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
|
||||
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
songs.add(song);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static List<Song> getSongs(Context context, String query) {
|
||||
Cursor cursor = makeAlbumSongCursor(context);
|
||||
List<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String songName = cursor.getString(1);
|
||||
if (songName.trim().toLowerCase().contains(query.trim().toLowerCase())) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
|
||||
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
songs.add(song);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Song getSong(Context context, int queryId) {
|
||||
Cursor cursor = makeAlbumSongCursor(context);
|
||||
Song song = null;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
if (id == queryId) {
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
break;
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return song;
|
||||
}
|
||||
|
||||
public static final Cursor makeAlbumSongCursor(final Context context) {
|
||||
final StringBuilder selection = new StringBuilder();
|
||||
selection.append(MediaStore.Audio.AudioColumns.IS_MUSIC + "=1");
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.AudioColumns.TITLE,
|
||||
/* 2 */
|
||||
MediaStore.Audio.AudioColumns.ARTIST,
|
||||
/* 3 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM,
|
||||
/* 4 */
|
||||
MediaStore.Audio.AudioColumns.DURATION,
|
||||
/* 5 */
|
||||
MediaStore.Audio.AudioColumns.TRACK,
|
||||
/* 6 */
|
||||
MediaStore.Audio.AudioColumns.ARTIST_ID,
|
||||
/* 7 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID
|
||||
}, selection.toString(), null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.kabouzeid.materialmusic.misc;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.12.14.
|
||||
*/
|
||||
public final class AppKeys {
|
||||
public static final String SP_THEME = "com.kabouzeid.materialmusic.THEME";
|
||||
public static final String SP_NAVIGATION_DRAWER_ITEM_POSITION = "com.kabouzeid.materialmusic.NAVIGATION_DRAWER_ITEM_POSITION";
|
||||
public static final String SP_USER_LEARNED_DRAWER = "com.kabouzeid.materialmusic.NAVIGATION_DRAWER_LEARNED";
|
||||
public static final String SP_ONLY_ON_WIFI = "com.kabouzeid.materialmusic.ONLY_ON_WIFI";
|
||||
public static final String SP_SHUFFLE_MODE = "com.kabouzeid.materialmusic.SHUFFLE_MODE";
|
||||
public static final String SP_REPEAT_MODE = "com.kabouzeid.materialmusic.REPEAT_MODE";
|
||||
|
||||
public static final String IS_PLAYING_QUEUE = "com.kabouzeid.materialmusic.PLAYING_QUEUE";
|
||||
public static final String IS_POSITION_IN_QUEUE = "com.kabouzeid.materialmusic.POSITION_IN_QUEUE";
|
||||
public static final String IS_ARTIST_JSON_INFO_CACHE = "com.kabouzeid.materialmusic.ARTIST_JSON_INFO_CACHE";
|
||||
|
||||
public static final String E_ALBUM = "com.kabouzeid.materialmusic.ALBUM";
|
||||
public static final String E_ARTIST = "com.kabouzeid.materialmusic.ARTIST";
|
||||
public static final String E_SONG = "com.kabouzeid.materialmusic.SONG";
|
||||
public static final String E_TAG_EDIT_MODE = "com.kabouzeid.materialmusic.TAG_EDIT_MODE";
|
||||
public static final String E_ID = "com.kabouzeid.materialmusic.ID";
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.kabouzeid.materialmusic.misc;
|
||||
|
||||
/**
|
||||
* Created by karim on 20.12.14.
|
||||
*/
|
||||
public class SmallAnimatorListener implements com.nineoldandroids.animation.Animator.AnimatorListener {
|
||||
@Override
|
||||
public void onAnimationStart(com.nineoldandroids.animation.Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(com.nineoldandroids.animation.Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(com.nineoldandroids.animation.Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(com.nineoldandroids.animation.Animator animation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.kabouzeid.materialmusic.misc;
|
||||
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;
|
||||
import com.github.ksoichiro.android.observablescrollview.ScrollState;
|
||||
|
||||
/**
|
||||
* Created by karim on 20.12.14.
|
||||
*/
|
||||
public class SmallObservableScrollViewCallbacks implements ObservableScrollViewCallbacks {
|
||||
@Override
|
||||
public void onScrollChanged(int i, boolean b, boolean b2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownMotionEvent() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.kabouzeid.materialmusic.misc;
|
||||
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* Created by karim on 20.12.14.
|
||||
*/
|
||||
public class SmallOnGestureListener implements GestureDetector.OnGestureListener {
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.kabouzeid.materialmusic.misc;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.transition.Transition;
|
||||
|
||||
/**
|
||||
* Created by karim on 20.12.14.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public class SmallTransitionListener implements Transition.TransitionListener {
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionCancel(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionPause(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionResume(Transition transition) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.kabouzeid.materialmusic.model;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.11.14.
|
||||
*/
|
||||
public class Album {
|
||||
|
||||
public int id;
|
||||
public int artistId;
|
||||
public String title;
|
||||
public String artistName;
|
||||
public int songCount;
|
||||
public int year;
|
||||
|
||||
public Album(final int id, final String title, final String artistName, final int artistId,
|
||||
final int songNumber, final int albumYear) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artistName = artistName;
|
||||
this.artistId = artistId;
|
||||
songCount = songNumber;
|
||||
year = albumYear;
|
||||
}
|
||||
|
||||
public Album() {
|
||||
this.id = -1;
|
||||
this.title = "";
|
||||
this.artistName = "";
|
||||
songCount = -1;
|
||||
year = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.kabouzeid.materialmusic.model;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class Artist {
|
||||
public int id;
|
||||
public String name;
|
||||
public int albumCount;
|
||||
public int songCount;
|
||||
|
||||
public Artist(final int id, final String name, final int songCount,
|
||||
final int albumCount) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.songCount = songCount;
|
||||
this.albumCount = albumCount;
|
||||
}
|
||||
|
||||
public Artist() {
|
||||
id = -1;
|
||||
name = "";
|
||||
songCount = -1;
|
||||
albumCount = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.kabouzeid.materialmusic.model;
|
||||
|
||||
/**
|
||||
* Created by karim on 19.12.14.
|
||||
*/
|
||||
public class MusicRemoteEvent {
|
||||
public static final int PLAY = 0;
|
||||
public static final int PAUSE = 1;
|
||||
public static final int RESUME = 2;
|
||||
public static final int STOP = 3;
|
||||
public static final int NEXT = 4;
|
||||
public static final int PREV = 5;
|
||||
|
||||
public static final int SONG_COMPLETED = 6;
|
||||
public static final int QUEUE_COMPLETED = 7;
|
||||
|
||||
public static final int SERVICE_CONNECTED = 8;
|
||||
public static final int SERVICE_DISCONNECTED = 9;
|
||||
|
||||
public static final int STATE_SAVED = 10;
|
||||
public static final int STATE_RESTORED = 11;
|
||||
|
||||
public static final int SHUFFLE_MODE_CHANGED = 12;
|
||||
public static final int REPEAT_MODE_CHANGED = 13;
|
||||
|
||||
private int action;
|
||||
|
||||
public MusicRemoteEvent(int action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.kabouzeid.materialmusic.model;
|
||||
|
||||
/**
|
||||
* Created by karim on 23.11.14.
|
||||
*/
|
||||
public class NavigationDrawerItem {
|
||||
public String title;
|
||||
public int imageRes;
|
||||
|
||||
public NavigationDrawerItem(String title, int imageRes) {
|
||||
this.title = title;
|
||||
this.imageRes = imageRes;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.kabouzeid.materialmusic.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by karim on 23.11.14.
|
||||
*/
|
||||
public class Song implements Serializable {
|
||||
public int id;
|
||||
public int albumId;
|
||||
public int artistId;
|
||||
public String title;
|
||||
public String artistName;
|
||||
public String albumName;
|
||||
public long duration;
|
||||
public int trackNumber;
|
||||
|
||||
public Song(final int id, final int albumId, final int artistId, final String title, final String artistName,
|
||||
final String albumName, final long duration, final int trackNumber) {
|
||||
this.id = id;
|
||||
this.albumId = albumId;
|
||||
this.artistId = artistId;
|
||||
this.title = title;
|
||||
this.artistName = artistName;
|
||||
this.albumName = albumName;
|
||||
this.duration = duration;
|
||||
this.trackNumber = trackNumber;
|
||||
}
|
||||
|
||||
public Song() {
|
||||
this.id = -1;
|
||||
this.albumId = -1;
|
||||
this.artistId = -1;
|
||||
this.title = "";
|
||||
this.artistName = "";
|
||||
this.albumName = "";
|
||||
this.duration = -1;
|
||||
this.trackNumber = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.kabouzeid.materialmusic.provider;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
public class AlbumJSONStore extends SQLiteOpenHelper {
|
||||
|
||||
private static final int VERSION = 1;
|
||||
public static final String DATABASE_NAME = "albumJSONLastFM.db";
|
||||
private static AlbumJSONStore sInstance = null;
|
||||
|
||||
public AlbumJSONStore(final Context context) {
|
||||
super(context, DATABASE_NAME, null, VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(final SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS " + AlbumJSONColumns.NAME +
|
||||
" (" + AlbumJSONColumns.ALBUMANDARTIST_NAME + " TEXT NOT NULL," +
|
||||
AlbumJSONColumns.JSON + " TEXT NOT NULL);"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + AlbumJSONColumns.NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
public static synchronized AlbumJSONStore getInstance(final Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new AlbumJSONStore(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void addAlbumJSON(final String albumAndArtistName, final String JSON) {
|
||||
if (albumAndArtistName == null || JSON == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final SQLiteDatabase database = getWritableDatabase();
|
||||
final ContentValues values = new ContentValues(2);
|
||||
|
||||
database.beginTransaction();
|
||||
|
||||
values.put(AlbumJSONColumns.ALBUMANDARTIST_NAME, albumAndArtistName.trim().toLowerCase());
|
||||
values.put(AlbumJSONColumns.JSON, JSON);
|
||||
|
||||
database.insert(AlbumJSONColumns.NAME, null, values);
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
public String getAlbumJSON(final String albumAndArtistName) {
|
||||
if (albumAndArtistName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final SQLiteDatabase database = getReadableDatabase();
|
||||
final String[] projection = new String[]{
|
||||
AlbumJSONColumns.JSON,
|
||||
AlbumJSONColumns.ALBUMANDARTIST_NAME
|
||||
};
|
||||
final String selection = AlbumJSONColumns.ALBUMANDARTIST_NAME + "=?";
|
||||
final String[] having = new String[]{
|
||||
albumAndArtistName.trim().toLowerCase()
|
||||
};
|
||||
Cursor cursor = database.query(AlbumJSONColumns.NAME, projection, selection, having, null,
|
||||
null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final String JSON = cursor.getString(cursor.getColumnIndexOrThrow(AlbumJSONColumns.JSON));
|
||||
cursor.close();
|
||||
return JSON;
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void deleteDatabase(final Context context) {
|
||||
context.deleteDatabase(DATABASE_NAME);
|
||||
}
|
||||
|
||||
public void removeItem(final String albumAndArtistName) {
|
||||
final SQLiteDatabase database = getReadableDatabase();
|
||||
database.delete(AlbumJSONColumns.NAME, AlbumJSONColumns.ALBUMANDARTIST_NAME + " = ?", new String[]{
|
||||
albumAndArtistName.trim().toLowerCase()
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public interface AlbumJSONColumns {
|
||||
public static final String NAME = "AlbumJSON";
|
||||
public static final String ALBUMANDARTIST_NAME = "AlbumAndArtistName";
|
||||
public static final String JSON = "JSON";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.kabouzeid.materialmusic.provider;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
public class ArtistJSONStore extends SQLiteOpenHelper {
|
||||
|
||||
private static final int VERSION = 1;
|
||||
public static final String DATABASE_NAME = "artistJSONLastFM.db";
|
||||
private static ArtistJSONStore sInstance = null;
|
||||
|
||||
public ArtistJSONStore(final Context context) {
|
||||
super(context, DATABASE_NAME, null, VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(final SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS " + ArtistJSONColumns.NAME +
|
||||
" (" + ArtistJSONColumns.ARTIST_NAME + " TEXT NOT NULL," +
|
||||
ArtistJSONColumns.JSON + " TEXT NOT NULL);"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + ArtistJSONColumns.NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
public static synchronized ArtistJSONStore getInstance(final Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ArtistJSONStore(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void addArtistJSON(final String artistName, final String JSON) {
|
||||
if (artistName == null || JSON == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final SQLiteDatabase database = getWritableDatabase();
|
||||
final ContentValues values = new ContentValues(2);
|
||||
|
||||
database.beginTransaction();
|
||||
|
||||
values.put(ArtistJSONColumns.ARTIST_NAME, artistName.trim().toLowerCase());
|
||||
values.put(ArtistJSONColumns.JSON, JSON);
|
||||
|
||||
database.insert(ArtistJSONColumns.NAME, null, values);
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
public String getArtistJSON(final String artistName) {
|
||||
if (artistName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final SQLiteDatabase database = getReadableDatabase();
|
||||
final String[] projection = new String[]{
|
||||
ArtistJSONColumns.JSON,
|
||||
ArtistJSONColumns.ARTIST_NAME
|
||||
};
|
||||
final String selection = ArtistJSONColumns.ARTIST_NAME + "=?";
|
||||
final String[] having = new String[]{
|
||||
artistName.trim().toLowerCase()
|
||||
};
|
||||
Cursor cursor = database.query(ArtistJSONColumns.NAME, projection, selection, having, null,
|
||||
null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final String JSON = cursor.getString(cursor.getColumnIndexOrThrow(ArtistJSONColumns.JSON));
|
||||
cursor.close();
|
||||
return JSON;
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void deleteDatabase(final Context context) {
|
||||
context.deleteDatabase(DATABASE_NAME);
|
||||
}
|
||||
|
||||
public void removeItem(final String artistName) {
|
||||
final SQLiteDatabase database = getReadableDatabase();
|
||||
database.delete(ArtistJSONColumns.NAME, ArtistJSONColumns.ARTIST_NAME + " = ?", new String[]{
|
||||
artistName.trim().toLowerCase()
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public interface ArtistJSONColumns {
|
||||
public static final String NAME = "ArtistJSON";
|
||||
public static final String ARTIST_NAME = "ArtistName";
|
||||
public static final String JSON = "JSON";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.kabouzeid.materialmusic.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class MediaButtonIntentReceiver extends BroadcastReceiver {
|
||||
public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName();
|
||||
|
||||
private static final int DOUBLE_CLICK = 500;
|
||||
private static long mLastClickTime = 0;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) {
|
||||
Log.i(TAG, intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT).toString());
|
||||
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (event == null)
|
||||
return;
|
||||
final int keycode = event.getKeyCode();
|
||||
final int action = event.getAction();
|
||||
final long eventTime = event.getEventTime();
|
||||
|
||||
String command = null;
|
||||
switch (keycode) {
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
command = MusicService.ACTION_STOP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
command = MusicService.ACTION_TOGGLE_PLAYBACK;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
command = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
command = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
command = MusicService.ACTION_PAUSE;
|
||||
break;
|
||||
}
|
||||
if (command != null) {
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (event.getRepeatCount() == 0) {
|
||||
/**
|
||||
* If another app received the broadcast first, this if statement will skip.
|
||||
*/
|
||||
//TODO triple click to rewind
|
||||
final Intent i = new Intent(context, MusicService.class);
|
||||
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK
|
||||
&& eventTime - mLastClickTime < DOUBLE_CLICK) {
|
||||
i.setAction(MusicService.ACTION_SKIP);
|
||||
mLastClickTime = 0;
|
||||
} else {
|
||||
i.setAction(command);
|
||||
mLastClickTime = eventTime;
|
||||
}
|
||||
context.startService(i);
|
||||
}
|
||||
}
|
||||
if (isOrderedBroadcast())
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,630 @@
|
|||
package com.kabouzeid.materialmusic.service;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.helper.NotificationHelper;
|
||||
import com.kabouzeid.materialmusic.helper.Shuffler;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.util.InternalStorageUtil;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener {
|
||||
private static final String TAG = MusicService.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_TOGGLE_PLAYBACK = "com.kabouzeid.materialmusic.action.TOGGLE_PLAYBACK";
|
||||
public static final String ACTION_PLAY = "com.kabouzeid.materialmusic.action.PLAY";
|
||||
public static final String ACTION_PAUSE = "com.kabouzeid.materialmusic.action.PAUSE";
|
||||
public static final String ACTION_STOP = "com.kabouzeid.materialmusic.action.STOP";
|
||||
public static final String ACTION_SKIP = "com.kabouzeid.materialmusic.action.SKIP";
|
||||
public static final String ACTION_REWIND = "com.kabouzeid.materialmusic.action.REWIND";
|
||||
public static final String ACTION_QUIT = "com.kabouzeid.materialmusic.action.QUIT";
|
||||
|
||||
public static final int SHUFFLE_MODE_NONE = 0;
|
||||
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
||||
|
||||
public static final int REPEAT_MODE_NONE = 0;
|
||||
public static final int REPEAT_MODE_ALL = 1;
|
||||
public static final int REPEAT_MODE_THIS = 2;
|
||||
|
||||
private MediaPlayer player;
|
||||
private List<Song> playingQueue;
|
||||
private LinkedList<Song> playingHistory;
|
||||
private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners;
|
||||
private int currentSongId = -1;
|
||||
private int position = -1;
|
||||
private int shuffleMode;
|
||||
private int repeatMode;
|
||||
private final IBinder musicBind = new MusicBinder();
|
||||
private boolean isPlayerPrepared;
|
||||
private boolean wasPlayingBeforeFocusLoss;
|
||||
private boolean thingsRegistered;
|
||||
private NotificationHelper notificationHelper;
|
||||
private AudioManager audioManager;
|
||||
private RemoteControlClient remoteControlClient;
|
||||
private Shuffler shuffler;
|
||||
|
||||
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
|
||||
pausePlaying();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private AudioManager getAudioManager() {
|
||||
if (audioManager == null) {
|
||||
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
return audioManager;
|
||||
}
|
||||
|
||||
public MusicService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
isPlayerPrepared = false;
|
||||
playingQueue = new ArrayList<>();
|
||||
playingHistory = new LinkedList<>();
|
||||
onMusicRemoteEventListeners = new ArrayList<>();
|
||||
notificationHelper = new NotificationHelper(this);
|
||||
|
||||
shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(AppKeys.SP_SHUFFLE_MODE, 0);
|
||||
repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(AppKeys.SP_REPEAT_MODE, 0);
|
||||
|
||||
registerEverything();
|
||||
}
|
||||
|
||||
private Shuffler getShuffler() {
|
||||
if (shuffler == null) {
|
||||
shuffler = new Shuffler(playingQueue.size());
|
||||
}
|
||||
return shuffler;
|
||||
}
|
||||
|
||||
private boolean requestFocus() {
|
||||
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
|
||||
AudioManager.AUDIOFOCUS_GAIN);
|
||||
|
||||
return (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
|
||||
}
|
||||
|
||||
private void initRemoteControlClient() {
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
|
||||
remoteControlClient = new RemoteControlClient(mediaPendingIntent);
|
||||
remoteControlClient.setTransportControlFlags(
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
|
||||
getAudioManager().registerRemoteControlClient(remoteControlClient);
|
||||
}
|
||||
|
||||
private void registerEverything() {
|
||||
if (!thingsRegistered) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
||||
registerReceiver(receiver, intentFilter);
|
||||
getAudioManager().registerMediaButtonEventReceiver(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
initRemoteControlClient();
|
||||
thingsRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterEverything() {
|
||||
if (thingsRegistered) {
|
||||
unregisterReceiver(receiver);
|
||||
getAudioManager().unregisterRemoteControlClient(remoteControlClient);
|
||||
getAudioManager().unregisterMediaButtonEventReceiver(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
thingsRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRemoteControlClient(Song song) {
|
||||
Bitmap loadedImage = ImageLoader.getInstance().loadImageSync(MusicUtil.getAlbumArtUri(song.albumId).toString());
|
||||
remoteControlClient
|
||||
.editMetadata(false)
|
||||
.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, song.artistName)
|
||||
.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, song.title)
|
||||
.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, song.duration)
|
||||
.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, loadedImage)
|
||||
.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
setUpMediaPlayerIfNeeded();
|
||||
if (intent != null) {
|
||||
if (intent.getAction() != null) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
case ACTION_TOGGLE_PLAYBACK:
|
||||
if (isPlaying()) {
|
||||
pausePlaying();
|
||||
} else {
|
||||
resumePlaying();
|
||||
}
|
||||
break;
|
||||
case ACTION_PAUSE:
|
||||
pausePlaying();
|
||||
break;
|
||||
case ACTION_PLAY:
|
||||
playSong();
|
||||
break;
|
||||
case ACTION_REWIND:
|
||||
back();
|
||||
break;
|
||||
case ACTION_SKIP:
|
||||
playNextSong();
|
||||
break;
|
||||
case ACTION_STOP:
|
||||
stopPlaying();
|
||||
break;
|
||||
case ACTION_QUIT:
|
||||
killEverythingAndReleaseResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.i(TAG, "onBind");
|
||||
return musicBind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
unregisterEverything();
|
||||
killEverythingAndReleaseResources();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SONG_COMPLETED);
|
||||
if (isLastTrack()) {
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.QUEUE_COMPLETED);
|
||||
notificationHelper.updatePlayState(isPlaying());
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STOP);
|
||||
} else {
|
||||
playNextSong();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
isPlayerPrepared = false;
|
||||
player.reset();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STOP);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
player.start();
|
||||
isPlayerPrepared = true;
|
||||
notificationHelper.updatePlayState(isPlaying());
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.PLAY);
|
||||
savePosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
unregisterEverything();
|
||||
killEverythingAndReleaseResources();
|
||||
}
|
||||
|
||||
private void killEverythingAndReleaseResources() {
|
||||
savePosition();
|
||||
saveQueue();
|
||||
stopPlaying();
|
||||
notificationHelper.killNotification();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void setUpMediaPlayerIfNeeded() {
|
||||
if (player == null) {
|
||||
player = new MediaPlayer();
|
||||
|
||||
player.setOnPreparedListener(this);
|
||||
player.setOnCompletionListener(this);
|
||||
player.setOnErrorListener(this);
|
||||
|
||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNotification() {
|
||||
notificationHelper.buildNotification(playingQueue.get(position), isPlaying());
|
||||
}
|
||||
|
||||
public void setPlayingQueue(List<Song> songs) {
|
||||
if (!playingQueue.equals(songs)) {
|
||||
this.playingQueue = songs;
|
||||
shuffler = new Shuffler(playingQueue.size());
|
||||
saveQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Song> getPlayingQueue() {
|
||||
return playingQueue;
|
||||
}
|
||||
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getCurrentSongId() {
|
||||
return currentSongId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
switch (focusChange) {
|
||||
case AudioManager.AUDIOFOCUS_GAIN:
|
||||
// resume playback
|
||||
registerEverything();
|
||||
player.setVolume(1.0f, 1.0f);
|
||||
if (wasPlayingBeforeFocusLoss) {
|
||||
resumePlaying();
|
||||
updateRemoteControlClient(getPlayingQueue().get(position));
|
||||
}
|
||||
updateRemoteControlClient(getPlayingQueue().get(position));
|
||||
break;
|
||||
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
// Lost focus for an unbounded amount of time: stop playback and release media player
|
||||
//TODO maybe also release player
|
||||
wasPlayingBeforeFocusLoss = isPlaying();
|
||||
pausePlaying();
|
||||
unregisterEverything();
|
||||
break;
|
||||
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
// Lost focus for a short time, but we have to stop
|
||||
// playback. We don't release the media player because playback
|
||||
// is likely to resume
|
||||
wasPlayingBeforeFocusLoss = isPlaying();
|
||||
pausePlaying();
|
||||
break;
|
||||
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
// Lost focus for a short time, but it's ok to keep playing
|
||||
// at an attenuated level
|
||||
player.setVolume(0.2f, 0.2f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class MusicBinder extends Binder {
|
||||
public MusicService getService() {
|
||||
return MusicService.this;
|
||||
}
|
||||
}
|
||||
|
||||
public void playSong() {
|
||||
if (requestFocus()) {
|
||||
setUpMediaPlayerIfNeeded();
|
||||
registerEverything();
|
||||
isPlayerPrepared = false;
|
||||
player.reset();
|
||||
Uri trackUri = getCurrentPositionTrackUri();
|
||||
try {
|
||||
player.setDataSource(getApplicationContext(), trackUri);
|
||||
currentSongId = playingQueue.get(position).id;
|
||||
updateNotification();
|
||||
updateRemoteControlClient(getPlayingQueue().get(position));
|
||||
player.prepareAsync();
|
||||
} catch (Exception e) {
|
||||
Log.e("MUSIC SERVICE", "Error setting data source", e);
|
||||
player.reset();
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STOP);
|
||||
notificationHelper.updatePlayState(false);
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
public void pausePlaying() {
|
||||
if (isPlaying()) {
|
||||
player.pause();
|
||||
notificationHelper.updatePlayState(isPlaying());
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
public void resumePlaying() {
|
||||
if (requestFocus()) {
|
||||
if (isPlayerPrepared) {
|
||||
player.start();
|
||||
notificationHelper.updatePlayState(isPlaying());
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.RESUME);
|
||||
} else {
|
||||
playSong();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPlaying() {
|
||||
isPlayerPrepared = false;
|
||||
player.stop();
|
||||
notificationHelper.updatePlayState(isPlaying());
|
||||
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
||||
player.release();
|
||||
player = null;
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STOP);
|
||||
}
|
||||
|
||||
public void playNextSong() {
|
||||
if (position != -1) {
|
||||
if (isPlayerPrepared) {
|
||||
|
||||
setPosition(getNextPosition());
|
||||
playSong();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.NEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playPreviousSong() {
|
||||
if (position != -1) {
|
||||
setPosition(getPreviousPosition());
|
||||
playSong();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.PREV);
|
||||
}
|
||||
}
|
||||
|
||||
public void back() {
|
||||
if (position != -1) {
|
||||
if (getSongProgressMillis() > 2000) {
|
||||
seekTo(0);
|
||||
} else {
|
||||
playPreviousSong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getNextPosition() {
|
||||
int position = 0;
|
||||
switch (repeatMode) {
|
||||
case REPEAT_MODE_NONE:
|
||||
switch (shuffleMode) {
|
||||
case SHUFFLE_MODE_NONE:
|
||||
position = getPosition() + 1;
|
||||
if (isLastTrack()) {
|
||||
position -= 1;
|
||||
}
|
||||
break;
|
||||
case SHUFFLE_MODE_SHUFFLE:
|
||||
position = getShuffler().nextInt(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REPEAT_MODE_ALL:
|
||||
switch (shuffleMode) {
|
||||
case SHUFFLE_MODE_NONE:
|
||||
position = getPosition() + 1;
|
||||
if (isLastTrack()) {
|
||||
position = 0;
|
||||
}
|
||||
break;
|
||||
case SHUFFLE_MODE_SHUFFLE:
|
||||
position = getShuffler().nextInt(true);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REPEAT_MODE_THIS:
|
||||
position = getPosition();
|
||||
break;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getPreviousPosition() {
|
||||
int position = 0;
|
||||
switch (repeatMode) {
|
||||
case REPEAT_MODE_NONE:
|
||||
switch (shuffleMode) {
|
||||
case SHUFFLE_MODE_NONE:
|
||||
position = getPosition() - 1;
|
||||
if (position < 0) {
|
||||
position = 0;
|
||||
}
|
||||
break;
|
||||
case SHUFFLE_MODE_SHUFFLE:
|
||||
position = getShuffler().previousInt();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REPEAT_MODE_ALL:
|
||||
switch (shuffleMode) {
|
||||
case SHUFFLE_MODE_NONE:
|
||||
position = getPosition() - 1;
|
||||
if (position < 0) {
|
||||
position = getPlayingQueue().size() - 1;
|
||||
}
|
||||
break;
|
||||
case SHUFFLE_MODE_SHUFFLE:
|
||||
position = getShuffler().previousInt();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REPEAT_MODE_THIS:
|
||||
position = getPosition();
|
||||
break;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return player != null && player.isPlaying();
|
||||
}
|
||||
|
||||
private Uri getCurrentPositionTrackUri() {
|
||||
return ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, playingQueue.get(position).id);
|
||||
}
|
||||
|
||||
public int getSongProgressMillis() {
|
||||
return player.getCurrentPosition();
|
||||
}
|
||||
|
||||
public int getSongDurationMillis() {
|
||||
return player.getDuration();
|
||||
}
|
||||
|
||||
public void seekTo(int millis) {
|
||||
player.seekTo(millis);
|
||||
}
|
||||
|
||||
public boolean isPlayerPrepared() {
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
return isPlayerPrepared;
|
||||
}
|
||||
|
||||
private boolean isLastTrack() {
|
||||
return getPosition() == getPlayingQueue().size() - 1;
|
||||
}
|
||||
|
||||
private void notifyOnMusicRemoteEventListeners(int event) {
|
||||
MusicRemoteEvent musicRemoteEvent = new MusicRemoteEvent(event);
|
||||
for (OnMusicRemoteEventListener listener : onMusicRemoteEventListeners) {
|
||||
listener.onMusicRemoteEvent(musicRemoteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void addOnMusicRemoteEventListener(OnMusicRemoteEventListener onMusicRemoteEventListener) {
|
||||
onMusicRemoteEventListeners.add(onMusicRemoteEventListener);
|
||||
}
|
||||
|
||||
public void saveQueue() {
|
||||
try {
|
||||
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_PLAYING_QUEUE, getPlayingQueue());
|
||||
Log.i(TAG, "saved current queue state");
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error while saving music service queue state", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void savePosition() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_POSITION_IN_QUEUE, getPosition());
|
||||
Log.i(TAG, "saved current position state");
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error while saving music service position state", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void setShuffleMode(final int shuffleMode) {
|
||||
switch (shuffleMode) {
|
||||
case SHUFFLE_MODE_SHUFFLE:
|
||||
shuffler = new Shuffler(getPlayingQueue().size());
|
||||
case SHUFFLE_MODE_NONE:
|
||||
this.shuffleMode = shuffleMode;
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||
.putInt(AppKeys.SP_SHUFFLE_MODE, shuffleMode)
|
||||
.apply();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SHUFFLE_MODE_CHANGED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRepeatMode(final int repeatMode) {
|
||||
switch (repeatMode) {
|
||||
case REPEAT_MODE_NONE:
|
||||
case REPEAT_MODE_ALL:
|
||||
case REPEAT_MODE_THIS:
|
||||
this.repeatMode = repeatMode;
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||
.putInt(AppKeys.SP_REPEAT_MODE, repeatMode)
|
||||
.apply();
|
||||
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.REPEAT_MODE_CHANGED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void cycleRepeatMode() {
|
||||
switch (repeatMode) {
|
||||
case REPEAT_MODE_NONE:
|
||||
setRepeatMode(REPEAT_MODE_ALL);
|
||||
break;
|
||||
case REPEAT_MODE_ALL:
|
||||
setRepeatMode(REPEAT_MODE_THIS);
|
||||
break;
|
||||
default:
|
||||
setRepeatMode(REPEAT_MODE_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleShuffle() {
|
||||
if (shuffleMode == SHUFFLE_MODE_NONE) {
|
||||
setShuffleMode(SHUFFLE_MODE_SHUFFLE);
|
||||
} else {
|
||||
setShuffleMode(SHUFFLE_MODE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRepeatMode() {
|
||||
return repeatMode;
|
||||
}
|
||||
|
||||
public int getShuffleMode() {
|
||||
return shuffleMode;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Transition;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableListView;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.songadapter.SongAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.SongTrackNumberComparator;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.loader.AlbumLoader;
|
||||
import com.kabouzeid.materialmusic.loader.AlbumSongLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.misc.SmallObservableScrollViewCallbacks;
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsFabActivity;
|
||||
import com.kabouzeid.materialmusic.ui.activities.tageditor.AlbumTagEditorActivity;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
import com.nhaarman.listviewanimations.appearance.AnimationAdapter;
|
||||
import com.nhaarman.listviewanimations.appearance.simple.ScaleInAnimationAdapter;
|
||||
import com.nineoldandroids.view.ViewHelper;
|
||||
import com.nineoldandroids.view.ViewPropertyAnimator;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
*
|
||||
* A lot of hackery is done in this activity. Changing things may will brake the whole activity.
|
||||
*
|
||||
* Should be kinda stable ONLY AS IT IS!!!
|
||||
*
|
||||
* */
|
||||
|
||||
public class AlbumDetailActivity extends AbsFabActivity implements OnMusicRemoteEventListener, KabViewsDisableAble {
|
||||
public static final String TAG = AlbumDetailActivity.class.getSimpleName();
|
||||
|
||||
private static final boolean TOOLBAR_IS_STICKY = true;
|
||||
private static final int DEFAULT_DELAY_NO_TRANSITION = 200;
|
||||
private static final int DEFAULT_DELAY = 450;
|
||||
private static final int DEFAULT_ANIMATION_TIME = 1000;
|
||||
|
||||
private App app;
|
||||
|
||||
private Album album;
|
||||
|
||||
private AnimationAdapter animatedSongsAdapter;
|
||||
private ObservableListView absSongListView;
|
||||
private View statusBar;
|
||||
private ImageView albumArtImageView;
|
||||
private View albumArtOverlayView;
|
||||
private View songsBackgroundView;
|
||||
private TextView albumTitleView;
|
||||
private FloatingActionButton fab;
|
||||
private Toolbar toolbar;
|
||||
private int toolbarHeight;
|
||||
private int headerOffset;
|
||||
private int titleViewHeight;
|
||||
private int albumArtViewHeight;
|
||||
private int toolbarColor;
|
||||
|
||||
private Bitmap albumCover;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getApplicationContext();
|
||||
setTheme(app.getAppTheme());
|
||||
setUpTranslucence();
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_album_detail);
|
||||
|
||||
Bundle intentExtras = getIntent().getExtras();
|
||||
int albumId = -1;
|
||||
if (intentExtras != null) {
|
||||
albumId = intentExtras.getInt(AppKeys.E_ALBUM);
|
||||
}
|
||||
album = AlbumLoader.getAlbum(this, albumId);
|
||||
if (album.id == -1) {
|
||||
finish();
|
||||
}
|
||||
|
||||
initViews();
|
||||
setUpObservableListViewParams();
|
||||
setUpToolBar();
|
||||
setUpViews();
|
||||
lollipopTransitionImageWrongSizeFix();
|
||||
animateEnterActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enableViews();
|
||||
updateFabIcon();
|
||||
app.getMusicPlayerRemote().addOnMusicRemoteEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
app.getMusicPlayerRemote().removeOnMusicRemoteEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_album_detail, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
super.onBackPressed();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
return true;
|
||||
case R.id.action_current_playing:
|
||||
return openCurrentPlayingIfPossible(null);
|
||||
case R.id.action_tag_editor:
|
||||
Intent intent = new Intent(this, AlbumTagEditorActivity.class);
|
||||
intent.putExtra(AppKeys.E_ID, album.id);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.action_go_to_artist:
|
||||
goToArtistDetailsActivity(album.artistId, null);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
albumArtImageView = (ImageView) findViewById(R.id.album_art);
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
albumArtOverlayView = findViewById(R.id.overlay);
|
||||
absSongListView = (ObservableListView) findViewById(R.id.list);
|
||||
albumTitleView = (TextView) findViewById(R.id.album_title);
|
||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
songsBackgroundView = findViewById(R.id.list_background);
|
||||
statusBar = findViewById(R.id.statusBar);
|
||||
}
|
||||
|
||||
private void setUpObservableListViewParams() {
|
||||
albumArtViewHeight = getResources().getDimensionPixelSize(R.dimen.header_image_height);
|
||||
toolbarColor = Util.resolveColor(this, R.attr.colorPrimary);
|
||||
toolbarHeight = Util.getActionBarSize(this);
|
||||
titleViewHeight = getResources().getDimensionPixelSize(R.dimen.title_view_height);
|
||||
headerOffset = toolbarHeight;
|
||||
headerOffset += getResources().getDimensionPixelSize(R.dimen.statusMargin);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
albumTitleView.setText(album.title);
|
||||
ViewHelper.setAlpha(albumArtOverlayView, 0);
|
||||
|
||||
prepareViewsForOpenAnimation();
|
||||
setUpAlbumArtAndApplyPalette();
|
||||
setUpListView();
|
||||
}
|
||||
|
||||
private void prepareViewsForOpenAnimation() {
|
||||
albumTitleView.setPivotY(0);
|
||||
albumTitleView.setScaleY(0);
|
||||
}
|
||||
|
||||
private void setUpAlbumArtAndApplyPalette() {
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(album.id).toString(), albumArtImageView, new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
albumArtImageView.setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
albumArtImageView.setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, final View view, Bitmap loadedImage) {
|
||||
albumCover = loadedImage;
|
||||
applyPalette(loadedImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
albumArtImageView.setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpSongsAdapter() {
|
||||
final List<Song> songs = AlbumSongLoader.getAlbumSongList(this, album.id);
|
||||
Collections.sort(songs, new SongTrackNumberComparator());
|
||||
final SongAdapter songAdapter = new SongAdapter(this, this, songs);
|
||||
|
||||
// SwingBottomInAnimationAdapter songsAdapter = new SwingBottomInAnimationAdapter(songAdapter);
|
||||
// SwingRightInAnimationAdapter songsAdapter = new SwingRightInAnimationAdapter(songAdapter);
|
||||
// SwingLeftInAnimationAdapter songsAdapter = new SwingLeftInAnimationAdapter(songAdapter);
|
||||
ScaleInAnimationAdapter songsAdapter = new ScaleInAnimationAdapter(songAdapter);
|
||||
// AlphaInAnimationAdapter songsAdapter = new AlphaInAnimationAdapter(songAdapter);
|
||||
|
||||
animatedSongsAdapter = songsAdapter;
|
||||
animatedSongsAdapter.setAbsListView(absSongListView);
|
||||
|
||||
absSongListView.setAdapter(animatedSongsAdapter);
|
||||
absSongListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position > 0) {
|
||||
app.getMusicPlayerRemote().setPlayingQueue(songs);
|
||||
app.getMusicPlayerRemote().playSongAt(position - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpListView() {
|
||||
absSongListView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
||||
setListViewPadding();
|
||||
final View contentView = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
contentView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
songsBackgroundView.getLayoutParams().height = contentView.getHeight();
|
||||
observableScrollViewCallbacks.onScrollChanged(0, false, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setListViewPadding() {
|
||||
setListViewPaddingTop();
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
setListViewPaddingBottom();
|
||||
}
|
||||
}
|
||||
|
||||
private void setListViewPaddingTop() {
|
||||
final View paddingView = new View(AlbumDetailActivity.this);
|
||||
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
|
||||
albumArtViewHeight + titleViewHeight);
|
||||
paddingView.setLayoutParams(lp);
|
||||
paddingView.setClickable(true);
|
||||
absSongListView.addHeaderView(paddingView);
|
||||
}
|
||||
|
||||
private void setListViewPaddingBottom() {
|
||||
final View paddingView = new View(AlbumDetailActivity.this);
|
||||
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
|
||||
Util.getNavigationBarHeight(this));
|
||||
paddingView.setLayoutParams(lp);
|
||||
paddingView.setClickable(true);
|
||||
absSongListView.addFooterView(paddingView);
|
||||
}
|
||||
|
||||
private void setUpToolBar() {
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setTitle(null);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
if (!TOOLBAR_IS_STICKY) {
|
||||
toolbar.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPalette(Bitmap bitmap) {
|
||||
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
|
||||
@Override
|
||||
public void onGenerated(Palette palette) {
|
||||
Palette.Swatch swatch = palette.getVibrantSwatch();
|
||||
if (swatch != null) {
|
||||
toolbarColor = swatch.getRgb();
|
||||
albumArtOverlayView.setBackgroundColor(swatch.getRgb());
|
||||
albumTitleView.setBackgroundColor(swatch.getRgb());
|
||||
albumTitleView.setTextColor(swatch.getTitleTextColor());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SmallObservableScrollViewCallbacks observableScrollViewCallbacks = new SmallObservableScrollViewCallbacks() {
|
||||
@Override
|
||||
public void onScrollChanged(int scrollY, boolean b, boolean b2) {
|
||||
super.onScrollChanged(scrollY, b, b2);
|
||||
// Translate overlay and image
|
||||
float flexibleRange = albumArtViewHeight - headerOffset;
|
||||
int minOverlayTransitionY = headerOffset - albumArtOverlayView.getHeight();
|
||||
ViewHelper.setTranslationY(albumArtOverlayView, Math.max(minOverlayTransitionY, Math.min(0, -scrollY)));
|
||||
ViewHelper.setTranslationY(albumArtImageView, Math.max(minOverlayTransitionY, Math.min(0, -scrollY / 2)));
|
||||
|
||||
// Translate list background
|
||||
ViewHelper.setTranslationY(songsBackgroundView, Math.max(0, -scrollY + albumArtViewHeight));
|
||||
|
||||
// Change alpha of overlay
|
||||
ViewHelper.setAlpha(albumArtOverlayView, Math.max(0, Math.min(1, (float) scrollY / flexibleRange)));
|
||||
|
||||
// Translate name text
|
||||
int maxTitleTranslationY = albumArtViewHeight;
|
||||
int titleTranslationY = maxTitleTranslationY - scrollY;
|
||||
if (TOOLBAR_IS_STICKY) {
|
||||
titleTranslationY = Math.max(headerOffset, titleTranslationY);
|
||||
}
|
||||
ViewHelper.setTranslationY(albumTitleView, titleTranslationY);
|
||||
|
||||
// Translate FAB
|
||||
int fabTranslationY = titleTranslationY + titleViewHeight - (fab.getHeight() / 2);
|
||||
ViewHelper.setTranslationY(fab, fabTranslationY);
|
||||
|
||||
if (TOOLBAR_IS_STICKY) {
|
||||
// Change alpha of toolbar background
|
||||
if (-scrollY + albumArtViewHeight <= headerOffset) {
|
||||
ViewUtil.setBackgroundAlpha(toolbar, 1, toolbarColor);
|
||||
ViewUtil.setBackgroundAlpha(statusBar, 1, toolbarColor);
|
||||
|
||||
} else {
|
||||
ViewUtil.setBackgroundAlpha(toolbar, 0, toolbarColor);
|
||||
ViewUtil.setBackgroundAlpha(statusBar, 0, toolbarColor);
|
||||
}
|
||||
} else {
|
||||
// Translate Toolbar
|
||||
if (scrollY < albumArtViewHeight) {
|
||||
ViewHelper.setTranslationY(toolbar, 0);
|
||||
} else {
|
||||
ViewHelper.setTranslationY(toolbar, -scrollY);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onMusicRemoteEvent(MusicRemoteEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MusicRemoteEvent.PLAY:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_pause_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.PAUSE:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_play_arrow_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.RESUME:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_pause_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.STOP:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_play_arrow_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.QUEUE_COMPLETED:
|
||||
fab.setImageResource(R.drawable.ic_play_arrow_white_48dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
super.enableViews();
|
||||
absSongListView.setEnabled(true);
|
||||
fab.setEnabled(true);
|
||||
toolbar.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
super.disableViews();
|
||||
absSongListView.setEnabled(false);
|
||||
fab.setEnabled(false);
|
||||
toolbar.setEnabled(false);
|
||||
}
|
||||
|
||||
private void setUpTranslucence() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
Util.setStatusBarTranslucent(getWindow(), true);
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
Util.setNavBarTranslucent(getWindow(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFabIcon() {
|
||||
if (app.getMusicPlayerRemote().isPlaying()) {
|
||||
fab.setImageResource(R.drawable.ic_pause_white_48dp);
|
||||
} else {
|
||||
fab.setImageResource(R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
}
|
||||
|
||||
private void animateHeader(int startDelay) {
|
||||
ViewPropertyAnimator.animate(albumTitleView)
|
||||
.scaleX(1)
|
||||
.scaleY(1)
|
||||
.setInterpolator(new DecelerateInterpolator(4))
|
||||
.setDuration(DEFAULT_ANIMATION_TIME)
|
||||
.setStartDelay(startDelay)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void animateFab(int startDelay) {
|
||||
ViewPropertyAnimator.animate(fab)
|
||||
.scaleX(1)
|
||||
.scaleY(1)
|
||||
.setInterpolator(new DecelerateInterpolator(4))
|
||||
.setDuration(DEFAULT_ANIMATION_TIME)
|
||||
.setStartDelay(startDelay)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void animateEnterActivity() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
animateHeader(0);
|
||||
setUpSongsAdapter();
|
||||
}
|
||||
}, DEFAULT_DELAY);
|
||||
|
||||
} else {
|
||||
setUpSongsAdapter();
|
||||
fab.setScaleX(0);
|
||||
fab.setScaleY(0);
|
||||
animateHeader(DEFAULT_DELAY_NO_TRANSITION);
|
||||
animateFab(DEFAULT_DELAY_NO_TRANSITION);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToAlbum(int albumId) {
|
||||
if (album.id != albumId) {
|
||||
goToAlbum(albumId);
|
||||
}
|
||||
}
|
||||
|
||||
private void lollipopTransitionImageWrongSizeFix() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().getSharedElementEnterTransition().addListener(new Transition.TransitionListener() {
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
if (albumCover == null) {
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(album.id).toString(), albumArtImageView, new ImageLoaderUtil.defaultAlbumArtOnFailed());
|
||||
} else {
|
||||
albumArtImageView.setImageBitmap(albumCover);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionCancel(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionPause(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionResume(Transition transition) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,490 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v13.app.FragmentPagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Transition;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;
|
||||
import com.github.ksoichiro.android.observablescrollview.ScrollState;
|
||||
import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.lastfm.artist.LastFMArtistImageLoader;
|
||||
import com.kabouzeid.materialmusic.loader.ArtistLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Artist;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsFabActivity;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.artistviewpager.AbsViewPagerTabArtistListFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.artistviewpager.ViewPagerTabArtistAlbumFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.artistviewpager.ViewPagerTabArtistBioFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.artistviewpager.ViewPagerTabArtistSongListFragment;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.nineoldandroids.animation.Animator;
|
||||
import com.nineoldandroids.view.ViewHelper;
|
||||
import com.nineoldandroids.view.ViewPropertyAnimator;
|
||||
|
||||
/*
|
||||
*
|
||||
* A lot of hackery is done in this activity. Changing things may will brake the whole activity.
|
||||
*
|
||||
* Should be kinda stable ONLY AS IT IS!!!
|
||||
*
|
||||
* */
|
||||
|
||||
public class ArtistDetailActivity extends AbsFabActivity implements OnMusicRemoteEventListener, KabViewsDisableAble, ObservableScrollViewCallbacks {
|
||||
public static final String TAG = ArtistDetailActivity.class.getSimpleName();
|
||||
|
||||
public static final String ARG_ARTIST_ID = "com.kabouzeid.materialmusic.artist.id";
|
||||
public static final String ARG_ARTIST_NAME = "com.kabouzeid.materialmusic.artist.name";
|
||||
|
||||
private static final boolean TOOLBAR_IS_STICKY = true;
|
||||
|
||||
private boolean isAnimating;
|
||||
|
||||
private Artist artist;
|
||||
|
||||
private SlidingTabLayout slidingTabs;
|
||||
private View statusBar;
|
||||
private ImageView artistImageView;
|
||||
private View artistArtOverlayView;
|
||||
private View absAlbumListBackgroundView;
|
||||
private TextView artistTitleText;
|
||||
private Toolbar toolbar;
|
||||
private ViewPager viewPager;
|
||||
private NavigationAdapter navigationAdapter;
|
||||
private int toolbarHeight;
|
||||
private int headerOffset;
|
||||
private int titleViewHeight;
|
||||
private int artistImageViewHeight;
|
||||
private int toolbarColor;
|
||||
private int tabHeight;
|
||||
|
||||
private Bitmap artistImage;
|
||||
|
||||
private Fragment currentFragment;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setUpTranslucence(true, true);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_artist_detail);
|
||||
|
||||
getIntentExtras();
|
||||
initViews();
|
||||
setUpObservableListViewParams();
|
||||
setUpToolBar();
|
||||
setUpViews();
|
||||
lollipopTransitionImageWrongSizeFix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_artist_detail, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
super.onBackPressed();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
return true;
|
||||
case R.id.action_current_playing:
|
||||
openCurrentPlayingIfPossible(null);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
artistImageView = (ImageView) findViewById(R.id.artist_image);
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
artistArtOverlayView = findViewById(R.id.overlay);
|
||||
artistTitleText = (TextView) findViewById(R.id.artist_name);
|
||||
absAlbumListBackgroundView = findViewById(R.id.list_background);
|
||||
statusBar = findViewById(R.id.statusBar);
|
||||
slidingTabs = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
|
||||
}
|
||||
|
||||
private void setUpObservableListViewParams() {
|
||||
artistImageViewHeight = getResources().getDimensionPixelSize(R.dimen.header_image_height);
|
||||
toolbarColor = Util.resolveColor(this, R.attr.colorPrimary);
|
||||
toolbarHeight = Util.getActionBarSize(this);
|
||||
titleViewHeight = getResources().getDimensionPixelSize(R.dimen.title_view_height);
|
||||
headerOffset = toolbarHeight;
|
||||
headerOffset += getResources().getDimensionPixelSize(R.dimen.statusMargin);
|
||||
tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
artistTitleText.setText(artist.name);
|
||||
ViewHelper.setAlpha(artistArtOverlayView, 0);
|
||||
|
||||
setUpArtistImageAndApplyPalette();
|
||||
setUpViewPatch();
|
||||
setUpSlidingTabs();
|
||||
}
|
||||
|
||||
private void setUpSlidingTabs() {
|
||||
navigationAdapter = new NavigationAdapter(this, artist);
|
||||
viewPager = (ViewPager) findViewById(R.id.pager);
|
||||
viewPager.setOffscreenPageLimit(2);
|
||||
viewPager.setAdapter(navigationAdapter);
|
||||
viewPager.setCurrentItem(1);
|
||||
|
||||
slidingTabs.setViewPager(viewPager);
|
||||
slidingTabs.setDistributeEvenly(true);
|
||||
slidingTabs.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);
|
||||
slidingTabs.setSelectedIndicatorColors(Util.resolveColor(this, R.attr.colorAccent));
|
||||
slidingTabs.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
currentFragment = navigationAdapter.getItemAt(position);
|
||||
if (currentFragment instanceof AbsViewPagerTabArtistListFragment) {
|
||||
restoreY(((AbsViewPagerTabArtistListFragment) currentFragment).getY());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpArtistImageAndApplyPalette() {
|
||||
if (artistImage == null) {
|
||||
LastFMArtistImageLoader.loadArtistImage(this, artist.name, new LastFMArtistImageLoader.ArtistImageLoaderCallback() {
|
||||
@Override
|
||||
public void onArtistImageLoaded(Bitmap artistImage) {
|
||||
if (artistImage != null) {
|
||||
ArtistDetailActivity.this.artistImage = artistImage;
|
||||
artistImageView.setImageBitmap(artistImage);
|
||||
applyPalette(artistImage);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
artistImageView.setImageBitmap(artistImage);
|
||||
applyPalette(artistImage);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpViewPatch() {
|
||||
final View contentView = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
contentView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
absAlbumListBackgroundView.getLayoutParams().height = contentView.getHeight();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpToolBar() {
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setTitle(null);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
if (!TOOLBAR_IS_STICKY) {
|
||||
toolbar.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPalette(Bitmap bitmap) {
|
||||
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
|
||||
@Override
|
||||
public void onGenerated(Palette palette) {
|
||||
Palette.Swatch swatch = palette.getVibrantSwatch();
|
||||
if (swatch != null) {
|
||||
toolbarColor = swatch.getRgb();
|
||||
artistArtOverlayView.setBackgroundColor(swatch.getRgb());
|
||||
artistTitleText.setBackgroundColor(swatch.getRgb());
|
||||
slidingTabs.setBackgroundColor(swatch.getRgb());
|
||||
artistTitleText.setTextColor(swatch.getTitleTextColor());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
super.enableViews();
|
||||
viewPager.setEnabled(true);
|
||||
toolbar.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
super.disableViews();
|
||||
viewPager.setEnabled(false);
|
||||
toolbar.setEnabled(false);
|
||||
}
|
||||
|
||||
private void getIntentExtras() {
|
||||
Bundle intentExtras = getIntent().getExtras();
|
||||
final int artistId = intentExtras.getInt(AppKeys.E_ARTIST);
|
||||
artist = ArtistLoader.getArtist(this, artistId);
|
||||
if (artist == null) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollChanged(int scrollY, boolean b, boolean b2) {
|
||||
if (!isAnimating) {
|
||||
int titleTranslationY = getTitleTranslation(scrollY);
|
||||
ViewHelper.setTranslationY(artistArtOverlayView, getOverlayTranslation(scrollY));
|
||||
ViewHelper.setTranslationY(artistImageView, getImageViewTranslation(scrollY));
|
||||
ViewHelper.setTranslationY(absAlbumListBackgroundView, getListBackgroundTranslation(scrollY));
|
||||
ViewHelper.setAlpha(artistArtOverlayView, getOverlayAlpha(scrollY));
|
||||
ViewHelper.setTranslationY(artistTitleText, titleTranslationY);
|
||||
ViewHelper.setTranslationY(slidingTabs, titleTranslationY);
|
||||
ViewHelper.setTranslationY(getFab(), getFabTranslation(scrollY));
|
||||
translateToolBar(scrollY);
|
||||
}
|
||||
}
|
||||
|
||||
private int getImageViewTranslation(int scrollY) {
|
||||
int minOverlayTransitionY = headerOffset - artistArtOverlayView.getHeight();
|
||||
return Math.max(minOverlayTransitionY, Math.min(0, -scrollY / 2));
|
||||
}
|
||||
|
||||
private int getOverlayTranslation(int scrollY) {
|
||||
int minOverlayTransitionY = headerOffset - artistArtOverlayView.getHeight();
|
||||
return Math.max(minOverlayTransitionY, Math.min(0, -scrollY));
|
||||
}
|
||||
|
||||
private int getListBackgroundTranslation(int scrollY) {
|
||||
return Math.max(0, -scrollY + artistImageViewHeight);
|
||||
}
|
||||
|
||||
private int getTitleTranslation(int scrollY) {
|
||||
int maxTitleTranslationY = artistImageViewHeight;
|
||||
int titleTranslationY = maxTitleTranslationY - scrollY;
|
||||
if (TOOLBAR_IS_STICKY) {
|
||||
titleTranslationY = Math.max(headerOffset, titleTranslationY);
|
||||
}
|
||||
return titleTranslationY;
|
||||
}
|
||||
|
||||
private int getFabTranslation(int scrollY) {
|
||||
return getTitleTranslation(scrollY) + titleViewHeight + tabHeight - (getFab().getHeight() / 2);
|
||||
}
|
||||
|
||||
private float getOverlayAlpha(int scrollY) {
|
||||
float flexibleRange = artistImageViewHeight - headerOffset;
|
||||
return Math.max(0, Math.min(1, (float) scrollY / flexibleRange));
|
||||
}
|
||||
|
||||
private void translateToolBar(int scrollY) {
|
||||
if (TOOLBAR_IS_STICKY) {
|
||||
// Change alpha of toolbar background
|
||||
if (-scrollY + artistImageViewHeight <= headerOffset) {
|
||||
ViewUtil.setBackgroundAlpha(toolbar, 1, toolbarColor);
|
||||
ViewUtil.setBackgroundAlpha(statusBar, 1, toolbarColor);
|
||||
|
||||
} else {
|
||||
ViewUtil.setBackgroundAlpha(toolbar, 0, toolbarColor);
|
||||
ViewUtil.setBackgroundAlpha(statusBar, 0, toolbarColor);
|
||||
}
|
||||
} else {
|
||||
// Translate Toolbar
|
||||
if (scrollY < artistImageViewHeight) {
|
||||
ViewHelper.setTranslationY(toolbar, 0);
|
||||
} else {
|
||||
ViewHelper.setTranslationY(toolbar, -scrollY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void restoreY(final int scrollY) {
|
||||
translateToolBar(scrollY);
|
||||
int animationTime = 1000;
|
||||
DecelerateInterpolator interpolator = new DecelerateInterpolator(4);
|
||||
int titleTranslationY = getTitleTranslation(scrollY);
|
||||
ViewPropertyAnimator.animate(artistArtOverlayView).y(getOverlayTranslation(scrollY)).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(artistImageView).y(getImageViewTranslation(scrollY)).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(absAlbumListBackgroundView).y(getListBackgroundTranslation(scrollY)).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(artistArtOverlayView).alpha(getOverlayAlpha(scrollY)).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(slidingTabs).y(titleTranslationY + titleViewHeight).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(artistTitleText).y(titleTranslationY).setDuration(animationTime).setInterpolator(interpolator).start();
|
||||
ViewPropertyAnimator.animate(getFab()).y(getFabTranslation(scrollY)).setDuration(animationTime).setInterpolator(interpolator).setListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
isAnimating = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
translateToolBar(scrollY);
|
||||
isAnimating = false;
|
||||
if (currentFragment instanceof AbsViewPagerTabArtistListFragment) {
|
||||
onScrollChanged((((AbsViewPagerTabArtistListFragment) currentFragment).getY()), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
isAnimating = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
isAnimating = true;
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownMotionEvent() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToArtist(int artistId) {
|
||||
if (artist.id != artistId) {
|
||||
super.goToArtist(artistId);
|
||||
}
|
||||
}
|
||||
|
||||
private void lollipopTransitionImageWrongSizeFix() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().getSharedElementEnterTransition().addListener(new Transition.TransitionListener() {
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
if (artistImage == null) {
|
||||
LastFMArtistImageLoader.loadArtistImage(ArtistDetailActivity.this, artist.name, new LastFMArtistImageLoader.ArtistImageLoaderCallback() {
|
||||
@Override
|
||||
public void onArtistImageLoaded(Bitmap artistImage) {
|
||||
if (artistImage != null) {
|
||||
artistImageView.setImageBitmap(artistImage);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
artistImageView.setImageBitmap(artistImage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionCancel(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionPause(Transition transition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionResume(Transition transition) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class NavigationAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private String[] titles;
|
||||
|
||||
private SparseArray<Fragment> mPages;
|
||||
private Artist artist;
|
||||
private Context context;
|
||||
|
||||
public NavigationAdapter(Activity activity, Artist artist) {
|
||||
super(activity.getFragmentManager());
|
||||
this.artist = artist;
|
||||
mPages = new SparseArray<>();
|
||||
context = activity;
|
||||
titles = new String[]{
|
||||
context.getResources().getString(R.string.tab_songs),
|
||||
context.getResources().getString(R.string.tab_albums),
|
||||
context.getResources().getString(R.string.tab_biography)
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_ARTIST_ID, artist.id);
|
||||
args.putString(ARG_ARTIST_NAME, artist.name);
|
||||
Fragment f;
|
||||
switch (position) {
|
||||
case 1:
|
||||
f = mPages.get(position, new ViewPagerTabArtistAlbumFragment());
|
||||
break;
|
||||
case 0:
|
||||
f = mPages.get(position, new ViewPagerTabArtistSongListFragment());
|
||||
break;
|
||||
case 2:
|
||||
f = mPages.get(position, new ViewPagerTabArtistBioFragment());
|
||||
break;
|
||||
default:
|
||||
f = mPages.get(position, new MainActivity.PlaceholderFragment());
|
||||
break;
|
||||
}
|
||||
f.setArguments(args);
|
||||
mPages.put(position, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public Fragment getItemAt(int position) {
|
||||
return mPages.get(position, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return titles.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
if (0 <= mPages.indexOfKey(position)) {
|
||||
mPages.remove(position);
|
||||
}
|
||||
super.destroyItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return titles[position];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Explode;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabSearchAbleFragment;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsFabActivity;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.NavigationDrawerFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments.AlbumViewFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments.ArtistViewFragment;
|
||||
import com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments.SongViewFragment;
|
||||
import com.kabouzeid.materialmusic.util.ImageLoaderUtil;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
|
||||
public class MainActivity extends AbsFabActivity
|
||||
implements NavigationDrawerFragment.NavigationDrawerCallbacks, OnMusicRemoteEventListener, KabViewsDisableAble {
|
||||
public static final String TAG = MainActivity.class.getSimpleName();
|
||||
|
||||
private int currentFragmentPosition = -1;
|
||||
|
||||
private DrawerLayout drawerLayout;
|
||||
private ActionBarDrawerToggle drawerToggle;
|
||||
private NavigationDrawerFragment navigationDrawerFragment;
|
||||
private CharSequence toolbarTitle;
|
||||
private Toolbar toolbar;
|
||||
private View statusBar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setUpTranslucence(true, true);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
initViews();
|
||||
setUpToolBar();
|
||||
|
||||
navigationDrawerFragment.setUp(
|
||||
R.id.navigation_drawer,
|
||||
drawerLayout
|
||||
);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setExitTransition(new Explode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateNavigationDrawerHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getApp().getMusicPlayerRemote().removeAllOnMusicRemoteEventListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationDrawerItemSelected(int position) {
|
||||
if (position == NavigationDrawerFragment.NAVIGATION_DRAWER_HEADER) {
|
||||
openCurrentPlayingIfPossible(null);
|
||||
} else {
|
||||
setFragment(position);
|
||||
}
|
||||
}
|
||||
|
||||
private void setFragment(int position) {
|
||||
if (currentFragmentPosition != position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
if (getApp().MainActivityFragments[position] == null) {
|
||||
getApp().MainActivityFragments[position] = new SongViewFragment();
|
||||
}
|
||||
toolbarTitle = getString(R.string.all_songs);
|
||||
break;
|
||||
case 1:
|
||||
if (getApp().MainActivityFragments[position] == null) {
|
||||
getApp().MainActivityFragments[position] = new AlbumViewFragment();
|
||||
}
|
||||
toolbarTitle = getString(R.string.albums);
|
||||
break;
|
||||
case 2:
|
||||
if (getApp().MainActivityFragments[position] == null) {
|
||||
getApp().MainActivityFragments[position] = new ArtistViewFragment();
|
||||
}
|
||||
toolbarTitle = getString(R.string.artists);
|
||||
break;
|
||||
case 3:
|
||||
if (getApp().MainActivityFragments[position] == null) {
|
||||
getApp().MainActivityFragments[position] = new PlaceholderFragment();
|
||||
}
|
||||
toolbarTitle = getString(R.string.genres);
|
||||
break;
|
||||
case 4:
|
||||
if (getApp().MainActivityFragments[position] == null) {
|
||||
getApp().MainActivityFragments[position] = new PlaceholderFragment();
|
||||
}
|
||||
toolbarTitle = getString(R.string.playlists);
|
||||
break;
|
||||
default:
|
||||
toolbarTitle = getString(R.string.app_name);
|
||||
return;
|
||||
}
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
fragmentManager.beginTransaction()
|
||||
.replace(R.id.container, getApp().MainActivityFragments[position])
|
||||
.commit();
|
||||
currentFragmentPosition = position;
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
navigationDrawerFragment = (NavigationDrawerFragment)
|
||||
getFragmentManager().findFragmentById(R.id.navigation_drawer);
|
||||
updateNavigationDrawerHeader();
|
||||
}
|
||||
|
||||
public void restoreActionBar() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setTitle(toolbarTitle);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.drawer, menu);
|
||||
restoreActionBar();
|
||||
|
||||
final MenuItem search = menu.findItem(R.id.action_search);
|
||||
search.setVisible(currentFragmentPosition != -1 && getApp().MainActivityFragments[currentFragmentPosition] instanceof KabSearchAbleFragment);
|
||||
|
||||
|
||||
SearchView searchView = (SearchView) MenuItemCompat.getActionView(search);
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
if (currentFragmentPosition != -1 && getApp().MainActivityFragments[currentFragmentPosition] instanceof KabSearchAbleFragment) {
|
||||
((KabSearchAbleFragment) getApp().MainActivityFragments[currentFragmentPosition]).search(newText);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
MenuItemCompat.setOnActionExpandListener(search, new MenuItemCompat.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
if (currentFragmentPosition != -1 && getApp().MainActivityFragments[currentFragmentPosition] instanceof KabSearchAbleFragment) {
|
||||
((KabSearchAbleFragment) getApp().MainActivityFragments[currentFragmentPosition]).returnToNonSearch();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (drawerToggle.onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case R.id.action_settings:
|
||||
return true;
|
||||
case R.id.action_current_playing:
|
||||
openCurrentPlayingIfPossible(null);
|
||||
return true;
|
||||
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
drawerToggle.onConfigurationChanged(newConfig);
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (navigationDrawerFragment.isDrawerOpen()) {
|
||||
drawerLayout.closeDrawers();
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void setUpToolBar() {
|
||||
toolbarTitle = getTitle();
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
statusBar = findViewById(R.id.statusBar);
|
||||
setSupportActionBar(toolbar);
|
||||
ViewUtil.setBackgroundAlpha(toolbar, 0.97f, Util.resolveColor(this, R.attr.colorPrimary));
|
||||
ViewUtil.setBackgroundAlpha(statusBar, 0.97f, Util.resolveColor(this, R.attr.colorPrimary));
|
||||
setUpDrawerToggle();
|
||||
}
|
||||
|
||||
private void setUpDrawerToggle() {
|
||||
drawerToggle = new ActionBarDrawerToggle(
|
||||
this,
|
||||
drawerLayout,
|
||||
R.string.navigation_drawer_open,
|
||||
R.string.navigation_drawer_close
|
||||
);
|
||||
drawerLayout.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
drawerToggle.syncState();
|
||||
}
|
||||
});
|
||||
drawerLayout.setDrawerListener(drawerToggle);
|
||||
}
|
||||
|
||||
private void updateNavigationDrawerHeader() {
|
||||
Song song = getApp().getMusicPlayerRemote().getCurrentSong();
|
||||
if (navigationDrawerFragment != null && song.id != -1) {
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(song.albumId).toString(), navigationDrawerFragment.getAlbumArtImageView(), new ImageLoaderUtil.defaultAlbumArtOnFailed());
|
||||
navigationDrawerFragment.getSongTitle().setText(song.title);
|
||||
navigationDrawerFragment.getSongArtist().setText(song.artistName);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableFragmentViews() {
|
||||
if (currentFragmentPosition >= 0 && currentFragmentPosition < getApp().MainActivityFragments.length) {
|
||||
if (getApp().MainActivityFragments[currentFragmentPosition] instanceof KabViewsDisableAble) {
|
||||
((KabViewsDisableAble) getApp().MainActivityFragments[currentFragmentPosition]).disableViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enableFragmentViews() {
|
||||
if (currentFragmentPosition >= 0 && currentFragmentPosition < getApp().MainActivityFragments.length) {
|
||||
if (getApp().MainActivityFragments[currentFragmentPosition] instanceof KabViewsDisableAble) {
|
||||
((KabViewsDisableAble) getApp().MainActivityFragments[currentFragmentPosition]).enableViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areFragmentViewsEnabled() {
|
||||
if (currentFragmentPosition >= 0 && currentFragmentPosition < getApp().MainActivityFragments.length) {
|
||||
if (getApp().MainActivityFragments[currentFragmentPosition] instanceof KabViewsDisableAble) {
|
||||
return ((KabViewsDisableAble) getApp().MainActivityFragments[currentFragmentPosition]).areViewsEnabled();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
try {
|
||||
super.enableViews();
|
||||
toolbar.setEnabled(true);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "wasn't able to enable the views", e.fillInStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
try {
|
||||
super.disableViews();
|
||||
toolbar.setEnabled(false);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "wasn't able to disable the views", e.fillInStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlaceholderFragment extends Fragment {
|
||||
|
||||
public PlaceholderFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_place_holder, container, false);
|
||||
return rootView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.helper.PlayingQueueDialogHelper;
|
||||
import com.kabouzeid.materialmusic.helper.SongDetailDialogHelper;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.lastfm.artist.LastFMArtistImageLoader;
|
||||
import com.kabouzeid.materialmusic.loader.SongFileLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.service.MusicService;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsFabActivity;
|
||||
import com.kabouzeid.materialmusic.ui.activities.tageditor.SongTagEditorActivity;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.nineoldandroids.view.ViewPropertyAnimator;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MusicControllerActivity extends AbsFabActivity implements OnMusicRemoteEventListener {
|
||||
public static final String TAG = MusicControllerActivity.class.getSimpleName();
|
||||
|
||||
private static final int DEFAULT_DELAY = 350;
|
||||
private static final int DEFAULT_ANIMATION_TIME = 1000;
|
||||
|
||||
private Song song;
|
||||
private ImageView albumArt;
|
||||
private ImageView artistArt;
|
||||
private TextView songTitle;
|
||||
private TextView songArtist;
|
||||
private TextView currentSongProgress;
|
||||
private TextView totalSongDuration;
|
||||
private View footer;
|
||||
private SeekBar progressSlider;
|
||||
private ImageButton nextButton;
|
||||
private ImageButton prevButton;
|
||||
private ImageButton repeatButton;
|
||||
private ImageButton shuffleButton;
|
||||
|
||||
private int lastFooterColor = -1;
|
||||
|
||||
private boolean killThreads = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setUpTranslucence(true, false);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_music_controller);
|
||||
|
||||
initViews();
|
||||
|
||||
moveSeekBarIntoPlace();
|
||||
|
||||
updateCurrentSong();
|
||||
|
||||
setUpMusicControllers();
|
||||
|
||||
prepareViewsForOpenAnimation();
|
||||
|
||||
setUpToolBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_title_playing, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
super.onBackPressed();
|
||||
return true;
|
||||
case R.id.action_playing_queue:
|
||||
final MaterialDialog materialDialog = PlayingQueueDialogHelper.getDialog(this);
|
||||
materialDialog.show();
|
||||
return true;
|
||||
case R.id.action_tag_editor:
|
||||
Intent intent = new Intent(this, SongTagEditorActivity.class);
|
||||
intent.putExtra(AppKeys.E_ID, song.id);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.action_details:
|
||||
String songFilePath = SongFileLoader.getSongFile(this, song.id);
|
||||
File songFile = new File(songFilePath);
|
||||
SongDetailDialogHelper.getDialog(this, songFile).show();
|
||||
return true;
|
||||
case R.id.action_go_to_album:
|
||||
goToAlbumDetailsActivity(song.albumId, null);
|
||||
return true;
|
||||
case R.id.action_go_to_artist:
|
||||
goToArtistDetailsActivity(song.artistId, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
startMusicControllerStateUpdateThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
killThreads = true;
|
||||
}
|
||||
|
||||
private void updateCurrentSong() {
|
||||
getCurrentSongAndQueue();
|
||||
setHeadersText();
|
||||
setUpArtistArt();
|
||||
setUpAlbumArtAndApplyPalette();
|
||||
totalSongDuration.setText(MusicUtil.getReadableDurationString(song.duration));
|
||||
currentSongProgress.setText(MusicUtil.getReadableDurationString(-1));
|
||||
}
|
||||
|
||||
private void moveSeekBarIntoPlace() {
|
||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) progressSlider.getLayoutParams();
|
||||
progressSlider.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
lp.setMargins(0, 0, 0, -(progressSlider.getMeasuredHeight() / 2));
|
||||
progressSlider.setLayoutParams(lp);
|
||||
}
|
||||
|
||||
private void setHeadersText() {
|
||||
songTitle.setText(song.title);
|
||||
songArtist.setText(song.artistName);
|
||||
}
|
||||
|
||||
private void setUpAlbumArtAndApplyPalette() {
|
||||
ImageLoader.getInstance().displayImage(MusicUtil.getAlbumArtUri(song.albumId).toString(), albumArt, new ImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
albumArt.setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
albumArt.setImageResource(R.drawable.default_album_art);
|
||||
setStandardColors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, final View view, Bitmap loadedImage) {
|
||||
applyPalette(loadedImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
albumArt.setImageResource(R.drawable.default_album_art);
|
||||
setStandardColors();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpArtistArt() {
|
||||
if (artistArt != null) {
|
||||
artistArt.setImageResource(R.drawable.default_artist_image);
|
||||
LastFMArtistImageLoader.loadArtistImage(this, song.artistName, new LastFMArtistImageLoader.ArtistImageLoaderCallback() {
|
||||
@Override
|
||||
public void onArtistImageLoaded(Bitmap artistImage) {
|
||||
artistArt.setImageBitmap(artistImage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void getCurrentSongAndQueue() {
|
||||
if (getApp().getMusicPlayerRemote().getPosition() >= 0) {
|
||||
song = getApp().getMusicPlayerRemote().getPlayingQueue().get(getApp().getMusicPlayerRemote().getPosition());
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
nextButton = (ImageButton) findViewById(R.id.next_button);
|
||||
prevButton = (ImageButton) findViewById(R.id.prev_button);
|
||||
repeatButton = (ImageButton) findViewById(R.id.repeat_button);
|
||||
shuffleButton = (ImageButton) findViewById(R.id.shuffle_button);
|
||||
albumArt = (ImageView) findViewById(R.id.album_art);
|
||||
artistArt = (ImageView) findViewById(R.id.artist_image);
|
||||
songTitle = (TextView) findViewById(R.id.song_title);
|
||||
songArtist = (TextView) findViewById(R.id.song_artist);
|
||||
currentSongProgress = (TextView) findViewById(R.id.song_current_progress);
|
||||
totalSongDuration = (TextView) findViewById(R.id.song_total_time);
|
||||
footer = findViewById(R.id.footer);
|
||||
progressSlider = (SeekBar) findViewById(R.id.progress_slider);
|
||||
}
|
||||
|
||||
private void applyPalette(Bitmap bitmap) {
|
||||
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
|
||||
@Override
|
||||
public void onGenerated(Palette palette) {
|
||||
Palette.Swatch swatch = palette.getVibrantSwatch();
|
||||
if (swatch != null) {
|
||||
animateColorChange(swatch.getRgb());
|
||||
songTitle.setTextColor(swatch.getTitleTextColor());
|
||||
songArtist.setTextColor(swatch.getBodyTextColor());
|
||||
} else {
|
||||
setStandardColors();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setStandardColors() {
|
||||
int songTitleTextColor = Util.resolveColor(this, R.attr.title_text_color);
|
||||
int artistNameTextColor = Util.resolveColor(this, R.attr.caption_text_color);
|
||||
int colorPrimary = Util.resolveColor(MusicControllerActivity.this, R.attr.colorPrimary);
|
||||
|
||||
animateColorChange(colorPrimary);
|
||||
|
||||
songTitle.setTextColor(songTitleTextColor);
|
||||
songArtist.setTextColor(artistNameTextColor);
|
||||
}
|
||||
|
||||
private void animateColorChange(final int newColor) {
|
||||
if (lastFooterColor != -1 && lastFooterColor != newColor) {
|
||||
ViewUtil.animateViewColor(footer, lastFooterColor, newColor, 300);
|
||||
} else {
|
||||
footer.setBackgroundColor(newColor);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setNavigationBarColor(newColor);
|
||||
}
|
||||
lastFooterColor = newColor;
|
||||
}
|
||||
|
||||
private void setUpMusicControllers() {
|
||||
setUpPrevNext();
|
||||
setUpRepeatButton();
|
||||
setUpShuffleButton();
|
||||
setUpProgressSlider();
|
||||
}
|
||||
|
||||
private void setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
getApp().getMusicPlayerRemote().seekTo(progress);
|
||||
}
|
||||
currentSongProgress.setText(MusicUtil.getReadableDurationString(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpPrevNext() {
|
||||
nextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getApp().getMusicPlayerRemote().playNextSong();
|
||||
}
|
||||
});
|
||||
prevButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getApp().getMusicPlayerRemote().back();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpShuffleButton() {
|
||||
updateShuffleState();
|
||||
shuffleButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getApp().getMusicPlayerRemote().toggleShuffleMode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpRepeatButton() {
|
||||
updateRepeatState();
|
||||
repeatButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getApp().getMusicPlayerRemote().cycleRepeatMode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateRepeatState() {
|
||||
switch (getApp().getMusicPlayerRemote().getRepeatMode()) {
|
||||
case MusicService.REPEAT_MODE_NONE:
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_grey600_48dp);
|
||||
break;
|
||||
case MusicService.REPEAT_MODE_ALL:
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_48dp);
|
||||
break;
|
||||
default:
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_48dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateShuffleState() {
|
||||
switch (getApp().getMusicPlayerRemote().getShuffleMode()) {
|
||||
case MusicService.SHUFFLE_MODE_SHUFFLE:
|
||||
shuffleButton.setImageResource(R.drawable.ic_shuffle_white_48dp);
|
||||
break;
|
||||
default:
|
||||
shuffleButton.setImageResource(R.drawable.ic_shuffle_grey600_48dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControllerState() {
|
||||
super.updateControllerState();
|
||||
updateRepeatState();
|
||||
updateShuffleState();
|
||||
}
|
||||
|
||||
private void startMusicControllerStateUpdateThread() {
|
||||
killThreads = false;
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int currentPosition = 0;
|
||||
int total = 0;
|
||||
while (getApp().getMusicPlayerRemote().isMusicBound() && !killThreads) {
|
||||
try {
|
||||
total = getApp().getMusicPlayerRemote().getSongDurationMillis();
|
||||
currentPosition = getApp().getMusicPlayerRemote().getSongProgressMillis();
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
progressSlider.setMax(total);
|
||||
progressSlider.setProgress(currentPosition);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicRemoteEvent(MusicRemoteEvent event) {
|
||||
super.onMusicRemoteEvent(event);
|
||||
switch (event.getAction()) {
|
||||
case MusicRemoteEvent.NEXT:
|
||||
updateCurrentSong();
|
||||
break;
|
||||
case MusicRemoteEvent.PREV:
|
||||
updateCurrentSong();
|
||||
break;
|
||||
case MusicRemoteEvent.REPEAT_MODE_CHANGED:
|
||||
updateRepeatState();
|
||||
break;
|
||||
case MusicRemoteEvent.SHUFFLE_MODE_CHANGED:
|
||||
updateShuffleState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareViewsForOpenAnimation() {
|
||||
footer.setPivotY(0);
|
||||
footer.setScaleY(0);
|
||||
}
|
||||
|
||||
private void animateActivityOpened(int startDelay) {
|
||||
ViewPropertyAnimator.animate(footer)
|
||||
.scaleX(1)
|
||||
.scaleY(1)
|
||||
.setInterpolator(new DecelerateInterpolator(4))
|
||||
.setDuration(DEFAULT_ANIMATION_TIME)
|
||||
.setStartDelay(startDelay)
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) {
|
||||
animateActivityOpened(DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpToolBar() {
|
||||
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
|
||||
getSupportActionBar().setTitle(null);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean openCurrentPlayingIfPossible(Pair[] sharedViews) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.songadapter.SongAdapter;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.ui.activities.AlbumDetailActivity;
|
||||
import com.kabouzeid.materialmusic.ui.activities.ArtistDetailActivity;
|
||||
import com.kabouzeid.materialmusic.ui.activities.MusicControllerActivity;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
/**
|
||||
* Created by karim on 20.01.15.
|
||||
*/
|
||||
public abstract class AbsBaseActivity extends ActionBarActivity implements KabViewsDisableAble, SongAdapter.GoToAble {
|
||||
private App app;
|
||||
private boolean areViewsEnabled;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(getApp().getAppTheme());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
enableViews();
|
||||
}
|
||||
|
||||
protected void setUpTranslucence(boolean statusBarTranslucent, boolean navigationBarTranslucent) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
Util.setStatusBarTranslucent(getWindow(), statusBarTranslucent);
|
||||
if (getApp().isInPortraitMode() || getApp().isTablet()) {
|
||||
Util.setNavBarTranslucent(getWindow(), navigationBarTranslucent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean openCurrentPlayingIfPossible(Pair[] sharedViews) {
|
||||
if (getApp().getMusicPlayerRemote().getPosition() != -1) {
|
||||
if (areViewsEnabled()) {
|
||||
disableViews();
|
||||
Intent intent = new Intent(this, MusicControllerActivity.class);
|
||||
if (sharedViews != null) {
|
||||
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
|
||||
sharedViews
|
||||
);
|
||||
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());
|
||||
} else {
|
||||
startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getResources().getString(R.string.nothing_playing), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToAlbum(int albumId) {
|
||||
goToAlbumDetailsActivity(albumId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToArtist(int artistId) {
|
||||
goToArtistDetailsActivity(artistId, null);
|
||||
}
|
||||
|
||||
public void goToAlbumDetailsActivity(int albumId, Pair[] sharedViews) {
|
||||
final Intent intent = new Intent(this, AlbumDetailActivity.class);
|
||||
intent.putExtra(AppKeys.E_ALBUM, albumId);
|
||||
if (sharedViews != null) {
|
||||
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
|
||||
sharedViews
|
||||
);
|
||||
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());
|
||||
} else {
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public void goToArtistDetailsActivity(int artistId, Pair[] sharedViews) {
|
||||
final Intent intent = new Intent(this, ArtistDetailActivity.class);
|
||||
intent.putExtra(AppKeys.E_ARTIST, artistId);
|
||||
if (sharedViews != null) {
|
||||
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
|
||||
sharedViews
|
||||
);
|
||||
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());
|
||||
} else {
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areViewsEnabled() {
|
||||
return areViewsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
areViewsEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
areViewsEnabled = false;
|
||||
}
|
||||
|
||||
protected App getApp() {
|
||||
if (app == null) {
|
||||
app = (App) getApplicationContext();
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
|
||||
import com.kabouzeid.materialmusic.misc.SmallOnGestureListener;
|
||||
import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.01.15.
|
||||
*/
|
||||
public abstract class AbsFabActivity extends AbsBaseActivity implements OnMusicRemoteEventListener {
|
||||
private FloatingActionButton fab;
|
||||
|
||||
protected FloatingActionButton getFab() {
|
||||
if (fab == null) {
|
||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
}
|
||||
return fab;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
setUpFab();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateControllerState();
|
||||
getApp().getMusicPlayerRemote().addOnMusicRemoteEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getApp().getMusicPlayerRemote().removeOnMusicRemoteEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
super.enableViews();
|
||||
fab.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
super.disableViews();
|
||||
fab.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean openCurrentPlayingIfPossible(Pair[] sharedViews) {
|
||||
return super.openCurrentPlayingIfPossible(getSharedViewsWithFab(sharedViews));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToArtistDetailsActivity(int artistId, Pair[] sharedViews) {
|
||||
super.goToArtistDetailsActivity(artistId, getSharedViewsWithFab(sharedViews));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToAlbumDetailsActivity(int albumId, Pair[] sharedViews) {
|
||||
super.goToAlbumDetailsActivity(albumId, getSharedViewsWithFab(sharedViews));
|
||||
}
|
||||
|
||||
private Pair[] getSharedViewsWithFab(Pair[] sharedViews) {
|
||||
Pair[] sharedViewsWithFab;
|
||||
if (sharedViews != null) {
|
||||
sharedViewsWithFab = new Pair[sharedViews.length + 1];
|
||||
for (int i = 0; i < sharedViews.length; i++) {
|
||||
sharedViewsWithFab[i] = sharedViews[i];
|
||||
}
|
||||
} else {
|
||||
sharedViewsWithFab = new Pair[1];
|
||||
}
|
||||
sharedViewsWithFab[sharedViewsWithFab.length - 1] = Pair.create((View) getFab(), getString(R.string.transition_fab));
|
||||
return sharedViewsWithFab;
|
||||
}
|
||||
|
||||
private void setUpFab() {
|
||||
updateFabState();
|
||||
final GestureDetector gestureDetector = new GestureDetector(this, new SmallOnGestureListener() {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
openCurrentPlayingIfPossible(null);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
getFab().setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (getApp().getMusicPlayerRemote().getPosition() != -1) {
|
||||
if (getApp().getMusicPlayerRemote().isPlaying()) {
|
||||
getApp().getMusicPlayerRemote().pauseSong();
|
||||
} else {
|
||||
getApp().getMusicPlayerRemote().resumePlaying();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(AbsFabActivity.this, getResources().getString(R.string.nothing_playing), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getFab().setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
Toast.makeText(AbsFabActivity.this, getResources().getString(R.string.hint_fling_to_open), Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
getFab().setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
gestureDetector.onTouchEvent(event);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void updateControllerState() {
|
||||
updateFabState();
|
||||
}
|
||||
|
||||
private void updateFabState() {
|
||||
if (getApp().getMusicPlayerRemote().isPlaying()) {
|
||||
getFab().setImageResource(R.drawable.ic_pause_white_48dp);
|
||||
} else {
|
||||
getFab().setImageResource(R.drawable.ic_play_arrow_white_48dp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicRemoteEvent(MusicRemoteEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MusicRemoteEvent.PLAY:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_pause_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.PAUSE:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_play_arrow_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.RESUME:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_pause_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.STOP:
|
||||
fab.setImageDrawable(getResources().getDrawable(R.drawable.ic_play_arrow_white_48dp));
|
||||
break;
|
||||
case MusicRemoteEvent.QUEUE_COMPLETED:
|
||||
fab.setImageResource(R.drawable.ic_play_arrow_white_48dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities.tageditor;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableScrollView;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.misc.SmallObservableScrollViewCallbacks;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.kabouzeid.materialmusic.util.ViewUtil;
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
import com.nineoldandroids.view.ViewHelper;
|
||||
import com.nineoldandroids.view.ViewPropertyAnimator;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by karim on 18.01.15.
|
||||
*/
|
||||
public abstract class AbsTagEditorActivity extends ActionBarActivity {
|
||||
public static final String TAG = AbsTagEditorActivity.class.getSimpleName();
|
||||
private static final int REQUEST_CODE_SELECT_IMAGE = 1337;
|
||||
|
||||
private App app;
|
||||
private int id;
|
||||
private int headerVariableSpace;
|
||||
private int paletteColorPrimary;
|
||||
private boolean isInNoImageMode;
|
||||
|
||||
private FloatingActionButton fab;
|
||||
private ObservableScrollView scrollView;
|
||||
private Toolbar toolBar;
|
||||
private ImageView image;
|
||||
private View header;
|
||||
|
||||
private List<String> songPaths;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getApplicationContext();
|
||||
setTheme(app.getAppTheme());
|
||||
setUpTranslucence();
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getContentViewResId());
|
||||
|
||||
getIntentExtras();
|
||||
headerVariableSpace = getResources().getDimensionPixelSize(R.dimen.tagEditorHeaderVariableSpace);
|
||||
songPaths = getSongPaths();
|
||||
|
||||
initViews();
|
||||
setUpViews();
|
||||
setUpToolBar();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
scrollView = (ObservableScrollView) findViewById(R.id.observableScrollView);
|
||||
toolBar = (Toolbar) findViewById(R.id.toolbar);
|
||||
image = (ImageView) findViewById(R.id.image);
|
||||
header = findViewById(R.id.header);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
restoreStandardColors();
|
||||
setUpScrollView();
|
||||
setUpFab();
|
||||
setUpImageView();
|
||||
}
|
||||
|
||||
private void setUpScrollView() {
|
||||
scrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
||||
scrollView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrollView.scrollVerticallyTo(headerVariableSpace / 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpImageView() {
|
||||
loadCurrentImage();
|
||||
image.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new MaterialDialog.Builder(AbsTagEditorActivity.this)
|
||||
.title("Update image")
|
||||
.items(new CharSequence[]{"Download from LastFM", "Pick from internal storage", "Web search", "Delete"})
|
||||
.itemsCallback(new MaterialDialog.ListCallback() {
|
||||
@Override
|
||||
public void onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
getImageFromLastFM();
|
||||
break;
|
||||
case 1:
|
||||
startImagePicker();
|
||||
break;
|
||||
case 2:
|
||||
searchImageOnWeb();
|
||||
break;
|
||||
case 3:
|
||||
deleteImage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void searchWebFor(List<String> strings) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (String string : strings) {
|
||||
stringBuilder.append(string);
|
||||
stringBuilder.append(" ");
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
||||
intent.putExtra(SearchManager.QUERY, stringBuilder.toString());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void startImagePicker() {
|
||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||
photoPickerIntent.setType("image/*");
|
||||
startActivityForResult(photoPickerIntent, REQUEST_CODE_SELECT_IMAGE);
|
||||
}
|
||||
|
||||
private void setUpTranslucence() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
Util.setStatusBarTranslucent(getWindow(), false);
|
||||
Util.setNavBarTranslucent(getWindow(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void getIntentExtras() {
|
||||
Bundle intentExtras = getIntent().getExtras();
|
||||
if (intentExtras != null) {
|
||||
id = intentExtras.getInt(AppKeys.E_ID);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUpToolBar() {
|
||||
setSupportActionBar(toolBar);
|
||||
getSupportActionBar().setTitle(getResources().getString(R.string.tag_editor));
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_tag_editor, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
super.onBackPressed();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected void setUpFab() {
|
||||
ViewHelper.setScaleX(fab, 0);
|
||||
ViewHelper.setScaleY(fab, 0);
|
||||
fab.setEnabled(false);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showFab() {
|
||||
ViewPropertyAnimator.animate(fab)
|
||||
.setDuration(500)
|
||||
.setInterpolator(new OvershootInterpolator())
|
||||
.scaleX(1)
|
||||
.scaleY(1)
|
||||
.start();
|
||||
fab.setEnabled(true);
|
||||
}
|
||||
|
||||
private SmallObservableScrollViewCallbacks observableScrollViewCallbacks = new SmallObservableScrollViewCallbacks() {
|
||||
@Override
|
||||
public void onScrollChanged(int scrollY, boolean b, boolean b2) {
|
||||
float alpha;
|
||||
if (!isInNoImageMode) {
|
||||
alpha = 1 - (float) Math.max(0, headerVariableSpace - scrollY) / headerVariableSpace;
|
||||
} else {
|
||||
ViewHelper.setTranslationY(header, scrollY);
|
||||
alpha = 1;
|
||||
}
|
||||
ViewUtil.setBackgroundAlpha(toolBar, alpha, paletteColorPrimary);
|
||||
ViewUtil.setBackgroundAlpha(header, alpha, paletteColorPrimary);
|
||||
ViewHelper.setTranslationY(image, scrollY / 2);
|
||||
}
|
||||
};
|
||||
|
||||
private void applyPalette(final Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
|
||||
@Override
|
||||
public void onGenerated(Palette palette) {
|
||||
final int vibrantColor = palette.getVibrantColor(Util.resolveColor(AbsTagEditorActivity.this, R.attr.colorPrimary));
|
||||
paletteColorPrimary = vibrantColor;
|
||||
observableScrollViewCallbacks.onScrollChanged(scrollView.getCurrentScrollY(), false, false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(vibrantColor);
|
||||
getWindow().setNavigationBarColor(vibrantColor);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
restoreStandardColors();
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreStandardColors() {
|
||||
final int vibrantColor = Util.resolveColor(this, R.attr.colorPrimary);
|
||||
paletteColorPrimary = vibrantColor;
|
||||
observableScrollViewCallbacks.onScrollChanged(scrollView.getCurrentScrollY(), false, false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(vibrantColor);
|
||||
getWindow().setNavigationBarColor(vibrantColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setNoImageMode() {
|
||||
isInNoImageMode = true;
|
||||
image.setVisibility(View.GONE);
|
||||
image.setEnabled(false);
|
||||
scrollView.setPadding(0, Util.getActionBarSize(this), 0, 0);
|
||||
observableScrollViewCallbacks.onScrollChanged(scrollView.getCurrentScrollY(), false, false);
|
||||
}
|
||||
|
||||
protected void dataChanged() {
|
||||
showFab();
|
||||
}
|
||||
|
||||
protected void setImageBitmap(final Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
image.setImageBitmap(bitmap);
|
||||
applyPalette(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setImageRes(int resId) {
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
|
||||
setImageBitmap(bitmap);
|
||||
}
|
||||
|
||||
private void rescanMedia() {
|
||||
String[] toBeScanned = new String[songPaths.size()];
|
||||
toBeScanned = songPaths.toArray(toBeScanned);
|
||||
MediaScannerConnection.scanFile(this, toBeScanned, null, null);
|
||||
}
|
||||
|
||||
private AudioFile getAudioFile(String path) {
|
||||
try {
|
||||
return AudioFileIO.read(new File(path));
|
||||
} catch (CannotReadException | ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException e) {
|
||||
Log.e(TAG, "error while trying to create the AudioFile from File", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void writeValuesToFiles(final Map<FieldKey, String> fieldKeyValueMap) {
|
||||
writeValuesToFiles(fieldKeyValueMap, null, false);
|
||||
}
|
||||
|
||||
protected void writeValuesToFiles(final Map<FieldKey, String> fieldKeyValueMap, final Artwork artwork) {
|
||||
if (artwork == null) {
|
||||
writeValuesToFiles(fieldKeyValueMap, null, true);
|
||||
} else {
|
||||
writeValuesToFiles(fieldKeyValueMap, artwork, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeValuesToFiles(final Map<FieldKey, String> fieldKeyValueMap, boolean deleteArtwork) {
|
||||
writeValuesToFiles(fieldKeyValueMap, null, deleteArtwork);
|
||||
}
|
||||
|
||||
protected void writeValuesToFiles(final Map<FieldKey, String> fieldKeyValueMap, final Artwork artwork, final boolean deleteArtwork) {
|
||||
final String writingFileStr = getResources().getString(R.string.writing_file_number);
|
||||
final MaterialDialog progressDialog = new MaterialDialog.Builder(AbsTagEditorActivity.this)
|
||||
.customView(R.layout.dialog_loading, true)
|
||||
.title(writingFileStr)
|
||||
.cancelable(false)
|
||||
.build();
|
||||
final TextView progressText = (TextView) progressDialog.getCustomView().findViewById(R.id.text);
|
||||
progressDialog.show();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < songPaths.size(); i++) {
|
||||
String songPath = songPaths.get(i);
|
||||
final int finalI = i;
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressText.setText((finalI + 1) + "/" + songPaths.size());
|
||||
}
|
||||
});
|
||||
try {
|
||||
AudioFile audioFile = AudioFileIO.read(new File(songPath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
for (Map.Entry<FieldKey, String> entry : fieldKeyValueMap.entrySet()) {
|
||||
tag.setField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (deleteArtwork) {
|
||||
tag.deleteArtworkField();
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField();
|
||||
tag.setField(artwork);
|
||||
}
|
||||
audioFile.commit();
|
||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
Log.e(TAG, "Error while reading audio file.", e);
|
||||
} catch (CannotWriteException e) {
|
||||
Log.e(TAG, "Error while writing audio file.", e);
|
||||
}
|
||||
}
|
||||
if (deleteArtwork) {
|
||||
String imagePath = MusicUtil.getAlbumArtUri(getId()).toString();
|
||||
ImageLoader.getInstance().getDiskCache().remove(imagePath);
|
||||
MemoryCacheUtils.removeFromCache(imagePath, ImageLoader.getInstance().getMemoryCache());
|
||||
MusicUtil.deleteAlbumArt(AbsTagEditorActivity.this, getId());
|
||||
} else if (artwork != null) {
|
||||
String imagePath = MusicUtil.getAlbumArtUri(getId()).toString();
|
||||
MemoryCacheUtils.removeFromCache(imagePath, ImageLoader.getInstance().getMemoryCache());
|
||||
ImageLoader.getInstance().getDiskCache().remove(imagePath);
|
||||
}
|
||||
progressDialog.dismiss();
|
||||
rescanMedia();
|
||||
restartApp();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void restartApp() {
|
||||
Intent i = getBaseContext().getPackageManager()
|
||||
.getLaunchIntentForPackage(getBaseContext().getPackageName());
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
|
||||
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SELECT_IMAGE:
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri selectedImage = imageReturnedIntent.getData();
|
||||
loadImageFromFile(selectedImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void save();
|
||||
|
||||
protected abstract int getContentViewResId();
|
||||
|
||||
protected abstract void loadCurrentImage();
|
||||
|
||||
protected abstract void getImageFromLastFM();
|
||||
|
||||
protected abstract void searchImageOnWeb();
|
||||
|
||||
protected abstract void loadImageFromFile(Uri selectedFile);
|
||||
|
||||
protected abstract void deleteImage();
|
||||
|
||||
protected abstract List<String> getSongPaths();
|
||||
|
||||
protected App getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
protected int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected String getSongTitle() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.TITLE);
|
||||
}
|
||||
|
||||
protected String getAlbumTitle() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.ALBUM);
|
||||
}
|
||||
|
||||
protected String getArtistName() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.ARTIST);
|
||||
}
|
||||
|
||||
protected String getAlbumArtistName() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.ALBUM_ARTIST);
|
||||
}
|
||||
|
||||
protected String getGenreName() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.GENRE);
|
||||
}
|
||||
|
||||
protected String getSongYear() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.YEAR);
|
||||
}
|
||||
|
||||
protected String getTrackNumber() throws NullPointerException {
|
||||
return getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirst(FieldKey.TRACK);
|
||||
}
|
||||
|
||||
protected Bitmap getAlbumArt() throws NullPointerException {
|
||||
Artwork artworkTag = getAudioFile(songPaths.get(0)).getTagOrCreateAndSetDefault().getFirstArtwork();
|
||||
if (artworkTag != null) {
|
||||
byte[] artworkBinaryData = artworkTag.getBinaryData();
|
||||
return BitmapFactory.decodeByteArray(artworkBinaryData, 0, artworkBinaryData.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities.tageditor;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.lastfm.album.LastFMAlbumImageLoader;
|
||||
import com.kabouzeid.materialmusic.loader.AlbumSongLoader;
|
||||
import com.kabouzeid.materialmusic.loader.SongFileLoader;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.util.MusicUtil;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
||||
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AlbumTagEditorActivity extends AbsTagEditorActivity implements TextWatcher {
|
||||
public static final String TAG = AlbumTagEditorActivity.class.getSimpleName();
|
||||
|
||||
private File albumArtFile;
|
||||
private Bitmap albumArtBitmap;
|
||||
private boolean deleteAlbumArt;
|
||||
|
||||
private EditText albumTitle;
|
||||
private EditText albumArtistName;
|
||||
private EditText genreName;
|
||||
private EditText year;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
initViews();
|
||||
setUpViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
albumTitle = (EditText) findViewById(R.id.album_title);
|
||||
albumArtistName = (EditText) findViewById(R.id.album_artist);
|
||||
genreName = (EditText) findViewById(R.id.genre);
|
||||
year = (EditText) findViewById(R.id.year);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
fillViewsWithFileTags();
|
||||
albumTitle.addTextChangedListener(this);
|
||||
albumArtistName.addTextChangedListener(this);
|
||||
genreName.addTextChangedListener(this);
|
||||
year.addTextChangedListener(this);
|
||||
}
|
||||
|
||||
|
||||
private void fillViewsWithFileTags() {
|
||||
albumTitle.setText(getAlbumTitle());
|
||||
albumArtistName.setText(getAlbumArtistName());
|
||||
genreName.setText(getGenreName());
|
||||
year.setText(getSongYear());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void save() {
|
||||
Artwork artwork = null;
|
||||
Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class);
|
||||
fieldKeyValueMap.put(FieldKey.ALBUM, albumTitle.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.ALBUM_ARTIST, albumArtistName.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.GENRE, genreName.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString());
|
||||
|
||||
try {
|
||||
albumArtFile = MusicUtil.getAlbumArtFile(this, String.valueOf(getId()));
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error while creating albumArtFile", e);
|
||||
}
|
||||
|
||||
if (albumArtBitmap != null && albumArtFile != null) {
|
||||
try {
|
||||
albumArtBitmap.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||
MusicUtil.insertAlbumArt(this, getId(), albumArtFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error while trying to create the artwork from file", e);
|
||||
}
|
||||
}
|
||||
writeValuesToFiles(fieldKeyValueMap, artwork, deleteAlbumArt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getContentViewResId() {
|
||||
return R.layout.activity_album_tag_editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadCurrentImage() {
|
||||
setImageBitmap(getAlbumArt());
|
||||
deleteAlbumArt = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getImageFromLastFM() {
|
||||
String albumTitleStr = albumTitle.getText().toString();
|
||||
String albumArtistNameStr = albumArtistName.getText().toString();
|
||||
if (albumArtistNameStr.trim().equals("") || albumTitleStr.trim().equals("")) {
|
||||
Toast.makeText(this, getResources().getString(R.string.album_or_artist_empty), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
LastFMAlbumImageLoader.loadAlbumImage(this, albumTitleStr, albumArtistNameStr, new LastFMAlbumImageLoader.AlbumImageLoaderCallback() {
|
||||
@Override
|
||||
public void onAlbumImageLoaded(Bitmap albumImage, String uri) {
|
||||
if (albumImage != null) {
|
||||
setImageBitmap(albumImage);
|
||||
albumArtBitmap = albumImage;
|
||||
deleteAlbumArt = false;
|
||||
dataChanged();
|
||||
Toast.makeText(AlbumTagEditorActivity.this, "Success.", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(AlbumTagEditorActivity.this, "Failed.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void searchImageOnWeb() {
|
||||
List<String> query = new ArrayList<>();
|
||||
query.add(albumTitle.getText().toString());
|
||||
query.add(albumArtistName.getText().toString());
|
||||
searchWebFor(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadImageFromFile(final Uri selectedFileUri) {
|
||||
DisplayImageOptions options = new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(false)
|
||||
.postProcessor(new BitmapProcessor() {
|
||||
@Override
|
||||
public Bitmap process(Bitmap bmp) {
|
||||
return Util.getAlbumArtScaledBitmap(bmp, true);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
albumArtBitmap = ImageLoader.getInstance().loadImageSync(selectedFileUri.toString(), options);
|
||||
if (albumArtBitmap != null) {
|
||||
setImageBitmap(albumArtBitmap);
|
||||
deleteAlbumArt = false;
|
||||
dataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteImage() {
|
||||
setImageRes(R.drawable.default_album_art);
|
||||
deleteAlbumArt = true;
|
||||
dataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSongPaths() {
|
||||
List<Song> songs = AlbumSongLoader.getAlbumSongList(this, getId());
|
||||
List<Integer> songIds = new ArrayList<>();
|
||||
for (Song song : songs) {
|
||||
songIds.add(song.id);
|
||||
}
|
||||
return SongFileLoader.getSongFiles(this, songIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
dataChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package com.kabouzeid.materialmusic.ui.activities.tageditor;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.loader.SongFileLoader;
|
||||
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SongTagEditorActivity extends AbsTagEditorActivity implements TextWatcher {
|
||||
public static final String TAG = SongTagEditorActivity.class.getSimpleName();
|
||||
|
||||
private EditText songTitle;
|
||||
private EditText albumTitle;
|
||||
private EditText artistName;
|
||||
private EditText genreName;
|
||||
private EditText year;
|
||||
private EditText trackNumber;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setNoImageMode();
|
||||
initViews();
|
||||
setUpViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
songTitle = (EditText) findViewById(R.id.title1);
|
||||
albumTitle = (EditText) findViewById(R.id.title2);
|
||||
artistName = (EditText) findViewById(R.id.artist);
|
||||
genreName = (EditText) findViewById(R.id.genre);
|
||||
year = (EditText) findViewById(R.id.year);
|
||||
trackNumber = (EditText) findViewById(R.id.track_number);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
fillViewsWithFileTags();
|
||||
songTitle.addTextChangedListener(this);
|
||||
albumTitle.addTextChangedListener(this);
|
||||
artistName.addTextChangedListener(this);
|
||||
genreName.addTextChangedListener(this);
|
||||
year.addTextChangedListener(this);
|
||||
trackNumber.addTextChangedListener(this);
|
||||
}
|
||||
|
||||
|
||||
private void fillViewsWithFileTags() {
|
||||
songTitle.setText(getSongTitle());
|
||||
albumTitle.setText(getAlbumTitle());
|
||||
artistName.setText(getArtistName());
|
||||
genreName.setText(getGenreName());
|
||||
year.setText(getSongYear());
|
||||
trackNumber.setText(getTrackNumber());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void save() {
|
||||
Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class);
|
||||
fieldKeyValueMap.put(FieldKey.TITLE, songTitle.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.ALBUM, albumTitle.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.ARTIST, artistName.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.GENRE, genreName.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString());
|
||||
fieldKeyValueMap.put(FieldKey.TRACK, trackNumber.getText().toString());
|
||||
writeValuesToFiles(fieldKeyValueMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getContentViewResId() {
|
||||
return R.layout.activity_song_tag_editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadCurrentImage() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getImageFromLastFM() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void searchImageOnWeb() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadImageFromFile(Uri imageFilePath) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteImage() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSongPaths() {
|
||||
List<Integer> tempIdList = new ArrayList<>();
|
||||
tempIdList.add(getId());
|
||||
return SongFileLoader.getSongFiles(this, tempIdList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
dataChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.NavigationDrawerItemAdapter;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.NavigationDrawerItem;
|
||||
import com.nhaarman.listviewanimations.appearance.simple.AlphaInAnimationAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NavigationDrawerFragment extends Fragment {
|
||||
private static final String TAG = NavigationDrawerFragment.class.getSimpleName();
|
||||
|
||||
public static final int NAVIGATION_DRAWER_HEADER = -1;
|
||||
private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
|
||||
|
||||
private App app;
|
||||
|
||||
private NavigationDrawerCallbacks mCallbacks;
|
||||
|
||||
private NavigationDrawerItemAdapter drawerAdapter;
|
||||
|
||||
private DrawerLayout drawerLayout;
|
||||
public View fragmentRootView;
|
||||
private ListView drawerListView;
|
||||
private View fragmentContainerView;
|
||||
|
||||
private Button headerButton;
|
||||
private ImageView albumArt;
|
||||
private TextView songTitle;
|
||||
private TextView songArtist;
|
||||
|
||||
private int currentSelectedPosition;
|
||||
private boolean fromSavedInstanceState;
|
||||
private boolean userLearnedDrawer;
|
||||
|
||||
public NavigationDrawerFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getActivity().getApplicationContext();
|
||||
userLearnedDrawer = app.getDefaultSharedPreferences().getBoolean(AppKeys.SP_USER_LEARNED_DRAWER, false);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
|
||||
fromSavedInstanceState = true;
|
||||
} else {
|
||||
currentSelectedPosition = app.getDefaultSharedPreferences().getInt(AppKeys.SP_NAVIGATION_DRAWER_ITEM_POSITION, 0);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
fragmentRootView = view;
|
||||
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
initViews();
|
||||
setUpViews();
|
||||
|
||||
selectItem(currentSelectedPosition);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
drawerListView = (ListView) fragmentRootView.findViewById(R.id.navigation_drawer_list);
|
||||
final View drawerHeader = fragmentRootView.findViewById(R.id.header);
|
||||
headerButton = (Button) drawerHeader.findViewById(R.id.header_clickable);
|
||||
albumArt = (ImageView) drawerHeader.findViewById(R.id.album_art);
|
||||
songTitle = (TextView) drawerHeader.findViewById(R.id.song_title);
|
||||
songArtist = (TextView) drawerHeader.findViewById(R.id.song_artist);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
headerButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
selectItem(NAVIGATION_DRAWER_HEADER);
|
||||
}
|
||||
});
|
||||
setUpListView();
|
||||
}
|
||||
|
||||
private void setUpListView() {
|
||||
final ArrayList<NavigationDrawerItem> navigationDrawerItems = new ArrayList<>();
|
||||
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.all_songs), R.drawable.songs));
|
||||
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.albums), R.drawable.album));
|
||||
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.artists), R.drawable.interpret));
|
||||
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.genres), R.drawable.songs));
|
||||
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.playlists), R.drawable.playlist));
|
||||
|
||||
drawerAdapter = new NavigationDrawerItemAdapter(getActivity(), R.id.navigation_drawer, navigationDrawerItems);
|
||||
|
||||
final AlphaInAnimationAdapter animationAdapter = new AlphaInAnimationAdapter(drawerAdapter);
|
||||
animationAdapter.setAbsListView(drawerListView);
|
||||
|
||||
drawerListView.setAdapter(animationAdapter);
|
||||
drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
selectItem(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isDrawerOpen() {
|
||||
return drawerLayout != null && drawerLayout.isDrawerOpen(fragmentContainerView);
|
||||
}
|
||||
|
||||
public void setUp(int fragmentId, final DrawerLayout drawerLayout) {
|
||||
fragmentContainerView = getActivity().findViewById(fragmentId);
|
||||
this.drawerLayout = drawerLayout;
|
||||
this.drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
||||
|
||||
|
||||
if (!userLearnedDrawer && !fromSavedInstanceState) {
|
||||
this.drawerLayout.openDrawer(fragmentContainerView);
|
||||
userLearnedDrawer = true;
|
||||
app.getDefaultSharedPreferences().edit().putBoolean(AppKeys.SP_USER_LEARNED_DRAWER, true).apply();
|
||||
}
|
||||
}
|
||||
|
||||
private void selectItem(int position) {
|
||||
if (position != NAVIGATION_DRAWER_HEADER) {
|
||||
currentSelectedPosition = position;
|
||||
if (drawerAdapter != null) {
|
||||
drawerAdapter.setChecked(position);
|
||||
}
|
||||
if (drawerLayout != null) {
|
||||
//close drawer lag workaround
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
drawerLayout.closeDrawer(fragmentContainerView);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
app.getDefaultSharedPreferences().edit().putInt(AppKeys.SP_NAVIGATION_DRAWER_ITEM_POSITION, position).apply();
|
||||
}
|
||||
if (mCallbacks != null) {
|
||||
mCallbacks.onNavigationDrawerItemSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
public TextView getSongArtist() {
|
||||
return songArtist;
|
||||
}
|
||||
|
||||
public ImageView getAlbumArtImageView() {
|
||||
return albumArt;
|
||||
}
|
||||
|
||||
public TextView getSongTitle() {
|
||||
return songTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
mCallbacks = (NavigationDrawerCallbacks) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mCallbacks = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition);
|
||||
}
|
||||
|
||||
public static interface NavigationDrawerCallbacks {
|
||||
void onNavigationDrawerItemSelected(int position);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.artistviewpager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableGridView;
|
||||
import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;
|
||||
import com.github.ksoichiro.android.observablescrollview.ScrollState;
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.ui.activities.ArtistDetailActivity;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
public abstract class AbsViewPagerTabArtistListFragment extends Fragment implements ObservableScrollViewCallbacks, KabViewsDisableAble {
|
||||
public static final String TAG = AbsViewPagerTabArtistListFragment.class.getSimpleName();
|
||||
protected App app;
|
||||
private ObservableGridView observableGridView;
|
||||
private Activity parentActivity;
|
||||
private int artistId = -1;
|
||||
private String artistName = "";
|
||||
private int paddingViewHeight;
|
||||
private boolean areViewsEnabled;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
app = (App) getActivity().getApplicationContext();
|
||||
parentActivity = getActivity();
|
||||
getArgs();
|
||||
|
||||
View view = inflater.inflate(R.layout.fragment_gridview, container, false);
|
||||
observableGridView = (ObservableGridView) view.findViewById(R.id.scroll);
|
||||
setGridViewPadding();
|
||||
observableGridView.setScrollViewCallbacks(this);
|
||||
ListAdapter adapter = getAdapter();
|
||||
if (adapter != null) {
|
||||
observableGridView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setGridViewPadding() {
|
||||
final int artistImageViewHeight = getResources().getDimensionPixelSize(R.dimen.header_image_height);
|
||||
final int titleViewHeight = getResources().getDimensionPixelSize(R.dimen.title_view_height);
|
||||
final int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);
|
||||
|
||||
paddingViewHeight = artistImageViewHeight + titleViewHeight + tabHeight;
|
||||
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
observableGridView.setPadding(0, paddingViewHeight, 0, Util.getNavigationBarHeight(getActivity()));
|
||||
} else {
|
||||
observableGridView.setPadding(0, paddingViewHeight, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void getArgs() {
|
||||
Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
artistId = args.getInt(ArtistDetailActivity.ARG_ARTIST_ID, -1);
|
||||
artistName = args.getString(ArtistDetailActivity.ARG_ARTIST_NAME, "");
|
||||
}
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return observableGridView.getCurrentScrollY() + paddingViewHeight;
|
||||
}
|
||||
|
||||
protected int getArtistId() {
|
||||
return artistId;
|
||||
}
|
||||
|
||||
protected String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
|
||||
public Activity getParentActivity() {
|
||||
return parentActivity;
|
||||
}
|
||||
|
||||
protected void setAdapter(ListAdapter adapter) {
|
||||
observableGridView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
protected void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {
|
||||
observableGridView.setOnItemClickListener(onItemClickListener);
|
||||
}
|
||||
|
||||
protected void setColumns(int columns) {
|
||||
observableGridView.setNumColumns(columns);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* You CAN return null here and use setAdapter(ListAdapter adapter) inside getAdapter() to manually set the adapter.
|
||||
*
|
||||
* (i.e. if you must set the adapter async).
|
||||
*
|
||||
* */
|
||||
protected abstract ListAdapter getAdapter();
|
||||
|
||||
@Override
|
||||
public void onScrollChanged(int scrollY, boolean b, boolean b2) {
|
||||
if (parentActivity instanceof ObservableScrollViewCallbacks) {
|
||||
if (getUserVisibleHint()) {
|
||||
((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY + paddingViewHeight, b, b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownMotionEvent() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
areViewsEnabled = false;
|
||||
observableGridView.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areViewsEnabled() {
|
||||
return areViewsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
areViewsEnabled = true;
|
||||
observableGridView.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enableViews();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.artistviewpager;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.AlbumViewGridAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.AlbumAlphabeticComparator;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.loader.ArtistAlbumLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
import com.kabouzeid.materialmusic.ui.activities.AlbumDetailActivity;
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 04.01.15.
|
||||
*/
|
||||
public class ViewPagerTabArtistAlbumFragment extends AbsViewPagerTabArtistListFragment {
|
||||
private FloatingActionButton fab;
|
||||
|
||||
@Override
|
||||
protected ListAdapter getAdapter() {
|
||||
List<Album> albums = ArtistAlbumLoader.getArtistAlbumList(getParentActivity(), getArtistId());
|
||||
Collections.sort(albums, new AlbumAlphabeticComparator());
|
||||
ListAdapter adapter = new AlbumViewGridAdapter(getParentActivity(), albums);
|
||||
setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Album album = (Album) parent.getItemAtPosition(position);
|
||||
View albumArtView = view.findViewById(R.id.album_art);
|
||||
|
||||
openAlbumDetailsActivityIfPossible(album, albumArtView);
|
||||
}
|
||||
});
|
||||
setColumns(2);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab = (FloatingActionButton) getParentActivity().findViewById(R.id.fab);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void openAlbumDetailsActivityIfPossible(Album album, View albumArtForTransition) {
|
||||
if (areParentActivitiesViewsEnabled()) {
|
||||
disableViews();
|
||||
disableParentActivitiesViews();
|
||||
|
||||
final Intent intent = new Intent(getActivity(), AlbumDetailActivity.class);
|
||||
intent.putExtra(AppKeys.E_ALBUM, album.id);
|
||||
|
||||
final ActivityOptionsCompat activityOptions;
|
||||
if (fab != null && albumArtForTransition != null) {
|
||||
activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
|
||||
Pair.create(albumArtForTransition, getString(R.string.transition_album_cover)),
|
||||
Pair.create((View) fab, getString(R.string.transition_fab))
|
||||
);
|
||||
} else {
|
||||
activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity());
|
||||
}
|
||||
ActivityCompat.startActivity(getActivity(), intent, activityOptions.toBundle());
|
||||
}
|
||||
}
|
||||
|
||||
private void disableParentActivitiesViews() {
|
||||
if (getParentActivity() instanceof KabViewsDisableAble) {
|
||||
((KabViewsDisableAble) getParentActivity()).disableViews();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areParentActivitiesViewsEnabled() {
|
||||
return !(getParentActivity() instanceof KabViewsDisableAble) || ((KabViewsDisableAble) getParentActivity()).areViewsEnabled();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.artistviewpager;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.lastfm.artist.LastFMArtistBiographyLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ViewPagerTabArtistBioFragment extends AbsViewPagerTabArtistListFragment {
|
||||
|
||||
|
||||
@Override
|
||||
protected ListAdapter getAdapter() {
|
||||
final List<String> strings = new ArrayList<>();
|
||||
strings.add("loading");
|
||||
ListAdapter adapter = new SimpleTextAdapter(getParentActivity(), strings);
|
||||
setAdapter(adapter);
|
||||
|
||||
LastFMArtistBiographyLoader.loadArtistBio(getParentActivity(), getArtistName(), new LastFMArtistBiographyLoader.ArtistBioLoaderCallback() {
|
||||
@Override
|
||||
public void onArtistBioLoaded(String biography) {
|
||||
if (biography == null || biography.trim().equals("")) {
|
||||
try {
|
||||
biography = getResources().getString(R.string.biography_unavailable);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "error while trying to get ressources", e);
|
||||
biography = "Errorm";
|
||||
}
|
||||
}
|
||||
strings.clear();
|
||||
strings.add(biography);
|
||||
ListAdapter adapter = new SimpleTextAdapter(getParentActivity(), strings);
|
||||
setAdapter(adapter);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class SimpleTextAdapter extends ArrayAdapter<String> {
|
||||
private Context context;
|
||||
|
||||
public SimpleTextAdapter(Context context, List<String> objects) {
|
||||
super(context, R.layout.item_artist_details_biography, objects);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
String string = getItem(position);
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_artist_details_biography, parent, false);
|
||||
}
|
||||
TextView text = (TextView) convertView.findViewById(R.id.text);
|
||||
text.setText(Html.fromHtml(string));
|
||||
text.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.artistviewpager;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import com.kabouzeid.materialmusic.adapter.songadapter.SongAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.SongAlphabeticComparator;
|
||||
import com.kabouzeid.materialmusic.loader.ArtistSongLoader;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsBaseActivity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 04.01.15.
|
||||
*/
|
||||
public class ViewPagerTabArtistSongListFragment extends AbsViewPagerTabArtistListFragment {
|
||||
@Override
|
||||
protected ListAdapter getAdapter() {
|
||||
final List<Song> songs = ArtistSongLoader.getArtistSongList(getParentActivity(), getArtistId());
|
||||
Collections.sort(songs, new SongAlphabeticComparator());
|
||||
AbsBaseActivity absBaseActivity = null;
|
||||
if (getParentActivity() instanceof AbsBaseActivity) {
|
||||
absBaseActivity = (AbsBaseActivity) getParentActivity();
|
||||
}
|
||||
ListAdapter adapter = new SongAdapter(getParentActivity(), absBaseActivity, songs);
|
||||
setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
app.getMusicPlayerRemote().setPlayingQueue(songs);
|
||||
app.getMusicPlayerRemote().playSongAt(position);
|
||||
}
|
||||
});
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.AlbumViewGridAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.AlbumAlphabeticComparator;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabSearchAbleFragment;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.loader.AlbumLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Album;
|
||||
import com.kabouzeid.materialmusic.ui.activities.AlbumDetailActivity;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.11.14.
|
||||
*/
|
||||
public class AlbumViewFragment extends Fragment implements KabViewsDisableAble, KabSearchAbleFragment {
|
||||
public static final String TAG = AlbumViewFragment.class.getSimpleName();
|
||||
|
||||
private App app;
|
||||
private AbsListView absListView;
|
||||
private View fragmentRootView;
|
||||
private FloatingActionButton fab;
|
||||
private boolean areViewsEnabled;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getActivity().getApplicationContext();
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_albumview, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
fragmentRootView = view;
|
||||
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
initViews();
|
||||
setUpViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enableViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
absListView = (AbsListView) fragmentRootView.findViewById(R.id.absList);
|
||||
fab = (FloatingActionButton) getActivity().findViewById(R.id.fab);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
|
||||
private void setUpAbsListView() {
|
||||
List<Album> albums = AlbumLoader.getAllAlbums(getActivity());
|
||||
fillAbsListView(albums);
|
||||
}
|
||||
|
||||
private void setUpAbsListView(String query) {
|
||||
List<Album> albums = AlbumLoader.getAlbums(getActivity(), query);
|
||||
fillAbsListView(albums);
|
||||
}
|
||||
|
||||
private void fillAbsListView(List<Album> albums) {
|
||||
Collections.sort(albums, new AlbumAlphabeticComparator());
|
||||
AlbumViewGridAdapter albumTileAdapter = new AlbumViewGridAdapter(getActivity(), albums);
|
||||
absListView.setAdapter(albumTileAdapter);
|
||||
absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Album album = (Album) parent.getItemAtPosition(position);
|
||||
View albumArtView = view.findViewById(R.id.album_art);
|
||||
|
||||
openAlbumDetailsActivityIfPossible(album, albumArtView);
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, Util.getNavigationBarHeight(getActivity()));
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, 0);
|
||||
}
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void openAlbumDetailsActivityIfPossible(Album album, View albumArtForTransition) {
|
||||
if (areParentActivitiesViewsEnabled()) {
|
||||
disableViews();
|
||||
disableParentActivitiesViews();
|
||||
|
||||
final Intent intent = new Intent(getActivity(), AlbumDetailActivity.class);
|
||||
intent.putExtra(AppKeys.E_ALBUM, album.id);
|
||||
|
||||
final ActivityOptionsCompat activityOptions;
|
||||
if (fab != null && albumArtForTransition != null) {
|
||||
activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
|
||||
Pair.create(albumArtForTransition, getString(R.string.transition_album_cover)),
|
||||
Pair.create((View) fab, getString(R.string.transition_fab))
|
||||
);
|
||||
} else {
|
||||
activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity());
|
||||
}
|
||||
ActivityCompat.startActivity(getActivity(), intent, activityOptions.toBundle());
|
||||
}
|
||||
}
|
||||
|
||||
private void disableParentActivitiesViews() {
|
||||
if (getActivity() instanceof KabViewsDisableAble) {
|
||||
((KabViewsDisableAble) getActivity()).disableViews();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areParentActivitiesViewsEnabled() {
|
||||
return !(getActivity() instanceof KabViewsDisableAble) || ((KabViewsDisableAble) getActivity()).areViewsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
areViewsEnabled = false;
|
||||
absListView.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areViewsEnabled() {
|
||||
return areViewsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
areViewsEnabled = true;
|
||||
absListView.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void search(String query) {
|
||||
setUpAbsListView(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnToNonSearch() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.ArtistViewListAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.ArtistAlphabeticComparator;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabSearchAbleFragment;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.materialmusic.loader.ArtistLoader;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
import com.kabouzeid.materialmusic.model.Artist;
|
||||
import com.kabouzeid.materialmusic.ui.activities.ArtistDetailActivity;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsFabActivity;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
*/
|
||||
public class ArtistViewFragment extends Fragment implements KabSearchAbleFragment, KabViewsDisableAble {
|
||||
public static final String TAG = ArtistViewFragment.class.getSimpleName();
|
||||
|
||||
private App app;
|
||||
private AbsListView absListView;
|
||||
private View fragmentRootView;
|
||||
private boolean areViewsEnabled;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getActivity().getApplicationContext();
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_artist_view, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
fragmentRootView = view;
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
initViews();
|
||||
setUpViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enableViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
absListView = (AbsListView) fragmentRootView.findViewById(R.id.absList);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
|
||||
private void setUpAbsListView() {
|
||||
List<Artist> artists = ArtistLoader.getAllArtists(getActivity());
|
||||
fillAbsListView(artists);
|
||||
}
|
||||
|
||||
private void setUpAbsListView(String query) {
|
||||
List<Artist> artists = ArtistLoader.getArtists(getActivity(), query);
|
||||
fillAbsListView(artists);
|
||||
}
|
||||
|
||||
private void fillAbsListView(List<Artist> artists) {
|
||||
Collections.sort(artists, new ArtistAlphabeticComparator());
|
||||
ArtistViewListAdapter artistAdapter = new ArtistViewListAdapter(getActivity(), artists);
|
||||
absListView.setAdapter(artistAdapter);
|
||||
absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
final Artist artist = (Artist) parent.getItemAtPosition(position);
|
||||
final View artistImageView = view.findViewById(R.id.artist_image);
|
||||
|
||||
if (getActivity() instanceof AbsFabActivity) {
|
||||
AbsFabActivity absFabActivity = (AbsFabActivity) getActivity();
|
||||
Pair[] sharedElements = {Pair.create(artistImageView, getString(R.string.transition_artist_image))};
|
||||
absFabActivity.goToArtistDetailsActivity(artist.id, sharedElements);
|
||||
} else {
|
||||
Intent intent = new Intent(getActivity(), ArtistDetailActivity.class);
|
||||
intent.putExtra(AppKeys.E_ARTIST, artist.id);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, Util.getNavigationBarHeight(getActivity()));
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, 0);
|
||||
}
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void search(String query) {
|
||||
setUpAbsListView(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnToNonSearch() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
|
||||
private void disableParentActivityViews() {
|
||||
if (getActivity() instanceof KabViewsDisableAble) {
|
||||
((KabViewsDisableAble) getActivity()).disableViews();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areParentActivityViewsEnabled() {
|
||||
return !(getActivity() instanceof KabViewsDisableAble) || ((KabViewsDisableAble) getActivity()).areViewsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableViews() {
|
||||
areViewsEnabled = true;
|
||||
absListView.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableViews() {
|
||||
areViewsEnabled = false;
|
||||
absListView.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areViewsEnabled() {
|
||||
return areViewsEnabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package com.kabouzeid.materialmusic.ui.fragments.mainactivityfragments;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.adapter.songadapter.SongViewListAdapter;
|
||||
import com.kabouzeid.materialmusic.comparator.SongAlphabeticComparator;
|
||||
import com.kabouzeid.materialmusic.interfaces.KabSearchAbleFragment;
|
||||
import com.kabouzeid.materialmusic.loader.SongLoader;
|
||||
import com.kabouzeid.materialmusic.model.Song;
|
||||
import com.kabouzeid.materialmusic.ui.activities.base.AbsBaseActivity;
|
||||
import com.kabouzeid.materialmusic.util.Util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class SongViewFragment extends Fragment implements KabSearchAbleFragment {
|
||||
public static final String TAG = SongViewFragment.class.getSimpleName();
|
||||
|
||||
private App app;
|
||||
private AbsListView absListView;
|
||||
private View fragmentRootView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
app = (App) getActivity().getApplicationContext();
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_songview, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
fragmentRootView = view;
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
initViews();
|
||||
setUpViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
absListView = (AbsListView) fragmentRootView.findViewById(R.id.absList);
|
||||
}
|
||||
|
||||
private void setUpViews() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
|
||||
private void setUpAbsListView() {
|
||||
List<Song> songs = SongLoader.getAllSongs(getActivity());
|
||||
fillAbsListView(songs);
|
||||
}
|
||||
|
||||
private void setUpAbsListView(String query) {
|
||||
List<Song> songs = SongLoader.getSongs(getActivity(), query);
|
||||
fillAbsListView(songs);
|
||||
}
|
||||
|
||||
private void fillAbsListView(final List<Song> songs) {
|
||||
Collections.sort(songs, new SongAlphabeticComparator());
|
||||
AbsBaseActivity absBaseActivity = null;
|
||||
if (getActivity() instanceof AbsBaseActivity) {
|
||||
absBaseActivity = (AbsBaseActivity) getActivity();
|
||||
}
|
||||
SongViewListAdapter songAdapter = new SongViewListAdapter(getActivity(), absBaseActivity, songs);
|
||||
absListView.setAdapter(songAdapter);
|
||||
absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
app.getMusicPlayerRemote().setPlayingQueue(songs);
|
||||
app.getMusicPlayerRemote().playSongAt(position);
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (app.isInPortraitMode() || app.isTablet()) {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, Util.getNavigationBarHeight(getActivity()));
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()) + Util.getStatusBarHeight(getActivity()), 0, 0);
|
||||
}
|
||||
} else {
|
||||
absListView.setPadding(0, Util.getActionBarSize(getActivity()), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void search(String query) {
|
||||
setUpAbsListView(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnToNonSearch() {
|
||||
setUpAbsListView();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.kabouzeid.materialmusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
import com.nostra13.universalimageloader.utils.L;
|
||||
|
||||
/**
|
||||
* Created by karim on 28.12.14.
|
||||
*/
|
||||
public class ImageLoaderUtil {
|
||||
public static void initImageLoader(Context context) {
|
||||
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(false)
|
||||
.build();
|
||||
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
|
||||
.defaultDisplayImageOptions(defaultOptions)
|
||||
//.memoryCache(new LRULimitedMemoryCache(1024*1024*CACHE_SIZE_MB))
|
||||
.build();
|
||||
ImageLoader.getInstance().init(config);
|
||||
|
||||
L.writeLogs(false);
|
||||
}
|
||||
|
||||
public static DisplayImageOptions getCacheOnDiskOptions() {
|
||||
return new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static DisplayImageOptions getCacheInMemoryOptions() {
|
||||
return new DisplayImageOptions.Builder()
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class defaultAlbumArtOnFailed implements ImageLoadingListener {
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_album_art);
|
||||
}
|
||||
}
|
||||
|
||||
public static class defaultArtistArtOnFailed implements ImageLoadingListener {
|
||||
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_artist_image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_artist_image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(String imageUri, View view) {
|
||||
((ImageView) view).setImageResource(R.drawable.default_artist_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.kabouzeid.materialmusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.12.14.
|
||||
*/
|
||||
public final class InternalStorageUtil {
|
||||
private static final String TAG = InternalStorageUtil.class.getSimpleName();
|
||||
|
||||
public static synchronized void writeObject(final Context context, final String key, final Object object) throws IOException {
|
||||
try {
|
||||
FileOutputStream fos;
|
||||
fos = context.openFileOutput(key, Context.MODE_PRIVATE);
|
||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
||||
oos.writeObject(object);
|
||||
oos.close();
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Writing Object to internal storage failed! Maybe the Object is not serializable?", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized Object readObject(Context context, String key) throws IOException,
|
||||
ClassNotFoundException {
|
||||
FileInputStream fis = context.openFileInput(key);
|
||||
ObjectInputStream ois = new ObjectInputStream(fis);
|
||||
return ois.readObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.kabouzeid.materialmusic.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by karim on 29.12.14.
|
||||
*/
|
||||
public class MusicUtil {
|
||||
public static final String TAG = MusicUtil.class.getSimpleName();
|
||||
|
||||
public static Uri getAlbumArtUri(int album_id) {
|
||||
final Uri sArtworkUri = Uri
|
||||
.parse("content://media/external/audio/albumart");
|
||||
|
||||
return ContentUris.withAppendedId(sArtworkUri, album_id);
|
||||
}
|
||||
|
||||
public static String getReadableDurationString(long songDurationMillis) {
|
||||
long minutes = (songDurationMillis / 1000) / 60;
|
||||
long seconds = (songDurationMillis / 1000) % 60;
|
||||
return String.format("%02d:%02d", minutes, seconds);
|
||||
}
|
||||
|
||||
|
||||
//iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3.
|
||||
//this method converts those values to normal tracknumbers
|
||||
public static int getFixedTrackNumber(int trackNumberToFix) {
|
||||
return trackNumberToFix % 1000;
|
||||
}
|
||||
|
||||
public static void insertAlbumArt(Context context, int albumId, String path) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
|
||||
Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
|
||||
contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null);
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("album_id", albumId);
|
||||
values.put("_data", path);
|
||||
|
||||
contentResolver.insert(artworkUri, values);
|
||||
}
|
||||
|
||||
public static void deleteAlbumArt(Context context, int albumId) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Uri localUri = Uri.parse("content://media/external/audio/albumart");
|
||||
contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null);
|
||||
}
|
||||
|
||||
public static File createAlbumArtDir(Context paramContext) {
|
||||
File albumArtDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "/.albumart/");
|
||||
if (!albumArtDir.exists()) {
|
||||
albumArtDir.mkdirs();
|
||||
try {
|
||||
new File(albumArtDir, ".nomedia").createNewFile();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error while creating .nomedia file", e);
|
||||
}
|
||||
}
|
||||
return albumArtDir;
|
||||
}
|
||||
|
||||
public static File getAlbumArtFile(Context context, String name)
|
||||
throws IOException {
|
||||
return new File(createAlbumArtDir(context), name + System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
179
app/src/main/java/com/kabouzeid/materialmusic/util/Util.java
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package com.kabouzeid.materialmusic.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.kabouzeid.materialmusic.App;
|
||||
import com.kabouzeid.materialmusic.R;
|
||||
import com.kabouzeid.materialmusic.misc.AppKeys;
|
||||
|
||||
/**
|
||||
* Created by karim on 12.12.14.
|
||||
*/
|
||||
public class Util {
|
||||
public static int resolveDrawable(Context context, int drawable) {
|
||||
TypedArray a = context.obtainStyledAttributes(new int[]{drawable});
|
||||
int resId = a.getResourceId(0, 0);
|
||||
a.recycle();
|
||||
return resId;
|
||||
}
|
||||
|
||||
public static int resolveColor(Context context, int color) {
|
||||
TypedArray a = context.obtainStyledAttributes(new int[]{color});
|
||||
int resId = a.getColor(0, context.getResources().getColor(R.color.materialmusic_color));
|
||||
a.recycle();
|
||||
return resId;
|
||||
}
|
||||
|
||||
public static boolean isWindowTranslucent(Context context) {
|
||||
TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.windowTranslucentStatus});
|
||||
boolean result = a.getBoolean(0, false);
|
||||
a.recycle();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int getActionBarSize(Context context) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
int[] textSizeAttr = new int[]{R.attr.actionBarSize};
|
||||
int indexOfAttrTextSize = 0;
|
||||
TypedArray a = context.obtainStyledAttributes(typedValue.data, textSizeAttr);
|
||||
int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1);
|
||||
a.recycle();
|
||||
return actionBarSize;
|
||||
}
|
||||
|
||||
public static int getStatusBarHeight(Context context) {
|
||||
int result = 0;
|
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int getNavigationBarHeight(Context context) {
|
||||
int result = 0;
|
||||
int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public static void setNavBarTranslucent(Window window, boolean translucent) {
|
||||
if (translucent) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowManager.LayoutParams attrs = window
|
||||
.getAttributes();
|
||||
attrs.flags &= (~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
||||
window.setAttributes(attrs);
|
||||
window.clearFlags(
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public static void setStatusBarTranslucent(Window window, boolean translucent) {
|
||||
if (translucent) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowManager.LayoutParams attrs = window
|
||||
.getAttributes();
|
||||
attrs.flags &= (~WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.setAttributes(attrs);
|
||||
window.clearFlags(
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
}
|
||||
|
||||
public static final boolean isOnline(final Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean state = false;
|
||||
final boolean onlyOnWifi = ((App) context.getApplicationContext()).getDefaultSharedPreferences().getBoolean(AppKeys.SP_ONLY_ON_WIFI, true);
|
||||
|
||||
/* Monitor network connections */
|
||||
final ConnectivityManager connectivityManager = (ConnectivityManager) context
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
/* Wi-Fi connection */
|
||||
final NetworkInfo wifiNetwork = connectivityManager
|
||||
.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
||||
if (wifiNetwork != null) {
|
||||
state = wifiNetwork.isConnectedOrConnecting();
|
||||
}
|
||||
|
||||
/* Mobile data connection */
|
||||
final NetworkInfo mbobileNetwork = connectivityManager
|
||||
.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
|
||||
if (mbobileNetwork != null) {
|
||||
if (!onlyOnWifi) {
|
||||
state = mbobileNetwork.isConnectedOrConnecting();
|
||||
}
|
||||
}
|
||||
|
||||
/* Other networks */
|
||||
final NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
|
||||
if (activeNetwork != null) {
|
||||
if (!onlyOnWifi) {
|
||||
state = activeNetwork.isConnectedOrConnecting();
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public static String getFileSizeString(long sizeInBytes) {
|
||||
long fileSizeInKB = sizeInBytes / 1024;
|
||||
long fileSizeInMB = fileSizeInKB / 1024;
|
||||
return fileSizeInMB + " MB";
|
||||
}
|
||||
|
||||
public static String getFilePathFromContentProviderUri(Context context, Uri uri) {
|
||||
String[] projection = {MediaStore.MediaColumns.DATA};
|
||||
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||
if (cursor == null) return null;
|
||||
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
|
||||
cursor.moveToFirst();
|
||||
String path = cursor.getString(column_index);
|
||||
cursor.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
private static int albumArtSize = 600;
|
||||
|
||||
public static Bitmap getAlbumArtScaledBitmap(final Bitmap bitmap, boolean keepAspectRatio) {
|
||||
if (keepAspectRatio) {
|
||||
double aspectRatio = (double) bitmap.getHeight() / (double) bitmap.getWidth();
|
||||
int targetWidth = albumArtSize;
|
||||
int targetHeight = (int) (targetWidth * aspectRatio);
|
||||
return Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, false);
|
||||
} else {
|
||||
return getScaledBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap getScaledBitmap(final Bitmap bitmap) {
|
||||
return Bitmap.createScaledBitmap(bitmap, albumArtSize, albumArtSize, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.kabouzeid.materialmusic.util;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.PathInterpolator;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
/**
|
||||
* Created by karim on 06.12.14.
|
||||
*/
|
||||
public class ViewUtil {
|
||||
public final static int DEFAULT_COLOR_ANIMATION_DURATION = 1000;
|
||||
|
||||
public static void disableViews(ViewGroup layout) {
|
||||
for (int i = 0; i < layout.getChildCount(); i++) {
|
||||
View child = layout.getChildAt(i);
|
||||
if (child instanceof ViewGroup) {
|
||||
disableViews((ViewGroup) child);
|
||||
} else {
|
||||
child.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableViews(ViewGroup layout) {
|
||||
for (int i = 0; i < layout.getChildCount(); i++) {
|
||||
View child = layout.getChildAt(i);
|
||||
if (child instanceof ViewGroup) {
|
||||
enableViews((ViewGroup) child);
|
||||
} else {
|
||||
child.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setListViewHeightBasedOnChildren(ListView listView) {
|
||||
ListAdapter listAdapter = listView.getAdapter();
|
||||
if (listAdapter == null)
|
||||
return;
|
||||
|
||||
int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.UNSPECIFIED);
|
||||
int totalHeight = 0;
|
||||
View view = null;
|
||||
for (int i = 0; i < listAdapter.getCount(); i++) {
|
||||
view = listAdapter.getView(i, view, listView);
|
||||
if (i == 0)
|
||||
view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
|
||||
totalHeight += view.getMeasuredHeight();
|
||||
}
|
||||
ViewGroup.LayoutParams params = listView.getLayoutParams();
|
||||
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
|
||||
listView.setLayoutParams(params);
|
||||
listView.requestLayout();
|
||||
}
|
||||
|
||||
public static void animateViewColor(final View v, final int startColor, final int endColor) {
|
||||
animateViewColor(v, startColor, endColor, DEFAULT_COLOR_ANIMATION_DURATION);
|
||||
}
|
||||
|
||||
public static void animateViewColor(final View v, final int startColor, final int endColor, final int duration) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofObject(v, "backgroundColor",
|
||||
new ArgbEvaluator(), startColor, endColor);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animator.setInterpolator(new PathInterpolator(0.4f, 0f, 1f, 1f));
|
||||
}
|
||||
animator.setDuration(duration);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public static void setBackgroundAlpha(View view, float alpha, int baseColor) {
|
||||
int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;
|
||||
int rgb = 0x00ffffff & baseColor;
|
||||
view.setBackgroundColor(a + rgb);
|
||||
}
|
||||
|
||||
public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) {
|
||||
ViewTreeObserver vto = view.getViewTreeObserver();
|
||||
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
} else {
|
||||
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.kabouzeid.materialmusic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* Created by karim on 22.11.14.
|
||||
*/
|
||||
public class SquareImageView extends ImageView {
|
||||
|
||||
public SquareImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
|
||||
}
|
||||
BIN
app/src/main/res/drawable-hdpi/default_album_art.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/res/drawable-hdpi/drawer_shadow.9.png
Normal file
|
After Width: | Height: | Size: 161 B |
BIN
app/src/main/res/drawable-hdpi/ic_drawer.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/drawable-mdpi/default_album_art.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/drawable-mdpi/drawer_shadow.9.png
Normal file
|
After Width: | Height: | Size: 142 B |
BIN
app/src/main/res/drawable-mdpi/ic_drawer.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
7
app/src/main/res/drawable-v21/notification_selector.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/grey_500">
|
||||
<item android:id="@android:id/mask">
|
||||
<color android:color="@color/white"/>
|
||||
</item>
|
||||
</ripple>
|
||||
BIN
app/src/main/res/drawable-xhdpi/default_album_art.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app/src/main/res/drawable-xhdpi/drawer_shadow.9.png
Normal file
|
After Width: | Height: | Size: 174 B |
BIN
app/src/main/res/drawable-xhdpi/ic_drawer.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/drawable-xxhdpi/album.png
Normal file
|
After Width: | Height: | Size: 962 B |
BIN
app/src/main/res/drawable-xxhdpi/close.png
Normal file
|
After Width: | Height: | Size: 527 B |
BIN
app/src/main/res/drawable-xxhdpi/default_album_art.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
app/src/main/res/drawable-xxhdpi/default_artist_image.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
Normal file
|
After Width: | Height: | Size: 208 B |
BIN
app/src/main/res/drawable-xxhdpi/heart.png
Normal file
|
After Width: | Height: | Size: 888 B |
BIN
app/src/main/res/drawable-xxhdpi/heart_outline.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_drawer.png
Normal file
|
After Width: | Height: | Size: 202 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_overflow.png
Executable file
|
After Width: | Height: | Size: 701 B |
BIN
app/src/main/res/drawable-xxhdpi/interpret.png
Normal file
|
After Width: | Height: | Size: 845 B |
BIN
app/src/main/res/drawable-xxhdpi/music_box.png
Normal file
|
After Width: | Height: | Size: 606 B |
BIN
app/src/main/res/drawable-xxhdpi/music_box_outline.png
Normal file
|
After Width: | Height: | Size: 675 B |
BIN
app/src/main/res/drawable-xxhdpi/play_box.png
Normal file
|
After Width: | Height: | Size: 567 B |
BIN
app/src/main/res/drawable-xxhdpi/playlist.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
app/src/main/res/drawable-xxhdpi/settings.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxhdpi/songs.png
Normal file
|
After Width: | Height: | Size: 740 B |