Removed libs folder
This commit is contained in:
parent
f77b426617
commit
960b057e0a
80 changed files with 1192 additions and 1673 deletions
|
@ -34,7 +34,7 @@ public class Constants {
|
|||
public static final String PAYPAL_ME_URL = "https://www.paypal.me/h4h14";
|
||||
public static final String GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/communities/110811566242871492162";
|
||||
public static final String TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534";
|
||||
public static final String GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicApp";
|
||||
public static final String GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer";
|
||||
public static final String BASE_API_URL_KUGOU = "http://lyrics.kugou.com/";
|
||||
public static final String TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog";
|
||||
public static final String USER_PROFILE = "profile.jpg";
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
import android.os.Handler;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.rest.KogouClient;
|
||||
import code.name.monkey.retromusic.rest.model.KuGouSearchLyricResult;
|
||||
import code.name.monkey.retromusic.util.LyricUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
public class KogouLyricsFetcher {
|
||||
|
||||
private KogouClient mKogouClient;
|
||||
private Song mSong;
|
||||
private KogouLyricsCallback mCallback;
|
||||
|
||||
public KogouLyricsFetcher(KogouLyricsCallback callback) {
|
||||
mCallback = callback;
|
||||
mKogouClient = new KogouClient();
|
||||
}
|
||||
|
||||
public void loadLyrics(Song song, String duration) {
|
||||
mSong = song;
|
||||
mKogouClient.getApiService()
|
||||
.searchLyric(mSong.title, duration)
|
||||
.subscribe(this::parseKugouResult,
|
||||
throwable -> mCallback.onNoLyrics());
|
||||
}
|
||||
|
||||
private void parseKugouResult(KuGouSearchLyricResult kuGouSearchLyricResult) {
|
||||
if (kuGouSearchLyricResult != null && kuGouSearchLyricResult.status == 200 &
|
||||
kuGouSearchLyricResult.candidates != null &&
|
||||
kuGouSearchLyricResult.candidates.size() != 0) {
|
||||
KuGouSearchLyricResult.Candidates candidates = kuGouSearchLyricResult.candidates.get(0);
|
||||
loadLyricsFile(candidates);
|
||||
} else {
|
||||
mCallback.onNoLyrics();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLyricsFile(KuGouSearchLyricResult.Candidates candidates) {
|
||||
mKogouClient.getApiService().getRawLyric(candidates.id, candidates.accesskey)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(kuGouRawLyric -> {
|
||||
if (kuGouRawLyric == null) {
|
||||
mCallback.onNoLyrics();
|
||||
return;
|
||||
}
|
||||
String rawLyric = LyricUtil.decryptBASE64(kuGouRawLyric.content);
|
||||
LyricUtil.writeLrcToLoc(mSong.title, mSong.artistName, rawLyric);
|
||||
new Handler().postDelayed(
|
||||
() -> mCallback.onLyrics(LyricUtil.getLocalLyricFile(mSong.title, mSong.artistName)),
|
||||
1);
|
||||
});
|
||||
}
|
||||
|
||||
public interface KogouLyricsCallback {
|
||||
|
||||
void onNoLyrics();
|
||||
|
||||
void onLyrics(File file);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
public interface LyricsEngine {
|
||||
String getLyrics(String artistName, String songTitle);
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class LyricsWikiEngine implements LyricsEngine {
|
||||
|
||||
private static final String TAG = LyricsWikiEngine.class.getSimpleName();
|
||||
|
||||
// Reads an InputStream and converts it to a String.
|
||||
private static String readIt(InputStream stream) throws IOException {
|
||||
Reader reader = new InputStreamReader(stream, "UTF-8");
|
||||
StringWriter sw = new StringWriter();
|
||||
char[] buffer = new char[4096];
|
||||
int count;
|
||||
while ((count = reader.read(buffer)) != -1) {
|
||||
sw.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLyrics(String artistName, String songTitle) {
|
||||
try {
|
||||
|
||||
String lyricsUrl = makeApiCall(artistName, songTitle);
|
||||
if (lyricsUrl == null) { // no URL in API answer or no correct answer at all
|
||||
return null;
|
||||
}
|
||||
|
||||
return parseFullLyricsPage(lyricsUrl);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Couldn't connect to lyrics wiki REST endpoints", e);
|
||||
return null;
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "Couldn't transform API answer to JSON entity", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First call
|
||||
*/
|
||||
private String makeApiCall(String artistName, String songTitle) throws IOException, JSONException {
|
||||
HttpsURLConnection apiCall = null;
|
||||
try {
|
||||
// build query
|
||||
// e.g. https://lyrics.wikia.com/api.php?func=getSong&artist=The%20Beatle&song=Girl&fmt=realjson
|
||||
Uri link = new Uri.Builder()
|
||||
.scheme("https")
|
||||
.authority("lyrics.wikia.com")
|
||||
.path("api.php")
|
||||
.appendQueryParameter("func", "getSong")
|
||||
.appendQueryParameter("fmt", "realjson")
|
||||
.appendQueryParameter("artist", artistName)
|
||||
.appendQueryParameter("song", songTitle)
|
||||
.build();
|
||||
|
||||
// construct an http request
|
||||
apiCall = (HttpsURLConnection) new URL(link.toString()).openConnection();
|
||||
apiCall.setReadTimeout(10_000);
|
||||
apiCall.setConnectTimeout(15_000);
|
||||
|
||||
// execute
|
||||
apiCall.connect();
|
||||
int response = apiCall.getResponseCode();
|
||||
if (response != HttpsURLConnection.HTTP_OK) {
|
||||
// redirects are handled internally, this is clearly an error
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream is = apiCall.getInputStream();
|
||||
String reply = readIt(is);
|
||||
JSONObject getSongAnswer = new JSONObject(reply);
|
||||
|
||||
return getLyricsUrl(getSongAnswer);
|
||||
} finally {
|
||||
if (apiCall != null) {
|
||||
apiCall.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Second call
|
||||
*/
|
||||
private String parseFullLyricsPage(String lyricsUrl) throws IOException {
|
||||
Document page = Jsoup.parse(new URL(lyricsUrl), 10_000);
|
||||
Element lyricsBox = page.select("div.lyricbox").first();
|
||||
if (lyricsBox == null) { // no lyrics frame on page
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove unneeded elements
|
||||
lyricsBox.select("div.rtMatcher").remove();
|
||||
lyricsBox.select("div.lyricsbreak").remove();
|
||||
lyricsBox.select("script").remove();
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Node curr : lyricsBox.childNodes()) {
|
||||
if (curr instanceof TextNode) {
|
||||
builder.append(((TextNode) curr).text());
|
||||
} else {
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getLyricsUrl(JSONObject getSongAnswer) {
|
||||
try {
|
||||
String pageId = getSongAnswer.getString("page_id");
|
||||
if (TextUtils.isEmpty(pageId)) {
|
||||
return null; // empty page_id means page wasn't created
|
||||
}
|
||||
return getSongAnswer.getString("url");
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "Unknown format of getSong API call answer", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
public class ParseLyrics extends AsyncTask<String, Void, String> {
|
||||
private LyricsCallback mLyricsCallback;
|
||||
|
||||
public ParseLyrics(LyricsCallback lyricsCallback) {
|
||||
mLyricsCallback = lyricsCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... strings) {
|
||||
LyricsEngine lyricsEngine = new LyricsWikiEngine();
|
||||
return lyricsEngine.getLyrics(strings[1], strings[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
super.onPostExecute(s);
|
||||
if (TextUtils.isEmpty(s)) {
|
||||
mLyricsCallback.onError();
|
||||
return;
|
||||
}
|
||||
mLyricsCallback.onShowLyrics(s);
|
||||
}
|
||||
|
||||
public interface LyricsCallback {
|
||||
void onShowLyrics(String lyrics);
|
||||
|
||||
void onError();
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
public class QueryResult {
|
||||
public static final String ITEM_LRC = "lrc";
|
||||
public static final String ATTRIBUTE_ID = "id";
|
||||
public static final String ATTRIBUTE_ARTIST = "artist";
|
||||
public static final String ATTRIBUTE_TITLE = "title";
|
||||
|
||||
public final int mId;
|
||||
public final String mArtist;
|
||||
public final String mTitle;
|
||||
|
||||
QueryResult(int id, String artist, String title) {
|
||||
mId = id;
|
||||
mArtist = artist;
|
||||
mTitle = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mTitle + " - " + mArtist;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,9 @@ package code.name.monkey.retromusic.ui.activities;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
|
@ -16,18 +14,6 @@ import android.view.WindowManager;
|
|||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.DialogAction;
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
@ -45,348 +31,300 @@ import code.name.monkey.retromusic.util.LyricUtil;
|
|||
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.views.LyricView;
|
||||
import com.afollestad.materialdialogs.DialogAction;
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.bumptech.glide.Glide;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
|
||||
public class LyricsActivity extends AbsMusicServiceActivity implements MusicProgressViewUpdateHelper.Callback {
|
||||
public class LyricsActivity extends AbsMusicServiceActivity implements
|
||||
MusicProgressViewUpdateHelper.Callback {
|
||||
|
||||
@BindView(R.id.title)
|
||||
TextView songTitle;
|
||||
@BindView(R.id.text)
|
||||
TextView songText;
|
||||
@BindView(R.id.lyrics)
|
||||
LyricView lyricView;
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
@BindView(R.id.offline_lyrics)
|
||||
TextView offlineLyrics;
|
||||
@BindView(R.id.actions)
|
||||
RadioGroup actionsLayout;
|
||||
@BindView(R.id.gradient_background)
|
||||
View background;
|
||||
@BindView(R.id.title)
|
||||
TextView songTitle;
|
||||
@BindView(R.id.text)
|
||||
TextView songText;
|
||||
|
||||
private MusicProgressViewUpdateHelper updateHelper;
|
||||
private AsyncTask updateLyricsAsyncTask;
|
||||
private CompositeDisposable disposable;
|
||||
private Song song;
|
||||
private Lyrics lyrics;
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
@BindView(R.id.offline_lyrics)
|
||||
TextView offlineLyrics;
|
||||
@BindView(R.id.actions)
|
||||
RadioGroup actionsLayout;
|
||||
@BindView(R.id.gradient_background)
|
||||
View background;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_lyrics);
|
||||
ButterKnife.bind(this);
|
||||
private MusicProgressViewUpdateHelper updateHelper;
|
||||
private AsyncTask updateLyricsAsyncTask;
|
||||
private CompositeDisposable disposable;
|
||||
private Song song;
|
||||
private Lyrics lyrics;
|
||||
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setTaskDescriptionColorAuto();
|
||||
setLightNavigationBar(true);
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_lyrics);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
updateHelper = new MusicProgressViewUpdateHelper(this, 500, 1000);
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setTaskDescriptionColorAuto();
|
||||
setLightNavigationBar(true);
|
||||
|
||||
setupToolbar();
|
||||
setupLyricsView();
|
||||
setupWakelock();
|
||||
updateHelper = new MusicProgressViewUpdateHelper(this, 500, 1000);
|
||||
|
||||
actionsLayout.setOnCheckedChangeListener((group, checkedId) -> selectLyricsTye(checkedId));
|
||||
setupToolbar();
|
||||
setupLyricsView();
|
||||
setupWakelock();
|
||||
|
||||
actionsLayout.setOnCheckedChangeListener((group, checkedId) -> selectLyricsTye(checkedId));
|
||||
}
|
||||
|
||||
private void selectLyricsTye(int group) {
|
||||
|
||||
RadioButton radioButton = actionsLayout.findViewById(group);
|
||||
radioButton.setBackgroundTintList(ColorStateList.valueOf(ThemeStore.accentColor(this)));
|
||||
radioButton.setTextColor(ThemeStore.textColorPrimary(this));
|
||||
|
||||
offlineLyrics.setVisibility(View.GONE);
|
||||
|
||||
switch (group) {
|
||||
case R.id.synced_lyrics:
|
||||
loadLRCLyrics();
|
||||
|
||||
break;
|
||||
default:
|
||||
case R.id.normal_lyrics:
|
||||
loadSongLyrics();
|
||||
offlineLyrics.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void selectLyricsTye(int group) {
|
||||
|
||||
RadioButton radioButton = actionsLayout.findViewById(group);
|
||||
radioButton.setBackgroundTintList(ColorStateList.valueOf(ThemeStore.accentColor(this)));
|
||||
radioButton.setTextColor(ThemeStore.textColorPrimary(this));
|
||||
|
||||
offlineLyrics.setVisibility(View.GONE);
|
||||
lyricView.setVisibility(View.GONE);
|
||||
switch (group) {
|
||||
case R.id.synced_lyrics:
|
||||
loadLRCLyrics();
|
||||
lyricView.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
default:
|
||||
case R.id.normal_lyrics:
|
||||
loadSongLyrics();
|
||||
offlineLyrics.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
private void loadLRCLyrics() {
|
||||
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLRCLyrics() {
|
||||
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName));
|
||||
}
|
||||
private void setupWakelock() {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
private void setupLyricsView() {
|
||||
disposable = new CompositeDisposable();
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
toolbar.setBackgroundColor(ThemeStore.primaryColor(this));
|
||||
toolbar.setTitle("");
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged();
|
||||
loadLrcFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateHelper.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
updateHelper.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
super.onServiceConnected();
|
||||
loadLrcFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposable.clear();
|
||||
|
||||
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask.isCancelled()) {
|
||||
updateLyricsAsyncTask.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupWakelock() {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void setupLyricsView() {
|
||||
disposable = new CompositeDisposable();
|
||||
//lyricView.setLineSpace(15.0f);
|
||||
//lyricView.setTextSize(17.0f);
|
||||
//lyricView.setPlayable(true);
|
||||
//lyricView.setTranslationY(DensityUtil.getScreenWidth(this) + DensityUtil.dip2px(this, 120));
|
||||
lyricView.setOnPlayerClickListener((progress, content) -> MusicPlayerRemote.seekTo((int) progress));
|
||||
@Override
|
||||
public void onUpdateProgressViews(int progress, int total) {
|
||||
|
||||
//lyricView.setHighLightTextColor(ThemeStore.accentColor(this));
|
||||
lyricView.setDefaultColor(ContextCompat.getColor(this, R.color.md_grey_400));
|
||||
//lyricView.setTouchable(false);
|
||||
lyricView.setHintColor(Color.WHITE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
toolbar.setBackgroundColor(ThemeStore.primaryColor(this));
|
||||
toolbar.setTitle("");
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged();
|
||||
loadLrcFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateHelper.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
updateHelper.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
super.onServiceConnected();
|
||||
loadLrcFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposable.clear();
|
||||
lyricView.setOnPlayerClickListener(null);
|
||||
|
||||
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask.isCancelled()) {
|
||||
updateLyricsAsyncTask.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateProgressViews(int progress, int total) {
|
||||
lyricView.setCurrentTimeMillis(progress);
|
||||
}
|
||||
|
||||
private void loadLrcFile() {
|
||||
song = MusicPlayerRemote.getCurrentSong();
|
||||
songTitle.setText(song.title);
|
||||
songText.setText(song.artistName);
|
||||
SongGlideRequest.Builder.from(Glide.with(this), song)
|
||||
.checkIgnoreMediaStore(this)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
.into(new RetroMusicColoredTarget(findViewById(R.id.image)) {
|
||||
@Override
|
||||
public void onColorReady(int color) {
|
||||
if (PreferenceUtil.getInstance(LyricsActivity.this).getAdaptiveColor()) {
|
||||
background.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
});
|
||||
selectLyricsTye(actionsLayout.getCheckedRadioButtonId());
|
||||
}
|
||||
|
||||
private void showLyricsLocal(File file) {
|
||||
if (file == null) {
|
||||
lyricView.reset();
|
||||
lyricView.setVisibility(View.GONE);
|
||||
} else {
|
||||
lyricView.setVisibility(View.VISIBLE);
|
||||
lyricView.setLyricFile(file, "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick({R.id.edit_lyrics})
|
||||
public void onViewClicked(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.edit_lyrics:
|
||||
switch (actionsLayout.getCheckedRadioButtonId()) {
|
||||
case R.id.synced_lyrics:
|
||||
showSyncedLyrics();
|
||||
break;
|
||||
case R.id.normal_lyrics:
|
||||
showLyricsSaveDialog();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void loadSongLyrics() {
|
||||
if (updateLyricsAsyncTask != null) updateLyricsAsyncTask.cancel(false);
|
||||
final Song song = MusicPlayerRemote.getCurrentSong();
|
||||
updateLyricsAsyncTask = new AsyncTask<Void, Void, Lyrics>() {
|
||||
@Override
|
||||
protected Lyrics doInBackground(Void... params) {
|
||||
String data = MusicUtil.getLyrics(song);
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
return Lyrics.parse(song, data);
|
||||
private void loadLrcFile() {
|
||||
song = MusicPlayerRemote.getCurrentSong();
|
||||
songTitle.setText(song.title);
|
||||
songText.setText(song.artistName);
|
||||
SongGlideRequest.Builder.from(Glide.with(this), song)
|
||||
.checkIgnoreMediaStore(this)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
.into(new RetroMusicColoredTarget(findViewById(R.id.image)) {
|
||||
@Override
|
||||
public void onColorReady(int color) {
|
||||
if (PreferenceUtil.getInstance(LyricsActivity.this).getAdaptiveColor()) {
|
||||
background.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
});
|
||||
selectLyricsTye(actionsLayout.getCheckedRadioButtonId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
lyrics = null;
|
||||
}
|
||||
private void showLyricsLocal(File file) {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Lyrics l) {
|
||||
lyrics = l;
|
||||
offlineLyrics.setVisibility(View.VISIBLE);
|
||||
if (l == null) {
|
||||
offlineLyrics.setText(R.string.no_lyrics_found);
|
||||
return;
|
||||
}
|
||||
offlineLyrics.setText(l.data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(Lyrics s) {
|
||||
onPostExecute(null);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void showSyncedLyrics() {
|
||||
String content = "";
|
||||
try {
|
||||
content = LyricUtil.getStringFromFile(song.title, song.artistName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@OnClick({R.id.edit_lyrics})
|
||||
public void onViewClicked(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.edit_lyrics:
|
||||
switch (actionsLayout.getCheckedRadioButtonId()) {
|
||||
case R.id.synced_lyrics:
|
||||
showSyncedLyrics();
|
||||
break;
|
||||
case R.id.normal_lyrics:
|
||||
showLyricsSaveDialog();
|
||||
break;
|
||||
}
|
||||
new MaterialDialog.Builder(this)
|
||||
.title("Add lyrics")
|
||||
.neutralText("Search")
|
||||
.content("Add time frame lyrics")
|
||||
.negativeText("Delete")
|
||||
.onNegative((dialog, which) -> {
|
||||
LyricUtil.deleteLrcFile(song.title, song.artistName);
|
||||
loadLrcFile();
|
||||
})
|
||||
.onNeutral((dialog, which) -> RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchLrcUrl()))
|
||||
.inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
||||
.input("Paste lyrics here", content, (dialog, input) -> {
|
||||
LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString());
|
||||
loadLrcFile();
|
||||
}).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String getGoogleSearchLrcUrl() {
|
||||
String baseUrl = "http://www.google.com/search?";
|
||||
String query = song.title + "+" + song.artistName;
|
||||
query = "q=" + query.replace(" ", "+") + " .lrc";
|
||||
baseUrl += query;
|
||||
return baseUrl;
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void loadSongLyrics() {
|
||||
if (updateLyricsAsyncTask != null) {
|
||||
updateLyricsAsyncTask.cancel(false);
|
||||
}
|
||||
|
||||
private void showLyricsSaveDialog() {
|
||||
String content = "";
|
||||
if (lyrics == null) {
|
||||
content = "";
|
||||
} else {
|
||||
content = lyrics.data;
|
||||
final Song song = MusicPlayerRemote.getCurrentSong();
|
||||
updateLyricsAsyncTask = new AsyncTask<Void, Void, Lyrics>() {
|
||||
@Override
|
||||
protected Lyrics doInBackground(Void... params) {
|
||||
String data = MusicUtil.getLyrics(song);
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
new MaterialDialog.Builder(this)
|
||||
.title("Add lyrics")
|
||||
.neutralText("Search")
|
||||
.onNeutral(new MaterialDialog.SingleButtonCallback() {
|
||||
@Override
|
||||
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
|
||||
RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.title, song.artistName));
|
||||
}
|
||||
})
|
||||
.inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
||||
.input("Paste lyrics here", content, (dialog, input) -> {
|
||||
Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class);
|
||||
fieldKeyValueMap.put(FieldKey.LYRICS, input.toString());
|
||||
return Lyrics.parse(song, data);
|
||||
}
|
||||
|
||||
new WriteTagsAsyncTask(LyricsActivity.this)
|
||||
.execute(new WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null));
|
||||
loadLrcFile();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
lyrics = null;
|
||||
}
|
||||
|
||||
private ArrayList<String> getSongPaths(Song song) {
|
||||
ArrayList<String> paths = new ArrayList<>(1);
|
||||
paths.add(song.data);
|
||||
return paths;
|
||||
}
|
||||
|
||||
private String getGoogleSearchUrl(String title, String text) {
|
||||
String baseUrl = "http://www.google.com/search?";
|
||||
String query = title + "+" + text;
|
||||
query = "q=" + query.replace(" ", "+") + " lyrics";
|
||||
baseUrl += query;
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/*
|
||||
private void loadLyricsWIki(String title, String artist) {
|
||||
offlineLyrics.setVisibility(View.GONE);
|
||||
if (lyricsWikiTask != null) {
|
||||
lyricsWikiTask.cancel(false);
|
||||
@Override
|
||||
protected void onPostExecute(Lyrics l) {
|
||||
lyrics = l;
|
||||
offlineLyrics.setVisibility(View.VISIBLE);
|
||||
if (l == null) {
|
||||
offlineLyrics.setText(R.string.no_lyrics_found);
|
||||
return;
|
||||
}
|
||||
lyricsWikiTask = new ParseLyrics(new ParseLyrics.LyricsCallback() {
|
||||
@Override
|
||||
public void onShowLyrics(String lyrics) {
|
||||
offlineLyrics.setVisibility(View.VISIBLE);
|
||||
offlineLyrics.setText(lyrics);
|
||||
}
|
||||
offlineLyrics.setText(l.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
loadSongLyrics();
|
||||
}
|
||||
}).execute(title, artist);
|
||||
}
|
||||
@Override
|
||||
protected void onCancelled(Lyrics s) {
|
||||
onPostExecute(null);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void callAgain(final String title, final String artist) {
|
||||
disposable.clear();
|
||||
disposable.add(loadLyrics.downloadLrcFile(title, artist, MusicPlayerRemote.getSongDurationMillis())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.doOnSubscribe(disposable -> {
|
||||
refresh.startAnimation(rotateAnimation);
|
||||
})
|
||||
.subscribe(this::showLyricsLocal, throwable -> {
|
||||
refresh.clearAnimation();
|
||||
showLyricsLocal(null);
|
||||
//loadLyricsWIki(songTitle, artist);
|
||||
toggleSyncLyrics(View.GONE);
|
||||
}, () -> {
|
||||
refresh.clearAnimation();
|
||||
Toast.makeText(this, "Lyrics downloaded", Toast.LENGTH_SHORT).show();
|
||||
}));
|
||||
private void showSyncedLyrics() {
|
||||
String content = "";
|
||||
try {
|
||||
content = LyricUtil.getStringFromFile(song.title, song.artistName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
*/
|
||||
new MaterialDialog.Builder(this)
|
||||
.title("Add lyrics")
|
||||
.neutralText("Search")
|
||||
.content("Add time frame lyrics")
|
||||
.negativeText("Delete")
|
||||
.onNegative((dialog, which) -> {
|
||||
LyricUtil.deleteLrcFile(song.title, song.artistName);
|
||||
loadLrcFile();
|
||||
})
|
||||
.onNeutral(
|
||||
(dialog, which) -> RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchLrcUrl()))
|
||||
.inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
||||
.input("Paste lyrics here", content, (dialog, input) -> {
|
||||
LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString());
|
||||
loadLrcFile();
|
||||
}).show();
|
||||
}
|
||||
|
||||
private String getGoogleSearchLrcUrl() {
|
||||
String baseUrl = "http://www.google.com/search?";
|
||||
String query = song.title + "+" + song.artistName;
|
||||
query = "q=" + query.replace(" ", "+") + " .lrc";
|
||||
baseUrl += query;
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
private void showLyricsSaveDialog() {
|
||||
String content = "";
|
||||
if (lyrics == null) {
|
||||
content = "";
|
||||
} else {
|
||||
content = lyrics.data;
|
||||
}
|
||||
new MaterialDialog.Builder(this)
|
||||
.title("Add lyrics")
|
||||
.neutralText("Search")
|
||||
.onNeutral(new MaterialDialog.SingleButtonCallback() {
|
||||
@Override
|
||||
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
|
||||
RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.title, song.artistName));
|
||||
}
|
||||
})
|
||||
.inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
||||
.input("Paste lyrics here", content, (dialog, input) -> {
|
||||
Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class);
|
||||
fieldKeyValueMap.put(FieldKey.LYRICS, input.toString());
|
||||
|
||||
new WriteTagsAsyncTask(LyricsActivity.this)
|
||||
.execute(
|
||||
new WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null));
|
||||
loadLrcFile();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private ArrayList<String> getSongPaths(Song song) {
|
||||
ArrayList<String> paths = new ArrayList<>(1);
|
||||
paths.add(song.data);
|
||||
return paths;
|
||||
}
|
||||
|
||||
private String getGoogleSearchUrl(String title, String text) {
|
||||
String baseUrl = "http://www.google.com/search?";
|
||||
String query = title + "+" + text;
|
||||
query = "q=" + query.replace(" ", "+") + " lyrics";
|
||||
baseUrl += query;
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,876 +0,0 @@
|
|||
package code.name.monkey.retromusic.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import org.mozilla.universalchardet.UniversalDetector;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
/**
|
||||
* Created by zhengken.me on 2016/11/27.
|
||||
* ClassName : LyricView
|
||||
* Description :
|
||||
*/
|
||||
public class LyricView extends View {
|
||||
|
||||
public static final int LEFT = 0;
|
||||
public static final int CENTER = 1;
|
||||
public static final int RIGHT = 2;
|
||||
private static final String TAG = "LyricView";
|
||||
private static final float SLIDE_COEFFICIENT = 0.2f;
|
||||
|
||||
private static final int UNITS_SECOND = 1000;
|
||||
private static final int UNITS_MILLISECOND = 1;
|
||||
|
||||
private static final int FLING_ANIMATOR_DURATION = 500 * UNITS_MILLISECOND;
|
||||
|
||||
private static final int THRESHOLD_Y_VELOCITY = 1600;
|
||||
|
||||
private static final int INDICATOR_ICON_PLAY_MARGIN_LEFT = 7;//dp
|
||||
private static final int INDICATOR_ICON_PLAY_WIDTH = 15;//sp
|
||||
private static final int INDICATOR_LINE_MARGIN = 10;//dp
|
||||
private static final int INDICATOR_TIME_TEXT_SIZE = 10;//sp
|
||||
private static final int INDICATOR_TIME_MARGIN_RIGHT = 7;//dp
|
||||
|
||||
private static final int DEFAULT_TEXT_SIZE = 16;//sp
|
||||
private static final int DEFAULT_MAX_LENGTH = 300;//dp
|
||||
private static final int DEFAULT_LINE_SPACE = 25;//dp
|
||||
|
||||
private int mHintColor;
|
||||
private int mDefaultColor;
|
||||
private int mHighLightColor;
|
||||
private int mTextAlign;
|
||||
|
||||
|
||||
private int mLineCount;
|
||||
private int mTextSize;
|
||||
private float mLineHeight;
|
||||
private LyricInfo mLyricInfo;
|
||||
private String mDefaultHint;
|
||||
private int mMaxLength;
|
||||
|
||||
private TextPaint mTextPaint;
|
||||
private Paint mBtnPlayPaint;
|
||||
private Paint mLinePaint;
|
||||
private Paint mTimerPaint;
|
||||
|
||||
private boolean mFling = false;
|
||||
private ValueAnimator mFlingAnimator;
|
||||
private float mScrollY = 0;
|
||||
private float mLineSpace = 0;
|
||||
private boolean mIsShade;
|
||||
private float mShaderWidth = 0;
|
||||
private int mCurrentPlayLine = 0;
|
||||
private boolean mShowIndicator;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mVelocity = 0;
|
||||
private float mDownX;
|
||||
private float mDownY;
|
||||
private float mLastScrollY;
|
||||
private boolean mUserTouch = false;
|
||||
Runnable hideIndicator = () -> {
|
||||
setUserTouch(false);
|
||||
invalidateView();
|
||||
};
|
||||
private int maxVelocity;
|
||||
private int mLineNumberUnderIndicator = 0;
|
||||
private Rect mBtnPlayRect = new Rect();
|
||||
private Rect mTimerRect;
|
||||
private String mDefaultTime = "00:00";
|
||||
private int mLineColor = Color.parseColor("#EFEFEF");
|
||||
private int mBtnColor = Color.parseColor("#EFEFEF");
|
||||
private List<Integer> mLineFeedRecord = new ArrayList<>();
|
||||
private boolean mEnableLineFeed = false;
|
||||
private int mExtraHeight = 0;
|
||||
private int mTextHeight;
|
||||
private String mCurrentLyricFilePath = null;
|
||||
private OnPlayerClickListener mClickListener;
|
||||
|
||||
public LyricView(Context context) {
|
||||
super(context);
|
||||
initMyView(context);
|
||||
}
|
||||
|
||||
public LyricView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
getAttrs(context, attributeSet);
|
||||
initMyView(context);
|
||||
|
||||
}
|
||||
|
||||
public LyricView(Context context, AttributeSet attributeSet, int i) {
|
||||
super(context, attributeSet, i);
|
||||
getAttrs(context, attributeSet);
|
||||
initMyView(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
mVelocityTracker.addMovement(event);
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
actionCancel(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
actionDown(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
actionMove(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
actionUp(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
invalidateView();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
mBtnPlayRect.set((int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_ICON_PLAY_MARGIN_LEFT),
|
||||
(int) (getHeight() * 0.5f - getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) * 0.5f),
|
||||
(int) (getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) + getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_ICON_PLAY_MARGIN_LEFT)),
|
||||
(int) (getHeight() * 0.5f + getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) * 0.5f));
|
||||
mShaderWidth = getWidth() * 0.4f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (scrollable()) {
|
||||
if (mShowIndicator) {
|
||||
drawIndicator(canvas);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mLineCount; i++) {
|
||||
float x = 0;
|
||||
switch (mTextAlign) {
|
||||
case LEFT:
|
||||
x = INDICATOR_ICON_PLAY_MARGIN_LEFT + INDICATOR_LINE_MARGIN + mBtnPlayRect.width();
|
||||
break;
|
||||
case CENTER:
|
||||
x = getWidth() * 0.5f;
|
||||
break;
|
||||
case RIGHT:
|
||||
x = getWidth() - INDICATOR_LINE_MARGIN * 2 - mTimerRect.width() - INDICATOR_ICON_PLAY_MARGIN_LEFT;
|
||||
break;
|
||||
}
|
||||
|
||||
float y;
|
||||
if (mEnableLineFeed && i > 0) {
|
||||
y = getMeasuredHeight() * 0.5f + i * mLineHeight - mScrollY + mLineFeedRecord.get(i - 1);
|
||||
} else {
|
||||
y = getMeasuredHeight() * 0.5f + i * mLineHeight - mScrollY;
|
||||
}
|
||||
|
||||
// float y = getHeight() * 0.5f + i * mLineHeight - mScrollY;
|
||||
if (y < 0) {
|
||||
continue;
|
||||
}
|
||||
if (y > getHeight()) {
|
||||
break;
|
||||
}
|
||||
if (i == mCurrentPlayLine - 1) {
|
||||
mTextPaint.setColor(mHighLightColor);
|
||||
} else if (i == mLineNumberUnderIndicator && mShowIndicator) {
|
||||
mTextPaint.setColor(Color.LTGRAY);
|
||||
} else {
|
||||
mTextPaint.setColor(mDefaultColor);
|
||||
}
|
||||
if (mIsShade && (y > getHeight() - mShaderWidth || y < mShaderWidth)) {
|
||||
if (y < mShaderWidth) {
|
||||
mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
|
||||
} else {
|
||||
mTextPaint.setAlpha(26 + (int) (23000.0f * (getHeight() - y) / mShaderWidth * 0.01f));
|
||||
}
|
||||
} else {
|
||||
mTextPaint.setAlpha(255);
|
||||
}
|
||||
|
||||
if (mEnableLineFeed) {
|
||||
StaticLayout staticLayout = new StaticLayout(mLyricInfo.songLines.get(i).content, mTextPaint,
|
||||
mMaxLength,
|
||||
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
canvas.save();
|
||||
canvas.translate(x, y);
|
||||
staticLayout.draw(canvas);
|
||||
canvas.restore();
|
||||
} else {
|
||||
canvas.drawText(mLyricInfo.songLines.get(i).content, x, y, mTextPaint);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mTextPaint.setColor(mHintColor);
|
||||
canvas.drawText(mDefaultHint, getMeasuredWidth() / 2, getMeasuredHeight() / 2, mTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void getAttrs(Context context, AttributeSet attrs) {
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LyricView);
|
||||
mIsShade = ta.getBoolean(R.styleable.LyricView_fadeInFadeOut, false);
|
||||
mDefaultHint = ta.getString(R.styleable.LyricView_hint) != null
|
||||
? ta.getString(R.styleable.LyricView_hint)
|
||||
: getResources().getString(R.string.default_hint);
|
||||
mHintColor = ta.getColor(R.styleable.LyricView_hintColor, Color.parseColor("#FFFFFF"));
|
||||
mDefaultColor = ta.getColor(R.styleable.LyricView_textColor, Color.parseColor("#8D8D8D"));
|
||||
mHighLightColor = ta.getColor(R.styleable.LyricView_highlightColor, Color.parseColor("#FFFFFF"));
|
||||
mTextSize = ta.getDimensionPixelSize(R.styleable.LyricView_textSize, (int) getRawSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE));
|
||||
mTextAlign = ta.getInt(R.styleable.LyricView_textAlign, CENTER);
|
||||
mMaxLength = ta.getDimensionPixelSize(R.styleable.LyricView_maxLength, (int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_LENGTH));
|
||||
mLineSpace = ta.getDimensionPixelSize(R.styleable.LyricView_lineSpace, (int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_LINE_SPACE));
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
public void setShade(boolean shade) {
|
||||
mIsShade = shade;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setHintColor(int hintColor) {
|
||||
mHintColor = hintColor;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setDefaultColor(int defaultColor) {
|
||||
mDefaultColor = defaultColor;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setHighLightColor(int highLightColor) {
|
||||
mHighLightColor = highLightColor;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setTextAlign(int textAlign) {
|
||||
mTextAlign = textAlign;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setLineCount(int lineCount) {
|
||||
mLineCount = lineCount;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setTextSize(int textSize) {
|
||||
mTextSize = textSize;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setDefaultHint(String defaultHint) {
|
||||
mDefaultHint = defaultHint;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setMaxLength(int maxLength) {
|
||||
mMaxLength = maxLength;
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
public void setOnPlayerClickListener(OnPlayerClickListener mClickListener) {
|
||||
this.mClickListener = mClickListener;
|
||||
}
|
||||
|
||||
public void setAlignment(@Alignment int alignment) {
|
||||
mTextAlign = alignment;
|
||||
}
|
||||
|
||||
public void setCurrentTimeMillis(long current) {
|
||||
scrollToCurrentTimeMillis(current);
|
||||
}
|
||||
|
||||
public void setLyricFile(File file) {
|
||||
|
||||
if (file == null || !file.exists()) {
|
||||
reset();
|
||||
mCurrentLyricFilePath = "";
|
||||
return;
|
||||
} else if (file.getPath().equals(mCurrentLyricFilePath)) {
|
||||
return;
|
||||
} else {
|
||||
mCurrentLyricFilePath = file.getPath();
|
||||
reset();
|
||||
}
|
||||
try {
|
||||
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
byte[] buf = new byte[1024];
|
||||
UniversalDetector detector = new UniversalDetector(null);
|
||||
int nread;
|
||||
while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
|
||||
detector.handleData(buf, 0, nread);
|
||||
}
|
||||
|
||||
detector.dataEnd();
|
||||
String encoding = detector.getDetectedCharset();
|
||||
if (encoding != null) {
|
||||
setLyricFile(file, encoding);
|
||||
} else {
|
||||
setLyricFile(file, "UTF-8");
|
||||
}
|
||||
detector.reset();
|
||||
fis.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLyricFile(File file, String charsetName) {
|
||||
if (file != null && file.exists()) {
|
||||
try {
|
||||
setupLyricResource(new FileInputStream(file), charsetName);
|
||||
|
||||
for (int i = 0; i < mLyricInfo.songLines.size(); i++) {
|
||||
|
||||
StaticLayout staticLayout = new StaticLayout(mLyricInfo.songLines.get(i).content, mTextPaint,
|
||||
(int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_LENGTH),
|
||||
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
|
||||
if (staticLayout.getLineCount() > 1) {
|
||||
mEnableLineFeed = true;
|
||||
mExtraHeight = mExtraHeight + (staticLayout.getLineCount() - 1) * mTextHeight;
|
||||
}
|
||||
|
||||
mLineFeedRecord.add(i, mExtraHeight);
|
||||
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
invalidateView();
|
||||
}
|
||||
}
|
||||
|
||||
private void setLineSpace(float lineSpace) {
|
||||
if (mLineSpace != lineSpace) {
|
||||
mLineSpace = getRawSize(TypedValue.COMPLEX_UNIT_DIP, lineSpace);
|
||||
measureLineHeight();
|
||||
mScrollY = measureCurrentScrollY(mCurrentPlayLine);
|
||||
invalidateView();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
resetView();
|
||||
}
|
||||
|
||||
private void actionCancel(MotionEvent event) {
|
||||
releaseVelocityTracker();
|
||||
}
|
||||
|
||||
private void actionDown(MotionEvent event) {
|
||||
removeCallbacks(hideIndicator);
|
||||
mLastScrollY = mScrollY;
|
||||
mDownX = event.getX();
|
||||
mDownY = event.getY();
|
||||
if (mFlingAnimator != null) {
|
||||
mFlingAnimator.cancel();
|
||||
mFlingAnimator = null;
|
||||
}
|
||||
setUserTouch(true);
|
||||
}
|
||||
|
||||
private boolean overScrolled() {
|
||||
|
||||
return scrollable() && (mScrollY > mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0) || mScrollY < 0);
|
||||
}
|
||||
|
||||
private void actionMove(MotionEvent event) {
|
||||
if (scrollable()) {
|
||||
final VelocityTracker tracker = mVelocityTracker;
|
||||
tracker.computeCurrentVelocity(UNITS_SECOND, maxVelocity);
|
||||
mScrollY = mLastScrollY + mDownY - event.getY();
|
||||
mVelocity = tracker.getYVelocity();
|
||||
measureCurrentLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void actionUp(MotionEvent event) {
|
||||
|
||||
postDelayed(hideIndicator, 3 * UNITS_SECOND);
|
||||
|
||||
releaseVelocityTracker();
|
||||
|
||||
if (scrollable()) {
|
||||
if (overScrolled() && mScrollY < 0) {
|
||||
smoothScrollTo(0);
|
||||
return;
|
||||
}
|
||||
if (overScrolled() && mScrollY > mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0)) {
|
||||
smoothScrollTo(mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0));
|
||||
return;
|
||||
}
|
||||
if (Math.abs(mVelocity) > THRESHOLD_Y_VELOCITY) {
|
||||
doFlingAnimator(mVelocity);
|
||||
return;
|
||||
}
|
||||
if (mShowIndicator && clickPlayer(event)) {
|
||||
if (mLineNumberUnderIndicator != mCurrentPlayLine) {
|
||||
mShowIndicator = false;
|
||||
if (mClickListener != null) {
|
||||
setUserTouch(false);
|
||||
mClickListener.onPlayerClicked(mLyricInfo.songLines.get(mLineNumberUnderIndicator).start, mLyricInfo.songLines.get(mLineNumberUnderIndicator).content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String measureCurrentTime() {
|
||||
DecimalFormat format = new DecimalFormat("00");
|
||||
if (mLyricInfo != null && mLineCount > 0 && mLineNumberUnderIndicator - 1 < mLineCount && mLineNumberUnderIndicator > 0) {
|
||||
return format.format(mLyricInfo.songLines.get(mLineNumberUnderIndicator - 1).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(mLineNumberUnderIndicator - 1).start / 1000 % 60);
|
||||
}
|
||||
if (mLyricInfo != null && mLineCount > 0 && (mLineNumberUnderIndicator - 1) >= mLineCount) {
|
||||
return format.format(mLyricInfo.songLines.get(mLineCount - 1).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(mLineCount - 1).start / 1000 % 60);
|
||||
}
|
||||
if (mLyricInfo != null && mLineCount > 0 && mLineNumberUnderIndicator - 1 <= 0) {
|
||||
return format.format(mLyricInfo.songLines.get(0).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(0).start / 1000 % 60);
|
||||
}
|
||||
return mDefaultTime;
|
||||
}
|
||||
|
||||
private void drawIndicator(Canvas canvas) {
|
||||
|
||||
//绘制 播放 按钮
|
||||
Path pathPlay = new Path();
|
||||
float yCoordinate = mBtnPlayRect.left + (float) Math.sqrt(Math.pow(mBtnPlayRect.width(), 2) - Math.pow(mBtnPlayRect.width() * 0.5f, 2));
|
||||
float remainWidth = mBtnPlayRect.right - yCoordinate;
|
||||
|
||||
pathPlay.moveTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() - mBtnPlayRect.height() * 0.5f);
|
||||
pathPlay.lineTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() + mBtnPlayRect.height() * 0.5f);
|
||||
pathPlay.lineTo(yCoordinate, mBtnPlayRect.centerY());
|
||||
pathPlay.lineTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() - mBtnPlayRect.height() * 0.5f);
|
||||
|
||||
canvas.drawPath(pathPlay, mBtnPlayPaint);
|
||||
|
||||
//绘制 分割线
|
||||
Path pathLine = new Path();
|
||||
pathLine.moveTo(mBtnPlayRect.right + getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_LINE_MARGIN) - remainWidth, getMeasuredHeight() * 0.5f);
|
||||
pathLine.lineTo(getWidth() - mTimerRect.width() - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_TIME_MARGIN_RIGHT) - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_LINE_MARGIN), getHeight() * 0.5f);
|
||||
canvas.drawPath(pathLine, mLinePaint);
|
||||
|
||||
//绘制 时间
|
||||
canvas.drawText(measureCurrentTime(), getWidth() - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_TIME_MARGIN_RIGHT), (getHeight() + mTimerRect.height()) * 0.5f, mTimerPaint);
|
||||
}
|
||||
|
||||
private boolean clickPlayer(MotionEvent event) {
|
||||
if (mBtnPlayRect != null && mDownX > (mBtnPlayRect.left - INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownX < (mBtnPlayRect.right + INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownY > (mBtnPlayRect.top - INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownY < (mBtnPlayRect.bottom + INDICATOR_ICON_PLAY_MARGIN_LEFT)) {
|
||||
float upX = event.getX();
|
||||
float upY = event.getY();
|
||||
return upX > (mBtnPlayRect.left - INDICATOR_ICON_PLAY_MARGIN_LEFT) && upX < (mBtnPlayRect.right + INDICATOR_ICON_PLAY_MARGIN_LEFT) && upY > (mBtnPlayRect.top - INDICATOR_ICON_PLAY_MARGIN_LEFT) && upY < (mBtnPlayRect.bottom + INDICATOR_ICON_PLAY_MARGIN_LEFT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doFlingAnimator(float velocity) {
|
||||
|
||||
float distance = (velocity / Math.abs(velocity) * (Math.abs(velocity) * SLIDE_COEFFICIENT));
|
||||
float to = Math.min(Math.max(0, (mScrollY - distance)), (mLineCount - 1) * mLineHeight + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0));
|
||||
|
||||
mFlingAnimator = ValueAnimator.ofFloat(mScrollY, to);
|
||||
mFlingAnimator.addUpdateListener(animation -> {
|
||||
mScrollY = (float) animation.getAnimatedValue();
|
||||
measureCurrentLine();
|
||||
invalidateView();
|
||||
});
|
||||
|
||||
mFlingAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
super.onAnimationStart(animation);
|
||||
mVelocity = 0;
|
||||
mFling = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
mFling = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
}
|
||||
});
|
||||
|
||||
mFlingAnimator.setDuration(FLING_ANIMATOR_DURATION);
|
||||
mFlingAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
mFlingAnimator.start();
|
||||
}
|
||||
|
||||
private void setUserTouch(boolean isUserTouch) {
|
||||
if (isUserTouch) {
|
||||
mUserTouch = true;
|
||||
mShowIndicator = true;
|
||||
} else {
|
||||
mUserTouch = false;
|
||||
mShowIndicator = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseVelocityTracker() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.clear();
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initMyView(Context context) {
|
||||
maxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
|
||||
initPaint();
|
||||
initAllBounds();
|
||||
}
|
||||
|
||||
private void initAllBounds() {
|
||||
setRawTextSize(mTextSize);
|
||||
|
||||
setLineSpace(mLineSpace);
|
||||
measureLineHeight();
|
||||
|
||||
mTimerRect = new Rect();
|
||||
mTimerPaint.getTextBounds(mDefaultTime, 0, mDefaultTime.length(), mTimerRect);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setDither(true);
|
||||
mTextPaint.setAntiAlias(true);
|
||||
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/circular_std_book.otf");
|
||||
mTextPaint.setTypeface(typeface);
|
||||
|
||||
switch (mTextAlign) {
|
||||
case LEFT:
|
||||
mTextPaint.setTextAlign(Paint.Align.LEFT);
|
||||
break;
|
||||
case CENTER:
|
||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||
break;
|
||||
case RIGHT:
|
||||
mTextPaint.setTextAlign(Paint.Align.RIGHT);
|
||||
break;
|
||||
}
|
||||
|
||||
mBtnPlayPaint = new Paint();
|
||||
mBtnPlayPaint.setDither(true);
|
||||
mBtnPlayPaint.setAntiAlias(true);
|
||||
mBtnPlayPaint.setColor(mBtnColor);
|
||||
mBtnPlayPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mBtnPlayPaint.setAlpha(128);
|
||||
|
||||
mLinePaint = new Paint();
|
||||
mLinePaint.setDither(true);
|
||||
mLinePaint.setAntiAlias(true);
|
||||
mLinePaint.setColor(mLineColor);
|
||||
mLinePaint.setAlpha(64);
|
||||
mLinePaint.setStrokeWidth(1.0f);
|
||||
mLinePaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
mTimerPaint = new Paint();
|
||||
mTimerPaint.setDither(true);
|
||||
mTimerPaint.setAntiAlias(true);
|
||||
mTimerPaint.setColor(Color.WHITE);
|
||||
mTimerPaint.setTextAlign(Paint.Align.RIGHT);
|
||||
mTimerPaint.setTextSize(getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_TIME_TEXT_SIZE));
|
||||
|
||||
|
||||
}
|
||||
|
||||
private float measureCurrentScrollY(int line) {
|
||||
if (mEnableLineFeed && line > 1) {
|
||||
return (line - 1) * mLineHeight + mLineFeedRecord.get(line - 1);
|
||||
}
|
||||
return (line - 1) * mLineHeight;
|
||||
}
|
||||
|
||||
private void invalidateView() {
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||
// 当前线程是主UI线程,直接刷新。
|
||||
invalidate();
|
||||
} else {
|
||||
// 当前线程是非UI线程,post刷新。
|
||||
postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void measureLineHeight() {
|
||||
Rect lineBound = new Rect();
|
||||
mTextPaint.getTextBounds(mDefaultHint, 0, mDefaultHint.length(), lineBound);
|
||||
mTextHeight = lineBound.height();
|
||||
mLineHeight = mTextHeight + mLineSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* To measure current showing line number based on the view's scroll Y
|
||||
*/
|
||||
private void measureCurrentLine() {
|
||||
float baseScrollY = mScrollY + mLineHeight * 0.5f;
|
||||
|
||||
if (mEnableLineFeed) {
|
||||
for (int i = mLyricInfo.songLines.size(); i >= 0; i--) {
|
||||
if (baseScrollY > measureCurrentScrollY(i) + mLineSpace * 0.2) {
|
||||
mLineNumberUnderIndicator = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mLineNumberUnderIndicator = (int) (baseScrollY / mLineHeight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void smoothScrollTo(float toY) {
|
||||
final ValueAnimator animator = ValueAnimator.ofFloat(mScrollY, toY);
|
||||
animator.addUpdateListener(valueAnimator -> {
|
||||
mScrollY = (Float) valueAnimator.getAnimatedValue();
|
||||
invalidateView();
|
||||
});
|
||||
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
mFling = false;
|
||||
measureCurrentLine();
|
||||
invalidateView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
mFling = true;
|
||||
}
|
||||
});
|
||||
animator.setDuration(640);
|
||||
animator.setInterpolator(new LinearInterpolator());
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private boolean scrollable() {
|
||||
return mLyricInfo != null && mLyricInfo.songLines != null && mLyricInfo.songLines.size() > 0;
|
||||
}
|
||||
|
||||
private void scrollToCurrentTimeMillis(long time) {
|
||||
|
||||
int position = 0;
|
||||
if (scrollable()) {
|
||||
for (int i = 0, size = mLineCount; i < size; i++) {
|
||||
LineInfo lineInfo = mLyricInfo.songLines.get(i);
|
||||
if (lineInfo != null && lineInfo.start >= time) {
|
||||
position = i;
|
||||
break;
|
||||
}
|
||||
if (i == mLineCount - 1) {
|
||||
position = mLineCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mCurrentPlayLine != position) {
|
||||
mCurrentPlayLine = position;
|
||||
if (!mFling && !mUserTouch) {
|
||||
smoothScrollTo(measureCurrentScrollY(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupLyricResource(InputStream inputStream, String charsetName) {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
LyricInfo lyricInfo = new LyricInfo();
|
||||
lyricInfo.songLines = new ArrayList<>();
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charsetName);
|
||||
BufferedReader reader = new BufferedReader(inputStreamReader);
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
analyzeLyric(lyricInfo, line);
|
||||
}
|
||||
reader.close();
|
||||
inputStream.close();
|
||||
inputStreamReader.close();
|
||||
|
||||
mLyricInfo = lyricInfo;
|
||||
mLineCount = mLyricInfo.songLines.size();
|
||||
invalidateView();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
invalidateView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 逐行解析歌词内容
|
||||
*/
|
||||
private void analyzeLyric(LyricInfo lyricInfo, String line) {
|
||||
int index = line.lastIndexOf("]");
|
||||
if (line.startsWith("[offset:")) {
|
||||
// time offset
|
||||
lyricInfo.songOffset = Long.parseLong(line.substring(8, index).trim());
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("[ti:")) {
|
||||
// title
|
||||
lyricInfo.songTitle = line.substring(4, index).trim();
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("[ar:")) {
|
||||
// artist
|
||||
lyricInfo.songArtist = line.substring(4, index).trim();
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("[al:")) {
|
||||
// album
|
||||
lyricInfo.songAlbum = line.substring(4, index).trim();
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("[by:")) {
|
||||
return;
|
||||
}
|
||||
if (index >= 9 && line.trim().length() > index + 1) {
|
||||
// lyrics
|
||||
LineInfo lineInfo = new LineInfo();
|
||||
lineInfo.content = line.substring(10, line.length());
|
||||
lineInfo.start = measureStartTimeMillis(line.substring(0, index));
|
||||
lyricInfo.songLines.add(lineInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中获得时间值
|
||||
*/
|
||||
private long measureStartTimeMillis(String str) {
|
||||
long minute = Long.parseLong(str.substring(1, 3));
|
||||
long second = Long.parseLong(str.substring(4, 6));
|
||||
long millisecond = Long.parseLong(str.substring(7, 9));
|
||||
return millisecond + second * 1000 + minute * 60 * 1000;
|
||||
}
|
||||
|
||||
private void resetLyricInfo() {
|
||||
if (mLyricInfo != null) {
|
||||
if (mLyricInfo.songLines != null) {
|
||||
mLyricInfo.songLines.clear();
|
||||
mLyricInfo.songLines = null;
|
||||
}
|
||||
mLyricInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetView() {
|
||||
mCurrentPlayLine = 0;
|
||||
resetLyricInfo();
|
||||
invalidateView();
|
||||
mLineCount = 0;
|
||||
mScrollY = 0;
|
||||
mEnableLineFeed = false;
|
||||
mLineFeedRecord.clear();
|
||||
mExtraHeight = 0;
|
||||
}
|
||||
|
||||
private float getRawSize(int unit, float size) {
|
||||
Context context = getContext();
|
||||
Resources resources;
|
||||
if (context == null) {
|
||||
resources = Resources.getSystem();
|
||||
} else {
|
||||
resources = context.getResources();
|
||||
}
|
||||
return TypedValue.applyDimension(unit, size, resources.getDisplayMetrics());
|
||||
}
|
||||
|
||||
private void setRawTextSize(float size) {
|
||||
if (size != mTextPaint.getTextSize()) {
|
||||
mTextPaint.setTextSize(size);
|
||||
measureLineHeight();
|
||||
mScrollY = measureCurrentScrollY(mCurrentPlayLine);
|
||||
invalidateView();
|
||||
}
|
||||
}
|
||||
|
||||
@IntDef({LEFT, CENTER, RIGHT})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Alignment {
|
||||
}
|
||||
|
||||
public interface OnPlayerClickListener {
|
||||
void onPlayerClicked(long progress, String content);
|
||||
}
|
||||
|
||||
private class LyricInfo {
|
||||
List<LineInfo> songLines;
|
||||
|
||||
String songArtist;
|
||||
String songTitle;
|
||||
String songAlbum;
|
||||
|
||||
long songOffset;
|
||||
}
|
||||
|
||||
private class LineInfo {
|
||||
String content;
|
||||
long start;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue