KOIN 설정 적용
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package kr.co.vividnext.sodalive.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.orhanobut.logger.AndroidLogAdapter
|
||||
import com.orhanobut.logger.Logger
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.di.AppDI
|
||||
|
||||
class SodaLiveApp : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Logger.addLogAdapter(object : AndroidLogAdapter() {
|
||||
override fun isLoggable(priority: Int, tag: String?): Boolean {
|
||||
return BuildConfig.DEBUG && isDebuggable()
|
||||
}
|
||||
})
|
||||
|
||||
AppDI(applicationContext, BuildConfig.DEBUG && isDebuggable())
|
||||
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
|
||||
SharedPreferenceManager.init(applicationContext)
|
||||
}
|
||||
|
||||
private fun isDebuggable(): Boolean {
|
||||
var debuggable = false
|
||||
|
||||
try {
|
||||
val appInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
packageManager.getApplicationInfo(
|
||||
packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(0L)
|
||||
)
|
||||
} else {
|
||||
packageManager.getApplicationInfo(packageName, 0)
|
||||
}
|
||||
debuggable = 0 != appInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
/* debuggable variable will remain false */
|
||||
}
|
||||
|
||||
return debuggable
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
object Constants {
|
||||
}
|
@@ -0,0 +1,171 @@
|
||||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
|
||||
object RealPathUtil {
|
||||
fun getRealPath(context: Context, fileUri: Uri): String? {
|
||||
return getRealPathFromURIAPI19(context, fileUri) // SDK > 19 (Android 4.4) and up
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||
* Framework Documents, as well as the _data field for the MediaStore and
|
||||
* other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @author Niks
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun getRealPathFromURIAPI19(context: Context, uri: Uri): String? {
|
||||
// DocumentProvider
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
if ("primary".equals(type, ignoreCase = true)) {
|
||||
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
|
||||
}
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = context.contentResolver.query(
|
||||
uri,
|
||||
arrayOf(MediaStore.MediaColumns.DISPLAY_NAME),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
cursor!!.moveToNext()
|
||||
val fileName = cursor.getString(0)
|
||||
val path = Environment.getExternalStorageDirectory()
|
||||
.toString() + "/Download/" + fileName
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
return path
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
val id = DocumentsContract.getDocumentId(uri)
|
||||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:".toRegex(), "")
|
||||
}
|
||||
val contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads"),
|
||||
java.lang.Long.valueOf(id)
|
||||
)
|
||||
|
||||
return getDataColumn(context, contentUri, null, null)
|
||||
} else if (isMediaDocument(uri)) {
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
var contentUri: Uri? = null
|
||||
when (type) {
|
||||
"image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
"video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
"audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
|
||||
val selection = "_id=?"
|
||||
val selectionArgs = arrayOf(split[1])
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs)
|
||||
} // MediaProvider
|
||||
// DownloadsProvider
|
||||
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
|
||||
|
||||
// Return the remote address
|
||||
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
|
||||
context,
|
||||
uri,
|
||||
null,
|
||||
null
|
||||
)
|
||||
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
|
||||
return uri.path
|
||||
} // File
|
||||
// MediaStore (and general)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
* @author Niks
|
||||
*/
|
||||
private fun getDataColumn(
|
||||
context: Context,
|
||||
uri: Uri?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): String? {
|
||||
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
|
||||
try {
|
||||
cursor =
|
||||
context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val index = cursor.getColumnIndexOrThrow(column)
|
||||
return cursor.getString(index)
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
private fun isExternalStorageDocument(uri: Uri): Boolean {
|
||||
return "com.android.externalstorage.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
private fun isDownloadsDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.downloads.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
private fun isMediaDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.media.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
private fun isGooglePhotosUri(uri: Uri): Boolean {
|
||||
return "com.google.android.apps.photos.content" == uri.authority
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
object SharedPreferenceManager {
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
fun init(context: Context) {
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
sharedPreferences.edit { it.clear() }
|
||||
}
|
||||
|
||||
private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
|
||||
val editor = this.edit()
|
||||
operation(editor)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
private operator fun SharedPreferences.set(key: String, value: Any?) {
|
||||
when (value) {
|
||||
is String? -> edit { it.putString(key, value) }
|
||||
is Int -> edit { it.putInt(key, value) }
|
||||
is Boolean -> edit { it.putBoolean(key, value) }
|
||||
is Float -> edit { it.putFloat(key, value) }
|
||||
is Long -> edit { it.putLong(key, value) }
|
||||
else -> throw UnsupportedOperationException("Error")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private operator fun <T> SharedPreferences.get(key: String, defaultValue: T? = null): T {
|
||||
return when (defaultValue) {
|
||||
is String, null -> getString(key, defaultValue as? String) as T
|
||||
is Int -> getInt(key, defaultValue as? Int ?: -1) as T
|
||||
is Boolean -> getBoolean(key, defaultValue as? Boolean ?: false) as T
|
||||
is Float -> getFloat(key, defaultValue as? Float ?: -1f) as T
|
||||
is Long -> getLong(key, defaultValue as? Long ?: -1) as T
|
||||
else -> throw UnsupportedOperationException("Error")
|
||||
}
|
||||
}
|
||||
}
|
66
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
Normal file
66
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package kr.co.vividnext.sodalive.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.GsonBuilder
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.network.TokenAuthenticator
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.module
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
private val baseUrl = BuildConfig.BASE_URL
|
||||
|
||||
private val otherModule = module {
|
||||
single { GsonBuilder().create() }
|
||||
}
|
||||
|
||||
private val networkModule = module {
|
||||
single {
|
||||
val logging = HttpLoggingInterceptor()
|
||||
|
||||
if (isDebugMode) {
|
||||
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||
} else {
|
||||
logging.setLevel(HttpLoggingInterceptor.Level.NONE)
|
||||
}
|
||||
|
||||
OkHttpClient().newBuilder()
|
||||
.addInterceptor(logging)
|
||||
.authenticator(TokenAuthenticator(get()))
|
||||
.build()
|
||||
}
|
||||
|
||||
single {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
|
||||
.client(get())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private val viewModelModule = module {}
|
||||
|
||||
private val repositoryModule = module {}
|
||||
|
||||
private val moduleList = listOf(
|
||||
networkModule,
|
||||
viewModelModule,
|
||||
repositoryModule,
|
||||
otherModule
|
||||
)
|
||||
|
||||
init {
|
||||
startKoin {
|
||||
androidContext(context)
|
||||
modules(moduleList)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package kr.co.vividnext.sodalive.network
|
||||
|
||||
import android.content.Context
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import okhttp3.Authenticator
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.Route
|
||||
|
||||
class TokenAuthenticator(
|
||||
private val context: Context
|
||||
) : Authenticator {
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
if (response.code == 401) {
|
||||
SharedPreferenceManager.clear()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user