/AOS_Util_Reference

안드로이드 앱 기능 구현 시 참고하는 코드 및 링크

AOS Util Reference


  1. 권한
  2. 바인딩
    2.1. dataBinding
    2.2. viewBinding
  3. 뷰모델
    3.1 ViewModel에서 Context 필요할 때
    3.2 LiveData
    3.3 Observer
    *https://jslee-tech.tistory.com/45
  4. 앱 재시작 코드
  5. 스피너
    5.1 정적 스피너
    5.2 동적 스피너
  6. 웹뷰

참고링크


1.권한

private val permissionRequestCode = 999

//필요 권한 리스트
private val permissionsArray: Array<String> = arrayOf(
    Manifest.permission.READ_CONTACTS
)

//권한 요청이 필요한 Activity/Fragment 에서 호출
fun checkPermissions() {
    //API 23 이상
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val isAllPermissionGranted: Boolean = permissionsArray.all { permission ->
            context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
        }
        if (isAllPermissionGranted) {
            permissionGranted()
        } else {
            ActivityCompat.requestPermissions(
                context as Activity,
                permissionsArray,
                permissionRequestCode
            )
        }
    } else {
      //API 23 미만 
    }
}

//override
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    if (grantResults.all { it == PackageManager.PERMISSION_GRANTED}) {
        //권한을 모두 승인 받았을 때
        permission.permissionGranted()
    }
    else { 
        //권한 승인이 하나라도 거절되었을 때
        permission.permissionDenied()
    }
}

//메서드 내부에서 권한 체크 시 사용
if (PermissionChecker.checkSelfPermission(context, Manifest.permission.CALL_PHONE)
    == PermissionChecker.PERMISSION_GRANTED
) {
    //권한 승인 시
} else {
    //권한 미승인 시
}



2.바인딩

2.1.dataBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }
}

//XML
//editText.text XML에서 바로 String 파라미터로 사용
android:onClick="@{()->callViewModel.makeCall(editText.getText().toString())}"


class EcdhFragment : Fragment() {
    private var _binding: FragmentEcdhBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = DataBindingUtil.inflate(inflater, R.layout.fragment_ecdh, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

2.2.viewBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}



3.뷰모델

3.1 ViewModel에서 Context 필요할 때

class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val context = getApplication<Application>().applicationContext

}    

3.2 LiveData

private var _isUserInfoFetching = MutableLiveData<Boolean>()
val isUserInfoFetching: LiveData<Boolean>
    get() = _isUserInfoFetching

3.3 observer

userInfoViewModel.isUserInfoFetching.observe(this){ isFetching ->

}



4.앱 재시작/종료 코드

//재시작
val packageManager: PackageManager = packageManager
val intent: Intent = packageManager.getLaunchIntentForPackage(packageName)!!
val componentName: ComponentName? = intent.component
val mainIntent = Intent.makeRestartActivityTask(componentName)
startActivity(mainIntent)
exitProcess(0)
//종료
requireActivity().finishAffinity()
exitProcess(0)



5.스피너

5.1 정적 스피너

val accountTypeList = resources.getStringArray(R.array.account_purpose)
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, accountTypeList)
binding.fragMainToolbarSpinner.adapter = adapter

5.2 동적 스피너

val useType = arrayListOf("a", "b", "c")
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, useType)
adapter.setDropDownViewResource(androidx.transition.R.layout.support_simple_spinner_dropdown_item)
binding.fragMainToolbarSpinner.adapter = adapter  



6.웹뷰

<WebView
    android:id="@+id/webview"
    
...

//cf.[Android] WebView 설정 모아보기 https://kyome.tistory.com/149
val data = remoteData.jsData //ex. <p>공지!!!!테스트</p><p><br></p><p><img src=\ .....<br></p><p><br></p>
binding.webview.settings.settings.apply {
    useWideViewPort = true //페이지에 뷰포트 메타가 있으면 태그에 지정된 너비 값이 사용된다.
    loadWithOverviewMode = true //컨텐츠가 웹뷰보다 클때 스크린 크기에 맞춘다
    javaScriptEnabled = true //자바스크립트 사용 여부
}
binding.webview.loadDataWithBaseURL(null, data!!, "text/html; charset=utf-8", "UTF-8", null)



BaseFragment
https://github.com/HYUNJUNEPARK/-Ref-AndroidUI/blob/main/3_ViewPager2_BottomNavigation/app/src/main/java/com/example/viewpager2_bottomnavigation/util/BaseFragment.kt
-> 원하는 프래그먼트에 상속시킨 후 initView() 를 오버라이딩해 사용 (dataBinding 사용)

//상속 예시
class AFragment : BaseFragment<FragmentABinding>(R.layout.fragment_a) {
    override fun initView() {
        super.initView()

        binding.apply {

        }
    }
}



NetworkConnection
https://github.com/HYUNJUNEPARK/-Ref-AndoridProgramming/blob/main/8_NetworkConnection/app/src/main/java/com/example/networkstate/NetworkConnectionCheckModule.kt
-> Activity onCreate/onDestroy 에 register()/unregister() 해 사용 (생명주기에 맞게 호출해 사용)

class MainActivity : AppCompatActivity() {
    private val networkCheck: NetworkConnectionCallback by lazy { NetworkConnectionCallback(this) }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        //...
        networkCheck.register()
    }
    
    override fun onDestroy() {
        //...
        networkCheck.unregister()
    }
}    



AlertDialog
-경고창, 팝업창 구현 시 사용

AlertDialog.Builder(context)
    .setTitle("권한 설정")
    .setMessage("권한 거절로 인해 일부기능이 제한됩니다.")
    .setPositiveButton("취소") { _, _ -> }
    .setNegativeButton("권한 설정하러 가기") { _, _ ->
        applicationInfo()
    }
    .create()
    .show()



Thread

fun showThreadName() {
    val threadName = Thread.currentThread().name
    println("Running on thread: $threadName")
}



DataCass -> JSON -> String

Gson().toJson(
    ErrorResponse(
        ExecStatus(
            code = response.code().toString(),
            message = message
        )
    )
)



JSONObject에서 원하는 데이터 추출

fun extractResponseCode(preferencesResponse: String): String {
    return try {
        JSONObject(preferencesResponse).getJSONObject(EXECSTATUS).getString(CODE)
    } catch (e: Exception) {
        e.printStackTrace()
        handleExceptionResponse(e.toString())
    }
}



build.gradle(.app) buildType 세팅 -> Sync Project with Gradle Files

buildTypes {
    debug {
        buildConfigField "String", "BASE_URL", "\"http://1-.2-.3-.7-:5---/\""
        buildConfigField "Boolean", "DEBUG_MODE", "true"
    }
    release {
        //...
        buildConfigField "String", "BASE_URL", "\"http://2--.7-.2--.7-:5---/\""
        buildConfigField "Boolean", "DEBUG_MODE", "false"
    }
}



invoke()
-'opertor'와 함께 invoke() 함수를 정의하여 클래스 인스턴스를 함수처럼 호출할 수 있다.

//invoke 가 사용된 use case 예시
class FormatDateUseCase(userRepository: UserRepository) {
    private val formatter = SimpleDateFormat(
        userRepository.getPreferredDateFormat(),
        userRepository.getPreferredLocale()
    )
    
    operator fun invoke(date: Date): Stirng {
        return formatter.format(date)
    }
}


//use case 호출 예시
class MyViewModel(formatDataUseCase: FormatDateUseCase): ViewModel() {
    init {
        val today = Calendar.getInstance()
        val todayDate = formateDateUserCase(today)
    }
    //...
}

참고 링크

AAR
Library AAR 파일 생성
https://cording-cossk3.tistory.com/156

프로젝트 구조 대화상자를 사용하여 종속 항목 추가
https://developer.android.com/studio/projects/android-library?hl=ko#psd-add-library-dependency
cf).aar 파일을 Project/app/libs 에 복사
-> File > Project Structure > Dependencies > Add Jar/Aar Dependency > Step 1 Provide a path to the library file or directory to add 'libs/aar파일명'

BuildConfig.DEBUG가 릴리즈 모드에서도 true가 되는 현상
https://satisfactoryplace.tistory.com/147

파이어베이스 gradle 세팅 방법(gradle 7.x 이상)
https://gift123.tistory.com/68
https://developer.android.com/studio/releases/gradle-plugin?hl=ko#updating-plugin

프래그먼트 화면 전환 시 상태 유지하기 (FragmentManager)
https://hanyeop.tistory.com/425

Android) Fragment 에서 View Binding 문제점, 제대로 사용하기
https://yoon-dailylife.tistory.com/57

AOS 외부 앱 실행
https://www.fun25.co.kr/blog/android-execute-3rdparty-app/?category=003

Android - PackageManager로 Package 정보 가져오기
https://codechacha.com/ko/android-query-package-info/

파이어베이스 그레들리 셋팅 방법(buildscript 설명 참고)
https://gift123.tistory.com/68

Kotlin Enum Class Ex
https://blog.logrocket.com/kotlin-enum-classes-complete-guide/

앱 재시작 코드
https://stackoverflow.com/questions/6609414/how-do-i-programmatically-restart-an-android-app

Cannot resolve symbol 'v7' 오류 해결(오래된 프로젝트 빌드 에러)
https://velog.io/@wonggamggik/Cannot-resolve-symbol-v7-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0