-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add network for external camera with retrofit [#35]
- 임시로 `http` 사용 가능하도록 설정 - 일부 `data` 모듈에서 사용할 예정인 공용 코드는 `util`에 구현
- Loading branch information
Showing
15 changed files
with
392 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
domain/src/main/java/com/foke/together/domain/output/ExternalCameraRepositoryInterface.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.foke.together.domain.output | ||
|
||
import android.graphics.Bitmap | ||
|
||
interface ExternalCameraRepositoryInterface { | ||
fun setCameraSourceUrl(url: String) | ||
suspend fun capture(): Result<Bitmap> | ||
fun getPreviewUrl(): String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
external/src/main/java/com/foke/together/external/network/ExternalCameraApi.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.foke.together.external.network | ||
|
||
import okhttp3.ResponseBody | ||
import retrofit2.http.GET | ||
import retrofit2.http.Query | ||
|
||
interface ExternalCameraApi { | ||
@GET("/capture") | ||
suspend fun capture( | ||
@Query("timeout") timeout: Int? = null | ||
): Result<ResponseBody> | ||
} |
21 changes: 21 additions & 0 deletions
21
external/src/main/java/com/foke/together/external/network/ExternalCameraDataSource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.foke.together.external.network | ||
|
||
import com.foke.together.external.network.interceptor.BaseUrlInterceptor | ||
import okhttp3.ResponseBody | ||
import javax.inject.Inject | ||
|
||
class ExternalCameraDataSource @Inject constructor( | ||
private val externalCameraApi: ExternalCameraApi, | ||
private val baseUrlInterceptor: BaseUrlInterceptor | ||
){ | ||
fun setBaseUrl(url: String) { | ||
baseUrlInterceptor.setBaseUrl(url) | ||
} | ||
|
||
suspend fun capture(timeout: Int? = null): Result<ResponseBody> { | ||
return externalCameraApi.capture(timeout) | ||
} | ||
|
||
fun getPreviewUrl(): String = | ||
"${baseUrlInterceptor.getBaseUrl()}/preview" | ||
} |
61 changes: 61 additions & 0 deletions
61
external/src/main/java/com/foke/together/external/network/ExternalCameraModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.foke.together.external.network | ||
|
||
import com.foke.together.external.network.interceptor.BaseUrlInterceptor | ||
import com.foke.together.util.AppPolicy | ||
import com.foke.together.util.retrofit.NetworkCallAdapterFactory | ||
import com.google.gson.GsonBuilder | ||
import dagger.Module | ||
import dagger.Provides | ||
import dagger.hilt.InstallIn | ||
import dagger.hilt.components.SingletonComponent | ||
import okhttp3.OkHttpClient | ||
import retrofit2.Retrofit | ||
import retrofit2.converter.gson.GsonConverterFactory | ||
import java.util.concurrent.TimeUnit | ||
import javax.inject.Named | ||
import javax.inject.Singleton | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
object ExternalCameraModule { | ||
|
||
@Provides | ||
@Named("cameraIPUrl") | ||
// TODO: need to check in phase3; is it valid injection? | ||
fun provideCameraIPUrl(): String = AppPolicy.EXTERNAL_CAMERA_DEFAULT_SERVER_URL | ||
|
||
@Provides | ||
@Singleton | ||
fun provideBaseUrlInterceptor() = BaseUrlInterceptor() | ||
|
||
@Singleton | ||
@Provides | ||
fun provideOkHttpClient( | ||
baseUrlInterceptor: BaseUrlInterceptor | ||
) = OkHttpClient.Builder() | ||
.connectTimeout(AppPolicy.EXTERNAL_CAMERA_CONNECT_TIMEOUT, TimeUnit.SECONDS) | ||
.readTimeout(AppPolicy.EXTERNAL_CAMERA_READ_TIMEOUT, TimeUnit.SECONDS) | ||
.writeTimeout(AppPolicy.EXTERNAL_CAMERA_WRITE_TIMEOUT, TimeUnit.SECONDS) | ||
// .addInterceptor(MockInterceptor()) // TODO: add mock code for test | ||
.addInterceptor(baseUrlInterceptor) | ||
.build() | ||
|
||
@Singleton | ||
@Provides | ||
fun provideExternalCameraServerRetrofit( | ||
okHttpClient: OkHttpClient, | ||
@Named("cameraIPUrl") cameraIPUrl: String | ||
) = Retrofit.Builder() | ||
.baseUrl(cameraIPUrl) | ||
.addConverterFactory(GsonConverterFactory.create( | ||
GsonBuilder().setLenient().create() | ||
)) | ||
.addCallAdapterFactory(NetworkCallAdapterFactory()) | ||
.client(okHttpClient) | ||
.build() | ||
|
||
@Singleton | ||
@Provides | ||
fun provideApiService(retrofit: Retrofit): ExternalCameraApi = | ||
retrofit.create(ExternalCameraApi::class.java) | ||
} |
61 changes: 61 additions & 0 deletions
61
external/src/main/java/com/foke/together/external/network/interceptor/BaseUrlInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.foke.together.external.network.interceptor | ||
|
||
import com.foke.together.util.AppLog | ||
import com.foke.together.util.AppPolicy | ||
import okhttp3.Interceptor | ||
import okhttp3.Response | ||
|
||
class BaseUrlInterceptor: Interceptor { | ||
@Volatile | ||
private var baseUrl: String? = null | ||
@Volatile | ||
private var scheme: String? = null | ||
@Volatile | ||
private var host: String? = null | ||
@Volatile | ||
private var port: Int? = null | ||
|
||
fun getBaseUrl() = baseUrl ?: AppPolicy.EXTERNAL_CAMERA_DEFAULT_SERVER_URL | ||
|
||
fun setBaseUrl(newBaseUrl: String) { | ||
baseUrl = newBaseUrl | ||
try { | ||
val sep = newBaseUrl.indexOf("://") | ||
scheme = newBaseUrl.substring(0, sep) | ||
val newHost = newBaseUrl.substring(sep + 3, newBaseUrl.length) | ||
|
||
val pSep = newHost.lastIndexOf(":") | ||
port = if (pSep != -1) { | ||
host = newHost.substring(0, pSep) | ||
newHost.substring(pSep + 1, newHost.length).toInt() | ||
} else { | ||
host = newHost | ||
null | ||
} | ||
} catch (e: Exception) { | ||
AppLog.e(TAG, "setBaseUrl", "cannot parse newBaseUrl") | ||
} | ||
} | ||
|
||
override fun intercept(chain: Interceptor.Chain): Response { | ||
var request = chain.request() | ||
scheme?.let { s -> | ||
host?.let { h -> | ||
val newUrl = request.url.newBuilder().run { | ||
scheme(s) | ||
host(h) | ||
port?.let { port(it) } | ||
build() | ||
} | ||
request = request.newBuilder() | ||
.url(newUrl) | ||
.build() | ||
} | ||
} | ||
return chain.proceed(request) | ||
} | ||
|
||
companion object { | ||
private val TAG: String = BaseUrlInterceptor::class.java.simpleName | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
external/src/main/java/com/foke/together/external/network/interceptor/MockInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.foke.together.external.network.interceptor | ||
|
||
import okhttp3.Interceptor | ||
import okhttp3.Protocol | ||
import okhttp3.Response | ||
|
||
class MockInterceptor: Interceptor { | ||
override fun intercept(chain: Interceptor.Chain): Response { | ||
// TODO: make mock for test | ||
return Response.Builder() | ||
.request(chain.request()) | ||
.protocol(Protocol.HTTP_2) | ||
.build() | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
external/src/main/java/com/foke/together/external/repository/ExternalCameraRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.foke.together.external.repository | ||
|
||
import android.graphics.Bitmap | ||
import android.graphics.BitmapFactory | ||
import com.foke.together.domain.output.ExternalCameraRepositoryInterface | ||
import com.foke.together.external.network.ExternalCameraDataSource | ||
import javax.inject.Inject | ||
|
||
class ExternalCameraRepository @Inject constructor( | ||
private val externalCameraDataSource: ExternalCameraDataSource | ||
): ExternalCameraRepositoryInterface { | ||
override fun setCameraSourceUrl(url: String) { | ||
externalCameraDataSource.setBaseUrl(url) | ||
} | ||
|
||
override suspend fun capture(): Result<Bitmap> { | ||
return externalCameraDataSource.capture() | ||
.map { responseBody -> | ||
BitmapFactory.decodeStream(responseBody.byteStream()) | ||
} | ||
} | ||
|
||
override fun getPreviewUrl() = | ||
externalCameraDataSource.getPreviewUrl() | ||
|
||
companion object { | ||
private val TAG = ExternalCameraRepository::class.java.simpleName | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
external/src/main/java/com/foke/together/external/repository/di/RepositoryModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.foke.together.external.repository.di | ||
|
||
import com.foke.together.domain.output.ExternalCameraRepositoryInterface | ||
import com.foke.together.external.repository.ExternalCameraRepository | ||
import dagger.Binds | ||
import dagger.Module | ||
import dagger.hilt.InstallIn | ||
import dagger.hilt.android.components.ViewModelComponent | ||
|
||
@Module | ||
@InstallIn(ViewModelComponent::class) | ||
abstract class RepositoryModule { | ||
@Binds | ||
abstract fun bindAppPreferenceRepository( | ||
externalCameraRepository: ExternalCameraRepository | ||
): ExternalCameraRepositoryInterface | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,13 @@ | ||
package com.foke.together.util | ||
|
||
object AppPolicy { | ||
const val isDebugMode = true | ||
|
||
const val DEFAULT_EXTERNAL_CAMERA_IP = "0.0.0.0" | ||
|
||
// network | ||
const val EXTERNAL_CAMERA_DEFAULT_SERVER_URL = "http://0.0.0.0" | ||
const val EXTERNAL_CAMERA_CONNECT_TIMEOUT = 10L | ||
const val EXTERNAL_CAMERA_READ_TIMEOUT = 10L | ||
const val EXTERNAL_CAMERA_WRITE_TIMEOUT = 10L | ||
} |
81 changes: 81 additions & 0 deletions
81
util/src/main/java/com/foke/together/util/retrofit/NetworkCall.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package com.foke.together.util.retrofit | ||
|
||
import com.foke.together.util.AppLog | ||
import com.foke.together.util.AppPolicy | ||
import okhttp3.Request | ||
import okio.IOException | ||
import okio.Timeout | ||
import retrofit2.Call | ||
import retrofit2.Callback | ||
import retrofit2.Response | ||
|
||
class NetworkCall<T>( | ||
private val call: Call<T>, | ||
): Call<Result<T>> { | ||
override fun enqueue(callback: Callback<Result<T>>) { | ||
call.enqueue(object: Callback<T> { | ||
override fun onResponse(call: Call<T>, response: Response<T>) { | ||
if (AppPolicy.isDebugMode) { | ||
AppLog.e(TAG, "onResponse", "success: $response") | ||
} | ||
|
||
if (response.isSuccessful) { | ||
// success | ||
callback.onResponse( | ||
this@NetworkCall, | ||
Response.success(Result.success(response.body()!!)) | ||
) | ||
} else { | ||
// error | ||
callback.onResponse( | ||
this@NetworkCall, | ||
Response.success( | ||
// TODO: change to custom `error type` | ||
Result.failure(Exception()) | ||
) | ||
) | ||
} | ||
} | ||
|
||
override fun onFailure(call: Call<T>, t: Throwable) { | ||
if (AppPolicy.isDebugMode) { | ||
AppLog.e(TAG, "onResponse", "failure: $t") | ||
} | ||
|
||
when(t) { | ||
// TODO: define each error cases by `error type` | ||
is IOException -> Result.failure<T>(t) | ||
else -> Result.failure<T>(t) | ||
}.also { | ||
callback.onResponse(this@NetworkCall, Response.success(it)) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
override fun clone(): Call<Result<T>> = | ||
NetworkCall(call.clone()) | ||
|
||
override fun execute(): Response<Result<T>> = | ||
// TODO: check in case of null body | ||
Response.success(Result.success(call.execute().body()!!)) | ||
|
||
override fun isExecuted(): Boolean = | ||
call.isExecuted | ||
|
||
override fun cancel() = | ||
call.cancel() | ||
|
||
override fun isCanceled(): Boolean = | ||
call.isCanceled | ||
|
||
override fun request(): Request = | ||
call.request() | ||
|
||
override fun timeout(): Timeout = | ||
call.timeout() | ||
|
||
companion object { | ||
private val TAG: String = NetworkCall::class.java.simpleName | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
util/src/main/java/com/foke/together/util/retrofit/NetworkCallAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.foke.together.util.retrofit | ||
|
||
import retrofit2.Call | ||
import retrofit2.CallAdapter | ||
import java.lang.reflect.Type | ||
|
||
class NetworkCallAdapter<T>( | ||
private val responseType: Type | ||
): CallAdapter<T, Call<Result<T>>> { | ||
override fun responseType(): Type = | ||
responseType | ||
|
||
override fun adapt(call: Call<T>): Call<Result<T>> = | ||
// TODO: add NetworkCall for each web-server and external-camera | ||
NetworkCall(call) | ||
} |
Oops, something went wrong.