diff --git a/app/app/src/debug/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt b/app/app/src/debug/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt index 5ed3b543a..8322d6b54 100644 --- a/app/app/src/debug/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt +++ b/app/app/src/debug/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt @@ -21,6 +21,9 @@ import com.github.livingwithhippos.unchained.data.remote.UnrestrictApiHelperImpl import com.github.livingwithhippos.unchained.data.remote.UserApi import com.github.livingwithhippos.unchained.data.remote.UserApiHelper import com.github.livingwithhippos.unchained.data.remote.UserApiHelperImpl +import com.github.livingwithhippos.unchained.data.remote.VariousApi +import com.github.livingwithhippos.unchained.data.remote.VariousApiHelper +import com.github.livingwithhippos.unchained.data.remote.VariousApiHelperImpl import com.github.livingwithhippos.unchained.utilities.BASE_AUTH_URL import com.github.livingwithhippos.unchained.utilities.BASE_URL import com.squareup.moshi.Moshi @@ -42,6 +45,12 @@ import javax.inject.Singleton @Module object ApiFactory { + /*********************************/ + // N.B. all updates to this code // + // also need to be ported to the // + // release build flavor version. // + /*********************************/ + @Provides @Singleton fun provideOkHttpClient(): OkHttpClient { @@ -159,4 +168,16 @@ object ApiFactory { @Singleton fun provideHostsApiHelper(apiHelper: HostsApiHelperImpl): HostsApiHelper = apiHelper + + // various api injection + @Provides + @Singleton + fun provideVariousApi(@ApiRetrofit retrofit: Retrofit): VariousApi { + return retrofit.create(VariousApi::class.java) + } + + @Provides + @Singleton + fun provideVariousApiHelper(apiHelper: VariousApiHelperImpl): VariousApiHelper = + apiHelper } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/authentication/viewmodel/AuthenticationViewModel.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/authentication/viewmodel/AuthenticationViewModel.kt index 75c10827e..2e79d84de 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/authentication/viewmodel/AuthenticationViewModel.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/authentication/viewmodel/AuthenticationViewModel.kt @@ -48,6 +48,7 @@ class AuthenticationViewModel @ViewModelInject constructor( * @param expireIn: the time in seconds before the deviceCode is not valid anymore for the secrets endpoint */ fun fetchSecrets(deviceCode: String, expireIn: Int) { + // 5 seconds is the value suggested by real debrid val waitTime = 5000L // this is just an estimate, keeping track of time would be more precise. As of now this value should be 120 var calls = (expireIn * 1000 / waitTime).toInt() - 10 @@ -55,7 +56,6 @@ class AuthenticationViewModel @ViewModelInject constructor( calls -= calls / 10 viewModelScope.launch { var secretData = authRepository.getSecrets(deviceCode) - secretLiveData.postValue(Event(secretData)) while (secretData?.clientId == null && calls-- > 0 && (getAuthState() != AuthenticationState.AUTHENTICATED || getAuthState() != AuthenticationState.AUTHENTICATED_NO_PREMIUM)) { delay(waitTime) secretData = authRepository.getSecrets(deviceCode) diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/base/MainActivity.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/base/MainActivity.kt index a5dca26bc..89d83b28f 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/base/MainActivity.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/base/MainActivity.kt @@ -245,8 +245,6 @@ class MainActivity : UnchainedActivity() { if (bottomNavManager?.onBackPressed()==false) { // check if we're in the home bottom bar, otherwise press back val bottomNav = findViewById(R.id.bottom_nav_view) - val asd = bottomNav.selectedItemId - val home = R.id.navigation_home if (bottomNav.selectedItemId != R.id.navigation_home) super.onBackPressed() else { diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/APIError.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/APIError.kt index be10fef08..795d359ca 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/APIError.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/APIError.kt @@ -8,6 +8,8 @@ import com.squareup.moshi.JsonClass data class APIError( @Json(name = "error") val error: String, + @Json(name = "error_details") + val errorDetails: String?, @Json(name = "error_code") val errorCode: Int? ) diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/Authentiication.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/Authentiication.kt index 38d7dda10..4fc3a2a58 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/Authentiication.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/Authentiication.kt @@ -42,7 +42,7 @@ data class Token( @Json(name = "access_token") val accessToken: String, @Json(name = "expires_in") - val expiresIn: String, + val expiresIn: Int, @Json(name = "token_type") val tokenType: String, @Json(name = "refresh_token") diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/NetworkResponse.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/NetworkResponse.kt index fb3ed48a3..252cf0e0a 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/NetworkResponse.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/model/NetworkResponse.kt @@ -13,4 +13,11 @@ sealed class NetworkResponse { data class Success(val data: T) : NetworkResponse() data class SuccessEmptyBody(val code: Int) : NetworkResponse() data class Error(val exception: Exception) : NetworkResponse() +} + +sealed class CompleteNetworkResponse { + data class Success(val data: T) : CompleteNetworkResponse() + data class SuccessEmptyBody(val code: Int) : CompleteNetworkResponse() + data class Error(val errorMessage: String) : CompleteNetworkResponse() + data class RDError(val error: U) : CompleteNetworkResponse() } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelper.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelper.kt index 7bf326d9b..ccb50fc82 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelper.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelper.kt @@ -16,10 +16,6 @@ interface AuthApiHelper { suspend fun getToken( clientId: String, clientSecret: String, - deviceCode: String + code: String ): Response - - suspend fun disableToken( - token: String - ): Response } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelperImpl.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelperImpl.kt index c0b11e657..9affde805 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelperImpl.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthApiHelperImpl.kt @@ -18,9 +18,6 @@ class AuthApiHelperImpl @Inject constructor(private val authenticationApi: Authe override suspend fun getToken( clientId: String, clientSecret: String, - deviceCode: String - ): Response = authenticationApi.getToken(clientId, clientSecret, deviceCode) - - override suspend fun disableToken(token: String): Response = - authenticationApi.disableToken(token) + code: String + ): Response = authenticationApi.getToken(clientId, clientSecret, code) } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthenticationApi.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthenticationApi.kt index e934928a4..257bb6f31 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthenticationApi.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/AuthenticationApi.kt @@ -38,15 +38,7 @@ interface AuthenticationApi { suspend fun getToken( @Field("client_id") clientId: String, @Field("client_secret") clientSecret: String, - @Field("code") deviceCode: String, + @Field("code") code: String, @Field("grant_type") grantType: String = OPEN_SOURCE_GRANT_TYPE ): Response - - /** - * Disable the current access token - */ - @GET("disable_access_token") - suspend fun disableToken( - @Header("Authorization") token: String - ): Response } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/DownloadApi.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/DownloadApi.kt index 6fc220595..b24a0f662 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/DownloadApi.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/DownloadApi.kt @@ -22,7 +22,7 @@ interface DownloadsApi { * @param limit Entries returned per page / request (must be within 0 and 100, default: 50) * @return a Response> a list of download items */ - @GET(" downloads") + @GET("downloads") suspend fun getDownloads( @Header("Authorization") token: String, @Query("offset") offset: Int?, diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/TorrentsApi.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/TorrentsApi.kt index b2941f272..1e79aff5c 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/TorrentsApi.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/TorrentsApi.kt @@ -65,7 +65,7 @@ interface TorrentsApi { @Query("offset") offset: Int? = 0, @Query("page") page: Int? = 1, @Query("limit") limit: Int? = 10, - @Query("filter ") filter: String? + @Query("filter") filter: String? ): Response> /** diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/UnrestrictApi.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/UnrestrictApi.kt index 0b30c8e36..fc1f679d2 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/UnrestrictApi.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/UnrestrictApi.kt @@ -37,7 +37,7 @@ interface UnrestrictApi { // Password to unlock the file access hoster side @Field("password") password: String? = null, // 0 or 1, use Remote traffic, dedicated servers and account sharing protections lifted - @Field("remote ") remote: Int? = null + @Field("remote") remote: Int? = null ): Response } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApi.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApi.kt new file mode 100644 index 000000000..f1f67ebc1 --- /dev/null +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApi.kt @@ -0,0 +1,18 @@ +package com.github.livingwithhippos.unchained.data.remote + +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Header + +/** + * This interface is used by Retrofit to manage various api calls not fitting elsewhere + */ +interface VariousApi { + /** + * Disable the current access token + */ + @GET("disable_access_token") + suspend fun disableToken( + @Header("Authorization") token: String + ): Response +} \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelper.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelper.kt new file mode 100644 index 000000000..2b4f3842c --- /dev/null +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelper.kt @@ -0,0 +1,7 @@ +package com.github.livingwithhippos.unchained.data.remote + +import retrofit2.Response + +interface VariousApiHelper { + suspend fun disableToken(token: String): Response +} \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelperImpl.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelperImpl.kt new file mode 100644 index 000000000..a34c2a034 --- /dev/null +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/remote/VariousApiHelperImpl.kt @@ -0,0 +1,9 @@ +package com.github.livingwithhippos.unchained.data.remote + +import retrofit2.Response +import javax.inject.Inject + +class VariousApiHelperImpl @Inject constructor(private val variousApi: VariousApi) : +VariousApiHelper { + override suspend fun disableToken(token: String): Response = variousApi.disableToken(token) +} \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/AuthenticationRepository.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/AuthenticationRepository.kt index 0109be725..66d65397b 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/AuthenticationRepository.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/AuthenticationRepository.kt @@ -23,55 +23,43 @@ class AuthenticationRepository @Inject constructor(private val apiHelper: AuthAp suspend fun getSecrets(code: String): Secrets? { - val authResponse = safeApiCall( + val secretResponse = safeApiCall( call = { apiHelper.getSecrets(deviceCode = code) }, errorMessage = "Error Fetching Secrets" ) - return authResponse; + return secretResponse; } - suspend fun getToken(clientId: String, clientSecret: String, deviceCode: String): Token? { + suspend fun getToken(clientId: String, clientSecret: String, code: String): Token? { - val authResponse = safeApiCall( + val tokenResponse = safeApiCall( call = { apiHelper.getToken( clientId = clientId, clientSecret = clientSecret, - deviceCode = deviceCode + code = code ) }, errorMessage = "Error Fetching Token" ) - return authResponse; + return tokenResponse; } - suspend fun disableToken(token: String): Any? { - - val authResponse = safeApiCall( - call = { apiHelper.disableToken("Bearer $token") }, - errorMessage = "Error disabling token" - ) - - return authResponse; - - } - - /** * Get a new open source Token that usually lasts one hour. * You can not use both offset and page at the same time, page is prioritized in case it happens. * @param clientId the client id obtained from the /device/credentials endpoint - * @param refreshCode the code obtained from the /token endpoint + * @param clientSecret the code obtained from the /token endpoint * @param deviceCode the device code obtained from the /device/code endpoint * @return the new Token */ - suspend fun refreshToken(clientId: String, refreshCode: String, deviceCode: String): Token? = - getToken(clientId, refreshCode, deviceCode) + suspend fun refreshToken(clientId: String, clientSecret: String, refreshToken: String): Token? = + getToken(clientId, clientSecret, refreshToken) suspend fun refreshToken(credentials: Credentials): Token? = - refreshToken(credentials.clientId!!, credentials.refreshToken!!, credentials.deviceCode) + refreshToken(credentials.clientId!!, credentials.clientSecret!!, credentials.refreshToken!!) } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/BaseRepository.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/BaseRepository.kt index 381a448d9..5430637ce 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/BaseRepository.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/BaseRepository.kt @@ -2,7 +2,11 @@ package com.github.livingwithhippos.unchained.data.repositoy import android.util.Log import com.github.livingwithhippos.unchained.BuildConfig +import com.github.livingwithhippos.unchained.data.model.APIError +import com.github.livingwithhippos.unchained.data.model.CompleteNetworkResponse import com.github.livingwithhippos.unchained.data.model.NetworkResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Callback @@ -15,25 +19,10 @@ import java.io.IOException */ open class BaseRepository { - suspend fun unsafeApiCall(call: suspend () -> Response, errorMessage: String): T? { - - val result: NetworkResponse = unsafeApiResult(call, errorMessage) - var data: T? = null - - when (result) { - is NetworkResponse.Success -> - data = result.data - is NetworkResponse.SuccessEmptyBody -> - if (BuildConfig.DEBUG) - Log.d("BaseRepository", "Successful call with empty body : ${result.code}") - is NetworkResponse.Error -> - if (BuildConfig.DEBUG) - Log.d("BaseRepository", errorMessage) - - } - - return data - } + //todo: inject this + private val jsonAdapter: JsonAdapter = Moshi.Builder() + .build() + .adapter(APIError::class.java) suspend fun safeApiCall(call: suspend () -> Response, errorMessage: String): T? { @@ -70,35 +59,6 @@ open class BaseRepository { return NetworkResponse.Error(IOException("Error Occurred while getting api result, error : $errorMessage")) } - private suspend fun unsafeApiResult( - call: suspend () -> Response, - errorMessage: String - ): NetworkResponse { - val response = call.invoke() - if (response.isSuccessful) { - val body = response.body() - if (body != null) - return NetworkResponse.Success(body) - else - return NetworkResponse.SuccessEmptyBody(response.code()) - } else { - //todo: implement error handling as JSON APIError class - /* - if (response.code() in 400..599) { - val moshi = Moshi.Builder().build() - val adapter: JsonAdapter = - moshi.adapter(APIError::class.java) - // todo: wrap with withContext(Dispatchers.IO)? - val apiError = adapter.fromJson(response.errorBody().toString()) - if (apiError!=null) - throw APIException(apiError) - }*/ - - } - - return NetworkResponse.Error(IOException("Error Occurred while getting api result, error : $errorMessage")) - } - suspend fun safeEmptyApiCall(call: suspend () -> Call, errorMessage: String): Int? { //fixme: this fun returns always -1 @@ -119,4 +79,27 @@ open class BaseRepository { return responseCode } + + public suspend fun errorApiResult( + call: suspend () -> Response, + errorMessage: String + ): CompleteNetworkResponse { + val response = call.invoke() + if (response.isSuccessful) { + val body = response.body() + if (body != null) + return CompleteNetworkResponse.Success(body) + else + return CompleteNetworkResponse.SuccessEmptyBody(response.code()) + } else { + try { + val error: APIError? = jsonAdapter.fromJson(response.errorBody()!!.string()) + return CompleteNetworkResponse.RDError(error) + } catch (e: IOException) { + e.printStackTrace() + } + } + + return CompleteNetworkResponse.Error(errorMessage) + } } \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/UnrestrictRepository.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/UnrestrictRepository.kt index 244cc404d..1370a5c8c 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/UnrestrictRepository.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/UnrestrictRepository.kt @@ -1,5 +1,6 @@ package com.github.livingwithhippos.unchained.data.repositoy +import com.github.livingwithhippos.unchained.data.model.CompleteNetworkResponse import com.github.livingwithhippos.unchained.data.model.DownloadItem import com.github.livingwithhippos.unchained.data.remote.UnrestrictApiHelper import kotlinx.coroutines.delay @@ -14,7 +15,7 @@ class UnrestrictRepository @Inject constructor(private val unrestrictApiHelper: remote: Int? = null ): DownloadItem? { - val linkResponse = unsafeApiCall( + val linkResponse = errorApiResult( call = { unrestrictApiHelper.getUnrestrictedLink( token = "Bearer $token", @@ -26,7 +27,24 @@ class UnrestrictRepository @Inject constructor(private val unrestrictApiHelper: errorMessage = "Error Fetching Unrestricted Link Info" ) - return linkResponse + when (linkResponse) { + is CompleteNetworkResponse.Success -> { + return linkResponse.data + } + is CompleteNetworkResponse.SuccessEmptyBody -> { + val code = linkResponse.code + return null + } + is CompleteNetworkResponse.RDError<*> -> { + val error = linkResponse.error + return null + } + is CompleteNetworkResponse.Error -> { + val message = linkResponse.errorMessage + return null + } + else -> return null + } } suspend fun getUnrestrictedLinkList( diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/VariousApiRepository.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/VariousApiRepository.kt new file mode 100644 index 000000000..abf994a4e --- /dev/null +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/data/repositoy/VariousApiRepository.kt @@ -0,0 +1,23 @@ +package com.github.livingwithhippos.unchained.data.repositoy + +import com.github.livingwithhippos.unchained.data.remote.VariousApiHelper +import javax.inject.Inject + +class VariousApiRepository @Inject constructor(private val variousApiHelper: VariousApiHelper) : + BaseRepository() { + + suspend fun disableToken(token: String): Unit? { + + val response = safeApiCall( + call = { + variousApiHelper.disableToken( + token = "Bearer $token" + ) + }, + errorMessage = "Error disabling token" + ) + + return response + } + +} \ No newline at end of file diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/newdownload/view/NewDownloadFragment.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/newdownload/view/NewDownloadFragment.kt index c7257d62e..8d3b0f18f 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/newdownload/view/NewDownloadFragment.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/newdownload/view/NewDownloadFragment.kt @@ -156,7 +156,7 @@ class NewDownloadFragment : UnchainedFragment(), NewDownloadListener { if (password.isNullOrBlank()) password = null val remote: Int? = - if (downloadBinding.switchRemote.isEnabled) REMOTE_TRAFFIC_ON else null + if (downloadBinding.switchRemote.isChecked) REMOTE_TRAFFIC_ON else null viewModel.fetchUnrestrictedLink( link, diff --git a/app/app/src/main/java/com/github/livingwithhippos/unchained/start/viewmodel/MainActivityViewModel.kt b/app/app/src/main/java/com/github/livingwithhippos/unchained/start/viewmodel/MainActivityViewModel.kt index 310f0978e..a30c238bf 100644 --- a/app/app/src/main/java/com/github/livingwithhippos/unchained/start/viewmodel/MainActivityViewModel.kt +++ b/app/app/src/main/java/com/github/livingwithhippos/unchained/start/viewmodel/MainActivityViewModel.kt @@ -14,9 +14,11 @@ import com.github.livingwithhippos.unchained.data.model.User import com.github.livingwithhippos.unchained.data.repositoy.AuthenticationRepository import com.github.livingwithhippos.unchained.data.repositoy.CredentialsRepository import com.github.livingwithhippos.unchained.data.repositoy.UserRepository +import com.github.livingwithhippos.unchained.data.repositoy.VariousApiRepository import com.github.livingwithhippos.unchained.lists.view.ListsTabFragment import com.github.livingwithhippos.unchained.utilities.Event import com.github.livingwithhippos.unchained.utilities.PRIVATE_TOKEN +import kotlinx.coroutines.delay import kotlinx.coroutines.launch /** @@ -27,7 +29,8 @@ class MainActivityViewModel @ViewModelInject constructor( @Assisted private val savedStateHandle: SavedStateHandle, private val authRepository: AuthenticationRepository, private val credentialRepository: CredentialsRepository, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val variousApiRepository: VariousApiRepository ) : ViewModel() { val authenticationState = MutableLiveData>() @@ -45,28 +48,48 @@ class MainActivityViewModel @ViewModelInject constructor( @SuppressLint("NullSafeMutableLiveData") fun fetchFirstWorkingCredentials() { viewModelScope.launch { + + var user: User? = null + val completeCredentials = credentialRepository .getAllCredentials() .filter { it.accessToken != null && it.clientId != null && it.clientSecret != null && it.deviceCode.isNotBlank() && it.refreshToken != null } - var user: User? = null + + if (completeCredentials.isNotEmpty()) { - val privateCredentials = - completeCredentials.firstOrNull { it.deviceCode == PRIVATE_TOKEN } - if (privateCredentials != null) { - user = checkCredentials(privateCredentials) + // step #1: test for private API token + completeCredentials.firstOrNull { it.deviceCode == PRIVATE_TOKEN }?.let{ + user = checkCredentials(it) } - // if the private token is not working this also gets triggered - if (user == null) - for (cred in completeCredentials) { - user = checkCredentials(cred) - if (user != null) { - break + // step #2: test for open source credentials + if (user == null) { + completeCredentials.firstOrNull { it.deviceCode != PRIVATE_TOKEN }?.let{ + authRepository.refreshToken(it)?.let { token -> + val newCredentials = Credentials( + it.deviceCode, + it.clientId, + it.clientSecret, + token.accessToken, + token.refreshToken + ) + + user = userRepository.getUserInfo(newCredentials.accessToken!!) + if (user != null) { + // update the credentials + credentialRepository.updateCredentials(newCredentials) + // program the refresh of the token + programTokenRefresh(token.expiresIn) + } } } + } + } - // passes null if no working credentials, otherwise pass the first working one + + // pass whatever user was retrieved, or null if none was found userLiveData.postValue(user) + } } @@ -102,7 +125,7 @@ class MainActivityViewModel @ViewModelInject constructor( credentialRepository.getFirstCredentials()?.let { if (it.refreshToken != null && it.refreshToken != PRIVATE_TOKEN) { //setUnauthenticated() - authRepository.disableToken(it.accessToken!!) + variousApiRepository.disableToken(it.accessToken!!) } } @@ -135,6 +158,9 @@ class MainActivityViewModel @ViewModelInject constructor( ) // update the credentials credentialRepository.updateCredentials(newCredentials) + + // program the refresh of the token + programTokenRefresh(it.expiresIn) } } } @@ -171,6 +197,15 @@ class MainActivityViewModel @ViewModelInject constructor( savedStateHandle.set(KEY_LAST_BACK_PRESS, time) } + fun programTokenRefresh(secondsDelay: Int) { + // todo: add job that is cancelled everytime this function is called + viewModelScope.launch { + // secondsDelay*950L -> expiration time - 5% + delay(secondsDelay * 950L) + refreshToken() + } + } + companion object { const val KEY_TORRENT_DOWNLOAD_ID = "torrent_download_id_key" const val KEY_TORRENT_PATH = "torrent_path_key" diff --git a/app/app/src/release/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt b/app/app/src/release/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt index 412c7024e..a42025117 100644 --- a/app/app/src/release/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt +++ b/app/app/src/release/java/com/github/livingwithhippos/unchained/di/ApiFactory.kt @@ -21,6 +21,9 @@ import com.github.livingwithhippos.unchained.data.remote.UnrestrictApiHelperImpl import com.github.livingwithhippos.unchained.data.remote.UserApi import com.github.livingwithhippos.unchained.data.remote.UserApiHelper import com.github.livingwithhippos.unchained.data.remote.UserApiHelperImpl +import com.github.livingwithhippos.unchained.data.remote.VariousApi +import com.github.livingwithhippos.unchained.data.remote.VariousApiHelper +import com.github.livingwithhippos.unchained.data.remote.VariousApiHelperImpl import com.github.livingwithhippos.unchained.utilities.BASE_AUTH_URL import com.github.livingwithhippos.unchained.utilities.BASE_URL import com.squareup.moshi.Moshi @@ -42,6 +45,12 @@ import javax.inject.Singleton @Module object ApiFactory { + /*********************************/ + // N.B. all updates to this code // + // also need to be ported to the // + // debug build flavor version. // + /*********************************/ + @Provides @Singleton fun provideOkHttpClient(): OkHttpClient = OkHttpClient() @@ -153,4 +162,16 @@ object ApiFactory { @Singleton fun provideHostsApiHelper(apiHelper: HostsApiHelperImpl): HostsApiHelper = apiHelper + + // various api injection + @Provides + @Singleton + fun provideVariousApi(@ApiRetrofit retrofit: Retrofit): VariousApi { + return retrofit.create(VariousApi::class.java) + } + + @Provides + @Singleton + fun provideVariousApiHelper(apiHelper: VariousApiHelperImpl): VariousApiHelper = + apiHelper } \ No newline at end of file