diff --git a/README.md b/README.md index 6cb9955..7fd12a5 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,13 @@ Has not been updated yet ## Built with * [Kotlin](https://github.com/JetBrains/kotlin) -* Android Studio +* C++ 14 * Gradle +* CMake +* curl +* openssl +* [json](https://github.com/nlohmann/json) +* [SQLiteCpp](https://github.com/SRombauts/SQLiteCpp) * [anko](https://github.com/Kotlin/anko) * [Picasso](https://github.com/square/picasso) diff --git a/app/src/main/cpp/Demo.cpp b/app/src/main/cpp/Demo.cpp index 53eb973..d5ada1f 100644 --- a/app/src/main/cpp/Demo.cpp +++ b/app/src/main/cpp/Demo.cpp @@ -35,6 +35,9 @@ std::vector retrieveSongs(const model::Token&, const std::string&); jobject songToObj(JNIEnv *env, const model::Song&); model::Song retrieveSong(const std::string&, const std::string&, const int); +model::Song retrieveSong(const model::Token&, const model::Song&, const std::string&); + +model::Token fettchToken(const model::User&, const std::string&); model::User retrieveCredentials(const std::string&); @@ -50,9 +53,8 @@ std::vector retrieveSongs(const model::Token& token, const std::str { std::vector songs; repository::SongRepository songRepo; - songs = songRepo.fetchSongs(token, baseUri); - return songs; + return songRepo.fetchSongs(token, baseUri); } @@ -96,10 +98,24 @@ model::Song retrieveSong(const std::string& token, const std::string& baseUri, c model::Song song(songId, "Smooth", "Amaze", "The Herb", "Blazing", 420, 420); - song = songRepo.retrieveSong(token, baseUri, song); - auto songs = songRepo.fetchSongs(model::Token(token), baseUri); + return songRepo.retrieveSong(token, song, baseUri); +} - return song; +model::Song retrieveSong(const model::Token& token, const model::Song& song, + const std::string& baseUri) +{ + repository::SongRepository songRepo; + + return songRepo.retrieveSong(token, song, baseUri); +} + + +model::Token fetchToken(const model::User& user, const std::string& apiUri) +{ + manager::Tok tokMgr; + auto token = tokMgr.fetchTokenTrans(user, apiUri); + + return token; } @@ -112,6 +128,7 @@ model::User retrieveCredentials(const std::string& dataPath) } +[[deprecated("use fetchToken(const model::User, std::string&")]] std::string fetchToken(const std::string& username, const std::string& password, const std::string& apiUri) { @@ -141,6 +158,7 @@ bool userCredentialExist(const std::string& dataPath) } +[[deprecated("c++ 17 issues")]] void iterateDirectory(const std::string& path) { /** @@ -178,21 +196,24 @@ void saveCredentials(const model::User& user, const std::string& appDirectory) extern "C" JNIEXPORT jobjectArray JNICALL -Java_com_example_mear_activities_IcarusSongActivity_retrieveSongs( +Java_com_example_mear_repositories_TrackRepository_retrieveSongs( JNIEnv *env, - jobject thisOnj, + jobject thisOnj, jobject token, jstring baseUri ) { - jclass songClass = env->FindClass( "com/example/mear/models/Song"); jclass tokenClass = env->FindClass("com/example/mear/models/Token"); - jobjectArray songs = env->NewObjectArray(2, songClass, nullptr); - auto tok = env->CallObjectMethod(token, env->GetMethodID(tokenClass, "setAccessToken", "(Ljava/lang/String;)V")); - std::string tokStr = env->GetStringUTFChars((jstring)tok, nullptr); + jclass songClass = env->FindClass( "com/example/mear/models/Song"); + + auto tokField = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;"); + auto tokObj = env->GetObjectField(token, tokField); + std::string tokStr = env->GetStringUTFChars((jstring)tokObj, nullptr); model::Token tk(tokStr); + const std::string uri = env->GetStringUTFChars(baseUri, nullptr); auto allSongs = retrieveSongs(tk, uri); + jobjectArray songs = env->NewObjectArray(allSongs.size(), songClass, nullptr); for (auto i = 0; i != allSongs.size(); ++i) { auto song = songToObj(env, allSongs[i]); env->SetObjectArrayElement(songs, i, song); @@ -213,8 +234,8 @@ Java_com_example_mear_activities_LoginActivity_retrieveSong( jint idOfSong ) { - const std::string tok = env->GetStringUTFChars(token, NULL); - const std::string baseUri = env->GetStringUTFChars(apiUri, NULL); + const std::string tok = env->GetStringUTFChars(token, nullptr); + const std::string baseUri = env->GetStringUTFChars(apiUri, nullptr); auto song = retrieveSong(tok, baseUri, idOfSong); jclass songClass = env->FindClass( "com/example/mear/models/Song"); @@ -248,6 +269,35 @@ Java_com_example_mear_activities_LoginActivity_retrieveSong( return songObj; } +extern "C" +JNIEXPORT jobject +JNICALL +Java_com_example_mear_repositories_TrackRepository_retrieveSong( + JNIEnv *env, + jobject thisObj, + jobject tokenObj, + jobject songObj, + jstring uriStr + ) { + auto tokenClass = env->GetObjectClass(tokenObj); + auto songClass = env->GetObjectClass(songObj); + + auto accessTokenField = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;"); + auto idField = env->GetFieldID(songClass, "id", "I"); + + auto accessTokenStr = (jstring)env->GetObjectField(tokenObj, accessTokenField); + auto idInt = env->GetIntField(songObj, idField); + + auto uri = env->GetStringUTFChars(uriStr, nullptr); + model::Token token(env->GetStringUTFChars(accessTokenStr, nullptr)); + model::Song song(idInt); + + song = retrieveSong(token, song, uri); + auto fetchedSongObj = songToObj(env, song); + + return fetchedSongObj; +} + extern "C" JNIEXPORT jobject JNICALL @@ -284,20 +334,17 @@ Java_com_example_mear_repositories_UserRepository_logUser( jobject user, jstring apiUri ) { jclass userClass = env->GetObjectClass(user); - //auto tok = env->CallObjectMethod(token, env->GetMethodID(tokenClass, "setAccessToken", "(Ljava/lang/String;)V")); - auto passwordField = env->GetFieldID(userClass, "getPassword", "(Ljava/lang/String;)V"); - //auto passwordM = env->CallObjectMethod(user, env->GetMethodID(userClass, "password", "(Ljava/lang/String;)V")); - //auto passwordMOne = env->CallObjectMethod(user, env->GetMethodID(userClass, "getPassword", "(Ljava/lang/String;)V")); - auto password = env->GetObjectField(user, passwordField); - //auto username = env->CallObjectMethod(user, env->GetMethodID(userClass, "setUsername", "(Ljava/lang/String;)V")); - auto usernameField = env->GetFieldID(userClass, "setUsername", "(Ljava/lang/String;)V"); - auto username = env->GetObjectField(user, usernameField); + auto passwordField = env->GetFieldID(userClass, "password", "Ljava/lang/String;"); + auto usernameField = env->GetFieldID(userClass, "username", "Ljava/lang/String;"); + + auto password = (jstring)env->GetObjectField(user, passwordField); + auto username = (jstring)env->GetObjectField(user, usernameField); - const std::string usr = env->GetStringUTFChars((jstring)username, nullptr); - const std::string pass = env->GetStringUTFChars((jstring)password, nullptr); const std::string api = env->GetStringUTFChars(apiUri, nullptr); + model::User us(env->GetStringUTFChars(username, nullptr), + env->GetStringUTFChars(password, nullptr)); - model::Token token(fetchToken(usr, pass, api)); + auto token = fetchToken(us, api); jclass tokenClass = env->FindClass( "com/example/mear/models/Token"); jmethodID jconstructor = env->GetMethodID( tokenClass, "", "()V" ); @@ -322,10 +369,11 @@ Java_com_example_mear_activities_LoginActivity_logUser( jstring username, jstring password, jstring apiUri ) { - - const std::string usr = env->GetStringUTFChars(username, NULL); - const std::string pass = env->GetStringUTFChars(password, NULL); - const std::string api = env->GetStringUTFChars(apiUri, NULL); + model::User user(env->GetStringUTFChars(username, nullptr), + env->GetStringUTFChars(password, nullptr)); + const std::string usr = env->GetStringUTFChars(username, nullptr); + const std::string pass = env->GetStringUTFChars(password, nullptr); + const std::string api = env->GetStringUTFChars(apiUri, nullptr); auto token = fetchToken(usr, pass, api); @@ -341,10 +389,9 @@ Java_com_example_mear_repositories_UserRepository_isUserTableEmpty( jobject thisObj, jstring dataPath ) { - const std::string dataPathStr = env->GetStringUTFChars(dataPath, NULL); - jboolean result = userCredentialExist(dataPathStr); + const std::string dataPathStr = env->GetStringUTFChars(dataPath, nullptr); - return result; + return userCredentialExist(dataPathStr); } extern "C" @@ -355,7 +402,7 @@ Java_com_example_mear_repositories_BaseRepository_doesDatabaseExist( jobject thidObj, jstring dataPath ) { - const std::string dataPathStr = env->GetStringUTFChars(dataPath, NULL); + const std::string dataPathStr = env->GetStringUTFChars(dataPath, nullptr); jboolean result = doesDatabaseExist(dataPathStr); return result; @@ -365,32 +412,24 @@ Java_com_example_mear_repositories_BaseRepository_doesDatabaseExist( extern "C" JNIEXPORT void JNICALL -Java_com_example_mear_activities_LoginActivity_pathIteratorDemo( +Java_com_example_mear_repositories_UserRepository_saveUserCredentials( JNIEnv *env, jobject thisObj, - jstring path - ) { - - const std::string pathStr = env->GetStringUTFChars(path, NULL); - iterateDirectory(pathStr); -} - - -extern "C" -JNIEXPORT void -JNICALL -Java_com_example_mear_activities_LoginActivity_saveUserCredentials( - JNIEnv *env, - jobject thisObj, - jstring username, - jstring password, + jobject user, jstring appDirectory ) { - const std::string usernameStr = env->GetStringUTFChars(username, NULL); - const std::string passwordStr = env->GetStringUTFChars(password, NULL); - const std::string appDirectoryStr = env->GetStringUTFChars(appDirectory, NULL); + jclass userClass = env->GetObjectClass(user); + auto usernameId = env->GetFieldID(userClass, "username", "Ljava/lang/String;"); + auto passwordId = env->GetFieldID(userClass, "password", "Ljava/lang/String;"); - model::User user(usernameStr, passwordStr); + auto usernameStr = (jstring) env->GetObjectField(user, usernameId); + auto passwordStr = (jstring) env->GetObjectField(user, passwordId); - saveCredentials(user, appDirectoryStr); + const std::string username = env->GetStringUTFChars(usernameStr, nullptr); + const std::string password = env->GetStringUTFChars(passwordStr, nullptr); + const std::string appDirectoryStr = env->GetStringUTFChars(appDirectory, nullptr); + + model::User usr(username, password); + + saveCredentials(usr, appDirectoryStr); } diff --git a/app/src/main/cpp/SongRepository.cpp b/app/src/main/cpp/SongRepository.cpp index 90830d2..04c234d 100644 --- a/app/src/main/cpp/SongRepository.cpp +++ b/app/src/main/cpp/SongRepository.cpp @@ -59,6 +59,7 @@ namespace repository { } + [[deprecated]] model::Song SongRepository::retrieveSong(const std::string& token, const int id) { std::string uri(""); uri.append(std::to_string(id)); @@ -104,6 +105,7 @@ namespace repository { return song; } + [[deprecated]] model::Song SongRepository::retrieveSong(const std::string& token, const std::string& baseUri, const int id) { std::string uri(baseUri); @@ -151,6 +153,7 @@ namespace repository { return song; } + [[deprecated]] model::Song SongRepository::retrieveSong(const std::string& token, const std::string& baseUri, const model::Song& sng) { std::string uri(baseUri); @@ -198,6 +201,53 @@ namespace repository { return sng; } + model::Song SongRepository::retrieveSong(const model::Token& token, const model::Song& sng, + const std::string& baseUri) { + std::string uri(baseUri); + uri.append("/api/v1/song/"); + uri.append(std::to_string(sng.id)); + model::Song song; + + CURL *curl; + CURLcode res; + struct curl_slist *chunk = NULL; + curl = curl_easy_init(); + + if (!curl) { + return sng; + } + + auto resp = std::make_unique(10000); + + std::string authInfo("Authorization: Bearer "); + authInfo.append(token.accessToken); + chunk = curl_slist_append(chunk, authInfo.c_str()); + + curl_easy_setopt(curl, CURLOPT_URL, uri.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp.get()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + if (res == CURLE_OK) { + auto s = nlohmann::json::parse(resp.get()); + song.id = s["id"].get(); + song.title = s["title"].get(); + song.album = s["album"].get(); + song.artist = s["artist"].get(); + song.genre = s["genre"].get(); + song.duration = s["duration"].get(); + song.year = s["year"].get(); + + return song; + } + + return sng; + } + size_t SongRepository::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e) { diff --git a/app/src/main/cpp/SongRepository.h b/app/src/main/cpp/SongRepository.h index 8f7ba08..9056778 100644 --- a/app/src/main/cpp/SongRepository.h +++ b/app/src/main/cpp/SongRepository.h @@ -20,6 +20,7 @@ namespace repository { model::Song retrieveSong(const std::string&, const int); model::Song retrieveSong(const std::string&, const std::string&, const int); model::Song retrieveSong(const std::string&, const std::string&, const model::Song&); + model::Song retrieveSong(const model::Token&, const model::Song&, const std::string&); private: static size_t respBodyRetriever(void*, size_t, size_t, char*); }; diff --git a/app/src/main/cpp/Tok.cpp b/app/src/main/cpp/Tok.cpp index 0105850..7697076 100644 --- a/app/src/main/cpp/Tok.cpp +++ b/app/src/main/cpp/Tok.cpp @@ -11,6 +11,40 @@ #include namespace manager { + model::Token Tok::fetchTokenTrans(const model::User &user, const std::string& uri) { + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + + if (!curl) { + return model::Token("none"); + } + + auto resp = std::make_unique(10000); + + const auto loginUri = fetchLoginUri(uri); + const auto obj = userJsonString(user); + + curl_easy_setopt(curl, CURLOPT_URL, loginUri.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, obj.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp.get()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + if (res == CURLE_OK) { + auto s = nlohmann::json::parse(resp.get()); + + return model::Token(s["token"].get()); + } + + return model::Token("failure"); + } + + std::string Tok::fetchToken(const model::User &user, const std::string& uri) { CURL *curl; CURLcode res; diff --git a/app/src/main/cpp/Tok.h b/app/src/main/cpp/Tok.h index aab9fe1..f959853 100644 --- a/app/src/main/cpp/Tok.h +++ b/app/src/main/cpp/Tok.h @@ -8,12 +8,15 @@ #include #include +#include "model/Token.h" #include "model/User.h" namespace manager { class Tok { public: + model::Token fetchTokenTrans(const model::User&, const std::string&); + std::string fetchToken(const model::User&, const std::string&); private: std::string fetchLoginUri(const std::string&); diff --git a/app/src/main/cpp/model/Song.h b/app/src/main/cpp/model/Song.h index de4f710..f7090d6 100644 --- a/app/src/main/cpp/model/Song.h +++ b/app/src/main/cpp/model/Song.h @@ -14,6 +14,7 @@ class Song { public: Song() = default; + Song(const int id) : id(id) { } Song(const int id, const std::string& title, const std::string& artist, const std::string& album, const std::string& genre, const int duration, const int year) : diff --git a/app/src/main/cpp/model/Token.h b/app/src/main/cpp/model/Token.h index 02665a6..2bb97d1 100644 --- a/app/src/main/cpp/model/Token.h +++ b/app/src/main/cpp/model/Token.h @@ -12,6 +12,7 @@ namespace model { class Token { public: + Token() = default; Token(const std::string& accessToken) : accessToken(accessToken) { } Token(const std::string&& accessToken) : accessToken(std::move(accessToken)) { } diff --git a/app/src/main/java/com/example/mear/activities/LoginActivity.kt b/app/src/main/java/com/example/mear/activities/LoginActivity.kt index 719d23e..5a69509 100644 --- a/app/src/main/java/com/example/mear/activities/LoginActivity.kt +++ b/app/src/main/java/com/example/mear/activities/LoginActivity.kt @@ -12,31 +12,13 @@ import org.jetbrains.anko.toast import com.example.mear.models.Song import com.example.mear.models.Token +import com.example.mear.models.Track import com.example.mear.models.User +import com.example.mear.repositories.TrackRepository import com.example.mear.repositories.UserRepository class LoginActivity : BaseServiceActivity() { - companion object { - init { - System.loadLibrary("native-lib") - } - } - - - private external fun retrieveSong(tok: String, api: String, songId: Int): Song - - //private external fun retrieveUserCredentials(path: String): User - - private external fun logUser(usr: String, pass: String, api: String): String - - //private external fun doesDatabaseExist(path: String): Boolean - //private external fun isUserTableEmpty(path: String): Boolean - - private external fun pathIteratorDemo(path: String) - private external fun saveUserCredentials(username: String, password: String, appDir: String) - - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) @@ -73,27 +55,23 @@ class LoginActivity : BaseServiceActivity() { } val saveCred = saveUserCred.isChecked - var apiUriStr = apiUri.text.toString() - val usr = User() - usr.username = username.text.toString() - usr.password = password.text.toString() + val usr = User(username.text.toString(), password.text.toString()) if (apiUriStr.isEmpty()) { - apiUriStr = "https://www.soaricarus.com" + apiUriStr = "" } - val myToken = Token(logUser(usr.username, usr.password, apiUriStr)) - //val token = logUser(usernameStr, passwordStr, apiUriStr) val usrRepo = UserRepository() - //val newToken = usrRepo.fetchToken(usr, apiUriStr) + val trackRepo = TrackRepository() + val myToken = usrRepo.fetchToken(usr, apiUriStr) try { - val songId = 4 - val song = retrieveSong(myToken.accessToken, apiUriStr, songId) val pa = appDirectory() + val so = Song(5) + val song = trackRepo.fetchSong(myToken, so, apiUriStr) if (saveCred && usrRepo.isTableEmpty(pa)) { - saveUserCredentials(usr.username, usr.password, pa) + usrRepo.saveCredentials(usr, pa) } //startActivity(Intent(this, IcarusSongActivity::class.java)) } diff --git a/app/src/main/java/com/example/mear/repositories/TokenRepository.kt b/app/src/main/java/com/example/mear/repositories/TokenRepository.kt new file mode 100644 index 0000000..c910cff --- /dev/null +++ b/app/src/main/java/com/example/mear/repositories/TokenRepository.kt @@ -0,0 +1,44 @@ +package com.example.mear.repositories + +import com.example.mear.models.Token + +class TokenRepository: BaseRepository() { + + companion object { + init { + System.loadLibrary("native-lib") + } + } + + + private external fun retrieveTokenRecord(path: String): Token + + private external fun isTokeTableEmpty(path: String): Boolean + + private external fun saveTokenRecord(token: Token, path: String) + private external fun deleteTokenRecord(path: String) + private external fun updateTokenRecord(token: Token, path: String) + + + fun retrieveToken(path: String): Token { + return retrieveTokenRecord(path) + } + + + fun isTableEmpty(path: String): Boolean { + return isTokeTableEmpty(path) + } + + + fun saveToken(token: Token, path: String) { + return saveTokenRecord(token, path) + } + + fun deleteToken(path: String) { + return deleteTokenRecord(path) + } + + fun updateToken(token: Token, path: String) { + return updateTokenRecord(token, path) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mear/repositories/TrackRepository.kt b/app/src/main/java/com/example/mear/repositories/TrackRepository.kt index 24d2d06..69c93be 100644 --- a/app/src/main/java/com/example/mear/repositories/TrackRepository.kt +++ b/app/src/main/java/com/example/mear/repositories/TrackRepository.kt @@ -5,14 +5,31 @@ 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 -class TrackRepository { - private var context: Context? = null +class TrackRepository(var context: Context? = null) { - constructor(context: Context) - { - this.context = context + companion object { + init { + System.loadLibrary("native-lib") + } + } + + + private external fun retrieveSongs(token: Token, uri: String): Array + + private external fun retrieveSong(token: Token, song: Song, uri: String): Song + + + fun fetchSongs(token: Token, uri: String): Array { + return retrieveSongs(token, uri) + } + + + fun fetchSong(token: Token, song: Song, uri: String): Song { + return retrieveSong(token, song, uri) } fun getAll(): List = context!!.database.use { diff --git a/app/src/main/java/com/example/mear/repositories/UserRepository.kt b/app/src/main/java/com/example/mear/repositories/UserRepository.kt index 3db3548..e8c3ecb 100644 --- a/app/src/main/java/com/example/mear/repositories/UserRepository.kt +++ b/app/src/main/java/com/example/mear/repositories/UserRepository.kt @@ -11,13 +11,11 @@ class UserRepository : BaseRepository() { private external fun isUserTableEmpty(path: String): Boolean - private external fun saveUserCredentials(username: User) + private external fun saveUserCredentials(username: User, path: String) fun fetchToken(user: User, apiUri: String): Token { - val usr = logUser(user, apiUri) - - return usr + return logUser(user, apiUri) } @@ -31,7 +29,7 @@ class UserRepository : BaseRepository() { } - fun saveCredentials(user: User) { - saveUserCredentials(user) + fun saveCredentials(user: User, path: String) { + saveUserCredentials(user, path) } } \ No newline at end of file