Downloading #57

Merged
kdeng00 merged 3 commits from downloading into master 2020-01-11 17:40:57 -05:00
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
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared", "-DANDROID=true"
@@ -38,6 +38,9 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
@@ -47,23 +50,41 @@ android {
}
dependencies {
def work_version = "2.2.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "org.jetbrains.anko:anko:0.10.8"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
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:ealvalog:0.5.0'
implementation 'com.ealva:ealvalog-core: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.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
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
+1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.mear">
<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) {
repository::local::UserRepository userRepo;
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.close();
song.path = downloadedSong.path;
song.downloaded = true;
repository::local::SongRepository localSongRepo(path);
if (!localSongRepo.doesTableExist(path)) {
localSongRepo.createSongTable(path);
}
localSongRepo.saveSong(downloadedSong, path);
}
@@ -524,6 +540,28 @@ Java_com_example_mear_repositories_TokenRepository_retrieveTokenRecord(
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"
JNIEXPORT jobject
JNICALL
@@ -684,6 +722,24 @@ Java_com_example_mear_repositories_TokenRepository_isTokenTableEmpty(
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"
JNIEXPORT jboolean
JNICALL
@@ -764,26 +820,6 @@ Java_com_example_mear_repositories_TokenRepository_saveTokenRecord(
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"
JNIEXPORT void
JNICALL
+39 -27
View File
@@ -22,7 +22,7 @@ namespace manager {
public:
template<typename Str = std::string>
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("/");
s.append(song.album);
@@ -51,7 +51,7 @@ namespace manager {
template<typename Str = std::string>
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("/");
s.append(song.album);
@@ -76,7 +76,7 @@ namespace manager {
template<typename Str = std::string>
Str artistPath(const Song& song, const Str& path) {
Str s = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
Str s = rootMusicDirectory(path);
s.append(song.albumArtist);
return s.c_str();
@@ -93,19 +93,18 @@ namespace manager {
template<typename Str = std::string, typename B = bool>
B deleteSong(const Song& song, const Str& path) {
auto s = fullSongPath(song, path);
auto result = std::remove(s.c_str());
const auto s = fullSongPath(song, path);
if (result == 0) {
return true;
} else {
return false;
}
return (remove(s.c_str()) == 0) ? true : false;
}
template<typename Str = std::string>
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)) {
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:
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>
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("/");
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());
if (dir) {
@@ -173,8 +169,9 @@ namespace manager {
}
template<typename Str = std::string, typename B = bool>
B artistDirectoryExists(const Song song, const Str& path) {
auto artistPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
B artistDirectoryExists(const Song& song, const Str& path) {
// auto artistPath = utility::GeneralUtility::appendForwardSlashToUri<std::string>(path);
auto artistPath = rootMusicDirectory<std::string>(path);
artistPath.append(song.albumArtist);
DIR* dir = opendir(artistPath.c_str());
@@ -187,6 +184,21 @@ namespace manager {
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);
curl_easy_cleanup(curl);
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;
}
@@ -3,10 +3,10 @@ package com.example.mear
import android.content.res.Configuration
import android.os.Bundle
import android.preference.PreferenceActivity
import android.support.annotation.LayoutRes
import android.support.v7.app.ActionBar
import android.support.v7.app.AppCompatDelegate
import android.support.v7.widget.Toolbar
import androidx.annotation.LayoutRes
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
@@ -5,10 +5,9 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.Environment
import android.os.IBinder
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.widget.Toast
import com.example.mear.R
@@ -1,8 +1,8 @@
package com.example.mear.activities
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.appcompat.widget.SearchView
import kotlinx.android.synthetic.main.content_song_view.*
@@ -48,7 +48,8 @@ class IcarusSongActivity : BaseServiceActivity() {
private fun initializeAdapter() {
try {
linearLayoutManager = LinearLayoutManager(this)
linearLayoutManager =
LinearLayoutManager(this)
trackList.layoutManager = linearLayoutManager
val pa = appDirectory()
@@ -57,7 +58,8 @@ class IcarusSongActivity : BaseServiceActivity() {
val apiRepo = APIRepository()
val token = tokenRepo.retrieveToken(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!!.sortedWith(compareBy{it.title})
@@ -5,7 +5,7 @@ import kotlinx.android.synthetic.main.content_login.*
import android.content.Intent
import android.os.Bundle
import android.support.design.widget.Snackbar
import com.google.android.material.snackbar.Snackbar
import org.jetbrains.anko.toast
import com.example.mear.models.*
@@ -16,6 +16,7 @@ class LoginActivity : BaseServiceActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.statusBarColor = resources.getColor(R.color.track_seek)
setContentView(R.layout.activity_login)
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_flow.*
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.view.MenuItem
import android.view.View
import android.widget.PopupMenu
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 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.ShuffleRepository.ShuffleTypes
import com.example.mear.util.ConvertByteArray
import org.jetbrains.anko.image
import com.example.mear.workers.IcarusSyncManager
class MainActivity : BaseServiceActivity() {
private val ctx: Context? = this
private var coverArtHandler: Handler? = Handler()
private var musicHandler: Handler? = Handler()
private var updateLibraryHandler: Handler? = Handler()
private var playCountUpdated: Boolean? = false
private var serviceBinded: Boolean? = false
private var metadataInitialized: Boolean = false
private var repeatOn: Boolean? = false
private var shuffleOn: Boolean? = false
@@ -335,22 +332,6 @@ class MainActivity : BaseServiceActivity() {
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) {
try {
@@ -371,30 +352,64 @@ class MainActivity : BaseServiceActivity() {
startActivity(Intent(this, IcarusSongActivity::class.java))
}
R.id.action_song_delete -> {
// TODO: handle song deletion
val ss = true
val appPath = appDirectory()
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 -> {
val appPath = appDirectory()
val apiRepo = APIRepository()
val tokenRepo = TokenRepository()
val trackRepo = TrackRepository()
val song = musicService!!.getCurrentSong()
var song = musicService!!.getCurrentSong()
val token = tokenRepo.retrieveToken(appPath)
val apiInfo = apiRepo.retrieveRecord(appPath)
trackRepo.download(token, song, appPath)
musicService!!.changeSongDownloadStatus()
// TODO: implement something to include the downloaded song into the songQueue
// essentially replacing the song that already exists, that way one can
// actually access the file without having to restart the app
Toast.makeText(this, "Downloading song", Toast.LENGTH_SHORT).show()
val constraints = androidx.work.Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED).build()
val data = Data.Builder()
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-> {
// TODO: not implemented
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()
}
*/
}
true
}
@@ -2,9 +2,7 @@ package com.example.mear.activities
import android.content.Context
import android.os.Bundle
import android.os.Environment
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView
import androidx.appcompat.app.AppCompatActivity;
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
@@ -13,11 +11,8 @@ import java.lang.Exception
import kotlinx.android.synthetic.main.activity_settings.*
import kotlinx.android.synthetic.main.content_settings.*
import com.example.mear.adapters.SettingsAdapter
import com.example.mear.R
import com.example.mear.management.MusicFiles
import com.example.mear.ui.popups.AboutPopup
import com.example.mear.repositories.TrackRepository
import kotlinx.android.synthetic.main.popup_layout.*
@@ -47,16 +42,6 @@ class SettingsActivity : AppCompatActivity() {
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 {
override fun onClick(v: View?) {
@@ -1,7 +1,7 @@
package com.example.mear.adapters
import android.app.Activity
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
@@ -1,6 +1,6 @@
package com.example.mear.adapters
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
@@ -3,7 +3,7 @@ package com.example.mear.adapters
import java.lang.Exception
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.ViewGroup
import android.widget.Filter
@@ -1,15 +1,10 @@
package com.example.mear
import android.content.Context
import android.support.annotation.LayoutRes
import androidx.annotation.LayoutRes
import android.view.LayoutInflater
import android.view.View
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 {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
@@ -1,6 +1,6 @@
package com.example.mear.holders
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import android.view.View
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) {
if (song.downloaded) {
offlinePlaySong(song)
@@ -77,6 +82,7 @@ class MusicService(var appPath: String = ""): Service() {
val uri = APIRepository.retrieveSongStreamUri(apiInfo, song)
val hddr = APIRepository.retrieveSongStreamHeader(token)
currentSong = song
currentSongIndex = songQueue.indexOfFirst { it.id == currentSong.id }
try {
trackPlayer!!.reset()
@@ -89,15 +95,29 @@ class MusicService(var appPath: String = ""): Service() {
}
}
fun changeSongDownloadStatus() {
currentSong.downloaded = true
songQueue.forEach {
s ->
if (s.id == currentSong.id) {
s.downloaded = true
fun changeSongDownloadStatus(song: Song) {
if (!song.downloaded) {
song.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) }
@@ -142,11 +162,8 @@ class MusicService(var appPath: String = ""): Service() {
if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) {
currentSongIndex = Random.nextInt(0, songQueue.size - 1)
} else if (!parseRepeatMode(repeatMode)) {
if (currentSongIndex == 0) {
currentSongIndex = songQueue.size - 1
} else {
currentSongIndex = currentSongIndex!! - 1
}
currentSongIndex = if (currentSongIndex == 0)
songQueue.size - 1 else currentSongIndex!! - 1
}
currentSong = songQueue[currentSongIndex!!]
@@ -184,12 +201,8 @@ class MusicService(var appPath: String = ""): Service() {
if (retrieveShuffleMode()!! && !parseRepeatMode(repeatMode)) {
currentSongIndex = Random.nextInt(0, songQueue.size - 1)
} else if (!parseRepeatMode(repeatMode)) {
if ((currentSongIndex!! + 1) == songQueue.size) {
currentSongIndex = 0
}
else {
currentSongIndex = currentSongIndex!! + 1
}
currentSongIndex = if ((currentSongIndex!! + 1) == songQueue.size)
0 else currentSongIndex!! + 1
}
currentSong = songQueue[currentSongIndex!!]
@@ -236,27 +249,23 @@ class MusicService(var appPath: String = ""): Service() {
val token = tokenRepo.retrieveToken(appPath)
val apiInfo = apiRepo.retrieveRecord(appPath)
// val songs = trackRepo.fetchSongs(token, apiInfo.uri)
val songs = trackRepo.fetchSongsIncludingDownloaded(token, apiInfo.uri, appPath)
songQueue = songs.toMutableList()
if (retrieveShuffleMode()!!) {
currentSongIndex = Random.nextInt(0, songs.size - 1)
} else {
currentSongIndex = 0
}
currentSongIndex = if (retrieveShuffleMode()!!)
Random.nextInt(0, songQueue.size - 1) else 0
currentSong = songQueue[currentSongIndex!!]
if (currentSong.downloaded) {
trackPlayer!!.setDataSource(currentSong.path)
}
else {
trackPlayer!!.setDataSource(this,
APIRepository.retrieveSongStreamUri(apiInfo, currentSong),
APIRepository.retrieveSongStreamHeader(token))
}
trackPlayer!!.prepareAsync()
trackPlayer!!.prepare()
trackPlayer!!.setOnCompletionListener {
playNextTrack()
}
@@ -269,37 +278,27 @@ class MusicService(var appPath: String = ""): Service() {
private fun retrieveShuffleMode(): Boolean? {
val shuffleRepo = ShuffleRepository(null)
val shuffleMode = shuffleRepo.shuffleMode(appPath)
var shuffleOn = false
when (shuffleMode) {
ShuffleTypes.ShuffleOn -> shuffleOn = true
ShuffleTypes.ShuffleOff -> shuffleOn = false
return when (shuffleRepo.shuffleMode(appPath)) {
ShuffleTypes.ShuffleOn -> true
ShuffleTypes.ShuffleOff -> false
else -> false
}
return shuffleOn
}
private fun parseRepeatMode(repeatMode: RepeatTypes): Boolean {
var repeatOn: Boolean? = null
when (repeatMode) {
RepeatTypes.RepeatOff -> repeatOn = false
RepeatTypes.RepeatSong -> repeatOn = true
return when (repeatMode) {
RepeatTypes.RepeatOff -> false
RepeatTypes.RepeatSong -> true
}
return repeatOn!!
}
private fun offlinePlaySong(song: Song) {
try {
currentSong = song
songQueue.forEach{ s ->
if (s.id == currentSong.id) {
s.downloaded = currentSong.downloaded
s.filename = currentSong.filename
s.path = currentSong.path
}
}
currentSongIndex = songQueue.indexOfFirst { it.id == song.id }
songQueue[currentSongIndex!!] = currentSong
trackPlayer!!.reset()
trackPlayer!!.setDataSource(song.path)
trackPlayer!!.prepare()
@@ -4,70 +4,8 @@ import android.content.Context
import org.jetbrains.anko.db.*
import com.example.mear.database
import com.example.mear.models.PlayCount
import com.example.mear.models.Track
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 com.example.mear.database
import com.example.mear.models.Song
import com.example.mear.models.Token
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 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> {
@@ -43,8 +43,11 @@ class TrackRepository(var context: Context? = null) {
downloadSong(token, song, uri)
}
fun download(token: Token, song: Song, path: String) {
downloadSong(token, song, path)
val s = 4
fun download(token: Token, song: Song, path: String): Song {
return downloadSong(token, song, path)
}
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.IcarusSongActivity">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -14,14 +14,14 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/color_background"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include
@@ -34,4 +34,4 @@
app:layout_constraintStart_toStartOf="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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.LoginActivity">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
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:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -30,4 +30,4 @@
<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"?>
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -8,7 +8,7 @@
android:background="@color/color_background"
tools:context=".activities.MainActivity">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -21,7 +21,7 @@
android:gravity="end"
android:orientation="horizontal">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="354dp"
android:layout_height="?attr/actionBarSize"
@@ -39,10 +39,10 @@
app:srcCompat="@drawable/ic_action_more_vert" />
</LinearLayout>
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include
android:id="@+id/include"
layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -1,5 +1,5 @@
<?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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -8,19 +8,19 @@
android:background="@color/color_background"
tools:context=".activities.SettingsActivity">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/color_background"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include
android:id="@+id/include3"
@@ -28,4 +28,4 @@
android:layout_width="match_parent"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -76,4 +76,4 @@
app:layout_constraintBottom_toTopOf="@+id/login"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -22,4 +22,4 @@
layout="@layout/fragment_play_controls"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -105,4 +105,4 @@
</LinearLayout>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -14,7 +14,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.SearchView
<androidx.appcompat.widget.SearchView
android:id="@+id/songSearch"
style="@style/SongSearchViewStyle"
android:layout_width="match_parent"
@@ -26,7 +26,7 @@
app:layout_constraintTop_toBottomOf="@+id/space2"
tools:layout_editor_absoluteX="8dp" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trackList"
marginTop="?android:attr/actionBarSize"
android:layout_width="wrap_content"
@@ -42,4 +42,4 @@
<!-- style="@style/SongSearchViewStyle" -->
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,5 @@
<?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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -43,4 +43,4 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
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"?>
<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"
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"
android:id="@+id/cv_settings"
android:layout_width="wrap_content"
@@ -70,7 +70,7 @@
</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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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:tools="http://schemas.android.com/tools"
android:id="@+id/cv"
@@ -42,7 +42,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<android.support.v7.widget.AppCompatImageView
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/trackCover"
android:layout_width="45dp"
android:layout_height="45dp"
@@ -75,6 +75,6 @@
</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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent">
@@ -19,4 +19,4 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.095"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent">
@@ -48,4 +48,4 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ArtistTitle"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent">
@@ -43,4 +43,4 @@
android:textColor="@color/track_details"
app:layout_constraintBottom_toBottomOf="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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent">
@@ -33,4 +33,4 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
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"?>
<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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -58,4 +58,4 @@
android:textAlignment="center"
android:textColor="@color/popup_text" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
-1
View File
@@ -19,7 +19,6 @@ allprojects {
repositories {
google()
jcenter()
}
}