Merge pull request #57 from kdeng00/downloading

Downloading
This commit was merged in pull request #57.
This commit is contained in:
Kun Deng
2020-01-11 17:40:57 -05:00
committed by GitHub
42 changed files with 436 additions and 639 deletions
+71 -59
View File
File diff suppressed because one or more lines are too long
+35 -14
View File
@@ -18,7 +18,7 @@ android {
targetSdkVersion 28 targetSdkVersion 28
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DANDROID_STL=c++_shared", "-DANDROID=true" arguments "-DANDROID_STL=c++_shared", "-DANDROID=true"
@@ -38,6 +38,9 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path "src/main/cpp/CMakeLists.txt" path "src/main/cpp/CMakeLists.txt"
@@ -47,23 +50,41 @@ android {
} }
dependencies { dependencies {
def work_version = "2.2.0"
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61' // Kotlin + coroutines
implementation 'com.android.support:appcompat-v7:28.0.0' implementation "androidx.work:work-runtime-ktx:$work_version"
implementation 'com.android.support:support-v4:28.0.0' implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.android.support:design:28.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "org.jetbrains.anko:anko:0.10.8" implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.ealva:ealvatag:0.4.3' implementation 'com.ealva:ealvatag:0.4.3'
implementation 'com.ealva:ealvalog:0.5.0' implementation 'com.ealva:ealvalog:0.5.0'
implementation 'com.ealva:ealvalog-core:0.5.0' implementation 'com.ealva:ealvalog-core:0.5.0'
implementation 'com.ealva:ealvalog-android:0.5.0' implementation 'com.ealva:ealvalog-android:0.5.0'
implementation 'com.google.guava:guava:23.0'
implementation 'com.squareup.okio:okio:2.4.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.github.XuDeveloper:XPopupWindow:1.0.1' implementation 'com.github.XuDeveloper:XPopupWindow:1.0.1'
// implementation 'com.google.guava:guava:27.0'
implementation 'com.google.guava:guava:27.0.1-android'
implementation 'com.squareup.okio:okio:2.4.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation "org.jetbrains.anko:anko:0.10.8"
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'junit:junit:4.12'
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support') {
if (!requested.name.startsWith("multidex")) {
details.useVersion '27.0.0'
}
}
}
} }
@@ -1,7 +1,7 @@
package com.example.mear package com.example.mear
import android.support.test.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
+1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.mear"> package="com.example.mear">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+60 -24
View File
@@ -175,6 +175,24 @@ int retrieveShuffleMode(const std::string& path) {
} }
template<class Song = model::Song, typename Str = std::string, typename B = bool>
B deleteSong(Song& song, const Str& path) {
manager::DirectoryManager dirMgr;
if (!dirMgr.doesSongExist(song, path)) {
return false;
}
auto result = dirMgr.deleteSong(song, path);
if (!result) {
return result;
}
repository::local::SongRepository songRepo(path);
songRepo.deleteSongFromTable(song, path);
return result;
}
bool doesDatabaseExist(const std::string& dataPath) { bool doesDatabaseExist(const std::string& dataPath) {
repository::local::UserRepository userRepo; repository::local::UserRepository userRepo;
const auto result = userRepo.databaseExist(dataPath); const auto result = userRepo.databaseExist(dataPath);
@@ -285,11 +303,9 @@ void downloadSong(Song& song, const Token& token, const Str& path) {
saveSong.write((char*)&downloadedSong.data[0], downloadedSong.data.size()); saveSong.write((char*)&downloadedSong.data[0], downloadedSong.data.size());
saveSong.close(); saveSong.close();
song.path = downloadedSong.path;
song.downloaded = true;
repository::local::SongRepository localSongRepo(path); repository::local::SongRepository localSongRepo(path);
if (!localSongRepo.doesTableExist(path)) {
localSongRepo.createSongTable(path);
}
localSongRepo.saveSong(downloadedSong, path); localSongRepo.saveSong(downloadedSong, path);
} }
@@ -524,6 +540,28 @@ Java_com_example_mear_repositories_TokenRepository_retrieveTokenRecord(
return tokenObj; return tokenObj;
} }
extern "C"
JNIEXPORT jobject
JNICALL
Java_com_example_mear_repositories_TrackRepository_downloadSong(
JNIEnv *env,
jobject thisObj,
jobject tokenObj,
jobject songObj,
jstring pathStr
) {
auto tokenClass = env->GetObjectClass(tokenObj);
auto accessTokenId = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;");
auto accessTokenVal = (jstring) env->GetObjectField(tokenObj, accessTokenId);
model::Token token(env->GetStringUTFChars(accessTokenVal, nullptr));
auto song = ObjToSong<jobject, JNIEnv>(env, songObj);
auto path = env->GetStringUTFChars(pathStr, nullptr);
downloadSong(song, token, path);
return songToObj(env, song);
}
extern "C" extern "C"
JNIEXPORT jobject JNIEXPORT jobject
JNICALL JNICALL
@@ -684,6 +722,24 @@ Java_com_example_mear_repositories_TokenRepository_isTokenTableEmpty(
return doesTokenExist(dataPath); return doesTokenExist(dataPath);
} }
extern "C"
JNIEXPORT jboolean
JNICALL
Java_com_example_mear_repositories_TrackRepository_deleteSong(
JNIEnv *env,
jobject thisObj,
jobject songObj,
jstring pathStr
) {
auto songClass = env->GetObjectClass(songObj);
auto path = env->GetStringUTFChars(pathStr, nullptr);
auto song = ObjToSong(env, songObj);
const auto result = deleteSong(song, path);
return result;
}
extern "C" extern "C"
JNIEXPORT jboolean JNIEXPORT jboolean
JNICALL JNICALL
@@ -764,26 +820,6 @@ Java_com_example_mear_repositories_TokenRepository_saveTokenRecord(
saveToken(token, path); saveToken(token, path);
} }
extern "C"
JNIEXPORT void
JNICALL
Java_com_example_mear_repositories_TrackRepository_downloadSong(
JNIEnv *env,
jobject thisObj,
jobject tokenObj,
jobject songObj,
jstring pathStr
) {
auto tokenClass = env->GetObjectClass(tokenObj);
auto accessTokenId = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;");
auto accessTokenVal = (jstring) env->GetObjectField(tokenObj, accessTokenId);
model::Token token(env->GetStringUTFChars(accessTokenVal, nullptr));
auto song = ObjToSong<jobject, JNIEnv>(env, songObj);
auto path = env->GetStringUTFChars(pathStr, nullptr);
downloadSong(song, token, path);
}
extern "C" extern "C"
JNIEXPORT void JNIEXPORT void
JNICALL JNICALL
+39 -27
View File
@@ -22,7 +22,7 @@ namespace manager {
public: public:
template<typename Str = std::string> template<typename Str = std::string>
std::string fullSongPath(const Song& song, const Str& path) { std::string fullSongPath(const Song& song, const Str& path) {
std::string s = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path); std::string s = rootMusicDirectory(path);
s.append(song.albumArtist); s.append(song.albumArtist);
s.append("/"); s.append("/");
s.append(song.album); s.append(song.album);
@@ -51,7 +51,7 @@ namespace manager {
template<typename Str = std::string> template<typename Str = std::string>
Str albumPath(const Song& song, const Str& path) { Str albumPath(const Song& song, const Str& path) {
std::string s = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path); auto s = rootMusicDirectory<std::string>(path);
s.append(song.albumArtist); s.append(song.albumArtist);
s.append("/"); s.append("/");
s.append(song.album); s.append(song.album);
@@ -76,7 +76,7 @@ namespace manager {
template<typename Str = std::string> template<typename Str = std::string>
Str artistPath(const Song& song, const Str& path) { Str artistPath(const Song& song, const Str& path) {
Str s = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path); Str s = rootMusicDirectory(path);
s.append(song.albumArtist); s.append(song.albumArtist);
return s.c_str(); return s.c_str();
@@ -93,19 +93,18 @@ namespace manager {
template<typename Str = std::string, typename B = bool> template<typename Str = std::string, typename B = bool>
B deleteSong(const Song& song, const Str& path) { B deleteSong(const Song& song, const Str& path) {
auto s = fullSongPath(song, path); const auto s = fullSongPath(song, path);
auto result = std::remove(s.c_str());
if (result == 0) { return (remove(s.c_str()) == 0) ? true : false;
return true;
} else {
return false;
}
} }
template<typename Str = std::string> template<typename Str = std::string>
void createSongDirectory(const Song& song, const Str& path) { void createSongDirectory(const Song& song, const Str& path) {
if (!rootMusicDirectoryExists<std::string>(path)) {
const std::string p(rootMusicDirectory<std::string>(path));
auto status = mkdir(p.c_str(), 0777);
}
if (!artistDirectoryExists(song, path)) { if (!artistDirectoryExists(song, path)) {
auto status = mkdir(artistPath<std::string>(song, path).c_str(), 0777); auto status = mkdir(artistPath<std::string>(song, path).c_str(), 0777);
} }
@@ -117,27 +116,24 @@ namespace manager {
} }
} }
template<typename Str = std::string>
void deleteSongDirectory(const Song& song, const Str& path) {
}
private: private:
template<typename Str = std::string>
static auto rootMusicDirectory(const Str& path) {
auto rootPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
rootPath.append("music/");
return rootPath;
}
template<typename Str = std::string, typename B = bool> template<typename Str = std::string, typename B = bool>
B albumDirectoryExists(const Song song, const Str& path) { B albumDirectoryExists(const Song song, const Str& path) {
auto albumPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path); // auto albumPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
auto albumPath = rootMusicDirectory<std::string>(path);
albumPath.append(song.albumArtist); albumPath.append(song.albumArtist);
albumPath.append("/"); albumPath.append("/");
albumPath.append(song.album); albumPath.append(song.album);
/**
if (song.disc == 0) {
albumPath.append("/");
albumPath.append("disc1");
} else {
albumPath.append("/");
albumPath.append("disc");
albumPath.append(std::to_string(song.disc));
}
*/
DIR* dir = opendir(albumPath.c_str()); DIR* dir = opendir(albumPath.c_str());
if (dir) { if (dir) {
@@ -173,8 +169,9 @@ namespace manager {
} }
template<typename Str = std::string, typename B = bool> template<typename Str = std::string, typename B = bool>
B artistDirectoryExists(const Song song, const Str& path) { B artistDirectoryExists(const Song& song, const Str& path) {
auto artistPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path); // auto artistPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
auto artistPath = rootMusicDirectory<std::string>(path);
artistPath.append(song.albumArtist); artistPath.append(song.albumArtist);
DIR* dir = opendir(artistPath.c_str()); DIR* dir = opendir(artistPath.c_str());
@@ -187,6 +184,21 @@ namespace manager {
return true; return true;
} }
template<typename Str = std::string, typename B = bool>
B rootMusicDirectoryExists(const Str& path) {
auto rootPath = rootMusicDirectory<std::string>(path);
DIR *dir = opendir(rootPath.c_str());
if (dir) {
closedir(dir);
return true;
} else if (ENOENT == errno) {
return false;
}
return true;
}
}; };
} }
@@ -151,16 +151,6 @@ namespace repository {
auto res = curl_easy_perform(curl); auto res = curl_easy_perform(curl);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
downloadedSong.data = std::move(std::vector<char>(data.begin(), data.end())); downloadedSong.data = std::move(std::vector<char>(data.begin(), data.end()));
/**
downloadedSong.filename = "track";
if (song.track < 10) {
downloadedSong.filename.append("0");
downloadedSong.filename.append(std::to_string(song.track));
} else {
downloadedSong.filename.append(std::to_string(song.track));
}
downloadedSong.filename.append(".mp3");
*/
return downloadedSong; return downloadedSong;
} }
@@ -3,10 +3,10 @@ package com.example.mear
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceActivity import android.preference.PreferenceActivity
import android.support.annotation.LayoutRes import androidx.annotation.LayoutRes
import android.support.v7.app.ActionBar import androidx.appcompat.app.ActionBar
import android.support.v7.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import android.support.v7.widget.Toolbar import androidx.appcompat.widget.Toolbar
import android.view.MenuInflater import android.view.MenuInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -5,10 +5,9 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.widget.Toast import android.widget.Toast
import com.example.mear.R import com.example.mear.R
@@ -1,8 +1,8 @@
package com.example.mear.activities package com.example.mear.activities
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.SearchView import androidx.appcompat.widget.SearchView
import kotlinx.android.synthetic.main.content_song_view.* import kotlinx.android.synthetic.main.content_song_view.*
@@ -48,7 +48,8 @@ class IcarusSongActivity : BaseServiceActivity() {
private fun initializeAdapter() { private fun initializeAdapter() {
try { try {
linearLayoutManager = LinearLayoutManager(this) linearLayoutManager =
LinearLayoutManager(this)
trackList.layoutManager = linearLayoutManager trackList.layoutManager = linearLayoutManager
val pa = appDirectory() val pa = appDirectory()
@@ -57,7 +58,8 @@ class IcarusSongActivity : BaseServiceActivity() {
val apiRepo = APIRepository() val apiRepo = APIRepository()
val token = tokenRepo.retrieveToken(pa) val token = tokenRepo.retrieveToken(pa)
val apiInfo = apiRepo.retrieveRecord(pa) val apiInfo = apiRepo.retrieveRecord(pa)
val fetchedSongs = trackRepo.fetchSongsIncludingDownloaded(token, apiInfo.uri, pa).toCollection(ArrayList()) val fetchedSongs = trackRepo.fetchSongsIncludingDownloaded(token, apiInfo.uri, pa)
.toCollection(ArrayList())
songs = fetchedSongs songs = fetchedSongs
songs!!.sortedWith(compareBy{it.title}) songs!!.sortedWith(compareBy{it.title})
@@ -5,7 +5,7 @@ import kotlinx.android.synthetic.main.content_login.*
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.Snackbar import com.google.android.material.snackbar.Snackbar
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import com.example.mear.models.* import com.example.mear.models.*
@@ -16,6 +16,7 @@ class LoginActivity : BaseServiceActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
window.statusBarColor = resources.getColor(R.color.track_seek)
setContentView(R.layout.activity_login) setContentView(R.layout.activity_login)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
@@ -11,19 +11,20 @@ import kotlinx.android.synthetic.main.fragment_track_details.*
import kotlinx.android.synthetic.main.fragment_track_elapsing.* import kotlinx.android.synthetic.main.fragment_track_elapsing.*
import kotlinx.android.synthetic.main.fragment_track_flow.* import kotlinx.android.synthetic.main.fragment_track_flow.*
import android.Manifest
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.work.Data
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import org.jetbrains.anko.image
import org.jetbrains.anko.imageBitmap import org.jetbrains.anko.imageBitmap
import com.example.mear.listeners.TrackElaspingChange import com.example.mear.listeners.TrackElaspingChange
@@ -32,18 +33,14 @@ import com.example.mear.repositories.*
import com.example.mear.repositories.RepeatRepository.RepeatTypes import com.example.mear.repositories.RepeatRepository.RepeatTypes
import com.example.mear.repositories.ShuffleRepository.ShuffleTypes import com.example.mear.repositories.ShuffleRepository.ShuffleTypes
import com.example.mear.util.ConvertByteArray import com.example.mear.util.ConvertByteArray
import org.jetbrains.anko.image import com.example.mear.workers.IcarusSyncManager
class MainActivity : BaseServiceActivity() { class MainActivity : BaseServiceActivity() {
private val ctx: Context? = this
private var coverArtHandler: Handler? = Handler()
private var musicHandler: Handler? = Handler() private var musicHandler: Handler? = Handler()
private var updateLibraryHandler: Handler? = Handler()
private var playCountUpdated: Boolean? = false private var playCountUpdated: Boolean? = false
private var serviceBinded: Boolean? = false private var serviceBinded: Boolean? = false
private var metadataInitialized: Boolean = false
private var repeatOn: Boolean? = false private var repeatOn: Boolean? = false
private var shuffleOn: Boolean? = false private var shuffleOn: Boolean? = false
@@ -335,22 +332,6 @@ class MainActivity : BaseServiceActivity() {
return true return true
} }
// TODO: Might need this down the road for playing songs off an external
// storage
private fun permissionPrompt() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
}
else {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 0)
}
}
}
private fun showPopup(view: View) { private fun showPopup(view: View) {
try { try {
@@ -371,30 +352,64 @@ class MainActivity : BaseServiceActivity() {
startActivity(Intent(this, IcarusSongActivity::class.java)) startActivity(Intent(this, IcarusSongActivity::class.java))
} }
R.id.action_song_delete -> { R.id.action_song_delete -> {
// TODO: handle song deletion val appPath = appDirectory()
val ss = true val trackRepo = TrackRepository()
var currSong = musicService!!.getCurrentSong()
Toast.makeText(this, "Deleting song", Toast.LENGTH_SHORT).show()
val result = trackRepo.delete(currSong, appPath)
if (result) {
musicService!!.removeSongDownloadStatus(currSong)
}
} }
R.id.action_song_download -> { R.id.action_song_download -> {
val appPath = appDirectory() val appPath = appDirectory()
val apiRepo = APIRepository() var song = musicService!!.getCurrentSong()
val tokenRepo = TokenRepository()
val trackRepo = TrackRepository()
val song = musicService!!.getCurrentSong()
val token = tokenRepo.retrieveToken(appPath) Toast.makeText(this, "Downloading song", Toast.LENGTH_SHORT).show()
val apiInfo = apiRepo.retrieveRecord(appPath)
trackRepo.download(token, song, appPath) val constraints = androidx.work.Constraints.Builder()
musicService!!.changeSongDownloadStatus() .setRequiredNetworkType(NetworkType.CONNECTED).build()
// TODO: implement something to include the downloaded song into the songQueue
// essentially replacing the song that already exists, that way one can val data = Data.Builder()
// actually access the file without having to restart the app data.putString("appPath", appPath)
data.putInt("songId", song.id)
data.putString("songTitle", song.title)
data.putString("songArtist", song.artist)
data.putString("songAlbum", song.album)
data.putString("songAlbumArtist", song.albumArtist)
data.putString("songGenre", song.genre)
data.putInt("songYear", song.year)
data.putInt("songDuration", song.duration)
data.putInt("songCoverArtId", song.coverArtId)
data.putBoolean("songDownloaded", false)
data.putInt("songDisc", song.disc)
data.putInt("songTrack", song.track)
val task = OneTimeWorkRequest.Builder(IcarusSyncManager::class.java)
.setInputData(data.build())
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueue(task)
WorkManager.getInstance(this).getWorkInfoByIdLiveData(task.id)
.observe(this, Observer { info ->
if (info != null && info.state.isFinished) {
song.path = info.outputData.getString("songPath")!!
song.filename = info.outputData.getString("songFilename")!!
song.downloaded = info.outputData.getBoolean("songDownloaded", true)!!
musicService!!.changeSongDownloadStatus(song)
} }
})
}
/**
R.id.action_song_play_count-> { R.id.action_song_play_count-> {
// TODO: not implemented
val trk = musicService!!.getCurrentSong() val trk = musicService!!.getCurrentSong()
val pc = PlayCountRepository(this).getPlayCount(trk.id)
val playCount = pc.playCount
Toast.makeText(this, "Song played $playCount times", Toast.LENGTH_LONG).show() Toast.makeText(this, "Song played $playCount times", Toast.LENGTH_LONG).show()
} }
*/
} }
true true
} }
@@ -2,9 +2,7 @@ package com.example.mear.activities
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Environment import androidx.appcompat.app.AppCompatActivity;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
@@ -13,11 +11,8 @@ import java.lang.Exception
import kotlinx.android.synthetic.main.activity_settings.* import kotlinx.android.synthetic.main.activity_settings.*
import kotlinx.android.synthetic.main.content_settings.* import kotlinx.android.synthetic.main.content_settings.*
import com.example.mear.adapters.SettingsAdapter
import com.example.mear.R import com.example.mear.R
import com.example.mear.management.MusicFiles
import com.example.mear.ui.popups.AboutPopup import com.example.mear.ui.popups.AboutPopup
import com.example.mear.repositories.TrackRepository
import kotlinx.android.synthetic.main.popup_layout.* import kotlinx.android.synthetic.main.popup_layout.*
@@ -47,16 +42,6 @@ class SettingsActivity : AppCompatActivity() {
window.statusBarColor = resources.getColor(R.color.track_seek) window.statusBarColor = resources.getColor(R.color.track_seek)
} }
private fun loadSongPaths(): MutableList<String> {
val demoPath = Environment.getExternalStorageDirectory()
val mfPaths = MusicFiles(demoPath)
mfPaths.loadAllMusicPaths()
val allSongs = mfPaths.allSongs
return allSongs!!
}
class AboutListener(var layout: LinearLayout?, val ctx: Context): View.OnClickListener { class AboutListener(var layout: LinearLayout?, val ctx: Context): View.OnClickListener {
override fun onClick(v: View?) { override fun onClick(v: View?) {
@@ -1,7 +1,7 @@
package com.example.mear.adapters package com.example.mear.adapters
import android.app.Activity import android.app.Activity
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Filter import android.widget.Filter
@@ -1,6 +1,6 @@
package com.example.mear.adapters package com.example.mear.adapters
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -3,7 +3,7 @@ package com.example.mear.adapters
import java.lang.Exception import java.lang.Exception
import kotlinx.android.synthetic.main.fragment_song_view.view.* import kotlinx.android.synthetic.main.fragment_song_view.view.*
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Filter import android.widget.Filter
@@ -1,15 +1,10 @@
package com.example.mear package com.example.mear
import android.content.Context import androidx.annotation.LayoutRes
import android.support.annotation.LayoutRes
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.example.mear.management.DatabaseManager
val Context.database: DatabaseManager
get() = DatabaseManager.getInstance(applicationContext)
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View { fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
@@ -1,6 +1,6 @@
package com.example.mear.holders package com.example.mear.holders
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import com.example.mear.models.SettingItems import com.example.mear.models.SettingItems
@@ -1,93 +0,0 @@
package com.example.mear.management
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.example.mear.constants.ControlTypes
import java.lang.Exception
import org.jetbrains.anko.db.*
import org.jetbrains.anko.db.ManagedSQLiteOpenHelper
import com.example.mear.models.PlayControls
import com.example.mear.repositories.ShuffleRepository
class DatabaseManager(ctx: Context): ManagedSQLiteOpenHelper(ctx, "Mear",
null, 1) {
companion object {
private var instance: DatabaseManager? = null
@Synchronized
fun getInstance(ctx: Context): DatabaseManager {
if (instance == null) {
instance = DatabaseManager(ctx.applicationContext)
}
return instance!!
}
}
override fun onCreate(db: SQLiteDatabase?) {
try {
db!!.createTable(
"Track", true,
"Id" to INTEGER + PRIMARY_KEY + UNIQUE,
"Title" to TEXT, "Album" to TEXT, "Artist" to TEXT,
"Duration" to INTEGER, "FilePath" to TEXT
)
db!!.createTable(
"TrackCount", true,
"Id" to INTEGER + PRIMARY_KEY + UNIQUE,
"TotalSongs" to INTEGER
)
db!!.createTable(
"PlayCount", true,
"Id" to INTEGER + PRIMARY_KEY + AUTOINCREMENT,
"PlayCount" to INTEGER,
"TrackId" to INTEGER
)
db!!.createTable(
"Settings", true,
"Id" to INTEGER + PRIMARY_KEY + UNIQUE,
"DarkTheme" to org.jetbrains.anko.db.REAL
)
db!!.createTable(
"Shuffle", true,
"Id" to INTEGER + PRIMARY_KEY + UNIQUE,
"Mode" to TEXT
)
db!!.createTable(
"Repeat", true,
"Id" to INTEGER + PRIMARY_KEY + UNIQUE,
"Mode" to TEXT)
}
catch (ex: Exception) {
}
initializeShuffle(db)
initializeRepeat(db)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
try {
db!!.dropTable("Track")
db!!.dropTable("TrackCount")
db!!.dropTable("PlayCount")
db!!.dropTable("Settings")
db!!.dropTable("Shuffle")
db!!.dropTable("Repeat")
}
catch (ex: Exception) {
}
}
private fun initializeShuffle(db: SQLiteDatabase?) {
db!!.insert("Shuffle",
"Mode" to ControlTypes.SHUFFLE_OFF)
}
private fun initializeRepeat(db: SQLiteDatabase?) {
db!!.insert("Repeat",
"Mode" to ControlTypes.REPEAT_OFF)
}
}
@@ -1,123 +0,0 @@
package com.example.mear.management
import android.os.Environment
import com.example.mear.R
import java.io.File
import java.lang.Exception
import com.example.mear.constants.DirectoryIgnore
import com.example.mear.constants.FileTypes
import com.example.mear.constants.SongSearch
class MusicFiles (private val demoPath: File) {
var allSongs: MutableList<String>?= null
private val musicSongLimit = Int.MAX_VALUE
fun loadAllMusicPaths() {
try {
allSongs = mutableListOf()
val demoPath = this.demoPath.absoluteFile.toString() + "/music/"
val f = File(demoPath)
var count = 0
val folder = f
val listOfFiles = folder.listFiles()
for (i in listOfFiles) {
for (j in i.listFiles()) {
if (j.absolutePath.toString().contains("zip")) {
}
for (k in j.listFiles()) {
if (k.absolutePath.toString().contains("mp3")) {
if (count < musicSongLimit) {
allSongs!!.add(k.absolutePath.toString())
count++
}
else
{
break
}
}
}
}
}
}
catch (ex: Exception) {
var exMsg = ex.message
}
}
fun initialMp3Search() {
try {
var pathList: MutableList<String>?
pathList = mutableListOf()
var songsAdded = 0
var fileSearch = File(demoPath.absolutePath)
fileSearch.walkTopDown().forEach {
println(it.absolutePath)
if (!ignoreThisDirectory(it.absolutePath)) {
if (it.isFile) {
if (it.extension == (FileTypes.Mp3)) {
if (songsAdded >= SongSearch.INITIAL_SEARCH_AMOUNT) {
return
}
pathList.add(it.absolutePath)
allSongs = pathList
songsAdded = songsAdded.inc()
}
}
}
}
}
catch (ex: Exception) {
val exMsg = ex.message
}
}
fun searchForMp3Songs() {
try {
var pathList: MutableList<String>?
pathList = mutableListOf()
var fileSearch = File(demoPath.absolutePath)
fileSearch.walkTopDown().forEach {
println(it.absolutePath)
if (!ignoreThisDirectory(it.absolutePath)) {
if (it.isFile) {
if (it.extension == (FileTypes.Mp3)) {
pathList.add(it.absolutePath)
}
}
}
}
allSongs = pathList
}
catch (ex: Exception) {
val exMsg = ex.message
}
}
private fun ignoreThisDirectory(path: String): Boolean {
var ignoreDirectory = false
try {
if (path.contains(DirectoryIgnore.Android_Root)) {
ignoreDirectory = true
}
if (path.contains(DirectoryIgnore.Notifications)) {
ignoreDirectory = true
}
if (path.contains(DirectoryIgnore.Ringtones)) {
ignoreDirectory = true
}
return ignoreDirectory
}
catch (ex: Exception) {
val exMsg = ex.message
}
return ignoreDirectory
}
}
@@ -68,6 +68,11 @@ class MusicService(var appPath: String = ""): Service() {
} }
fun downloadSong(token: Token, song: Song, appPath: String) {
val trackRepo = TrackRepository()
val s = trackRepo.download(token, currentSong, appPath)
changeSongDownloadStatus(s)
}
fun icarusPlaySong(token: Token, song: Song, apiInfo: APIInfo) { fun icarusPlaySong(token: Token, song: Song, apiInfo: APIInfo) {
if (song.downloaded) { if (song.downloaded) {
offlinePlaySong(song) offlinePlaySong(song)
@@ -77,6 +82,7 @@ class MusicService(var appPath: String = ""): Service() {
val uri = APIRepository.retrieveSongStreamUri(apiInfo, song) val uri = APIRepository.retrieveSongStreamUri(apiInfo, song)
val hddr = APIRepository.retrieveSongStreamHeader(token) val hddr = APIRepository.retrieveSongStreamHeader(token)
currentSong = song currentSong = song
currentSongIndex = songQueue.indexOfFirst { it.id == currentSong.id }
try { try {
trackPlayer!!.reset() trackPlayer!!.reset()
@@ -89,15 +95,29 @@ class MusicService(var appPath: String = ""): Service() {
} }
} }
fun changeSongDownloadStatus() { fun changeSongDownloadStatus(song: Song) {
currentSong.downloaded = true if (!song.downloaded) {
songQueue.forEach { song.downloaded = true
s ->
if (s.id == currentSong.id) {
s.downloaded = true
} }
currentSong = song
currentSongIndex = songQueue.indexOfFirst { it.id == currentSong.id }
songQueue[currentSongIndex!!] = currentSong
val curPosition = currentPositionOfTrack()
trackPlayer!!.reset()
trackPlayer!!.setDataSource(currentSong.path)
trackPlayer!!.prepare()
trackPlayer!!.seekTo(curPosition)
trackPlayer!!.start()
} }
// songQueue[currentSongIndex!!].downloaded = true
fun removeSongDownloadStatus(song: Song) {
if (song.downloaded) {
song.downloaded = false
}
currentSong = song
currentSongIndex = songQueue.indexOfFirst { it.id == currentSong.id }
songQueue[currentSongIndex!!] = currentSong
} }
fun goToPosition(progress: Int) { trackPlayer!!.seekTo(progress) } fun goToPosition(progress: Int) { trackPlayer!!.seekTo(progress) }
@@ -142,11 +162,8 @@ class MusicService(var appPath: String = ""): Service() {
if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) { if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) {
currentSongIndex = Random.nextInt(0, songQueue.size - 1) currentSongIndex = Random.nextInt(0, songQueue.size - 1)
} else if (!parseRepeatMode(repeatMode)) { } else if (!parseRepeatMode(repeatMode)) {
if (currentSongIndex == 0) { currentSongIndex = if (currentSongIndex == 0)
currentSongIndex = songQueue.size - 1 songQueue.size - 1 else currentSongIndex!! - 1
} else {
currentSongIndex = currentSongIndex!! - 1
}
} }
currentSong = songQueue[currentSongIndex!!] currentSong = songQueue[currentSongIndex!!]
@@ -184,12 +201,8 @@ class MusicService(var appPath: String = ""): Service() {
if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) { if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) {
currentSongIndex = Random.nextInt(0, songQueue.size - 1) currentSongIndex = Random.nextInt(0, songQueue.size - 1)
} else if (!parseRepeatMode(repeatMode)) { } else if (!parseRepeatMode(repeatMode)) {
if ((currentSongIndex!! + 1) == songQueue.size) { currentSongIndex = if ((currentSongIndex!! + 1) == songQueue.size)
currentSongIndex = 0 0 else currentSongIndex!! + 1
}
else {
currentSongIndex = currentSongIndex!! + 1
}
} }
currentSong = songQueue[currentSongIndex!!] currentSong = songQueue[currentSongIndex!!]
@@ -236,27 +249,23 @@ class MusicService(var appPath: String = ""): Service() {
val token = tokenRepo.retrieveToken(appPath) val token = tokenRepo.retrieveToken(appPath)
val apiInfo = apiRepo.retrieveRecord(appPath) val apiInfo = apiRepo.retrieveRecord(appPath)
// val songs = trackRepo.fetchSongs(token, apiInfo.uri)
val songs = trackRepo.fetchSongsIncludingDownloaded(token, apiInfo.uri, appPath) val songs = trackRepo.fetchSongsIncludingDownloaded(token, apiInfo.uri, appPath)
songQueue = songs.toMutableList() songQueue = songs.toMutableList()
if (retrieveShuffleMode()!!) { currentSongIndex = if (retrieveShuffleMode()!!)
currentSongIndex = Random.nextInt(0, songs.size - 1) Random.nextInt(0, songQueue.size - 1) else 0
} else {
currentSongIndex = 0
}
currentSong = songQueue[currentSongIndex!!] currentSong = songQueue[currentSongIndex!!]
if (currentSong.downloaded) { if (currentSong.downloaded) {
trackPlayer!!.setDataSource(currentSong.path) trackPlayer!!.setDataSource(currentSong.path)
} }
else { else {
trackPlayer!!.setDataSource(this, trackPlayer!!.setDataSource(this,
APIRepository.retrieveSongStreamUri(apiInfo, currentSong), APIRepository.retrieveSongStreamUri(apiInfo, currentSong),
APIRepository.retrieveSongStreamHeader(token)) APIRepository.retrieveSongStreamHeader(token))
} }
trackPlayer!!.prepareAsync()
trackPlayer!!.prepare()
trackPlayer!!.setOnCompletionListener { trackPlayer!!.setOnCompletionListener {
playNextTrack() playNextTrack()
} }
@@ -269,37 +278,27 @@ class MusicService(var appPath: String = ""): Service() {
private fun retrieveShuffleMode(): Boolean? { private fun retrieveShuffleMode(): Boolean? {
val shuffleRepo = ShuffleRepository(null) val shuffleRepo = ShuffleRepository(null)
val shuffleMode = shuffleRepo.shuffleMode(appPath)
var shuffleOn = false
when (shuffleMode) { return when (shuffleRepo.shuffleMode(appPath)) {
ShuffleTypes.ShuffleOn -> shuffleOn = true ShuffleTypes.ShuffleOn -> true
ShuffleTypes.ShuffleOff -> shuffleOn = false ShuffleTypes.ShuffleOff -> false
else -> false
} }
return shuffleOn
} }
private fun parseRepeatMode(repeatMode: RepeatTypes): Boolean { private fun parseRepeatMode(repeatMode: RepeatTypes): Boolean {
var repeatOn: Boolean? = null return when (repeatMode) {
when (repeatMode) { RepeatTypes.RepeatOff -> false
RepeatTypes.RepeatOff -> repeatOn = false RepeatTypes.RepeatSong -> true
RepeatTypes.RepeatSong -> repeatOn = true
} }
return repeatOn!!
} }
private fun offlinePlaySong(song: Song) { private fun offlinePlaySong(song: Song) {
try { try {
currentSong = song currentSong = song
songQueue.forEach{ s -> currentSongIndex = songQueue.indexOfFirst { it.id == song.id }
if (s.id == currentSong.id) { songQueue[currentSongIndex!!] = currentSong
s.downloaded = currentSong.downloaded
s.filename = currentSong.filename
s.path = currentSong.path
}
}
trackPlayer!!.reset() trackPlayer!!.reset()
trackPlayer!!.setDataSource(song.path) trackPlayer!!.setDataSource(song.path)
trackPlayer!!.prepare() trackPlayer!!.prepare()
@@ -4,70 +4,8 @@ import android.content.Context
import org.jetbrains.anko.db.* import org.jetbrains.anko.db.*
import com.example.mear.database
import com.example.mear.models.PlayCount import com.example.mear.models.PlayCount
import com.example.mear.models.Track import com.example.mear.models.Track
class PlayCountRepository(val context: Context) { class PlayCountRepository(val context: Context) {
fun getAll(): List<PlayCount> = context.database.use {
val playCounts = mutableListOf<PlayCount>()
select("PlayCount" )
.parseList(object : MapRowParser<PlayCount>{
override fun parseRow(columns: Map<String, Any?>): PlayCount {
val id = columns.getValue("Id").toString().toInt()
val songPlayedAmount = columns.getValue("PlayCount").toString().toInt()
val trackId = columns.getValue("TrackId").toString().toInt()
val playCount = PlayCount(id, songPlayedAmount, trackId)
playCounts.add(playCount)
return playCount
}
})
playCounts
}
fun getPlayCount(id: Int): PlayCount = context.database.use {
select("PlayCount").where("Id = $id")
.parseSingle(object: MapRowParser<PlayCount>{
override fun parseRow(columns: Map<String, Any?>): PlayCount {
val id = columns.getValue("Id").toString().toInt()
val songPlayedAmount = columns.getValue("PlayCount").toString().toInt()
val trackId = columns.getValue("TrackId").toString().toInt()
val playCount = PlayCount(id, songPlayedAmount, trackId)
return playCount
}
})
}
fun insertPlayCounts(tracks: List<Track>) = context.database.use {
transaction {
var i = 0
for (track in tracks) {
insert("PlayCount", "Id" to i++,
"PlayCount" to 0, "TrackId" to track.id)
}
}
}
fun insertPlayCount(track: Track) = context.database.use {
insert("PlayCount",
"Id" to track.id,
"PlayCount" to 0,
"TrackId" to track.id)
}
fun updatePlayCount(playCount: PlayCount) = context.database.use {
update("PlayCount",
"PlayCount" to playCount.playCount + 1)
.where("Id = {playCountId}", "playCountId" to playCount.id).exec()
}
fun delete(table: PlayCount?) = context.database.use {
delete("PlayCount", whereClause = "id = {$table.id}")
}
fun delete() = context.database.use {
delete("PlayCount")
}
} }
@@ -1,41 +0,0 @@
package com.example.mear.repositories
import android.content.Context
import org.jetbrains.anko.db.*
import com.example.mear.database
import com.example.mear.models.Settings
class SettingRepository(val context: Context) {
fun getSettings(id: Int): Settings = context.database.use {
select("SettingsActivity").where("Id = $id")
.parseSingle(object: MapRowParser<Settings>{
override fun parseRow(columns: Map<String, Any?>): Settings {
val id = columns.getValue("Id").toString().toInt()
val darkTheme = columns.getValue("DarkTheme").toString().toBoolean()
val settings = Settings (id, darkTheme)
return settings
}
})
}
fun insertSettings(settings: Settings) = context.database.use {
insert("SettingsActivity",
"Id" to settings.id,
"DarkTheme" to false)
}
fun updateSettings(settings: Settings) = context.database.use {
update("SettingsActivity",
"DarkTheme" to settings.darkTheme)
.where("Id = {settingId}", "settingId" to settings.id).exec()
}
fun delete(table: Settings?) = context.database.use {
delete("SettingsActivity", whereClause = "id = {$table.id}")
}
fun delete() = context.database.use {
delete("SettingsActivity")
}
}
@@ -4,7 +4,6 @@ import android.content.Context
import org.jetbrains.anko.db.* import org.jetbrains.anko.db.*
import com.example.mear.database
import com.example.mear.models.Song import com.example.mear.models.Song
import com.example.mear.models.Token import com.example.mear.models.Token
import com.example.mear.models.Track import com.example.mear.models.Track
@@ -22,8 +21,9 @@ class TrackRepository(var context: Context? = null) {
private external fun retrieveSongsIncludingDownloaded(token: Token, uri: String, path: String): Array<Song> private external fun retrieveSongsIncludingDownloaded(token: Token, uri: String, path: String): Array<Song>
private external fun retrieveSong(token: Token, song: Song, uri: String): Song private external fun retrieveSong(token: Token, song: Song, uri: String): Song
private external fun downloadSong(token: Token, song: Song, path: String): Song
private external fun downloadSong(token: Token, song: Song, path: String) private external fun deleteSong(song: Song, path: String): Boolean
fun fetchSongs(token: Token, uri: String): Array<Song> { fun fetchSongs(token: Token, uri: String): Array<Song> {
@@ -43,8 +43,11 @@ class TrackRepository(var context: Context? = null) {
downloadSong(token, song, uri) downloadSong(token, song, uri)
} }
fun download(token: Token, song: Song, path: String) { fun download(token: Token, song: Song, path: String): Song {
downloadSong(token, song, path) return downloadSong(token, song, path)
val s = 4 }
fun delete(song: Song, path: String): Boolean {
return deleteSong(song, path)
} }
} }
@@ -0,0 +1,46 @@
package com.example.mear.workers
import android.content.Context
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.example.mear.models.Song
import com.example.mear.repositories.TokenRepository
import com.example.mear.repositories.TrackRepository
class IcarusSyncManager(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
val appPath: String = inputData.getString("appPath")!!
val songId = inputData.getInt("songId", 0)
if (songId == 0) {
return Result.failure()
}
val tokenRepo = TokenRepository()
val trackRepo = TrackRepository()
val tok = tokenRepo.retrieveToken(appPath)
val song = Song()
song.id = songId
song.title = inputData.getString("songTitle")!!
song.artist = inputData.getString("songArtist")!!
song.album = inputData.getString("songAlbum")!!
song.albumArtist = inputData.getString("songAlbumArtist")!!
song.genre = inputData.getString("songGenre")!!
song.year = inputData.getInt("songYear", 0)
song.duration = inputData.getInt("songDuration", 0)
song.coverArtId = inputData.getInt("songCoverArtId", 0)
song.disc = inputData.getInt("songDisc", 0)
song.track = inputData.getInt("songTrack", 0)
val downloadedSong = trackRepo.download(tok, song, appPath)
val output: Data = workDataOf("songPath" to downloadedSong.path,
"songFilename" to downloadedSong.filename,
"songDownloaded" to true)
return Result.success(output)
}
}
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".activities.IcarusSongActivity"> tools:context=".activities.IcarusSongActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar" android:id="@+id/app_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -14,14 +14,14 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="@color/color_background" android:background="@color/color_background"
app:popupTheme="@style/AppTheme.PopupOverlay" /> app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<include <include
@@ -34,4 +34,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+6 -6
View File
@@ -1,26 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".activities.LoginActivity"> tools:context=".activities.LoginActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"> android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" /> app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<android.support.design.widget.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -30,4 +30,4 @@
<include layout="@layout/content_login" /> <include layout="@layout/content_login" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
+5 -5
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@@ -8,7 +8,7 @@
android:background="@color/color_background" android:background="@color/color_background"
tools:context=".activities.MainActivity"> tools:context=".activities.MainActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -21,7 +21,7 @@
android:gravity="end" android:gravity="end"
android:orientation="horizontal"> android:orientation="horizontal">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="354dp" android:layout_width="354dp"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
@@ -39,10 +39,10 @@
app:srcCompat="@drawable/ic_action_more_vert" /> app:srcCompat="@drawable/ic_action_more_vert" />
</LinearLayout> </LinearLayout>
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<include <include
android:id="@+id/include" android:id="@+id/include"
layout="@layout/content_main" /> layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -8,19 +8,19 @@
android:background="@color/color_background" android:background="@color/color_background"
tools:context=".activities.SettingsActivity"> tools:context=".activities.SettingsActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"> android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="@color/color_background" android:background="@color/color_background"
app:popupTheme="@style/AppTheme.PopupOverlay" /> app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<include <include
android:id="@+id/include3" android:id="@+id/include3"
@@ -28,4 +28,4 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -76,4 +76,4 @@
app:layout_constraintBottom_toTopOf="@+id/login" app:layout_constraintBottom_toTopOf="@+id/login"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -22,4 +22,4 @@
layout="@layout/fragment_play_controls" layout="@layout/fragment_play_controls"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -105,4 +105,4 @@
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -14,7 +14,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.SearchView <androidx.appcompat.widget.SearchView
android:id="@+id/songSearch" android:id="@+id/songSearch"
style="@style/SongSearchViewStyle" style="@style/SongSearchViewStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -26,7 +26,7 @@
app:layout_constraintTop_toBottomOf="@+id/space2" app:layout_constraintTop_toBottomOf="@+id/space2"
tools:layout_editor_absoluteX="8dp" /> tools:layout_editor_absoluteX="8dp" />
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/trackList" android:id="@+id/trackList"
marginTop="?android:attr/actionBarSize" marginTop="?android:attr/actionBarSize"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -42,4 +42,4 @@
<!-- style="@style/SongSearchViewStyle" --> <!-- style="@style/SongSearchViewStyle" -->
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -43,4 +43,4 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@android:drawable/ic_media_next" /> app:srcCompat="@android:drawable/ic_media_next" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_height="match_parent"> android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/cv_settings" android:id="@+id/cv_settings"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -70,7 +70,7 @@
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp"> android:padding="8dp">
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cv" android:id="@+id/cv"
@@ -42,7 +42,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<android.support.v7.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/trackCover" android:id="@+id/trackCover"
android:layout_width="45dp" android:layout_width="45dp"
android:layout_height="45dp" android:layout_height="45dp"
@@ -75,6 +75,6 @@
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"> android:layout_width="match_parent" android:layout_height="match_parent">
@@ -19,4 +19,4 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.095" app:layout_constraintVertical_bias="0.095"
tools:srcCompat="@tools:sample/backgrounds/scenic[1]" /> tools:srcCompat="@tools:sample/backgrounds/scenic[1]" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"> android:layout_width="match_parent" android:layout_height="match_parent">
@@ -48,4 +48,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ArtistTitle" app:layout_constraintTop_toBottomOf="@+id/ArtistTitle"
app:layout_constraintVertical_bias="0.0" /> app:layout_constraintVertical_bias="0.0" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"> android:layout_width="match_parent" android:layout_height="match_parent">
@@ -43,4 +43,4 @@
android:textColor="@color/track_details" android:textColor="@color/track_details"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"> android:layout_width="match_parent" android:layout_height="match_parent">
@@ -33,4 +33,4 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.826" /> app:layout_constraintVertical_bias="0.826" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -58,4 +58,4 @@
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/popup_text" /> android:textColor="@color/popup_text" />
</LinearLayout> </LinearLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
-1
View File
@@ -19,7 +19,6 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
} }
} }