update
This commit is contained in:
parent
4daa74c16b
commit
c7c6a07e45
@ -31,11 +31,13 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
implementation 'org.projectlombok:lombok:1.18.12'
|
||||
implementation 'jp.co.cyberagent.android:gpuimage:2.1.0'
|
||||
implementation 'com.alibaba:fastjson:1.1.72.android'
|
||||
//noinspection GradleCompatible
|
||||
implementation 'com.android.support:support-media-compat:28.0.0'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'com.github.zrunker:ZTextView:v1.0.2'
|
||||
|
||||
}
|
@ -1,35 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.yutou.nas_music_player">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".MyApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".views.OpenActivity">
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".views.OpenActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".views.MainActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
<activity
|
||||
android:name=".views.MainActivity"
|
||||
android:fitsSystemWindows="true"
|
||||
/>
|
||||
<service android:name=".services.MusicService"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar" />
|
||||
|
||||
<service
|
||||
android:name=".services.MusicService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
|
||||
<receiver android:name="androidx.media.session.MediaButtonReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
|
@ -30,6 +30,7 @@ public class MediaBrowserHelper {
|
||||
subscriptionCallback=new MediaBrowserSubscriptionCallback();
|
||||
mMediaBrowser = new MediaBrowserCompat(context, new ComponentName(context, MusicService.class), browserConnectionCallback, null);
|
||||
mMediaBrowser.connect();
|
||||
MusicContainer.getInstance().setBrowserHelper(this);
|
||||
}
|
||||
public void onStop(){
|
||||
if(controllerCompat!=null){
|
||||
@ -40,7 +41,12 @@ public class MediaBrowserHelper {
|
||||
mMediaBrowser=null;
|
||||
}
|
||||
}
|
||||
public boolean isPlayer(){
|
||||
return MusicContainer.getInstance().getMediaPlayer().isPlaying();
|
||||
}
|
||||
public MediaControllerCompat.TransportControls getTransportControls() {
|
||||
if(controllerCompat==null)
|
||||
return null;
|
||||
return controllerCompat.getTransportControls();
|
||||
}
|
||||
private List<MediaControllerCompat.Callback> callbacks=new ArrayList<>();
|
||||
|
@ -81,7 +81,6 @@ public class MediaNotificationManager {
|
||||
@NonNull PlaybackStateCompat state,
|
||||
MediaSessionCompat.Token token) {
|
||||
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
|
||||
MediaDescriptionCompat description = metadata.getDescription();
|
||||
NotificationCompat.Builder builder =
|
||||
buildNotification(state, token, isPlaying, metadata);
|
||||
return builder.build();
|
||||
|
@ -7,46 +7,88 @@ import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.yutou.nas_music_player.Interfaces.NetworkInterface;
|
||||
import com.yutou.nas_music_player.tools.NetworkTool;
|
||||
import com.yutou.nas_music_player.tools.StringUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.media.MediaMetadata.METADATA_KEY_DISPLAY_ICON;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
public class MusicContainer {
|
||||
public static final int PLAY_MODEL_RANDOM = 0;
|
||||
public static final int PLAY_MODEL_LIST = 1;
|
||||
public static final int PLAY_MODEL_TMP_LIST = 2;
|
||||
public static final int PLAY_MODEL_ALBUM = 3;
|
||||
private static MusicContainer container;
|
||||
private MusicLibs libs;
|
||||
private MediaPlayer mediaPlayer;
|
||||
private int playIndex = 0;
|
||||
private MusicData nowPlayData = null;
|
||||
private final MusicLibsInitInterface initInterface;
|
||||
private List<MediaPlayer.OnCompletionListener> completionListener;
|
||||
private MediaBrowserHelper browserHelper;
|
||||
private List<MusicData> playList, tmpList;
|
||||
private Map<String, List<MusicData>> albumMapList;
|
||||
private int playModel = PLAY_MODEL_RANDOM;
|
||||
|
||||
|
||||
private MusicContainer() {
|
||||
private MusicContainer(MusicLibsInitInterface initInterface) {
|
||||
this.initInterface = initInterface;
|
||||
mediaPlayer = new MediaPlayer();
|
||||
playList = new ArrayList<>();
|
||||
tmpList = new ArrayList<>();
|
||||
albumMapList = new HashMap<>();
|
||||
initMediaPlayer();
|
||||
libs = new MusicLibs();
|
||||
}
|
||||
private void initMediaPlayer(){
|
||||
|
||||
public void setPlayModel(int playModel) {
|
||||
this.playModel = playModel;
|
||||
}
|
||||
|
||||
private void initMediaPlayer() {
|
||||
completionListener = new ArrayList<>();
|
||||
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
|
||||
playTimer.cancel();
|
||||
playTimer = null;
|
||||
browserHelper.getTransportControls().skipToNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
public MediaPlayer getMediaPlayer(){
|
||||
|
||||
public void addCompletionListener(MediaPlayer.OnCompletionListener listener) {
|
||||
completionListener.add(listener);
|
||||
}
|
||||
|
||||
public MediaPlayer getMediaPlayer() {
|
||||
return mediaPlayer;
|
||||
}
|
||||
public MusicData getNowPlayData(){
|
||||
|
||||
public MusicData getNowPlayData() {
|
||||
return nowPlayData;
|
||||
}
|
||||
|
||||
public void setNowPlayData(MusicData musicData) {
|
||||
this.nowPlayData = musicData;
|
||||
}
|
||||
|
||||
public void setBrowserHelper(MediaBrowserHelper browserHelper) {
|
||||
this.browserHelper = browserHelper;
|
||||
}
|
||||
|
||||
public static MusicContainer getInstance() {
|
||||
return getInstance(null);
|
||||
}
|
||||
|
||||
public static MusicContainer getInstance(MusicLibsInitInterface initInterface) {
|
||||
if (container == null) {
|
||||
container = new MusicContainer();
|
||||
container = new MusicContainer(initInterface);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
@ -56,12 +98,20 @@ public class MusicContainer {
|
||||
}
|
||||
|
||||
public void playOfAllRandom() {
|
||||
System.out.println("随机下一首");
|
||||
randomMusic();
|
||||
play(libs.mainData.get(playIndex));
|
||||
}
|
||||
|
||||
public void play(MusicData data) {
|
||||
String url =data.getPlayUrl();
|
||||
System.out.println("准备播放:" + data.getTitle());
|
||||
if (isPause) {
|
||||
mediaPlayer.start();
|
||||
isPause = false;
|
||||
return;
|
||||
}
|
||||
nowPlayData = data;
|
||||
String url = data.getPlayUrl();
|
||||
try {
|
||||
if (mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.stop();
|
||||
@ -70,18 +120,48 @@ public class MusicContainer {
|
||||
mediaPlayer.setDataSource(url);
|
||||
mediaPlayer.prepare();
|
||||
mediaPlayer.start();
|
||||
if (playTimer == null) {
|
||||
playTimer = new Timer();
|
||||
playTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (MediaPlayer.OnCompletionListener listener : completionListener) {
|
||||
if (listener != null) {
|
||||
listener.onCompletion(mediaPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, 1000);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Timer playTimer;
|
||||
|
||||
public MediaMetadataCompat getNowPlayMetadataCompat() {
|
||||
if(nowPlayData==null){
|
||||
if (nowPlayData == null) {
|
||||
return null;
|
||||
}
|
||||
return getMetadataCompat(nowPlayData);
|
||||
}
|
||||
|
||||
public Long getYear(String year) {
|
||||
try {
|
||||
return Long.parseLong(year);
|
||||
} catch (Exception e) {
|
||||
if (year.contains("-")) {
|
||||
try {
|
||||
return Long.parseLong(year.split("-")[0]);
|
||||
} catch (Exception e1) {
|
||||
return 8888L;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 8888L;
|
||||
}
|
||||
|
||||
public MediaMetadataCompat getMetadataCompat(MusicData data) {
|
||||
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
|
||||
builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, data.getTitle());
|
||||
@ -89,13 +169,14 @@ public class MusicContainer {
|
||||
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, data.getTitle());
|
||||
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, data.getAlbum());
|
||||
builder.putString(MediaMetadataCompat.METADATA_KEY_COMPOSER, data.getComposer());
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, Long.parseLong(data.getYear()));
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, getYear(data.getYear()));
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, Long.parseLong(data.getTrack()));
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, Long.parseLong(data.getDisc_no()));
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION,3*60*1000);
|
||||
if(data.getImg()!=null){
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,data.getImg());
|
||||
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, ((int) data.getDurationAsDouble()) * 1000);
|
||||
if (data.getImg() != null) {
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, data.getImg());
|
||||
}
|
||||
builder.putString("md5", data.getMd5());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@ -103,6 +184,42 @@ public class MusicContainer {
|
||||
return getInstance().libs;
|
||||
}
|
||||
|
||||
private boolean isPause = false;
|
||||
|
||||
public void pause() {
|
||||
isPause = true;
|
||||
mediaPlayer.pause();
|
||||
}
|
||||
|
||||
public void playNext() {
|
||||
switch (playModel) {
|
||||
case PLAY_MODEL_LIST:
|
||||
playList(true);
|
||||
break;
|
||||
case PLAY_MODEL_TMP_LIST:
|
||||
playTmpList(true);
|
||||
break;
|
||||
case PLAY_MODEL_ALBUM:
|
||||
playAlbumList(true);
|
||||
break;
|
||||
case PLAY_MODEL_RANDOM:
|
||||
default:
|
||||
playOfAllRandom();
|
||||
}
|
||||
}
|
||||
|
||||
private void playList(boolean isNext) {
|
||||
|
||||
}
|
||||
|
||||
private void playTmpList(boolean isNext) {
|
||||
|
||||
}
|
||||
|
||||
private void playAlbumList(boolean isNext) {
|
||||
|
||||
}
|
||||
|
||||
public class MusicLibs {
|
||||
private List<MusicData> mainData;
|
||||
|
||||
@ -111,6 +228,10 @@ public class MusicContainer {
|
||||
initData();
|
||||
}
|
||||
|
||||
public List<MusicData> getMainData() {
|
||||
return mainData;
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
NetworkTool.init().httpGet(NetworkTool.NetworkAPI.MUSIC_ALL, new JSONObject(), new NetworkInterface() {
|
||||
@Override
|
||||
@ -123,6 +244,14 @@ public class MusicContainer {
|
||||
nowPlayData = mainData.get(0);
|
||||
}
|
||||
System.out.println("获取列表完成");
|
||||
for (MusicData mainDatum : mainData) {
|
||||
if(StringUtil.isEmpty(mainDatum.getMd5())){
|
||||
System.out.println(mainDatum.getFile());
|
||||
}
|
||||
}
|
||||
if (initInterface != null) {
|
||||
initInterface.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,5 +262,17 @@ public class MusicContainer {
|
||||
});
|
||||
}
|
||||
|
||||
public MusicData findMusic(String md5) {
|
||||
for (MusicData data : mainData) {
|
||||
if (md5.contains(data.getMd5())) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface MusicLibsInitInterface {
|
||||
void init();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.graphics.Color;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.yutou.nas_music_player.Interfaces.DownloadInterface;
|
||||
import com.yutou.nas_music_player.Interfaces.NetworkInterface;
|
||||
import com.yutou.nas_music_player.tools.NetworkTool;
|
||||
import com.yutou.nas_music_player.tools.StringUtil;
|
||||
@ -28,6 +29,16 @@ public class MusicData {
|
||||
private boolean isDir = false;
|
||||
private Bitmap img;
|
||||
|
||||
private int bitRate;//比特率
|
||||
private int sampleRate;//采样率
|
||||
private long noOfSamples;//采样数
|
||||
private int channelCount;//声道
|
||||
private String encodingType;//解码类型
|
||||
private double durationAsDouble;//持续时长
|
||||
private boolean lossless;//无损
|
||||
private boolean variableBitRate;//固定码率
|
||||
private String md5;//确保是同一个文件
|
||||
|
||||
public MusicData() {
|
||||
}
|
||||
|
||||
@ -175,10 +186,83 @@ public class MusicData {
|
||||
});
|
||||
return img;
|
||||
}
|
||||
public int getImageColor(){
|
||||
if(img==null){
|
||||
|
||||
public int getImageColor() {
|
||||
if (img == null) {
|
||||
return -1;
|
||||
}
|
||||
return Palette.from(img).generate().getLightVibrantColor(Color.parseColor("#6DE1A1"));
|
||||
}
|
||||
|
||||
public int getBitRate() {
|
||||
return bitRate;
|
||||
}
|
||||
|
||||
public void setBitRate(int bitRate) {
|
||||
this.bitRate = bitRate;
|
||||
}
|
||||
|
||||
public int getSampleRate() {
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
public void setSampleRate(int sampleRate) {
|
||||
this.sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
public long getNoOfSamples() {
|
||||
return noOfSamples;
|
||||
}
|
||||
|
||||
public void setNoOfSamples(long noOfSamples) {
|
||||
this.noOfSamples = noOfSamples;
|
||||
}
|
||||
|
||||
public int getChannelCount() {
|
||||
return channelCount;
|
||||
}
|
||||
|
||||
public void setChannelCount(int channelCount) {
|
||||
this.channelCount = channelCount;
|
||||
}
|
||||
|
||||
public String getEncodingType() {
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
public void setEncodingType(String encodingType) {
|
||||
this.encodingType = encodingType;
|
||||
}
|
||||
|
||||
public double getDurationAsDouble() {
|
||||
return durationAsDouble;
|
||||
}
|
||||
|
||||
public void setDurationAsDouble(double durationAsDouble) {
|
||||
this.durationAsDouble = durationAsDouble;
|
||||
}
|
||||
|
||||
public boolean isLossless() {
|
||||
return lossless;
|
||||
}
|
||||
|
||||
public void setLossless(boolean lossless) {
|
||||
this.lossless = lossless;
|
||||
}
|
||||
|
||||
public boolean isVariableBitRate() {
|
||||
return variableBitRate;
|
||||
}
|
||||
|
||||
public void setVariableBitRate(boolean variableBitRate) {
|
||||
this.variableBitRate = variableBitRate;
|
||||
}
|
||||
|
||||
public String getMd5() {
|
||||
return md5;
|
||||
}
|
||||
|
||||
public void setMd5(String md5) {
|
||||
this.md5 = md5;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package com.yutou.nas_music_player.services;
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
@ -25,6 +27,8 @@ import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static android.media.MediaPlayer.SEEK_PREVIOUS_SYNC;
|
||||
|
||||
public class MusicService extends MediaBrowserServiceCompat {
|
||||
public MediaSessionCompat mediaSession;
|
||||
private PlaybackStateCompat.Builder stateBuilder;
|
||||
@ -53,6 +57,7 @@ public class MusicService extends MediaBrowserServiceCompat {
|
||||
mediaSession.setPlaybackState(stateBuilder.build());
|
||||
mediaSession.setCallback(new MediaSessionCallback());
|
||||
setSessionToken(mediaSession.getSessionToken());
|
||||
MusicContainer.getInstance().addCompletionListener(new PlayCompletionListener());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -73,82 +78,125 @@ public class MusicService extends MediaBrowserServiceCompat {
|
||||
result.sendResult(mediaItems);
|
||||
}
|
||||
|
||||
public void onSeekTo(long pos) {
|
||||
MusicData data = MusicContainer.getInstance().getNowPlayData();
|
||||
MediaMetadataCompat metadataCompat = MusicContainer.getInstance().getMetadataCompat(data);
|
||||
Notification notification = notificationManager.getNotification(metadataCompat, builderState(pos).build(), getSessionToken());
|
||||
notificationManager.getManager()
|
||||
.notify(MediaNotificationManager.NOTIFICATION_ID, notification);
|
||||
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
|
||||
.setActions(MEDIA_SESSION_ACTIONS)
|
||||
.setState(mState, pos, 1.0f, SystemClock.elapsedRealtime()).build());
|
||||
}
|
||||
|
||||
public PlaybackStateCompat.Builder builderState(long position) {
|
||||
PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
|
||||
stateBuilder.setActions(getAvailableActions());
|
||||
stateBuilder.setState(mState,
|
||||
position,
|
||||
1.0f,
|
||||
SystemClock.elapsedRealtime());
|
||||
return stateBuilder;
|
||||
}
|
||||
|
||||
private long getAvailableActions() {
|
||||
long actions = PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID
|
||||
| PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||
switch (mState) {
|
||||
case PlaybackStateCompat.STATE_STOPPED:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_PAUSE;
|
||||
break;
|
||||
case PlaybackStateCompat.STATE_PLAYING:
|
||||
actions |= PlaybackStateCompat.ACTION_STOP
|
||||
| PlaybackStateCompat.ACTION_PAUSE
|
||||
| PlaybackStateCompat.ACTION_SEEK_TO;
|
||||
break;
|
||||
case PlaybackStateCompat.STATE_PAUSED:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_STOP;
|
||||
break;
|
||||
default:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
| PlaybackStateCompat.ACTION_STOP
|
||||
| PlaybackStateCompat.ACTION_PAUSE;
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
public class MediaSessionCallback extends MediaSessionCompat.Callback {
|
||||
public void updateNotification(){
|
||||
MediaMetadataCompat metadataCompat = MusicContainer.getInstance().getNowPlayMetadataCompat();
|
||||
mediaSession.setMetadata(metadataCompat);
|
||||
mediaSession.setActive(true);
|
||||
Notification notification =
|
||||
notificationManager.getNotification(
|
||||
metadataCompat, builderState(0).build(), getSessionToken());
|
||||
notificationManager.getManager()
|
||||
.notify(MediaNotificationManager.NOTIFICATION_ID, notification);
|
||||
}
|
||||
@Override
|
||||
public void onPlay() {
|
||||
super.onPlay();
|
||||
System.out.println("接收到播放指令");
|
||||
mState=PlaybackStateCompat.STATE_PLAYING;
|
||||
MediaMetadataCompat metadataCompat = MusicContainer.getInstance().getNowPlayMetadataCompat();
|
||||
mediaSession.setMetadata(metadataCompat);
|
||||
mediaSession.setActive(true);
|
||||
|
||||
Notification notification = notificationManager.getNotification(metadataCompat, builderState(0).build(), getSessionToken());
|
||||
startForeground(MediaNotificationManager.NOTIFICATION_ID, notification);
|
||||
new Timer().schedule(new TimerTask() {
|
||||
long position = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
position += 1000;
|
||||
MusicData data=MusicContainer.getInstance().getNowPlayData();
|
||||
data.setTitle(position+"");
|
||||
MediaMetadataCompat metadataCompat = MusicContainer.getInstance().getMetadataCompat(data);
|
||||
Notification notification = notificationManager.getNotification(metadataCompat, builderState(position).build(), getSessionToken());
|
||||
notificationManager.getManager()
|
||||
.notify(MediaNotificationManager.NOTIFICATION_ID, notification);
|
||||
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
|
||||
.setActions(MEDIA_SESSION_ACTIONS)
|
||||
.setState(PlaybackStateCompat.STATE_PLAYING,position,1.0f,SystemClock.elapsedRealtime()).build());
|
||||
}
|
||||
}, 0, 1000);
|
||||
MusicContainer.getInstance().play(MusicContainer.getInstance().getNowPlayData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mState=PlaybackStateCompat.STATE_PAUSED;
|
||||
MusicContainer.getInstance().pause();
|
||||
//MusicService.this.onSeekTo(-1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSeekTo(long pos) {
|
||||
super.onSeekTo(pos);
|
||||
}
|
||||
|
||||
public PlaybackStateCompat.Builder builderState(long position) {
|
||||
PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
|
||||
stateBuilder.setActions(getAvailableActions());
|
||||
stateBuilder.setState(mState,
|
||||
position,
|
||||
1.0f,
|
||||
SystemClock.elapsedRealtime());
|
||||
return stateBuilder;
|
||||
}
|
||||
|
||||
private long getAvailableActions() {
|
||||
long actions = PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID
|
||||
| PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||
switch (mState) {
|
||||
case PlaybackStateCompat.STATE_STOPPED:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_PAUSE;
|
||||
break;
|
||||
case PlaybackStateCompat.STATE_PLAYING:
|
||||
actions |= PlaybackStateCompat.ACTION_STOP
|
||||
| PlaybackStateCompat.ACTION_PAUSE
|
||||
| PlaybackStateCompat.ACTION_SEEK_TO;
|
||||
break;
|
||||
case PlaybackStateCompat.STATE_PAUSED:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_STOP;
|
||||
break;
|
||||
default:
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY
|
||||
| PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
| PlaybackStateCompat.ACTION_STOP
|
||||
| PlaybackStateCompat.ACTION_PAUSE;
|
||||
System.out.println("SeeTo :"+pos);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
MusicContainer.getInstance().getMediaPlayer().seekTo(pos,SEEK_PREVIOUS_SYNC);
|
||||
}else{
|
||||
MusicContainer.getInstance().getMediaPlayer().seekTo((int) pos);
|
||||
}
|
||||
return actions;
|
||||
MusicService.this.onSeekTo(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToNext() {
|
||||
super.onSkipToNext();
|
||||
MusicContainer.getInstance().playNext();
|
||||
updateNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToPrevious() {
|
||||
super.onSkipToPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
mState=PlaybackStateCompat.STATE_STOPPED;
|
||||
MusicContainer.getInstance().getMediaPlayer().stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class PlayCompletionListener implements MediaPlayer.OnCompletionListener {
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
onSeekTo(mp.getCurrentPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.yutou.nas_music_player.tools;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.yutou.nas_music_player.MyApplication;
|
||||
|
||||
public class ConfigTools {
|
||||
public static final String previous_music="previous";
|
||||
public static SharedPreferences getPreferences(){
|
||||
return MyApplication.application.getSharedPreferences("app_config", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
}
|
@ -2,94 +2,229 @@ package com.yutou.nas_music_player.views;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaControllerCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.yutou.nas_music_player.R;
|
||||
import com.yutou.nas_music_player.containers.MediaBrowserHelper;
|
||||
import com.yutou.nas_music_player.containers.MusicContainer;
|
||||
import com.yutou.nas_music_player.containers.MusicData;
|
||||
import com.yutou.nas_music_player.tools.AppData;
|
||||
import com.yutou.nas_music_player.tools.ConfigTools;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import cc.ibooker.ztextviewlib.MarqueeTextView;
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation;
|
||||
import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private ImageButton play, previous, next;
|
||||
private MediaBrowserHelper browserHelper;
|
||||
private ImageView imageView2;
|
||||
private ImageView album_image, background_image;
|
||||
private MarqueeTextView title, album, artist;
|
||||
private TextView positionTime, durationTime, bitRate;
|
||||
private SeekBar seekBar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setBarColor(android.R.color.transparent);
|
||||
setContentView(R.layout.activity_main);
|
||||
MusicContainer.getInstance();
|
||||
play = findViewById(R.id.play);
|
||||
next = findViewById(R.id.next);
|
||||
previous = findViewById(R.id.previous);
|
||||
imageView2 = findViewById(R.id.imageView2);
|
||||
initView();
|
||||
play.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
if (browserHelper.isPlayer()) {
|
||||
browserHelper.getTransportControls().pause();
|
||||
} else {
|
||||
browserHelper.getTransportControls().play();
|
||||
}
|
||||
}
|
||||
});
|
||||
next.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
browserHelper.getTransportControls().skipToNext();
|
||||
}
|
||||
});
|
||||
previous.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MusicData data=MusicContainer.getInstance().getNowPlayData();
|
||||
if(data!=null){
|
||||
setBarColor(data.getImageColor());
|
||||
}
|
||||
browserHelper.getTransportControls().skipToPrevious();
|
||||
}
|
||||
});
|
||||
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (browserHelper.getTransportControls() != null)
|
||||
browserHelper.getTransportControls().seekTo(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
browserHelper = new MediaBrowserHelper(this);
|
||||
browserHelper.regPlayListener(new PlayListener());
|
||||
initData();
|
||||
}
|
||||
public void setBarColor(int color){
|
||||
|
||||
private void initData() {
|
||||
String previousMusic = ConfigTools.getPreferences().getString(ConfigTools.previous_music, null);
|
||||
MusicData data;
|
||||
if (previousMusic != null) {
|
||||
data = JSON.parseObject(previousMusic, MusicData.class);
|
||||
MusicContainer.getInstance().setNowPlayData(data);
|
||||
setPlayData(data);
|
||||
} else {
|
||||
data = MusicContainer.getLibs().getMainData().size() > 0 ? MusicContainer.getLibs().getMainData().get(0) : null;
|
||||
if (data != null) {
|
||||
setPlayData(data);
|
||||
} else {
|
||||
Toast.makeText(this, "播放列表获取失败", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void setPlayData(final MusicData data) {
|
||||
System.out.println("设置歌曲");
|
||||
System.out.println(data);
|
||||
if(data==null)
|
||||
return;
|
||||
title.setText(data.getTitle());
|
||||
artist.setText(data.getArtist());
|
||||
album.setText(data.getAlbum());
|
||||
bitRate.setText(data.getBitRate() + "kbps / " + data.getSampleRate() + "hz");
|
||||
seekBar.setMax((int) data.getDurationAsDouble());
|
||||
int minutes = (int) (data.getDurationAsDouble() / 60);
|
||||
int seconds = (int) (data.getDurationAsDouble() % 60);
|
||||
durationTime.setText(minutes + ":" + seconds);
|
||||
|
||||
if (data.getImg() != null) {
|
||||
setImage(data);
|
||||
} else {
|
||||
new Timer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (data.getImg() != null) {
|
||||
AppData.handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setImage(data);
|
||||
}
|
||||
});
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
}, 0, 100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setImage(MusicData data) {
|
||||
album_image.setImageBitmap(data.getImg());
|
||||
background_image.setImageBitmap(data.getImg());
|
||||
setBarColor(data.getImageColor());
|
||||
Glide.with(this).load(data.getImg())
|
||||
.apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(25, 3)))
|
||||
.into(album_image);
|
||||
Glide.with(this).load(data.getImg())
|
||||
.apply(RequestOptions.bitmapTransform(new BlurTransformation(25, 3)))
|
||||
.into(background_image);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
play = findViewById(R.id.play);
|
||||
next = findViewById(R.id.next);
|
||||
previous = findViewById(R.id.previous);
|
||||
album_image = findViewById(R.id.album_image);
|
||||
background_image = findViewById(R.id.background_image);
|
||||
title = findViewById(R.id.title);
|
||||
artist = findViewById(R.id.artist);
|
||||
album = findViewById(R.id.album);
|
||||
bitRate = findViewById(R.id.bitRate);
|
||||
positionTime = findViewById(R.id.positionTime);
|
||||
durationTime = findViewById(R.id.durationTime);
|
||||
seekBar = findViewById(R.id.seekBar);
|
||||
}
|
||||
|
||||
public void setBarColor(int color) {
|
||||
getWindow().setStatusBarColor(color);
|
||||
getWindow().setNavigationBarColor(color);
|
||||
}
|
||||
|
||||
private class PlayListener extends MediaControllerCompat.Callback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
||||
super.onPlaybackStateChanged(state);
|
||||
System.out.println("播放状态变化:" + state.getState());
|
||||
if (state.getState() == PlaybackStateCompat.STATE_PLAYING) {
|
||||
int duration = (int) (state.getPosition() / 1000);
|
||||
int minutes = duration / 60;
|
||||
int seconds = duration % 60;
|
||||
positionTime.setText(minutes + ":" + seconds);
|
||||
seekBar.setProgress((int) (state.getPosition() / 1000));
|
||||
play.setImageResource(android.R.drawable.ic_media_pause);
|
||||
} else {
|
||||
play.setImageResource(android.R.drawable.ic_media_play);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadataChanged(MediaMetadataCompat metadata) {
|
||||
super.onMetadataChanged(metadata);
|
||||
if(metadata!=null){
|
||||
imageView2.setImageBitmap(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON));
|
||||
String md5=null;
|
||||
try {
|
||||
System.out.println("播放歌曲变换:"+metadata.getString("md5"));
|
||||
md5=metadata.getString("md5");
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (md5 != null) {
|
||||
setPlayData(MusicContainer.getLibs().findMusic(md5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionDestroyed() {
|
||||
super.onSessionDestroyed();
|
||||
System.out.println("连接被销毁");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
|
||||
super.onQueueChanged(queue);
|
||||
System.out.println("不知道是啥变换了");
|
||||
}
|
||||
public void setImage(){
|
||||
|
||||
public void setImage() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,29 @@ package com.yutou.nas_music_player.views;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.yutou.nas_music_player.R;
|
||||
import com.yutou.nas_music_player.containers.MusicContainer;
|
||||
|
||||
public class OpenActivity extends AppCompatActivity {
|
||||
TextView textView;
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Intent intent=new Intent(this,MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
setContentView(R.layout.activity_open);
|
||||
textView=findViewById(R.id.textView);
|
||||
MusicContainer.getInstance(new MusicContainer.MusicLibsInitInterface() {
|
||||
@Override
|
||||
public void init() {
|
||||
Intent intent=new Intent(OpenActivity.this,MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:id="@+id/background_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="fitXY"
|
||||
@ -27,16 +27,33 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:id="@+id/album_image"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="150dp"
|
||||
android:contentDescription="专辑图"
|
||||
android:src="@drawable/ic_launcher_foreground"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<cc.ibooker.ztextviewlib.MarqueeTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="26dp"
|
||||
android:shadowColor="@color/textColorGray"
|
||||
android:shadowDx="5"
|
||||
android:shadowDy="5"
|
||||
android:shadowRadius="1"
|
||||
android:text="title"
|
||||
android:textColor="@color/musicTitle"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_image"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -60,7 +77,16 @@
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_weight="1"
|
||||
android:text="00:00"
|
||||
android:textColor="@color/colorBlack" />
|
||||
android:textColor="@color/textColorWhite" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bitRate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/textColorWhite"
|
||||
android:text="TextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/durationTime"
|
||||
@ -70,11 +96,11 @@
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:text="00:00"
|
||||
android:textColor="@color/colorBlack" />
|
||||
android:textColor="@color/textColorGray" />
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
<SeekBar
|
||||
android:id="@+id/seekBar"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -82,7 +108,6 @@
|
||||
android:backgroundTint="#D12828"
|
||||
android:elevation="6dp"
|
||||
android:max="100"
|
||||
android:progress="50"
|
||||
android:progressBackgroundTintMode="add"
|
||||
android:progressTint="#EC1E1E"
|
||||
android:textColor="@color/colorBlack" />
|
||||
@ -134,25 +159,21 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
<cc.ibooker.ztextviewlib.MarqueeTextView
|
||||
android:id="@+id/album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:text="title"
|
||||
android:shadowColor="@color/textColorGray"
|
||||
android:shadowRadius="1"
|
||||
android:shadowDx="5"
|
||||
android:shadowDy="5"
|
||||
android:textColor="@color/musicTitle"
|
||||
android:textSize="24sp" />
|
||||
android:text="album"
|
||||
android:textColor="@color/textColorWhite"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
<cc.ibooker.ztextviewlib.MarqueeTextView
|
||||
android:id="@+id/artist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="artist-album"
|
||||
android:textColor="@color/musicArtist"
|
||||
android:text="artist"
|
||||
android:textColor="@color/textColorWhite"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
28
app/src/main/res/layout/activity_open.xml
Normal file
28
app/src/main/res/layout/activity_open.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:gravity="center"
|
||||
android:text="初始化中"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView3" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -8,4 +8,5 @@
|
||||
<color name="musicTitle">#000000</color>
|
||||
<color name="musicArtist">#000000</color>
|
||||
<color name="textColorGray">#DEDEDE</color>
|
||||
<color name="textColorWhite">#FFFFFF</color>
|
||||
</resources>
|
@ -16,6 +16,7 @@ allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user