added shortcuts and widgets

main
h4h13 2018-12-08 09:03:02 +05:30
parent d47aeccd87
commit 03aeb88964
54 changed files with 2646 additions and 935 deletions

View File

@ -3,6 +3,8 @@ package code.name.monkey.retromusic
import android.content.Context import android.content.Context
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.TransactionDetails
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -25,6 +27,10 @@ class App : MultiDexApplication() {
.commit() .commit()
} }
if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).initDynamicShortcuts()
// automatically restores purchases // automatically restores purchases
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSE_KEY, billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSE_KEY,
object : BillingProcessor.IBillingHandler { object : BillingProcessor.IBillingHandler {

View File

@ -10,7 +10,7 @@ import code.name.monkey.retromusic.util.schedulers.SchedulerProvider
object Injection { object Injection {
fun provideRepository(): Repository { fun provideRepository(): Repository {
return RepositoryImpl.getInstance() return RepositoryImpl.instance
} }
fun provideSchedulerProvider(): BaseSchedulerProvider { fun provideSchedulerProvider(): BaseSchedulerProvider {

View File

@ -0,0 +1,70 @@
package code.name.monkey.retromusic.appshortcuts;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.TypedValue;
import androidx.annotation.RequiresApi;
import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroUtil;
/**
* @author Adrian Campos
*/
@RequiresApi(Build.VERSION_CODES.N_MR1)
public final class AppShortcutIconGenerator {
public static Icon generateThemedIcon(Context context, int iconId) {
if (PreferenceUtil.getInstance().coloredAppShortcuts()) {
return generateUserThemedIcon(context, iconId);
} else {
return generateDefaultThemedIcon(context, iconId);
}
}
private static Icon generateDefaultThemedIcon(Context context, int iconId) {
// Return an Icon of iconId with default colors
return generateThemedIcon(context, iconId,
context.getColor(R.color.app_shortcut_default_foreground),
context.getColor(R.color.app_shortcut_default_background)
);
}
private static Icon generateUserThemedIcon(Context context, int iconId) {
// Get background color from context's theme
final TypedValue typedColorBackground = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedColorBackground, true);
// Return an Icon of iconId with those colors
return generateThemedIcon(context, iconId,
ThemeStore.accentColor(context),
typedColorBackground.data
);
}
private static Icon generateThemedIcon(Context context, int iconId, int foregroundColor, int backgroundColor) {
// Get and tint foreground and background drawables
Drawable vectorDrawable = RetroUtil.getTintedVectorDrawable(context, iconId, foregroundColor);
Drawable backgroundDrawable = RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_app_shortcut_background, backgroundColor);
// Squash the two drawables together
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{backgroundDrawable, vectorDrawable});
// Return as an Icon
return Icon.createWithBitmap(drawableToBitmap(layerDrawable));
}
private static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}

View File

@ -0,0 +1,77 @@
package code.name.monkey.retromusic.appshortcuts;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist;
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist;
import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType;
import code.name.monkey.retromusic.service.MusicService;
import static code.name.monkey.retromusic.Constants.*;
/**
* @author Adrian Campos
*/
public class AppShortcutLauncherActivity extends Activity {
public static final String KEY_SHORTCUT_TYPE = "code.name.monkey.retromusic.appshortcuts.ShortcutType";
public static final int SHORTCUT_TYPE_SHUFFLE_ALL = 0;
public static final int SHORTCUT_TYPE_TOP_TRACKS = 1;
public static final int SHORTCUT_TYPE_LAST_ADDED = 2;
public static final int SHORTCUT_TYPE_NONE = 3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int shortcutType = SHORTCUT_TYPE_NONE;
// Set shortcutType from the intent extras
Bundle extras = getIntent().getExtras();
if (extras != null) {
//noinspection WrongConstant
shortcutType = extras.getInt(KEY_SHORTCUT_TYPE, SHORTCUT_TYPE_NONE);
}
switch (shortcutType) {
case SHORTCUT_TYPE_SHUFFLE_ALL:
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_SHUFFLE,
new ShuffleAllPlaylist(getApplicationContext()));
DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.getId());
break;
case SHORTCUT_TYPE_TOP_TRACKS:
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE,
new MyTopTracksPlaylist(getApplicationContext()));
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.getId());
break;
case SHORTCUT_TYPE_LAST_ADDED:
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE,
new LastAddedPlaylist(getApplicationContext()));
DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.getId());
break;
}
finish();
}
private void startServiceWithPlaylist(int shuffleMode, Playlist playlist) {
Intent intent = new Intent(this, MusicService.class);
intent.setAction(ACTION_PLAY_PLAYLIST);
Bundle bundle = new Bundle();
bundle.putParcelable(INTENT_EXTRA_PLAYLIST, playlist);
bundle.putInt(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode);
intent.putExtras(bundle);
startService(intent);
}
}

View File

@ -0,0 +1,63 @@
package code.name.monkey.retromusic.appshortcuts;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import java.util.Arrays;
import java.util.List;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType;
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType;
/**
* @author Adrian Campos
*/
@TargetApi(Build.VERSION_CODES.N_MR1)
public class DynamicShortcutManager {
private Context context;
private ShortcutManager shortcutManager;
public DynamicShortcutManager(Context context) {
this.context = context;
shortcutManager = this.context.getSystemService(ShortcutManager.class);
}
public static ShortcutInfo createShortcut(Context context, String id, String shortLabel, String longLabel, Icon icon, Intent intent) {
return new ShortcutInfo.Builder(context, id)
.setShortLabel(shortLabel)
.setLongLabel(longLabel)
.setIcon(icon)
.setIntent(intent)
.build();
}
public static void reportShortcutUsed(Context context, String shortcutId) {
context.getSystemService(ShortcutManager.class).reportShortcutUsed(shortcutId);
}
public void initDynamicShortcuts() {
if (shortcutManager.getDynamicShortcuts().size() == 0) {
shortcutManager.setDynamicShortcuts(getDefaultShortcuts());
}
}
public void updateDynamicShortcuts() {
shortcutManager.updateShortcuts(getDefaultShortcuts());
}
public List<ShortcutInfo> getDefaultShortcuts() {
return (Arrays.asList(
new ShuffleAllShortcutType(context).getShortcutInfo(),
new TopTracksShortcutType(context).getShortcutInfo(),
new LastAddedShortcutType(context).getShortcutInfo()
));
}
}

View File

@ -0,0 +1,50 @@
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.Bundle;
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
/**
* @author Adrian Campos
*/
@TargetApi(Build.VERSION_CODES.N_MR1)
public abstract class BaseShortcutType {
static final String ID_PREFIX = "code.name.monkey.retromusic.appshortcuts.id.";
Context context;
public BaseShortcutType(Context context) {
this.context = context;
}
static public String getId() {
return ID_PREFIX + "invalid";
}
abstract ShortcutInfo getShortcutInfo();
/**
* Creates an Intent that will launch MainActivtiy and immediately play {@param songs} in either shuffle or normal mode
*
* @param shortcutType Describes the type of shortcut to create (ShuffleAll, TopTracks, custom playlist, etc.)
* @return
*/
Intent getPlaySongsIntent(int shortcutType) {
Intent intent = new Intent(context, AppShortcutLauncherActivity.class);
intent.setAction(Intent.ACTION_VIEW);
Bundle b = new Bundle();
b.putInt(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE, shortcutType);
intent.putExtras(b);
return intent;
}
}

View File

@ -0,0 +1,34 @@
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
/**
* @author Adrian Campos
*/
@TargetApi(Build.VERSION_CODES.N_MR1)
public final class LastAddedShortcutType extends BaseShortcutType {
public LastAddedShortcutType(Context context) {
super(context);
}
public static String getId() {
return ID_PREFIX + "last_added";
}
public ShortcutInfo getShortcutInfo() {
return new ShortcutInfo.Builder(context, getId())
.setShortLabel(context.getString(R.string.app_shortcut_last_added_short))
.setLongLabel(context.getString(R.string.app_shortcut_last_added_long))
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_last_added))
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_LAST_ADDED))
.build();
}
}

View File

@ -0,0 +1,35 @@
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
/**
* @author Adrian Campos
*/
@TargetApi(Build.VERSION_CODES.N_MR1)
public final class ShuffleAllShortcutType extends BaseShortcutType {
public ShuffleAllShortcutType(Context context) {
super(context);
}
public static String getId() {
return ID_PREFIX + "shuffle_all";
}
public ShortcutInfo getShortcutInfo() {
return new ShortcutInfo.Builder(context, getId())
.setShortLabel(context.getString(R.string.app_shortcut_shuffle_all_short))
.setLongLabel(context.getString(R.string.app_shortcut_shuffle_all_long))
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_shuffle_all))
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SHUFFLE_ALL))
.build();
}
}

View File

@ -0,0 +1,35 @@
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
/**
* @author Adrian Campos
*/
@TargetApi(Build.VERSION_CODES.N_MR1)
public final class TopTracksShortcutType extends BaseShortcutType {
public TopTracksShortcutType(Context context) {
super(context);
}
public static String getId() {
return ID_PREFIX + "top_tracks";
}
public ShortcutInfo getShortcutInfo() {
return new ShortcutInfo.Builder(context, getId())
.setShortLabel(context.getString(R.string.app_shortcut_top_tracks_short))
.setLongLabel(context.getString(R.string.app_shortcut_top_tracks_long))
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_top_tracks))
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_TOP_TRACKS))
.build();
}
}

View File

@ -0,0 +1,167 @@
package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.ui.activities.MainActivity
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
class AppWidgetBig : BaseAppWidget() {
private var target: Target<Bitmap>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName,
R.layout.app_widget_big)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
MaterialValueHelper.getPrimaryTextColor(context, false))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getPrimaryTextColor(context, false))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
MaterialValueHelper.getPrimaryTextColor(context, false))!!, 1f))
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName,
R.layout.app_widget_big)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else {
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
}
// Set correct drawable for pause state
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_24dp
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.createBitmap(
RetroUtil.getTintedVectorDrawable(service, playPauseRes,
MaterialValueHelper.getPrimaryTextColor(service, false))!!, 1f))
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
MaterialValueHelper.getPrimaryTextColor(service, false))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getPrimaryTextColor(service, false))!!, 1f))
// Link actions buttons to intents
linkButtons(service, appWidgetView)
// Load the album cover async and push the update on completion
val p = RetroUtil.getScreenSize(service)
val widgetImageSize = Math.min(p.x, p.y)
val appContext = service.applicationContext
service.runOnUiThread {
if (target != null) {
Glide.clear(target!!)
}
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
.checkIgnoreMediaStore(appContext)
.asBitmap().build()
.into(object : SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
override fun onResourceReady(resource: Bitmap,
glideAnimation: GlideAnimation<in Bitmap>) {
update(resource)
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
update(null)
}
private fun update(bitmap: Bitmap?) {
if (bitmap == null) {
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
} else {
appWidgetView.setImageViewBitmap(R.id.image, bitmap)
}
pushUpdate(appContext, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent)
// Previous track
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
}
companion object {
const val NAME: String = "app_widget_big"
private var mInstance: AppWidgetBig? = null
val instance: AppWidgetBig
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetBig()
}
return mInstance!!
}
}
}

View File

@ -0,0 +1,182 @@
package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.ui.activities.MainActivity
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
class AppWidgetCard : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName,
R.layout.app_widget_card)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName,
R.layout.app_widget_card)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else {
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
}
// Set correct drawable for pause state
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_24dp
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(service, playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true))!!, 1f))
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
MaterialValueHelper.getSecondaryTextColor(service, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getSecondaryTextColor(service, true))!!, 1f))
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_card_image_size)
}
if (cardRadius == 0f) {
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
}
// Load the album cover async and push the update on completion
service.runOnUiThread {
if (target != null) {
Glide.clear(target!!)
}
target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady(resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>) {
val palette = resource.palette
update(resource.bitmap, palette.getVibrantColor(palette
.getMutedColor(MaterialValueHelper.getSecondaryTextColor(service, true))))
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
}
private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state
val playPauseRest = if (isPlaying) R.drawable.ic_pause_white_24dp
else
R.drawable.ic_play_arrow_white_24dp
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRest, color)!!, 1f))
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color)!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color)!!, 1f))
val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = BaseAppWidget.Companion.createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0f, cardRadius, 0f)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
pushUpdate(service, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action: Intent = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
}
companion object {
const val NAME = "app_widget_card"
private var mInstance: AppWidgetCard? = null
private var imageSize = 0
private var cardRadius = 0f
val instance: AppWidgetCard
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetCard()
}
return mInstance!!
}
}
}

View File

@ -0,0 +1,166 @@
package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.ui.activities.MainActivity
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
class AppWidgetClassic : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_classic)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName,
R.layout.app_widget_classic)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else {
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
}
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
imageSize = service.resources
.getDimensionPixelSize(R.dimen.app_widget_classic_image_size)
}
if (cardRadius == 0f) {
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
}
// Load the album cover async and push the update on completion
val appContext = service.applicationContext
service.runOnUiThread {
if (target != null) {
Glide.clear(target!!)
}
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
.checkIgnoreMediaStore(appContext)
.generatePalette(service).build()
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady(resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>) {
val palette = resource.palette
update(resource.bitmap, palette.getVibrantColor(palette.getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true))))
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true))
}
private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state
val playPauseRes = if (isPlaying)
R.drawable.ic_pause_white_24dp
else
R.drawable.ic_play_arrow_white_24dp
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause,
BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color)!!, 1f))
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color)!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color)!!, 1f))
val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = BaseAppWidget.createRoundedBitmap(image, imageSize, imageSize,
cardRadius, 0f, cardRadius, 0f)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
pushUpdate(appContext, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
}
companion object {
const val NAME = "app_widget_classic"
private var mInstance: AppWidgetClassic? = null
private var imageSize = 0
private var cardRadius = 0f
val instance: AppWidgetClassic
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetClassic()
}
return mInstance!!
}
}
}

View File

@ -0,0 +1,170 @@
package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.ui.activities.MainActivity
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
class AppWidgetSmall : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_small)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName,
R.layout.app_widget_small)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else {
if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) {
appWidgetView.setTextViewText(R.id.text_separator, "")
} else {
appWidgetView.setTextViewText(R.id.text_separator, "")
}
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, song.artistName)
}
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_small_image_size)
}
if (cardRadius == 0f) {
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
}
// Load the album cover async and push the update on completion
val appContext = service.applicationContext
service.runOnUiThread {
if (target != null) {
Glide.clear(target!!)
}
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
.checkIgnoreMediaStore(appContext)
.generatePalette(service).build()
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady(resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>) {
val palette = resource.palette
update(resource.bitmap, palette.getVibrantColor(palette
.getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true))))
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true))
}
private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state
val playPauseRes = if (isPlaying)
R.drawable.ic_pause_white_24dp
else
R.drawable.ic_play_arrow_white_24dp
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color)!!, 1f))
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(R.id.button_next, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color)!!, 1f))
appWidgetView.setImageViewBitmap(R.id.button_prev, BaseAppWidget.Companion.createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color)!!, 1f))
val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = BaseAppWidget.createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0f, 0f, 0f)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
pushUpdate(appContext, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
}
companion object {
const val NAME:String = "app_widget_small"
private var mInstance: AppWidgetSmall? = null
private var imageSize = 0
private var cardRadius = 0f
val instance: AppWidgetSmall
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetSmall()
}
return mInstance!!
}
}
}

View File

@ -0,0 +1,31 @@
package code.name.monkey.retromusic.appwidgets
import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import code.name.monkey.retromusic.service.MusicService
/**
* @author Eugene Cheung (arkon)
*/
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val widgetManager = AppWidgetManager.getInstance(context)
// Start music service if there are any existing widgets
if (widgetManager.getAppWidgetIds(ComponentName(context, AppWidgetBig::class.java)).isNotEmpty() ||
widgetManager.getAppWidgetIds(ComponentName(context, AppWidgetClassic::class.java)).isNotEmpty() ||
widgetManager.getAppWidgetIds(ComponentName(context, AppWidgetSmall::class.java)).isNotEmpty() ||
widgetManager.getAppWidgetIds(ComponentName(context, AppWidgetCard::class.java)).isNotEmpty()) {
val serviceIntent = Intent(context, MusicService::class.java)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // not allowed on Oreo
context.startService(serviceIntent)
}
}
}
}

View File

@ -0,0 +1,163 @@
package code.name.monkey.retromusic.appwidgets.base
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.TextUtils
import android.widget.RemoteViews
import androidx.core.content.ContextCompat
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.MusicService
abstract class BaseAppWidget : AppWidgetProvider() {
/**
* {@inheritDoc}
*/
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray) {
defaultAppWidget(context, appWidgetIds)
val updateIntent = Intent(Constants.APP_WIDGET_UPDATE)
updateIntent.putExtra(Constants.EXTRA_APP_WIDGET_NAME, NAME)
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
context.sendBroadcast(updateIntent)
}
/**
* Handle a change notification coming over from [MusicService]
*/
fun notifyChange(service: MusicService, what: String) {
if (hasInstances(service)) {
if (Constants.META_CHANGED == what || Constants.PLAY_STATE_CHANGED == what) {
performUpdate(service, null)
}
}
}
protected fun pushUpdate(context: Context, appWidgetIds: IntArray?,
views: RemoteViews) {
val appWidgetManager = AppWidgetManager.getInstance(context)
if (appWidgetIds != null) {
appWidgetManager.updateAppWidget(appWidgetIds, views)
} else {
appWidgetManager.updateAppWidget(ComponentName(context, javaClass), views)
}
}
/**
* Check against [AppWidgetManager] if there are any instances of this widget.
*/
protected fun hasInstances(context: Context): Boolean {
val appWidgetManager = AppWidgetManager.getInstance(context)
val mAppWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context,
javaClass))
return mAppWidgetIds.size > 0
}
protected fun buildPendingIntent(context: Context, action: String,
serviceName: ComponentName): PendingIntent {
val intent = Intent(action)
intent.component = serviceName
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PendingIntent.getForegroundService(context, 0, intent, 0)
} else {
PendingIntent.getService(context, 0, intent, 0)
}
}
protected abstract fun defaultAppWidget(context: Context, appWidgetIds: IntArray)
abstract fun performUpdate(service: MusicService, appWidgetIds: IntArray?)
protected fun getAlbumArtDrawable(resources: Resources, bitmap: Bitmap?): Drawable {
return if (bitmap == null) {
ContextCompat.getDrawable(App.context, R.drawable.default_album_art)!!
} else {
BitmapDrawable(resources, bitmap)
}
}
protected fun getSongArtistAndAlbum(song: Song): String {
val builder = StringBuilder()
builder.append(song.artistName)
if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) {
builder.append("")
}
builder.append(song.albumName)
return builder.toString()
}
companion object {
const val NAME: String = "app_widget"
fun createRoundedBitmap(drawable: Drawable?, width: Int, height: Int, tl: Float,
tr: Float, bl: Float, br: Float): Bitmap? {
if (drawable == null) {
return null
}
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val c = Canvas(bitmap)
drawable.setBounds(0, 0, width, height)
drawable.draw(c)
val rounded = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(rounded)
val paint = Paint()
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.isAntiAlias = true
canvas.drawPath(composeRoundedRectPath(RectF(0f, 0f, width.toFloat(), height.toFloat()), tl, tr, bl, br), paint)
return rounded
}
fun createBitmap(drawable: Drawable, sizeMultiplier: Float): Bitmap {
val bitmap = Bitmap.createBitmap((drawable.intrinsicWidth * sizeMultiplier).toInt(),
(drawable.intrinsicHeight * sizeMultiplier).toInt(), Bitmap.Config.ARGB_8888)
val c = Canvas(bitmap)
drawable.setBounds(0, 0, c.width, c.height)
drawable.draw(c)
return bitmap
}
protected fun composeRoundedRectPath(rect: RectF, tl: Float, tr: Float, bl: Float, br: Float): Path {
var tlf = tl
var trf = tr
var blf = bl
var brf = br
val path = Path()
tlf = if (tl < 0) 0F else tl
trf = if (tr < 0) 0f else tr
blf = if (bl < 0) 0f else bl
brf = if (br < 0) 0f else br
path.moveTo(rect.left + tl, rect.top)
path.lineTo(rect.right - tr, rect.top)
path.quadTo(rect.right, rect.top, rect.right, rect.top + tr)
path.lineTo(rect.right, rect.bottom - br)
path.quadTo(rect.right, rect.bottom, rect.right - br, rect.bottom)
path.lineTo(rect.left + bl, rect.bottom)
path.quadTo(rect.left, rect.bottom, rect.left, rect.bottom - bl)
path.lineTo(rect.left, rect.top + tl)
path.quadTo(rect.left, rect.top, rect.left + tl, rect.top)
path.close()
return path
}
}
}

View File

@ -24,7 +24,7 @@ class DeletePlaylistDialog : RoundedBottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val playlists = arguments!!.getParcelableArrayList<Playlist>("playlists") val playlists = arguments!!.getParcelableArrayList<Playlist>("playlist")
val content: CharSequence val content: CharSequence
content = if (playlists!!.size > 1) { content = if (playlists!!.size > 1) {

View File

@ -29,16 +29,16 @@ class SortOrder {
companion object { companion object {
/* Artist sort order A-Z */ /* Artist sort order A-Z */
val ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER const val ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
/* Artist sort order Z-A */ /* Artist sort order Z-A */
val ARTIST_Z_A = "$ARTIST_A_Z DESC" const val ARTIST_Z_A = "$ARTIST_A_Z DESC"
/* Artist sort order number of songs */ /* Artist sort order number of songs */
val ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS + " DESC" const val ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS + " DESC"
/* Artist sort order number of albums */ /* Artist sort order number of albums */
val ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS + " DESC" const val ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS + " DESC"
} }
} }
@ -49,20 +49,20 @@ class SortOrder {
companion object { companion object {
/* Album sort order A-Z */ /* Album sort order A-Z */
val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER const val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
/* Album sort order Z-A */ /* Album sort order Z-A */
val ALBUM_Z_A = "$ALBUM_A_Z DESC" const val ALBUM_Z_A = "$ALBUM_A_Z DESC"
/* Album sort order songs */ /* Album sort order songs */
val ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS + " DESC" const val ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS + " DESC"
/* Album sort order artist */ /* Album sort order artist */
val ALBUM_ARTIST = (MediaStore.Audio.Artists.DEFAULT_SORT_ORDER const val ALBUM_ARTIST = (MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
+ ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER) + ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER)
/* Album sort order year */ /* Album sort order year */
val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC" const val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
} }
} }
@ -73,25 +73,25 @@ class SortOrder {
companion object { companion object {
/* Song sort order A-Z */ /* Song sort order A-Z */
val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
/* Song sort order Z-A */ /* Song sort order Z-A */
val SONG_Z_A = "$SONG_A_Z DESC" const val SONG_Z_A = "$SONG_A_Z DESC"
/* Song sort order artist */ /* Song sort order artist */
val SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER const val SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
/* Song sort order album */ /* Song sort order album */
val SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER const val SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
/* Song sort order year */ /* Song sort order year */
val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC" const val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
/* Song sort order duration */ /* Song sort order duration */
val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC" const val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"
/* Song sort order date */ /* Song sort order date */
val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC" const val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"
} }
} }
@ -102,17 +102,17 @@ class SortOrder {
companion object { companion object {
/* Album song sort order A-Z */ /* Album song sort order A-Z */
val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
/* Album song sort order Z-A */ /* Album song sort order Z-A */
val SONG_Z_A = "$SONG_A_Z DESC" const val SONG_Z_A = "$SONG_A_Z DESC"
/* Album song sort order track list */ /* Album song sort order track list */
val SONG_TRACK_LIST = (MediaStore.Audio.Media.TRACK + ", " const val SONG_TRACK_LIST = (MediaStore.Audio.Media.TRACK + ", "
+ MediaStore.Audio.Media.DEFAULT_SORT_ORDER) + MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
/* Album song sort order duration */ /* Album song sort order duration */
val SONG_DURATION = SongSortOrder.SONG_DURATION const val SONG_DURATION = SongSortOrder.SONG_DURATION
} }
} }
@ -123,22 +123,22 @@ class SortOrder {
companion object { companion object {
/* Artist song sort order A-Z */ /* Artist song sort order A-Z */
val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
/* Artist song sort order Z-A */ /* Artist song sort order Z-A */
val SONG_Z_A = "$SONG_A_Z DESC" const val SONG_Z_A = "$SONG_A_Z DESC"
/* Artist song sort order album */ /* Artist song sort order album */
val SONG_ALBUM = MediaStore.Audio.Media.ALBUM const val SONG_ALBUM = MediaStore.Audio.Media.ALBUM
/* Artist song sort order year */ /* Artist song sort order year */
val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC" const val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
/* Artist song sort order duration */ /* Artist song sort order duration */
val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC" const val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"
/* Artist song sort order date */ /* Artist song sort order date */
val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC" const val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"
} }
} }
@ -149,16 +149,16 @@ class SortOrder {
companion object { companion object {
/* Artist album sort order A-Z */ /* Artist album sort order A-Z */
val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER const val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
/* Artist album sort order Z-A */ /* Artist album sort order Z-A */
val ALBUM_Z_A = "$ALBUM_A_Z DESC" const val ALBUM_Z_A = "$ALBUM_A_Z DESC"
/* Artist album sort order year */ /* Artist album sort order year */
val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC" const val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
/* Artist album sort order year */ /* Artist album sort order year */
val ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR + " ASC" const val ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR + " ASC"
} }
} }
@ -169,10 +169,10 @@ class SortOrder {
companion object { companion object {
/* Genre sort order A-Z */ /* Genre sort order A-Z */
val GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER const val GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER
/* Genre sort order Z-A */ /* Genre sort order Z-A */
val ALBUM_Z_A = "$GENRE_A_Z DESC" const val ALBUM_Z_A = "$GENRE_A_Z DESC"
} }
} }

View File

@ -2,18 +2,17 @@ package code.name.monkey.retromusic.loaders
import android.content.Context import android.content.Context
import android.text.TextUtils import android.text.TextUtils
import java.util.ArrayList
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import io.reactivex.Observable import io.reactivex.Observable
import java.util.*
object SearchLoader { object SearchLoader {
fun searchAll(context: Context, query: String): Observable<ArrayList<Any>> { fun searchAll(context: Context, query: String?): Observable<ArrayList<Any>> {
val results = ArrayList<Any>() val results = ArrayList<Any>()
return Observable.create { e -> return Observable.create { e ->
if (!TextUtils.isEmpty(query)) { if (!TextUtils.isEmpty(query)) {
SongLoader.getSongs(context, query) SongLoader.getSongs(context, query!!)
.subscribe { songs -> .subscribe { songs ->
if (!songs.isEmpty()) { if (!songs.isEmpty()) {
results.add(context.resources.getString(R.string.songs)) results.add(context.resources.getString(R.string.songs))

View File

@ -114,7 +114,7 @@ object SongLoader {
return newSelectionValues return newSelectionValues
} }
fun getSong(cursor: Cursor?): Observable<Song> { private fun getSong(cursor: Cursor?): Observable<Song> {
return Observable.create { e -> return Observable.create { e ->
val song: Song = if (cursor != null && cursor.moveToFirst()) { val song: Song = if (cursor != null && cursor.moveToFirst()) {
getSongFromCursorImpl(cursor) getSongFromCursorImpl(cursor)

View File

@ -2,8 +2,7 @@ package code.name.monkey.retromusic.mvp.contract
import code.name.monkey.retromusic.mvp.BasePresenter import code.name.monkey.retromusic.mvp.BasePresenter
import code.name.monkey.retromusic.mvp.BaseView import code.name.monkey.retromusic.mvp.BaseView
import java.util.*
import java.util.ArrayList
/** /**
@ -14,6 +13,6 @@ interface SearchContract {
interface SearchView : BaseView<ArrayList<Any>> interface SearchView : BaseView<ArrayList<Any>>
interface SearchPresenter : BasePresenter<SearchView> { interface SearchPresenter : BasePresenter<SearchView> {
fun search(query: String) fun search(query: String?)
} }
} }

View File

@ -27,7 +27,7 @@ class SearchPresenter(private val view: SearchContract.SearchView) : Presenter()
} }
} }
override fun search(query: String) { override fun search(query: String?) {
disposable.add(repository.search(query) disposable.add(repository.search(query)
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.subscribeOn(schedulerProvider.computation()) .subscribeOn(schedulerProvider.computation())

View File

@ -1,169 +0,0 @@
package code.name.monkey.retromusic.providers;
import android.content.Context;
import java.io.File;
import java.util.ArrayList;
import code.name.monkey.retromusic.Injection;
import code.name.monkey.retromusic.App;
import code.name.monkey.retromusic.loaders.AlbumLoader;
import code.name.monkey.retromusic.loaders.ArtistLoader;
import code.name.monkey.retromusic.loaders.GenreLoader;
import code.name.monkey.retromusic.loaders.HomeLoader;
import code.name.monkey.retromusic.loaders.LastAddedSongsLoader;
import code.name.monkey.retromusic.loaders.PlaylistLoader;
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
import code.name.monkey.retromusic.loaders.SearchLoader;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Album;
import code.name.monkey.retromusic.model.Artist;
import code.name.monkey.retromusic.model.Genre;
import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist;
import code.name.monkey.retromusic.providers.interfaces.Repository;
import code.name.monkey.retromusic.rest.model.KuGouRawLyric;
import code.name.monkey.retromusic.rest.model.KuGouSearchLyricResult;
import code.name.monkey.retromusic.rest.service.KuGouApiService;
import code.name.monkey.retromusic.util.LyricUtil;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
public class RepositoryImpl implements Repository {
private static RepositoryImpl INSTANCE;
private Context context;
public RepositoryImpl(Context context) {
this.context = context;
}
public static synchronized RepositoryImpl getInstance() {
if (INSTANCE == null) {
INSTANCE = new RepositoryImpl(App.Companion.getInstance());
}
return INSTANCE;
}
@Override
public Observable<ArrayList<Song>> getAllSongs() {
return SongLoader.INSTANCE.getAllSongs(context);
}
@Override
public Observable<ArrayList<Song>> getSuggestionSongs() {
return SongLoader.INSTANCE.suggestSongs(context);
}
@Override
public Observable<Song> getSong(int id) {
return SongLoader.INSTANCE.getSong(context, id);
}
@Override
public Observable<ArrayList<Album>> getAllAlbums() {
return AlbumLoader.Companion.getAllAlbums(context);
}
@Override
public Observable<ArrayList<Album>> getRecentAlbums() {
return LastAddedSongsLoader.INSTANCE.getLastAddedAlbums(context);
}
@Override
public Observable<ArrayList<Album>> getTopAlbums() {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopAlbums(context);
}
@Override
public Observable<Album> getAlbum(int albumId) {
return AlbumLoader.Companion.getAlbum(context, albumId);
}
@Override
public Observable<ArrayList<Artist>> getAllArtists() {
return ArtistLoader.INSTANCE.getAllArtists(context);
}
@Override
public Observable<ArrayList<Artist>> getRecentArtists() {
return LastAddedSongsLoader.INSTANCE.getLastAddedArtists(context);
}
@Override
public Observable<ArrayList<Artist>> getTopArtists() {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopArtists(context);
}
@Override
public Observable<Artist> getArtistById(long artistId) {
return ArtistLoader.INSTANCE.getArtist(context, (int) artistId);
}
@Override
public Observable<ArrayList<Playlist>> getAllPlaylists() {
return PlaylistLoader.INSTANCE.getAllPlaylists(context);
}
@Override
public Observable<ArrayList<Song>> getFavoriteSongs() {
return null;
}
@Override
public Observable<ArrayList<Object>> search(String query) {
return SearchLoader.INSTANCE.searchAll(context, query);
}
@Override
public Observable<ArrayList<Song>> getPlaylistSongs(Playlist playlist) {
return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, playlist);
}
@Override
public Observable<ArrayList<Playlist>> getHomeList() {
return HomeLoader.INSTANCE.getHomeLoader(context);
}
@Override
public Observable<ArrayList<AbsSmartPlaylist>> getAllThings() {
return HomeLoader.INSTANCE.getRecentAndTopThings(context);
}
@Override
public Observable<ArrayList<Genre>> getAllGenres() {
return GenreLoader.INSTANCE.getAllGenres(context);
}
@Override
public Observable<ArrayList<Song>> getGenre(int genreId) {
return GenreLoader.INSTANCE.getSongs(context, genreId);
}
@Override
public Observable<File> downloadLrcFile(String title, String artist, long duration) {
KuGouApiService service = Injection.INSTANCE.provideKuGouApiService();
return service.searchLyric(title, String.valueOf(duration))
.subscribeOn(Schedulers.io())
.flatMap(kuGouSearchLyricResult -> {
if (kuGouSearchLyricResult.status == 200
&& kuGouSearchLyricResult.candidates != null
&& kuGouSearchLyricResult.candidates.size() != 0) {
KuGouSearchLyricResult.Candidates candidates = kuGouSearchLyricResult.candidates.get(0);
return service.getRawLyric(candidates.id, candidates.accesskey);
} else {
return Observable.just(new KuGouRawLyric());
}
}).map(kuGouRawLyric -> {
if (kuGouRawLyric == null) {
return null;
}
String rawLyric = LyricUtil.decryptBASE64(kuGouRawLyric.content);
if (rawLyric != null && rawLyric.isEmpty()) {
return null;
}
return LyricUtil.writeLrcToLoc(title, artist, rawLyric);
});
}
}

View File

@ -0,0 +1,86 @@
package code.name.monkey.retromusic.providers
import android.content.Context
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.loaders.*
import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
import code.name.monkey.retromusic.providers.interfaces.Repository
import io.reactivex.Observable
import java.util.*
class RepositoryImpl(private val context: Context) : Repository {
override val allSongs: Observable<ArrayList<Song>>
get() = SongLoader.getAllSongs(context)
override val suggestionSongs: Observable<ArrayList<Song>>
get() = SongLoader.suggestSongs(context)
override val allAlbums: Observable<ArrayList<Album>>
get() = AlbumLoader.getAllAlbums(context)
override val recentAlbums: Observable<ArrayList<Album>>
get() = LastAddedSongsLoader.getLastAddedAlbums(context)
override val topAlbums: Observable<ArrayList<Album>>
get() = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context)
override val allArtists: Observable<ArrayList<Artist>>
get() = ArtistLoader.getAllArtists(context)
override val recentArtists: Observable<ArrayList<Artist>>
get() = LastAddedSongsLoader.getLastAddedArtists(context)
override val topArtists: Observable<ArrayList<Artist>>
get() = TopAndRecentlyPlayedTracksLoader.getTopArtists(context)
override val allPlaylists: Observable<ArrayList<Playlist>>
get() = PlaylistLoader.getAllPlaylists(context)
override val homeList: Observable<ArrayList<Playlist>>
get() = HomeLoader.getHomeLoader(context)
override val allThings: Observable<ArrayList<AbsSmartPlaylist>>
get() = HomeLoader.getRecentAndTopThings(context)
override val allGenres: Observable<ArrayList<Genre>>
get() = GenreLoader.getAllGenres(context)
override fun getSong(id: Int): Observable<Song> {
return SongLoader.getSong(context, id)
}
override fun getAlbum(albumId: Int): Observable<Album> {
return AlbumLoader.getAlbum(context, albumId)
}
override fun getArtistById(artistId: Long): Observable<Artist> {
return ArtistLoader.getArtist(context, artistId.toInt())
}
override fun search(query: String?): Observable<ArrayList<Any>> {
return SearchLoader.searchAll(context, query)
}
override fun getPlaylistSongs(playlist: Playlist): Observable<ArrayList<Song>> {
return PlaylistSongsLoader.getPlaylistSongList(context, playlist)
}
override fun getGenre(genreId: Int): Observable<ArrayList<Song>> {
return GenreLoader.getSongs(context, genreId)
}
companion object {
private var INSTANCE: RepositoryImpl? = null
val instance: RepositoryImpl
@Synchronized get() {
if (INSTANCE == null) {
INSTANCE = RepositoryImpl(App.instance)
}
return INSTANCE!!
}
}
}

View File

@ -1,14 +1,9 @@
package code.name.monkey.retromusic.providers.interfaces package code.name.monkey.retromusic.providers.interfaces
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
import io.reactivex.Observable import io.reactivex.Observable
import java.io.File import java.util.*
import java.util.ArrayList
/** /**
* Created by hemanths on 11/08/17. * Created by hemanths on 11/08/17.
@ -34,8 +29,6 @@ interface Repository {
val allPlaylists: Observable<ArrayList<Playlist>> val allPlaylists: Observable<ArrayList<Playlist>>
val favoriteSongs: Observable<ArrayList<Song>>
val homeList: Observable<ArrayList<Playlist>> val homeList: Observable<ArrayList<Playlist>>
val allThings: Observable<ArrayList<AbsSmartPlaylist>> val allThings: Observable<ArrayList<AbsSmartPlaylist>>
@ -46,15 +39,13 @@ interface Repository {
fun getAlbum(albumId: Int): Observable<Album> fun getAlbum(albumId: Int): Observable<Album>
fun getArtistById(artistId: Long): Observable<Artist> fun getArtistById(artistId: Long): Observable<Artist>
fun search(query: String): Observable<ArrayList<Any>> fun search(query: String?): Observable<ArrayList<Any>>
fun getPlaylistSongs(playlist: Playlist): Observable<ArrayList<Song>> fun getPlaylistSongs(playlist: Playlist): Observable<ArrayList<Song>>
fun getGenre(genreId: Int): Observable<ArrayList<Song>> fun getGenre(genreId: Int): Observable<ArrayList<Song>>
fun downloadLrcFile(title: String, artist: String, duration: Long): Observable<File>
} }

View File

@ -2,6 +2,7 @@ package code.name.monkey.retromusic.service;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@ -47,6 +48,10 @@ import java.util.Random;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
import code.name.monkey.retromusic.glide.BlurTransformation; import code.name.monkey.retromusic.glide.BlurTransformation;
import code.name.monkey.retromusic.glide.SongGlideRequest; import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.helper.ShuffleHelper; import code.name.monkey.retromusic.helper.ShuffleHelper;
@ -92,12 +97,10 @@ import static code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED;
*/ */
public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public static final String TAG = MusicService.class.getSimpleName(); public static final String TAG = MusicService.class.getSimpleName();
public static final String SAVED_POSITION = "POSITION"; public static final String SAVED_POSITION = "POSITION";
public static final String SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK"; public static final String SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK";
public static final String SAVED_SHUFFLE_MODE = "SHUFFLE_MODE"; public static final String SAVED_SHUFFLE_MODE = "SHUFFLE_MODE";
public static final String SAVED_REPEAT_MODE = "REPEAT_MODE"; public static final String SAVED_REPEAT_MODE = "REPEAT_MODE";
public static final int RELEASE_WAKELOCK = 0; public static final int RELEASE_WAKELOCK = 0;
public static final int TRACK_ENDED = 1; public static final int TRACK_ENDED = 1;
public static final int TRACK_WENT_TO_NEXT = 2; public static final int TRACK_WENT_TO_NEXT = 2;
@ -122,11 +125,35 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
| PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SEEK_TO; | PlaybackStateCompat.ACTION_SEEK_TO;
private final IBinder musicBind = new MusicBinder(); private final IBinder musicBind = new MusicBinder();
private AppWidgetBig appWidgetBig = AppWidgetBig.Companion.getInstance();
private AppWidgetClassic appWidgetClassic = AppWidgetClassic.Companion.getInstance();
private AppWidgetSmall appWidgetSmall = AppWidgetSmall.Companion.getInstance();
private AppWidgetCard appWidgetCard = AppWidgetCard.Companion.getInstance();
private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() { private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(final Context context, final Intent intent) { public void onReceive(final Context context, final Intent intent) {
final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME); final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME);
final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
switch (command) {
case AppWidgetClassic.NAME: {
appWidgetClassic.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetSmall.NAME: {
appWidgetSmall.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetBig.NAME: {
appWidgetBig.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetCard.NAME: {
appWidgetCard.performUpdate(MusicService.this, ids);
break;
}
}
} }
}; };
private Playback playback; private Playback playback;
@ -1083,7 +1110,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private void sendChangeInternal(final String what) { private void sendChangeInternal(final String what) {
sendBroadcast(new Intent(what)); sendBroadcast(new Intent(what));
appWidgetBig.notifyChange(this, what);
appWidgetClassic.notifyChange(this, what);
appWidgetSmall.notifyChange(this, what);
appWidgetCard.notifyChange(this, what);
} }
private void handleChangeInternal(@NonNull final String what) { private void handleChangeInternal(@NonNull final String what) {

View File

@ -45,10 +45,8 @@ class PlayingNotificationImpl : PlayingNotification() {
val isPlaying = service.isPlaying val isPlaying = service.isPlaying
val notificationLayout = RemoteViews(service.packageName, val notificationLayout = RemoteViews(service.packageName, R.layout.notification)
R.layout.notification) val notificationLayoutBig = RemoteViews(service.packageName, R.layout.notification_big)
val notificationLayoutBig = RemoteViews(service.packageName,
R.layout.notification_big)
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE) notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE)
@ -58,8 +56,7 @@ class PlayingNotificationImpl : PlayingNotification() {
notificationLayout.setTextViewText(R.id.text, song.artistName) notificationLayout.setTextViewText(R.id.text, song.artistName)
} }
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils.isEmpty(song.albumName)) {
.isEmpty(song.albumName)) {
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE) notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else { } else {
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE) notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE)

View File

@ -0,0 +1,82 @@
package code.name.monkey.retromusic.ui.activities
import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
import android.view.WindowManager
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.r0adkll.slidr.Slidr
import com.r0adkll.slidr.model.SlidrConfig
import com.r0adkll.slidr.model.SlidrPosition
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.ui.fragments.player.lockscreen.LockScreenPlayerControlsFragment
class LockScreenActivity : AbsMusicServiceActivity() {
private var mFragment: LockScreenPlayerControlsFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
or WindowManager.LayoutParams.FLAG_FULLSCREEN
or WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
setDrawUnderStatusBar()
setContentView(R.layout.activity_lock_screen_old_style)
hideStatusBar()
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
val config = SlidrConfig.Builder()
.position(SlidrPosition.BOTTOM)
.build()
Slidr.attach(this, config)
mFragment = supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
findViewById<View>(R.id.slide).setTranslationY(100f)
findViewById<View>(R.id.slide).setAlpha(0f)
ViewCompat.animate(findViewById<View>(R.id.slide))
.translationY(0f)
.alpha(1f)
.setDuration(1500)
.start()
findViewById<View>(R.id.root_layout).setBackgroundColor(ThemeStore.primaryColor(this))
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSongs()
}
override fun onServiceConnected() {
super.onServiceConnected()
updateSongs()
}
private fun updateSongs() {
val song = MusicPlayerRemote.currentSong
SongGlideRequest.Builder.from(Glide.with(this), song)
.checkIgnoreMediaStore(this)
.generatePalette(this)
.build().into(object : RetroMusicColoredTarget(findViewById(R.id.image)) {
override fun onColorReady(color: Int) {
mFragment!!.setDark(color)
}
})
}
}

View File

@ -1,168 +1,104 @@
package code.name.monkey.retromusic.ui.activities package code.name.monkey.retromusic.ui.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.text.TextUtils import android.text.TextUtils
import android.view.MenuItem import android.view.*
import android.view.View
import android.view.WindowManager
import android.widget.RadioButton
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.Lyrics import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.ui.activities.tageditor.WriteTagsAsyncTask import code.name.monkey.retromusic.ui.activities.tageditor.WriteTagsAsyncTask
import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.util.LyricUtil import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.bumptech.glide.Glide
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.activity_lyrics.* import kotlinx.android.synthetic.main.activity_lyrics.*
import kotlinx.android.synthetic.main.fragment_lyrics.*
import kotlinx.android.synthetic.main.fragment_synced.*
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.io.File import java.io.File
import java.util.* import java.util.*
class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback, View.OnClickListener { class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener {
override fun onClick(v: View?) {
when (viewPager.currentItem) {
0 -> showSyncedLyrics()
1 -> showLyricsSaveDialog()
}
}
private var updateHelper: MusicProgressViewUpdateHelper? = null private lateinit var song: Song
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
private var disposable: CompositeDisposable? = null
private var song: Song? = null
private var lyrics: Lyrics? = null private var lyrics: Lyrics? = null
private val googleSearchLrcUrl: String private val googleSearchLrcUrl: String
get() { get() {
var baseUrl = "http://www.google.com/search?" var baseUrl = "http://www.google.com/search?"
var query = song!!.title + "+" + song!!.artistName var query = song.title + "+" + song.artistName
query = "q=" + query.replace(" ", "+") + " .lrc" query = "q=" + query.replace(" ", "+") + " .lrc"
baseUrl += query baseUrl += query
return baseUrl return baseUrl
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
//setDrawUnderNavigationBar();
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lyrics) setContentView(R.layout.activity_lyrics)
setStatusbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
setNavigationbarColorAuto() setNavigationbarColorAuto()
setLightStatusbar(true)
container.isFit = !PreferenceUtil.getInstance().fullScreenMode toolbar.setBackgroundColor(ThemeStore.primaryColor(this))
toolbar.navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.ic_keyboard_backspace_black_24dp), ThemeStore.textColorSecondary(this@LyricsActivity))
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
setSupportActionBar(toolbar)
setSupportActionBar(bottomAppBar)
Objects.requireNonNull<Drawable>(bottomAppBar!!.navigationIcon)
.setColorFilter(ThemeStore.textColorPrimary(this), PorterDuff.Mode.SRC_IN)
bottomAppBar!!.backgroundTint = ColorStateList.valueOf(ThemeStore.primaryColor(this))
TintHelper.setTintAuto(fab, ThemeStore.accentColor(this), true) TintHelper.setTintAuto(fab, ThemeStore.accentColor(this), true)
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
setupLyricsView()
setupWakelock() setupWakelock()
loadLrcFile()
actions.setOnCheckedChangeListener { _, checkedId -> selectLyricsTye(checkedId) }
actions.check(PreferenceUtil.getInstance().lastLyricsType)
viewPager.apply {
adapter = PagerAdapter(supportFragmentManager)
}
tabs.apply {
setupWithViewPager(viewPager)
setSelectedTabIndicator(TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.tab_indicator), ThemeStore.accentColor(this@LyricsActivity)))
setTabTextColors(ThemeStore.textColorSecondary(this@LyricsActivity), ThemeStore.accentColor(this@LyricsActivity))
setSelectedTabIndicatorColor(ThemeStore.accentColor(context))
}
fab.setOnClickListener(this) fab.setOnClickListener(this)
} }
private fun selectLyricsTye(group: Int) {
PreferenceUtil.getInstance().lastLyricsType = group
val radioButton = actions.findViewById<RadioButton>(group)
if (radioButton != null) {
radioButton.backgroundTintList = ColorStateList.valueOf(Color.WHITE)
//radioButton.setTextColor(ThemeStore.textColorPrimary(this));
}
offlineLyrics!!.visibility = View.GONE
lyricsView.visibility = View.GONE
when (group) {
R.id.syncedLyrics -> {
loadLRCLyrics()
lyricsView!!.visibility = View.VISIBLE
}
R.id.normalLyrics -> {
loadSongLyrics()
offlineLyrics!!.visibility = View.VISIBLE
}
else -> {
loadSongLyrics()
offlineLyrics!!.visibility = View.VISIBLE
}
}
}
private fun loadLRCLyrics() {
if (LyricUtil.isLrcFileExist(song!!.title, song!!.artistName)) {
showLyricsLocal(LyricUtil.getLocalLyricFile(song!!.title, song!!.artistName))
}
}
private fun setupWakelock() {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun setupLyricsView() {
disposable = CompositeDisposable()
lyricsView!!.apply {
setOnPlayerClickListener { progress, _ -> MusicPlayerRemote.seekTo(progress.toInt()) }
//lyricView.setHighLightTextColor(ThemeStore.accentColor(this));
setDefaultColor(ContextCompat.getColor(this@LyricsActivity, R.color.md_grey_400))
//lyricView.setTouchable(false);
setHintColor(Color.WHITE)
}
}
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged() super.onPlayingMetaChanged()
loadLrcFile() song = MusicPlayerRemote.currentSong
} toolbar.title = song.title
toolbar.subtitle = song.artistName
override fun onResume() {
super.onResume()
updateHelper!!.start()
}
override fun onPause() {
super.onPause()
updateHelper!!.stop()
} }
override fun onServiceConnected() { override fun onServiceConnected() {
super.onServiceConnected() super.onServiceConnected()
loadLrcFile() song = MusicPlayerRemote.currentSong
toolbar.title = song.title
toolbar.subtitle = song.artistName
} }
override fun onDestroy() { private fun setupWakelock() {
super.onDestroy() window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
disposable!!.clear()
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
updateLyricsAsyncTask!!.cancel(true)
}
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -172,84 +108,10 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onUpdateProgressViews(progress: Int, total: Int) {
lyricsView!!.setCurrentTimeMillis(progress.toLong())
}
private fun loadLrcFile() {
song = MusicPlayerRemote.currentSong
bottomAppBar.title = song!!.title
bottomAppBar.subtitle = song!!.artistName
SongGlideRequest.Builder.from(Glide.with(this), song!!)
.checkIgnoreMediaStore(this)
.generatePalette(this)
.build()
.into(object : RetroMusicColoredTarget(findViewById(R.id.image)) {
override fun onColorReady(color: Int) {
if (PreferenceUtil.getInstance().adaptiveColor) {
//background.setBackgroundColor(color);
}
}
})
}
private fun showLyricsLocal(file: File?) {
if (file == null) {
lyricsView!!.reset()
} else {
lyricsView!!.setLyricFile(file, "UTF-8")
}
}
override fun onClick(view: View) {
when (view.id) {
android.R.id.home -> onBackPressed()
R.id.fab -> when (actions.checkedRadioButtonId) {
R.id.syncedLyrics -> showSyncedLyrics()
R.id.normalLyrics -> showLyricsSaveDialog()
}
}
}
@SuppressLint("StaticFieldLeak")
private fun loadSongLyrics() {
if (updateLyricsAsyncTask != null) {
updateLyricsAsyncTask!!.cancel(false)
}
val song = MusicPlayerRemote.currentSong
updateLyricsAsyncTask = object : AsyncTask<Void, Void, Lyrics>() {
override fun doInBackground(vararg params: Void): Lyrics? {
val data = MusicUtil.getLyrics(song)
return if (TextUtils.isEmpty(data)) {
null
} else Lyrics.parse(song, data)
}
override fun onPreExecute() {
super.onPreExecute()
lyrics = null
}
override fun onPostExecute(l: Lyrics?) {
lyrics = l
offlineLyrics!!.visibility = View.VISIBLE
if (l == null) {
offlineLyrics!!.setText(R.string.no_lyrics_found)
return
}
offlineLyrics!!.text = l.data
}
override fun onCancelled(s: Lyrics) {
onPostExecute(null)
}
}.execute()
}
private fun showSyncedLyrics() { private fun showSyncedLyrics() {
var content = "" var content = ""
try { try {
content = LyricUtil.getStringFromFile(song!!.title, song!!.artistName) content = LyricUtil.getStringFromFile(song.title, song.artistName)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@ -260,14 +122,12 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
.content("Add time frame lyrics") .content("Add time frame lyrics")
.negativeText("Delete") .negativeText("Delete")
.onNegative { _, _ -> .onNegative { _, _ ->
LyricUtil.deleteLrcFile(song!!.title, song!!.artistName) LyricUtil.deleteLrcFile(song.title, song.artistName)
loadLrcFile()
} }
.onNeutral { _, _ -> RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl) } .onNeutral { _, _ -> RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl) }
.inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) .inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE)
.input("Paste lyrics here", content) { _, input -> .input("Paste lyrics here", content) { _, input ->
LyricUtil.writeLrcToLoc(song!!.title, song!!.artistName, input.toString()) LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString())
loadLrcFile()
}.show() }.show()
} }
@ -280,13 +140,12 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
MaterialDialog.Builder(this) MaterialDialog.Builder(this)
.title("Add lyrics") .title("Add lyrics")
.neutralText("Search") .neutralText("Search")
.onNeutral { _, _ -> RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl(song!!.title, song!!.artistName)) } .onNeutral { _, _ -> RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl(song.title, song.artistName)) }
.inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) .inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE)
.input("Paste lyrics here", content) { _, input -> .input("Paste lyrics here", content) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString() fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
WriteTagsAsyncTask(this@LyricsActivity).execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(song!!), fieldKeyValueMap, null)) WriteTagsAsyncTask(this@LyricsActivity).execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null))
loadLrcFile()
} }
.show() .show()
} }
@ -305,3 +164,154 @@ class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.
return baseUrl return baseUrl
} }
} }
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> SyncedLyricsFragment()
1 -> OfflineLyricsFragment()
else -> {
SyncedLyricsFragment()
}
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> App.context.getString(R.string.synced_lyrics)
1 -> App.context.getString(R.string.normal_lyrics)
else -> {
App.context.getString(R.string.synced_lyrics)
}
}
}
override fun getCount(): Int {
return 2
}
}
class OfflineLyricsFragment : AbsMusicServiceFragment() {
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
private var lyrics: Lyrics? = null
@SuppressLint("StaticFieldLeak")
private fun loadSongLyrics() {
if (updateLyricsAsyncTask != null) {
updateLyricsAsyncTask!!.cancel(false)
}
val song = MusicPlayerRemote.currentSong
updateLyricsAsyncTask = object : AsyncTask<Void?, Void?, Lyrics?>() {
override fun doInBackground(vararg params: Void?): Lyrics? {
val data = MusicUtil.getLyrics(song)
return if (TextUtils.isEmpty(data)) {
null
} else Lyrics.parse(song, data)
}
override fun onPreExecute() {
super.onPreExecute()
lyrics = null
}
override fun onPostExecute(l: Lyrics?) {
lyrics = l
offlineLyrics.visibility = View.VISIBLE
if (l == null) {
offlineLyrics.setText(R.string.no_lyrics_found)
return
}
offlineLyrics.text = l.data
}
override fun onCancelled(s: Lyrics?) {
onPostExecute(null)
}
}.execute()
}
override fun onDestroyView() {
super.onDestroyView()
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
updateLyricsAsyncTask!!.cancel(true)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loadSongLyrics()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_lyrics, container, false)
}
}
class SyncedLyricsFragment : AbsMusicServiceFragment(), MusicProgressViewUpdateHelper.Callback {
private lateinit var updateHelper: MusicProgressViewUpdateHelper
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_synced, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupLyricsView()
}
private fun setupLyricsView() {
lyricsView.apply {
setOnPlayerClickListener { progress, _ -> MusicPlayerRemote.seekTo(progress.toInt()) }
setDefaultColor(ContextCompat.getColor(context, R.color.md_grey_400))
setHintColor(ThemeStore.textColorPrimary(context))
setHighLightColor(ThemeStore.textColorPrimary(context))
setTextSize(RetroUtil.convertDpToPixel(18f, context).toInt())
}
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
loadLRCLyrics()
}
override fun onServiceConnected() {
super.onServiceConnected()
loadLRCLyrics()
}
override fun onResume() {
super.onResume()
updateHelper.start()
}
override fun onPause() {
super.onPause()
updateHelper.stop()
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
lyricsView.setCurrentTimeMillis(progress.toLong())
}
private fun loadLRCLyrics() {
val song = MusicPlayerRemote.currentSong
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName))
}
}
private fun showLyricsLocal(file: File?) {
if (file == null) {
lyricsView!!.reset()
} else {
lyricsView!!.setLyricFile(file, "UTF-8")
}
}
}

View File

@ -11,6 +11,7 @@ import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.app.ActivityCompat
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
@ -49,10 +50,10 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
val action = intent.action val action = intent.action
if (action != null && action == Intent.ACTION_SCREEN_OFF) { if (action != null && action == Intent.ACTION_SCREEN_OFF) {
if (PreferenceUtil.getInstance().lockScreen && MusicPlayerRemote.isPlaying) { if (PreferenceUtil.getInstance().lockScreen && MusicPlayerRemote.isPlaying) {
/*Intent activity = new Intent(context, LockScreenActivity.class); val activity = Intent(context, LockScreenActivity::class.java)
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
ActivityCompat.startActivity(context, activity, null);*/ ActivityCompat.startActivity(context, activity, null)
} }
} }
} }
@ -210,8 +211,14 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
if (!hasPermissions()) { if (!hasPermissions()) {
requestPermissions() requestPermissions()
} }
checkSetUpPro(); // good chance that pro version check was delayed on first start
} }
REQUEST_CODE_THEME, APP_USER_INFO_REQUEST -> postRecreate() REQUEST_CODE_THEME, APP_USER_INFO_REQUEST -> postRecreate()
PURCHASE_REQUEST -> {
if (resultCode == RESULT_OK) {
checkSetUpPro();
}
}
} }
} }
@ -369,10 +376,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private fun updateNavigationDrawerHeader() {
}
companion object { companion object {
const val APP_INTRO_REQUEST = 2323 const val APP_INTRO_REQUEST = 2323
const val LIBRARY = 1 const val LIBRARY = 1

View File

@ -21,7 +21,7 @@ class PlayingQueueActivity : AbsMusicServiceActivity() {
private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var wrappedAdapter: RecyclerView.Adapter<*>? = null
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
private var playingQueueAdapter: PlayingQueueAdapter? = null private var playingQueueAdapter: PlayingQueueAdapter? = null
private lateinit var layoutManager: LinearLayoutManager private lateinit var linearLayoutManager: LinearLayoutManager
private val upNextAndQueueTime: String private val upNextAndQueueTime: String
get() = resources.getString(R.string.up_next) + "" + MusicUtil.getReadableDurationString(MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)) get() = resources.getString(R.string.up_next) + "" + MusicUtil.getReadableDurationString(MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position))
@ -54,16 +54,16 @@ class PlayingQueueActivity : AbsMusicServiceActivity() {
R.layout.item_queue) R.layout.item_queue)
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(playingQueueAdapter!!) wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(playingQueueAdapter!!)
layoutManager = LinearLayoutManager(this) linearLayoutManager = LinearLayoutManager(this)
recyclerView.apply { recyclerView.apply {
layoutManager = layoutManager layoutManager = linearLayoutManager
adapter = wrappedAdapter adapter = wrappedAdapter
itemAnimator = animator itemAnimator = animator
recyclerViewDragDropManager!!.attachRecyclerView(this) recyclerViewDragDropManager!!.attachRecyclerView(this)
} }
layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@ -109,7 +109,7 @@ class PlayingQueueActivity : AbsMusicServiceActivity() {
private fun resetToCurrentPosition() { private fun resetToCurrentPosition() {
recyclerView.stopScroll() recyclerView.stopScroll()
layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
} }
override fun onPause() { override fun onPause() {

View File

@ -78,14 +78,13 @@ class ProVersionActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
override fun onProductPurchased(productId: String, details: TransactionDetails?) { override fun onProductPurchased(productId: String, details: TransactionDetails?) {
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
setResult(Activity.RESULT_OK) setResult(RESULT_OK)
} }
override fun onPurchaseHistoryRestored() { override fun onPurchaseHistoryRestored() {
if (App.isProVersion) { if (App.isProVersion) {
Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG) Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show()
.show() setResult(RESULT_OK)
setResult(Activity.RESULT_OK)
} else { } else {
Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
} }
@ -161,7 +160,6 @@ class ProVersionActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
} }
companion object { companion object {
private const val TAG: String = "ProVersionActivity"
private val TAG: String = "ProVersionActivity"
} }
} }

View File

@ -55,7 +55,7 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, SearchCon
if (intent.getBooleanExtra("mic_search", false)) { if (intent.getBooleanExtra("mic_search", false)) {
startMicSearch() startMicSearch()
} }
back.setOnClickListener { onBackPressed() }
voiceSearch.setOnClickListener { startMicSearch() } voiceSearch.setOnClickListener { startMicSearch() }
} }
@ -83,7 +83,7 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, SearchCon
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
searchPresenter!!.subscribe() searchPresenter!!.subscribe()
searchPresenter!!.search(query!!) searchPresenter!!.search(query)
} }
override fun onDestroy() { override fun onDestroy() {

View File

@ -9,7 +9,9 @@ import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.ui.fragments.settings.MainSettingsFragment import code.name.monkey.retromusic.ui.fragments.settings.MainSettingsFragment
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
@ -32,6 +34,8 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback, Sh
} }
R.string.accent_color -> ThemeStore.editTheme(this).accentColor(selectedColor).commit() R.string.accent_color -> ThemeStore.editTheme(this).accentColor(selectedColor).commit()
} }
if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).updateDynamicShortcuts()
recreate() recreate()
} }

View File

@ -12,6 +12,7 @@ import android.text.TextUtils
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.Constants.USER_PROFILE import code.name.monkey.retromusic.Constants.USER_PROFILE
@ -32,13 +33,11 @@ import java.util.*
class UserInfoActivity : AbsBaseActivity() { class UserInfoActivity : AbsBaseActivity() {
private var disposable: CompositeDisposable? = null private var disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_info) setContentView(R.layout.activity_user_info)
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto() setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
@ -46,8 +45,6 @@ class UserInfoActivity : AbsBaseActivity() {
setupToolbar() setupToolbar()
disposable = CompositeDisposable()
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this)) bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
nameContainer.boxStrokeColor = ThemeStore.accentColor(this) nameContainer.boxStrokeColor = ThemeStore.accentColor(this)
name!!.setText(PreferenceUtil.getInstance().userName) name!!.setText(PreferenceUtil.getInstance().userName)
@ -58,7 +55,7 @@ class UserInfoActivity : AbsBaseActivity() {
if (!PreferenceUtil.getInstance().bannerImage.isEmpty()) { if (!PreferenceUtil.getInstance().bannerImage.isEmpty()) {
loadBannerFromStorage(PreferenceUtil.getInstance().bannerImage) loadBannerFromStorage(PreferenceUtil.getInstance().bannerImage)
} }
bannerImage.setOnClickListener { userImage.setOnClickListener {
MaterialDialog.Builder(this) MaterialDialog.Builder(this)
.title("Set a profile photo") .title("Set a profile photo")
.items(Arrays.asList(getString(R.string.new_profile_photo), getString(R.string.remove_profile_photo))) .items(Arrays.asList(getString(R.string.new_profile_photo), getString(R.string.remove_profile_photo)))
@ -82,6 +79,7 @@ class UserInfoActivity : AbsBaseActivity() {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} }
TintHelper.setTintAuto(next, ThemeStore.accentColor(this), true)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -96,11 +94,11 @@ class UserInfoActivity : AbsBaseActivity() {
toolbar.apply { toolbar.apply {
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp) setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
setBackgroundColor(primaryColor) setBackgroundColor(primaryColor)
ToolbarContentTintHelper.colorBackButton(this, ThemeStore.accentColor(this@UserInfoActivity))
setSupportActionBar(this)
} }
appBarLayout.setBackgroundColor(primaryColor) appBarLayout.setBackgroundColor(primaryColor)
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this))
title = null title = null
setSupportActionBar(toolbar)
} }
private fun showBannerOptions() { private fun showBannerOptions() {
@ -226,8 +224,8 @@ class UserInfoActivity : AbsBaseActivity() {
companion object { companion object {
private val PICK_IMAGE_REQUEST = 9002 private const val PICK_IMAGE_REQUEST = 9002
private val PICK_BANNER_REQUEST = 9003 private const val PICK_BANNER_REQUEST = 9003
private val PROFILE_ICON_SIZE = 400 private const val PROFILE_ICON_SIZE = 400
} }
} }

View File

@ -0,0 +1,94 @@
package code.name.monkey.retromusic.ui.activities;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.webkit.WebView;
import android.widget.TextView;
import com.afollestad.materialdialogs.internal.ThemeSingleton;
import com.google.android.material.appbar.AppBarLayout;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.appthemehelper.util.ColorUtil;
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity;
import code.name.monkey.retromusic.util.PreferenceUtil;
public class WhatsNewActivity extends AbsBaseActivity {
WebView webView;
TextView title;
Toolbar toolbar;
AppBarLayout appBarLayout;
private static void setChangelogRead(@NonNull Context context) {
try {
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
int currentVersion = pInfo.versionCode;
PreferenceUtil.getInstance().setLastChangeLogVersion(currentVersion);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
private static String colorToHex(int color) {
return Integer.toHexString(color).substring(2);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_whats_new);
webView = findViewById(R.id.webView);
title = findViewById(R.id.bannerTitle);
toolbar = findViewById(R.id.toolbar);
appBarLayout= findViewById(R.id.appBarLayout);
setStatusbarColorAuto();
setNavigationbarColorAuto();
setTaskDescriptionColorAuto();
toolbar.setBackgroundColor(ThemeStore.primaryColor(this));
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this));
setSupportActionBar(toolbar);
setTitle(null);
toolbar.setNavigationOnClickListener(v -> onBackPressed());
title.setTextColor(ThemeStore.textColorPrimary(this));
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this));
try {
// Load from phonograph-changelog.html in the assets folder
StringBuilder buf = new StringBuilder();
InputStream json = getAssets().open("retro-changelog.html");
BufferedReader in = new BufferedReader(new InputStreamReader(json, "UTF-8"));
String str;
while ((str = in.readLine()) != null)
buf.append(str);
in.close();
// Inject color values for WebView body background and links
final String backgroundColor = colorToHex(ThemeStore.primaryColor(this));
final String contentColor = ThemeSingleton.get().darkTheme ? "#ffffff" : "#000000";
webView.loadData(buf.toString()
.replace("{style-placeholder}",
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
.replace("{link-color}", colorToHex(ThemeSingleton.get().positiveColor.getDefaultColor()))
.replace("{link-color-active}", colorToHex(ColorUtil.lightenColor(ThemeSingleton.get().positiveColor.getDefaultColor())))
, "text/html", "UTF-8");
} catch (Throwable e) {
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
}
setChangelogRead(this);
}
}

View File

@ -1,74 +0,0 @@
package code.name.monkey.retromusic.ui.activities
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.util.PreferenceUtil
import com.afollestad.materialdialogs.internal.ThemeSingleton
import kotlinx.android.synthetic.main.activity_whats_new.*
import java.io.BufferedReader
import java.io.InputStreamReader
class WhatsNewActivity : AbsBaseActivity() {
private fun setChangelogRead(context: Context) {
try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val currentVersion = pInfo.versionCode
PreferenceUtil.getInstance().setLastChangeLogVersion(currentVersion)
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
private fun colorToHex(color: Int): String {
return Integer.toHexString(color).substring(2)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_whats_new)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
toolbar.setBackgroundColor(ThemeStore.primaryColor(this))
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
setSupportActionBar(toolbar)
title = null
toolbar.setNavigationOnClickListener { onBackPressed() }
whatNewtitle.setTextColor(ThemeStore.textColorPrimary(this))
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this))
try {
val buf = StringBuilder()
val json = assets.open("retro-changelog.html")
val inputStream = BufferedReader(InputStreamReader(json, "UTF-8"))
while (inputStream.readLine() != null) {
buf.append(inputStream.readLine())
}
inputStream.close()
// Inject color values for WebView body background and links
val backgroundColor = colorToHex(ThemeStore.primaryColor(this))
val contentColor = if (ThemeSingleton.get().darkTheme) "#ffffff" else "#000000"
webView.loadData(buf.toString()
.replace("{style-placeholder}",
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
.replace("{link-color}", colorToHex(ThemeSingleton.get().positiveColor.defaultColor))
.replace("{link-color-active}", colorToHex(ColorUtil.lightenColor(ThemeSingleton.get().positiveColor.defaultColor))), "text/html", "UTF-8")
} catch (e: Throwable) {
webView.loadData("<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8")
}
setChangelogRead(this)
}
}

View File

@ -10,6 +10,7 @@ import android.view.ViewTreeObserver
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -28,6 +29,7 @@ import code.name.monkey.retromusic.ui.fragments.player.material.MaterialFragment
import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment
import code.name.monkey.retromusic.ui.fragments.player.plain.PlainPlayerFragment import code.name.monkey.retromusic.ui.fragments.player.plain.PlainPlayerFragment
import code.name.monkey.retromusic.ui.fragments.player.simple.SimplePlayerFragment import code.name.monkey.retromusic.ui.fragments.player.simple.SimplePlayerFragment
import code.name.monkey.retromusic.util.ColorUtils
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.views.BottomNavigationBarTinted import code.name.monkey.retromusic.views.BottomNavigationBarTinted
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@ -268,6 +270,13 @@ abstract class AbsSlidingMusicPanelActivity protected constructor() : AbsMusicSe
currentNowPlayingScreen == NowPlayingScreen.BLUR || currentNowPlayingScreen == NowPlayingScreen.BLUR_CARD) { currentNowPlayingScreen == NowPlayingScreen.BLUR || currentNowPlayingScreen == NowPlayingScreen.BLUR_CARD) {
super.setLightStatusbar(false) super.setLightStatusbar(false)
super.setLightNavigationBar(true) super.setLightNavigationBar(true)
} else if (currentNowPlayingScreen == NowPlayingScreen.COLOR) {
super.setNavigationbarColor(paletteColor)
super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(isColorLight)
} else {
super.setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this)))
super.setLightNavigationBar(true)
} }
} }
} }

View File

@ -59,7 +59,7 @@ open class MediaEntryViewHolder(w: View) : RecyclerView.ViewHolder(w), View.OnCl
recyclerView = w.findViewById(R.id.recycler_view) recyclerView = w.findViewById(R.id.recycler_view)
mask = w.findViewById(R.id.mask) mask = w.findViewById(R.id.mask)
playSongs = w.findViewById(R.id.play_songs) playSongs = w.findViewById(R.id.playSongs)
w.setOnClickListener(this) w.setOnClickListener(this)
w.setOnLongClickListener(this) w.setOnLongClickListener(this)

View File

@ -177,6 +177,7 @@ open class SongAdapter @JvmOverloads constructor(protected val activity: AppComp
init { init {
setImageTransitionName(activity.getString(R.string.transition_album_art)) setImageTransitionName(activity.getString(R.string.transition_album_art))
if (menu != null) {
menu!!.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { menu!!.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
override val song: Song override val song: Song
get() = this@ViewHolder.song get() = this@ViewHolder.song
@ -189,6 +190,7 @@ open class SongAdapter @JvmOverloads constructor(protected val activity: AppComp
} }
}) })
} }
}
protected open fun onSongMenuItemClick(item: MenuItem): Boolean { protected open fun onSongMenuItemClick(item: MenuItem): Boolean {
if (image != null && image!!.visibility == View.VISIBLE) { if (image != null && image!!.visibility == View.VISIBLE) {

View File

@ -9,17 +9,18 @@ enum class NowPlayingScreen constructor(@param:StringRes @field:StringRes
val titleRes: Int, val titleRes: Int,
@param:DrawableRes @field:DrawableRes val drawableResId: Int, @param:DrawableRes @field:DrawableRes val drawableResId: Int,
val id: Int) { val id: Int) {
NORMAL(R.string.normal, R.drawable.np_normal, 0), ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10),
BLUR(R.string.blur, R.drawable.np_blur, 4),
BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9),
CARD(R.string.card, R.drawable.np_card, 6),
COLOR(R.string.color, R.drawable.np_color, 5),
FIT(R.string.fit, R.drawable.np_adaptive, 12),
FLAT(R.string.flat, R.drawable.np_flat, 1), FLAT(R.string.flat, R.drawable.np_flat, 1),
FULL(R.string.full, R.drawable.np_full, 2), FULL(R.string.full, R.drawable.np_full, 2),
PLAIN(R.string.plain, R.drawable.np_plain, 3),
BLUR(R.string.blur, R.drawable.np_blur, 4),
COLOR(R.string.color, R.drawable.np_color, 5),
CARD(R.string.card, R.drawable.np_card, 6),
//TINY(R.string.tiny, R.drawable.np_tiny, 7),
SIMPLE(R.string.simple, R.drawable.np_simple, 8),
BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9),
ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10),
MATERIAL(R.string.material, R.drawable.np_material, 11), MATERIAL(R.string.material, R.drawable.np_material, 11),
FIT(R.string.fit, R.drawable.np_adaptive, 12) NORMAL(R.string.normal, R.drawable.np_normal, 0),
PLAIN(R.string.plain, R.drawable.np_plain, 3),
//TINY(R.string.tiny, R.drawable.np_tiny, 7),
SIMPLE(R.string.simple, R.drawable.np_simple, 8)
} }

View File

@ -1,7 +1,6 @@
package code.name.monkey.retromusic.ui.fragments.base package code.name.monkey.retromusic.ui.fragments.base
import android.os.Bundle import android.os.Bundle
import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment
open class AbsLibraryPagerFragment : AbsMusicServiceFragment() { open class AbsLibraryPagerFragment : AbsMusicServiceFragment() {

View File

@ -41,15 +41,15 @@ abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment<A : RecyclerVie
activity!!.resources.getInteger(R.integer.default_list_columns_land) activity!!.resources.getInteger(R.integer.default_list_columns_land)
} else activity!!.resources.getInteger(R.integer.default_list_columns) } else activity!!.resources.getInteger(R.integer.default_list_columns)
protected val isLandscape: Boolean private val isLandscape: Boolean
get() = RetroUtil.isLandscape() get() = RetroUtil.isLandscape()
fun getGridSize(): Int { fun getGridSize(): Int {
if (gridSize == 0) { if (gridSize == 0) {
if (isLandscape) { gridSize = if (isLandscape) {
gridSize = loadGridSizeLand() loadGridSizeLand()
} else { } else {
gridSize = loadGridSize() loadGridSize()
} }
} }
return gridSize return gridSize

View File

@ -0,0 +1,461 @@
package code.name.monkey.retromusic.ui.fragments.mainactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.afollestad.materialcab.MaterialCab;
import com.google.android.material.appbar.AppBarLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.appthemehelper.common.ATHToolbarActivity;
import code.name.monkey.appthemehelper.util.ATHUtil;
import code.name.monkey.appthemehelper.util.TintHelper;
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog;
import code.name.monkey.retromusic.dialogs.SleepTimerDialog;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.helper.SortOrder;
import code.name.monkey.retromusic.interfaces.CabHolder;
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.ui.activities.SettingsActivity;
import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment;
import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment;
import code.name.monkey.retromusic.util.NavigationUtil;
import code.name.monkey.retromusic.util.RetroColorUtil;
import code.name.monkey.retromusic.util.RetroUtil;
public class LibraryFragment extends AbsMainActivityFragment implements CabHolder, MainActivityFragmentCallbacks {
public static final String TAG = "LibraryFragment";
private static final String CURRENT_TAB_ID = "current_tab_id";
private Toolbar toolbar;
private AppBarLayout appBarLayout;
private TextView bannerTitle;
private View contentContainer;
private MaterialCab cab;
private FragmentManager fragmentManager;
public static Fragment newInstance(int tab) {
Bundle args = new Bundle();
args.putInt(CURRENT_TAB_ID, tab);
LibraryFragment fragment = new LibraryFragment();
fragment.setArguments(args);
return fragment;
}
public static Fragment newInstance() {
return new LibraryFragment();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_library, container, false);
contentContainer = view.findViewById(R.id.fragmentContainer);
bannerTitle = view.findViewById(R.id.bannerTitle);
appBarLayout = view.findViewById(R.id.appBarLayout);
toolbar = view.findViewById(R.id.toolbar);
return view;
}
public void setTitle(@StringRes int name) {
bannerTitle.setText(getString(name));
}
public void addOnAppBarOffsetChangedListener(AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
appBarLayout.addOnOffsetChangedListener(onOffsetChangedListener);
}
public void removeOnAppBarOffsetChangedListener(AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
appBarLayout.removeOnOffsetChangedListener(onOffsetChangedListener);
}
public int getTotalAppBarScrollingRange() {
return appBarLayout.getTotalScrollRange();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setStatusbarColorAuto(view);
setupToolbar();
inflateFragment();
}
private void inflateFragment() {
if (getArguments() == null) {
selectedFragment(SongsFragment.Companion.newInstance());
return;
}
switch (getArguments().getInt(CURRENT_TAB_ID)) {
default:
case R.id.action_song:
selectedFragment(SongsFragment.Companion.newInstance());
break;
case R.id.action_album:
selectedFragment(AlbumsFragment.Companion.newInstance());
break;
case R.id.action_artist:
selectedFragment(ArtistsFragment.Companion.newInstance());
break;
case R.id.action_playlist:
selectedFragment(PlaylistsFragment.Companion.newInstance());
break;
}
}
@SuppressWarnings("ConstantConditions")
private void setupToolbar() {
bannerTitle.setTextColor(ThemeStore.textColorPrimary(getContext()));
int primaryColor = ThemeStore.primaryColor(getContext());
TintHelper.setTintAuto(contentContainer, primaryColor, true);
toolbar.setBackgroundColor(primaryColor);
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
appBarLayout.setBackgroundColor(primaryColor);
appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) ->
getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext())));
getMainActivity().setTitle(null);
getMainActivity().setSupportActionBar(toolbar);
}
private Fragment getCurrentFragment() {
if (fragmentManager == null) {
return SongsFragment.Companion.newInstance();
}
return fragmentManager.findFragmentByTag(LibraryFragment.TAG);
}
@Override
public boolean handleBackPress() {
if (cab != null && cab.isActive()) {
cab.finish();
return true;
}
return false;
}
private void selectedFragment(Fragment fragment) {
fragmentManager = getChildFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.replace(R.id.fragmentContainer, fragment, TAG)
.commit();
}
@NonNull
@Override
public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) {
if (cab != null && cab.isActive()) {
cab.finish();
}
//noinspection ConstantConditions
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
.start(callback);
return cab;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_main, menu);
Fragment currentFragment = getCurrentFragment();
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment
&& currentFragment.isAdded()) {
AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size);
if (RetroUtil.isLandscape()) {
gridSizeItem.setTitle(R.string.action_grid_size_land);
}
setUpGridSizeMenu(fragment, gridSizeItem.getSubMenu());
setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).getSubMenu());
} else {
menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title);
menu.removeItem(R.id.action_grid_size);
}
Activity activity = getActivity();
if (activity == null) {
return;
}
ToolbarContentTintHelper.handleOnCreateOptionsMenu(getActivity(), toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor(toolbar));
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
Activity activity = getActivity();
if (activity == null) {
return;
}
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(activity, toolbar);
}
private void setUpSortOrderMenu(
@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
@NonNull SubMenu sortOrderMenu) {
String currentSortOrder = fragment.getSortOrder();
sortOrderMenu.clear();
if (fragment instanceof AlbumsFragment) {
sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z)
.setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_A_Z));
sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a)
.setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_Z_A));
sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist)
.setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_ARTIST));
sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year)
.setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_YEAR));
} else if (fragment instanceof ArtistsFragment) {
sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z)
.setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_A_Z));
sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a)
.setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_Z_A));
} else if (fragment instanceof SongsFragment) {
sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_A_Z));
sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_Z_A));
sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ARTIST));
sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ALBUM));
sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_YEAR));
sortOrderMenu.add(0, R.id.action_song_sort_order_date, 4, R.string.sort_order_date)
.setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_DATE));
}
sortOrderMenu.setGroupCheckable(0, true, true);
}
private boolean handleSortOrderMenuItem(
@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment
fragment, @NonNull MenuItem item) {
String sortOrder = null;
if (fragment instanceof AlbumsFragment) {
switch (item.getItemId()) {
case R.id.action_album_sort_order_asc:
sortOrder = SortOrder.AlbumSortOrder.ALBUM_A_Z;
break;
case R.id.action_album_sort_order_desc:
sortOrder = SortOrder.AlbumSortOrder.ALBUM_Z_A;
break;
case R.id.action_album_sort_order_artist:
sortOrder = SortOrder.AlbumSortOrder.ALBUM_ARTIST;
break;
case R.id.action_album_sort_order_year:
sortOrder = SortOrder.AlbumSortOrder.ALBUM_YEAR;
break;
}
} else if (fragment instanceof ArtistsFragment) {
switch (item.getItemId()) {
case R.id.action_artist_sort_order_asc:
sortOrder = SortOrder.ArtistSortOrder.ARTIST_A_Z;
break;
case R.id.action_artist_sort_order_desc:
sortOrder = SortOrder.ArtistSortOrder.ARTIST_Z_A;
break;
}
} else if (fragment instanceof SongsFragment) {
switch (item.getItemId()) {
case R.id.action_song_sort_order_asc:
sortOrder = SortOrder.SongSortOrder.SONG_A_Z;
break;
case R.id.action_song_sort_order_desc:
sortOrder = SortOrder.SongSortOrder.SONG_Z_A;
break;
case R.id.action_song_sort_order_artist:
sortOrder = SortOrder.SongSortOrder.SONG_ARTIST;
break;
case R.id.action_song_sort_order_album:
sortOrder = SortOrder.SongSortOrder.SONG_ALBUM;
break;
case R.id.action_song_sort_order_year:
sortOrder = SortOrder.SongSortOrder.SONG_YEAR;
break;
case R.id.action_song_sort_order_date:
sortOrder = SortOrder.SongSortOrder.SONG_DATE;
break;
}
}
if (sortOrder != null) {
item.setChecked(true);
fragment.setAndSaveSortOrder(sortOrder);
return true;
}
return false;
}
@SuppressWarnings("ConstantConditions")
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
//if (pager == null) return false;
Fragment currentFragment = getCurrentFragment();
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) {
AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
if (handleGridSizeMenuItem(fragment, item)) {
return true;
}
if (handleSortOrderMenuItem(fragment, item)) {
return true;
}
}
int id = item.getItemId();
switch (id) {
case R.id.action_new_playlist:
CreatePlaylistDialog.Companion.create().show(getChildFragmentManager(), "CREATE_PLAYLIST");
return true;
case R.id.action_shuffle_all:
MusicPlayerRemote.INSTANCE.openAndShuffleQueue(SongLoader.INSTANCE.getAllSongs(getContext())
.blockingFirst(), true);
return true;
case R.id.action_search:
NavigationUtil.goToSearch(getMainActivity());
break;
case R.id.action_equalizer:
NavigationUtil.openEqualizer(getActivity());
return true;
case R.id.action_sleep_timer:
if (getFragmentManager() != null) {
new SleepTimerDialog().show(getFragmentManager(), TAG);
}
return true;
case R.id.action_settings:
startActivity(new Intent(getContext(), SettingsActivity.class));
break;
}
return super.onOptionsItemSelected(item);
}
private void setUpGridSizeMenu(
@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
@NonNull SubMenu gridSizeMenu) {
switch (fragment.getGridSize()) {
case 1:
gridSizeMenu.findItem(R.id.action_grid_size_1).setChecked(true);
break;
case 2:
gridSizeMenu.findItem(R.id.action_grid_size_2).setChecked(true);
break;
case 3:
gridSizeMenu.findItem(R.id.action_grid_size_3).setChecked(true);
break;
case 4:
gridSizeMenu.findItem(R.id.action_grid_size_4).setChecked(true);
break;
case 5:
gridSizeMenu.findItem(R.id.action_grid_size_5).setChecked(true);
break;
case 6:
gridSizeMenu.findItem(R.id.action_grid_size_6).setChecked(true);
break;
case 7:
gridSizeMenu.findItem(R.id.action_grid_size_7).setChecked(true);
break;
case 8:
gridSizeMenu.findItem(R.id.action_grid_size_8).setChecked(true);
break;
}
int maxGridSize = fragment.getMaxGridSize();
if (maxGridSize < 8) {
gridSizeMenu.findItem(R.id.action_grid_size_8).setVisible(false);
}
if (maxGridSize < 7) {
gridSizeMenu.findItem(R.id.action_grid_size_7).setVisible(false);
}
if (maxGridSize < 6) {
gridSizeMenu.findItem(R.id.action_grid_size_6).setVisible(false);
}
if (maxGridSize < 5) {
gridSizeMenu.findItem(R.id.action_grid_size_5).setVisible(false);
}
if (maxGridSize < 4) {
gridSizeMenu.findItem(R.id.action_grid_size_4).setVisible(false);
}
if (maxGridSize < 3) {
gridSizeMenu.findItem(R.id.action_grid_size_3).setVisible(false);
}
}
private boolean handleGridSizeMenuItem(
@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment
fragment, @NonNull MenuItem item) {
int gridSize = 0;
switch (item.getItemId()) {
case R.id.action_grid_size_1:
gridSize = 1;
break;
case R.id.action_grid_size_2:
gridSize = 2;
break;
case R.id.action_grid_size_3:
gridSize = 3;
break;
case R.id.action_grid_size_4:
gridSize = 4;
break;
case R.id.action_grid_size_5:
gridSize = 5;
break;
case R.id.action_grid_size_6:
gridSize = 6;
break;
case R.id.action_grid_size_7:
gridSize = 7;
break;
case R.id.action_grid_size_8:
gridSize = 8;
break;
}
if (gridSize > 0) {
item.setChecked(true);
fragment.setAndSaveGridSize(gridSize);
return true;
}
return false;
}
}

View File

@ -1,364 +0,0 @@
package code.name.monkey.retromusic.ui.fragments.mainactivity
import android.content.Intent
import android.os.Bundle
import android.view.*
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.SleepTimerDialog
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
import code.name.monkey.retromusic.loaders.SongLoader
import code.name.monkey.retromusic.ui.activities.SettingsActivity
import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialcab.MaterialCab
import com.google.android.material.appbar.AppBarLayout
class LibraryFragment : AbsMainActivityFragment(), CabHolder, MainActivityFragmentCallbacks, AppBarLayout.OnOffsetChangedListener {
override fun onOffsetChanged(p0: AppBarLayout?, p1: Int) {
mainActivity.setLightStatusbar(!ATHUtil.isWindowBackgroundDark(context))
}
lateinit var toolbar: Toolbar
lateinit var appbar: AppBarLayout
lateinit var title: TextView
lateinit var contentContainer: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_library, container, false)
toolbar = view.findViewById(R.id.toolbar)
appbar = view.findViewById(R.id.app_bar)
title = view.findViewById(R.id.title)
contentContainer = view.findViewById(R.id.fragment_container)
return view
}
private var cab: MaterialCab? = null
val totalAppBarScrollingRange: Int
get() = appbar.totalScrollRange
private fun getCurrentFragment(): Fragment? {
return if (fragmentManager == null) {
SongsFragment.newInstance()
} else fragmentManager!!.findFragmentById(R.id.fragment_container)
}
private fun selectedFragment(fragment: Fragment) {
val fragmentManager = childFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction
.replace(R.id.fragment_container, fragment, TAG)
.commit()
fragmentManager.executePendingTransactions()
}
fun setTitle(@StringRes name: Int) {
title.text = getString(name)
}
fun addOnAppBarOffsetChangedListener(onOffsetChangedListener: AppBarLayout.OnOffsetChangedListener) {
appbar.addOnOffsetChangedListener(onOffsetChangedListener)
}
fun removeOnAppBarOffsetChangedListener(onOffsetChangedListener: AppBarLayout.OnOffsetChangedListener) {
appbar.removeOnOffsetChangedListener(onOffsetChangedListener)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setStatusbarColorAuto(view)
setupToolbar()
inflateFragment()
}
private fun inflateFragment() {
if (arguments == null) {
selectedFragment(SongsFragment.newInstance())
return
}
when (arguments!!.getInt(CURRENT_TAB_ID)) {
R.id.action_song -> selectedFragment(SongsFragment.newInstance())
R.id.action_album -> selectedFragment(AlbumsFragment.newInstance())
R.id.action_artist -> selectedFragment(ArtistsFragment.newInstance())
R.id.action_playlist -> selectedFragment(PlaylistsFragment.newInstance())
else -> selectedFragment(SongsFragment.newInstance())
}
}
private fun setupToolbar() {
title.setTextColor(ThemeStore.textColorPrimary(context!!))
val primaryColor = ThemeStore.primaryColor(context!!)
TintHelper.setTintAuto(contentContainer, primaryColor, true)
toolbar.setBackgroundColor(primaryColor)
appbar.setBackgroundColor(primaryColor)
appbar.addOnOffsetChangedListener(this)
mainActivity.title = null
mainActivity.setSupportActionBar(toolbar)
toolbar.navigationIcon = RetroUtil.getTintedDrawable(mainActivity, R.drawable.ic_menu_white_24dp, ThemeStore.textColorPrimary(mainActivity))
}
override fun handleBackPress(): Boolean {
if (cab != null && cab!!.isActive) {
cab!!.finish()
return true
}
return false
}
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
if (cab != null && cab!!.isActive) {
cab!!.finish()
}
cab = MaterialCab(mainActivity, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(activity!!)))
.start(callback)
return cab as MaterialCab
}
private fun isPlaylistFragment(): Boolean {
return true
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
inflater!!.inflate(R.menu.menu_main, menu)
if (isPlaylistFragment()) {
menu!!.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title)
}
val currentFragment = getCurrentFragment()
if (currentFragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *> && currentFragment.isAdded) {
val fragment = currentFragment as AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>?
val gridSizeItem = menu!!.findItem(R.id.action_grid_size)
if (RetroUtil.isLandscape()) {
gridSizeItem.setTitle(R.string.action_grid_size_land)
}
setUpGridSizeMenu(fragment!!, gridSizeItem.subMenu)
setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).subMenu)
} else {
menu!!.removeItem(R.id.action_grid_size)
}
ToolbarContentTintHelper.handleOnCreateOptionsMenu(activity!!, toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor(toolbar))
}
override fun onPrepareOptionsMenu(menu: Menu?) {
super.onPrepareOptionsMenu(menu)
val activity = activity ?: return
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(activity, toolbar)
}
private fun setUpSortOrderMenu(
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
sortOrderMenu: SubMenu) {
val currentSortOrder = fragment.getSortOrder()
sortOrderMenu.clear()
when (fragment) {
is AlbumsFragment -> {
sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_A_Z
sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_Z_A
sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_ARTIST
sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year).isChecked = currentSortOrder == SortOrder.AlbumSortOrder.ALBUM_YEAR
}
is ArtistsFragment -> {
sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z
sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.ArtistSortOrder.ARTIST_Z_A
}
is SongsFragment -> {
sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_A_Z
sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_Z_A
sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_ARTIST
sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_ALBUM
sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_YEAR
sortOrderMenu.add(0, R.id.action_song_sort_order_date, 4, R.string.sort_order_date).isChecked = currentSortOrder == SortOrder.SongSortOrder.SONG_DATE
}
}
sortOrderMenu.setGroupCheckable(0, true, true)
}
private fun handleSortOrderMenuItem(
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, item: MenuItem): Boolean {
var sortOrder: String? = null
when (fragment) {
is AlbumsFragment -> when (item.itemId) {
R.id.action_album_sort_order_asc -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_A_Z
R.id.action_album_sort_order_desc -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_Z_A
R.id.action_album_sort_order_artist -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_ARTIST
R.id.action_album_sort_order_year -> sortOrder = SortOrder.AlbumSortOrder.ALBUM_YEAR
}
is ArtistsFragment -> when (item.itemId) {
R.id.action_artist_sort_order_asc -> sortOrder = SortOrder.ArtistSortOrder.ARTIST_A_Z
R.id.action_artist_sort_order_desc -> sortOrder = SortOrder.ArtistSortOrder.ARTIST_Z_A
}
is SongsFragment -> when (item.itemId) {
R.id.action_song_sort_order_asc -> sortOrder = SortOrder.SongSortOrder.SONG_A_Z
R.id.action_song_sort_order_desc -> sortOrder = SortOrder.SongSortOrder.SONG_Z_A
R.id.action_song_sort_order_artist -> sortOrder = SortOrder.SongSortOrder.SONG_ARTIST
R.id.action_song_sort_order_album -> sortOrder = SortOrder.SongSortOrder.SONG_ALBUM
R.id.action_song_sort_order_year -> sortOrder = SortOrder.SongSortOrder.SONG_YEAR
R.id.action_song_sort_order_date -> sortOrder = SortOrder.SongSortOrder.SONG_DATE
}
}
if (sortOrder != null) {
item.isChecked = true
fragment.setAndSaveSortOrder(sortOrder)
return true
}
return false
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//if (pager == null) return false;
val currentFragment = getCurrentFragment()
if (currentFragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>) {
val fragment = currentFragment as AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>?
if (handleGridSizeMenuItem(fragment!!, item)) {
return true
}
if (handleSortOrderMenuItem(fragment, item)) {
return true
}
}
val id = item.itemId
when (id) {
R.id.action_new_playlist -> {
CreatePlaylistDialog.create().show(childFragmentManager, "CREATE_PLAYLIST")
return true
}
R.id.action_shuffle_all -> {
MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(context!!).blockingFirst(), true)
return true
}
R.id.action_equalizer -> {
NavigationUtil.openEqualizer(activity!!)
return true
}
R.id.action_sleep_timer -> {
if (fragmentManager != null) {
SleepTimerDialog().show(fragmentManager!!, TAG)
}
return true
}
R.id.action_settings -> startActivity(Intent(context, SettingsActivity::class.java))
}
return super.onOptionsItemSelected(item)
}
private fun setUpGridSizeMenu(
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
gridSizeMenu: SubMenu) {
when (fragment.getGridSize()) {
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
}
val maxGridSize = fragment.maxGridSize
if (maxGridSize < 8) {
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
}
if (maxGridSize < 7) {
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
}
if (maxGridSize < 6) {
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
}
if (maxGridSize < 5) {
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
}
if (maxGridSize < 4) {
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
}
if (maxGridSize < 3) {
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
}
}
private fun handleGridSizeMenuItem(
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>, item: MenuItem): Boolean {
var gridSize = 0
when (item.itemId) {
R.id.action_grid_size_1 -> gridSize = 1
R.id.action_grid_size_2 -> gridSize = 2
R.id.action_grid_size_3 -> gridSize = 3
R.id.action_grid_size_4 -> gridSize = 4
R.id.action_grid_size_5 -> gridSize = 5
R.id.action_grid_size_6 -> gridSize = 6
R.id.action_grid_size_7 -> gridSize = 7
R.id.action_grid_size_8 -> gridSize = 8
}
if (gridSize > 0) {
item.isChecked = true
fragment.setAndSaveGridSize(gridSize)
return true
}
return false
}
companion object {
const val TAG: String = "LibraryFragment"
private const val CURRENT_TAB_ID = "current_tab_id"
fun newInstance(tab: Int): Fragment {
val args = Bundle()
args.putInt(CURRENT_TAB_ID, tab)
val fragment = LibraryFragment()
fragment.arguments = args
return fragment
}
fun newInstance(): Fragment {
return LibraryFragment()
}
}
}

View File

@ -40,6 +40,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.abs_playlists.*
import kotlinx.android.synthetic.main.fragment_banner_home.* import kotlinx.android.synthetic.main.fragment_banner_home.*
import kotlinx.android.synthetic.main.home_section_content.* import kotlinx.android.synthetic.main.home_section_content.*
import java.io.File import java.io.File
@ -181,6 +182,10 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
loadImageFromStorage(userImage) loadImageFromStorage(userImage)
getTimeOfTheDay() getTimeOfTheDay()
searchView.setOnClickListener {
NavigationUtil.goToSearch(activity!!)
}
} }
private fun setupToolbar() { private fun setupToolbar() {
@ -212,7 +217,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
} }
override fun showData(homes: ArrayList<Any>) { override fun showData(list: ArrayList<Any>) {
//homeAdapter.swapDataSet(homes); //homeAdapter.swapDataSet(homes);
} }

View File

@ -7,8 +7,10 @@ import android.view.ViewGroup
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.ui.adapter.album.AlbumCoverPagerAdapter import code.name.monkey.retromusic.ui.adapter.album.AlbumCoverPagerAdapter
import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen
import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.android.synthetic.main.fragment_player_album_cover.* import kotlinx.android.synthetic.main.fragment_player_album_cover.*
@ -40,7 +42,15 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(), ViewPager.OnPageChan
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewPager.addOnPageChangeListener(this) viewPager.addOnPageChangeListener(this)
viewPager.setPageTransformer(true, PreferenceUtil.getInstance().getAlbumCoverTransform(context)) //noinspection ConstantConditions
if (PreferenceUtil.getInstance().carouselEffect() && !((PreferenceUtil.getInstance().nowPlayingScreen == NowPlayingScreen.FULL) || (PreferenceUtil.getInstance().nowPlayingScreen == NowPlayingScreen.FIT))) {
viewPager.clipToPadding = false;
viewPager.setPadding(96, 0, 96, 0);
viewPager.pageMargin = 18;
viewPager.setPageTransformer(false, CarousalPagerTransformer(context!!));
} else {
viewPager.setPageTransformer(true, PreferenceUtil.getInstance().getAlbumCoverTransform(context!!));
}
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -34,8 +34,8 @@ class AdaptiveFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {
playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playback_controls_fragment) as AdaptivePlaybackControlsFragment playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as AdaptivePlaybackControlsFragment
val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.player_album_cover_fragment) as PlayerAlbumCoverFragment val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
playerAlbumCoverFragment.apply { playerAlbumCoverFragment.apply {
removeSlideEffect() removeSlideEffect()
}.setCallbacks(this) }.setCallbacks(this)

View File

@ -210,14 +210,14 @@ class ColorFragment : AbsPlayerFragment() {
updateLyricsAsyncTask!!.cancel(false) updateLyricsAsyncTask!!.cancel(false)
} }
val song = MusicPlayerRemote.currentSong val song = MusicPlayerRemote.currentSong
updateLyricsAsyncTask = object : AsyncTask<Void, Void, Lyrics>() { updateLyricsAsyncTask = object : AsyncTask<Void?, Void?, Lyrics?>() {
override fun onPreExecute() { override fun onPreExecute() {
super.onPreExecute() super.onPreExecute()
lyrics = null lyrics = null
playerToolbar.menu.removeItem(R.id.action_show_lyrics) playerToolbar.menu.removeItem(R.id.action_show_lyrics)
} }
override fun doInBackground(vararg params: Void): Lyrics? { override fun doInBackground(vararg params: Void?): Lyrics? {
val data = MusicUtil.getLyrics(song) val data = MusicUtil.getLyrics(song)
return if (TextUtils.isEmpty(data)) { return if (TextUtils.isEmpty(data)) {
null null
@ -233,7 +233,7 @@ class ColorFragment : AbsPlayerFragment() {
} }
} }
override fun onCancelled(s: Lyrics) { override fun onCancelled(s: Lyrics?) {
onPostExecute(null) onPostExecute(null)
} }
}.execute() }.execute()

View File

@ -0,0 +1,229 @@
package code.name.monkey.retromusic.ui.fragments.player.lockscreen
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.SeekBar
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.util.MusicUtil
import kotlinx.android.synthetic.main.fragment_lock_screen_playback_controls.*
import kotlinx.android.synthetic.main.media_button.*
import kotlinx.android.synthetic.main.player_time.*
/**
* @author Hemanth S (h4h13).
*/
class LockScreenPlayerControlsFragment : AbsPlayerControlsFragment() {
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_lock_screen_playback_controls, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpMusicControllers()
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
title.text = song.title
text.text = String.format("%s - %s", song.artistName, song.albumName)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper!!.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper!!.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
updateShuffleState()
updateSong()
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSong()
}
override fun onPlayStateChanged() {
updatePlayPauseDrawableState()
}
override fun onRepeatModeChanged() {
updateRepeatState()
}
override fun onShuffleModeChanged() {
updateShuffleState()
}
override fun setDark(color: Int) {
setProgressBarColor(progressSlider, color)
val colorBg = ATHUtil.resolveColor(activity, android.R.attr.colorBackground)
if (ColorUtil.isColorLight(colorBg)) {
lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true)
lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true)
} else {
lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false)
lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false)
}
updatePrevNextColor()
val isDark = ColorUtil.isColorLight(color)
text!!.setTextColor(color)
TintHelper.setTintAuto(playPauseButton, MaterialValueHelper.getPrimaryTextColor(context, isDark), false)
TintHelper.setTintAuto(playPauseButton, color, true)
}
fun setProgressBarColor(progressBar: SeekBar?, newColor: Int) {
TintHelper.setTintAuto(progressBar!!, newColor, false)
//LayerDrawable ld = (LayerDrawable) progressBar.getProgressDrawable();
//ClipDrawable clipDrawable = (ClipDrawable) ld.findDrawableByLayerId(android.R.id.progress);
//clipDrawable.setColorFilter(newColor, PorterDuff.Mode.SRC_IN);
}
private fun setUpPlayPauseFab() {
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
} else {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpProgressSlider()
setUpShuffleButton()
setUpRepeatButton()
}
private fun setUpPrevNext() {
updatePrevNextColor()
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
}
private fun setUpRepeatButton() {
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
MusicService.REPEAT_MODE_ALL -> {
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
MusicService.REPEAT_MODE_THIS -> {
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
}
}
public override fun show() {
playPauseButton!!.animate()
.scaleX(1f)
.scaleY(1f)
.rotation(360f)
.setInterpolator(DecelerateInterpolator())
.start()
}
public override fun hide() {
if (playPauseButton != null) {
playPauseButton!!.apply {
scaleX = 0f
scaleY = 0f
rotation = 0f
}
}
}
override fun setUpProgressSlider() {
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
progressSlider.max = total
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
animator.duration = 1500
animator.interpolator = LinearInterpolator()
animator.start()
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
}

View File

@ -121,9 +121,9 @@ class MaterialControlsFragment : AbsPlayerControlsFragment() {
private fun updatePlayPauseDrawableState() { private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) { if (MusicPlayerRemote.isPlaying) {
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) playPauseButton.setImageResource(R.drawable.ic_pause_white_big);
} else { } else {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp) playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_big);
} }
} }

View File

@ -96,6 +96,7 @@ class PlayerFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
snowfall.visibility= if (PreferenceUtil.getInstance().isSnowFall) View.VISIBLE else View.GONE
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View File

@ -1,16 +1,28 @@
package code.name.monkey.retromusic.ui.fragments.settings package code.name.monkey.retromusic.ui.fragments.settings
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.Constants.USER_PROFILE
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.ui.activities.SettingsActivity import code.name.monkey.retromusic.ui.activities.SettingsActivity
import code.name.monkey.retromusic.util.Compressor
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_main_settings.* import kotlinx.android.synthetic.main.fragment_main_settings.*
import java.io.File
import java.util.*
class MainSettingsFragment : Fragment(), View.OnClickListener { class MainSettingsFragment : Fragment(), View.OnClickListener {
override fun onClick(v: View) { override fun onClick(v: View) {
@ -44,6 +56,12 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
imageSettings.setOnClickListener(this) imageSettings.setOnClickListener(this)
notificationSettings.setOnClickListener(this) notificationSettings.setOnClickListener(this)
otherSettings.setOnClickListener(this) otherSettings.setOnClickListener(this)
text.setTextColor(ThemeStore.textColorSecondary(context!!));
titleWelcome.setTextColor(ThemeStore.textColorPrimary(context!!));
titleWelcome.text = String.format("%s %s!", getTimeOfTheDay(), PreferenceUtil.getInstance().userName);
loadImageFromStorage();
userInfoContainer.setOnClickListener { NavigationUtil.goToUserInfo(activity!!) }
} }
private fun inflateFragment(fragment: Fragment, @StringRes title: Int) { private fun inflateFragment(fragment: Fragment, @StringRes title: Int) {
@ -51,4 +69,42 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
(activity as SettingsActivity).setupFragment(fragment, title) (activity as SettingsActivity).setupFragment(fragment, title)
} }
} }
private fun getTimeOfTheDay(): String {
var message = getString(R.string.title_good_day)
val c = Calendar.getInstance()
val timeOfDay = c.get(Calendar.HOUR_OF_DAY)
when (timeOfDay) {
in 0..5 -> message = getString(R.string.title_good_night)
in 6..11 -> message = getString(R.string.title_good_morning)
in 12..15 -> message = getString(R.string.title_good_afternoon)
in 16..19 -> message = getString(R.string.title_good_evening)
in 20..23 -> message = getString(R.string.title_good_night)
}
return message
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
}
private val disposable = CompositeDisposable()
private fun loadImageFromStorage() {
disposable.add(Compressor(context!!)
.setMaxHeight(300)
.setMaxWidth(300)
.setQuality(75)
.setCompressFormat(Bitmap.CompressFormat.WEBP)
.compressToBitmapAsFlowable(
File(PreferenceUtil.getInstance().profileImage, USER_PROFILE))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ userImage.setImageBitmap(it) }, {
userImage.setImageDrawable(ContextCompat.getDrawable(context!!, R.drawable.ic_person_flat))
}))
}
} }

View File

@ -11,6 +11,7 @@ import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.afollestad.materialdialogs.color.ColorChooserDialog import com.afollestad.materialdialogs.color.ColorChooserDialog
@ -64,6 +65,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
activity!!.setTheme(PreferenceUtil.getThemeResFromPrefValue(theme)) activity!!.setTheme(PreferenceUtil.getThemeResFromPrefValue(theme))
DynamicShortcutManager(activity!!).updateDynamicShortcuts()
} }
activity!!.recreate() activity!!.recreate()
//invalidateSettings(); //invalidateSettings();
@ -92,6 +94,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() {
colorAppShortcuts.setOnPreferenceChangeListener { _, newValue -> colorAppShortcuts.setOnPreferenceChangeListener { _, newValue ->
// Save preference // Save preference
PreferenceUtil.getInstance().setColoredAppShortcuts(newValue as Boolean) PreferenceUtil.getInstance().setColoredAppShortcuts(newValue as Boolean)
DynamicShortcutManager(activity!!).updateDynamicShortcuts()
true true
} }
} }

View File

@ -113,6 +113,7 @@ public final class PreferenceUtil {
private static final String ALBUM_DETAIL_STYLE = "album_detail_style"; private static final String ALBUM_DETAIL_STYLE = "album_detail_style";
private static final String PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"; private static final String PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume";
private static final String NOW_PLAYING_SCREEN = "now_playing_screen"; private static final String NOW_PLAYING_SCREEN = "now_playing_screen";
private static final String SNOW_FALL_EFFECT = "snow_fall_effect";
private static PreferenceUtil sInstance; private static PreferenceUtil sInstance;
private final SharedPreferences mPreferences; private final SharedPreferences mPreferences;
@ -144,8 +145,12 @@ public final class PreferenceUtil {
} }
} }
public boolean isSnowFall() {
return mPreferences.getBoolean(SNOW_FALL_EFFECT, false);
}
public final String getArtistSortOrder() { public final String getArtistSortOrder() {
return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.Companion.getARTIST_A_Z()); return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z);
} }
public void setArtistSortOrder(final String sortOrder) { public void setArtistSortOrder(final String sortOrder) {
@ -155,7 +160,7 @@ public final class PreferenceUtil {
} }
public final String getArtistSongSortOrder() { public final String getArtistSongSortOrder() {
return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.Companion.getSONG_A_Z()); return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z);
} }
public final boolean isHomeBanner() { public final boolean isHomeBanner() {
@ -164,11 +169,11 @@ public final class PreferenceUtil {
public final String getArtistAlbumSortOrder() { public final String getArtistAlbumSortOrder() {
return mPreferences return mPreferences
.getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.Companion.getALBUM_YEAR()); .getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.ALBUM_YEAR);
} }
public final String getAlbumSortOrder() { public final String getAlbumSortOrder() {
return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.Companion.getALBUM_A_Z()); return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z);
} }
public void setAlbumSortOrder(final String sortOrder) { public void setAlbumSortOrder(final String sortOrder) {
@ -179,11 +184,11 @@ public final class PreferenceUtil {
public final String getAlbumSongSortOrder() { public final String getAlbumSongSortOrder() {
return mPreferences return mPreferences
.getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.Companion.getSONG_TRACK_LIST()); .getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST);
} }
public final String getSongSortOrder() { public final String getSongSortOrder() {
return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.Companion.getSONG_A_Z()); return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z);
} }
public void setSongSortOrder(final String sortOrder) { public void setSongSortOrder(final String sortOrder) {
@ -193,7 +198,7 @@ public final class PreferenceUtil {
} }
public final String getGenreSortOrder() { public final String getGenreSortOrder() {
return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.Companion.getGENRE_A_Z()); return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.GENRE_A_Z);
} }
public boolean isScreenOnEnabled() { public boolean isScreenOnEnabled() {
@ -288,9 +293,6 @@ public final class PreferenceUtil {
editor.apply(); editor.apply();
} }
public int getLastLyricsType() {
return mPreferences.getInt(LAST_KNOWN_LYRICS_TYPE, R.id.normalLyrics);
}
public void setLastLyricsType(int group) { public void setLastLyricsType(int group) {
final SharedPreferences.Editor editor = mPreferences.edit(); final SharedPreferences.Editor editor = mPreferences.edit();
@ -632,7 +634,7 @@ public final class PreferenceUtil {
public String getAlbumDetailSongSortOrder() { public String getAlbumDetailSongSortOrder() {
return mPreferences return mPreferences
.getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.Companion.getSONG_TRACK_LIST()); .getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST);
} }
public void setAlbumDetailSongSortOrder(String sortOrder) { public void setAlbumDetailSongSortOrder(String sortOrder) {
@ -643,7 +645,7 @@ public final class PreferenceUtil {
public String getArtistDetailSongSortOrder() { public String getArtistDetailSongSortOrder() {
return mPreferences return mPreferences
.getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.Companion.getSONG_A_Z()); .getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z);
} }
public void setArtistDetailSongSortOrder(String sortOrder) { public void setArtistDetailSongSortOrder(String sortOrder) {