Development record of developer who study hard everyday.

레이블이 안드로이드인 게시물을 표시합니다. 모든 게시물 표시
레이블이 안드로이드인 게시물을 표시합니다. 모든 게시물 표시
, ,

How to watch CPU usage using adb in android

 How to watch CPU usage using adb in android


1. Show CPU usage respectively in apps.

adb shell top -n 1 -s cpu


->details about top options

-m num : Maximum number of processes to display

-n num : Updates to show before exiting

-d num : Seconds to wait between updates

-s col : Column to sort by (cpu, vss, rss, thr)

-H : Show threads instead of prcesses

-h : Display this help screen


2. Show CPU usage in specific app

adb shell top -n 60 -d 1 | FINDSTR <package name>

This command shows CPU usages during 60 seconds by 1 second.





Share:
Read More
,

(Sloved) Android Studio APK install error: "local path doesn't exist"

 


I encounter the error "Android Studio APK install error: "local path doesn't exist" while building android source.

The solution is

  • Go to your project directory.In that directory search for .apk file....
  • You will find a apk with name of your project+debug-unalighned.apk.
  • Open .iml file insideyour project folder that contain project source directory.
  • Open and add write <option name="APK_PATH" value="/build/apk/(apk file name that was found earlier with apk extension)" /> inside <configration> <configration/> tags.
  • Now go and run your project...


Share:
Read More
, ,

How to separate string data by sentences

How to separate string data by sentences

안드로이드 블로그

When I am developing android app using Kotlin, I had to separate string data by one sentence.

There might be so many solutions, but I want short code using regular expression.

My solution is

1
2
3
4
5
6
7
8
9
10
fun main() {
    val text = "감오리는 사막이나 농촌지역에서 주로 발견되며, 땅속에 다닐 수 있는 능력이 있어 땅속에서 먹이를 찾기 위해 파묻기를 합니다. 감오리는 매우 활발한 동물로 매일 새로운 먹이를 찾으며 유혈자로 알려져 있습니다. 감오리는 오래 살아 가는 동물이며, 50년에서 70년까지 살 수 있습니다. 감오리의 수명은 종류에 따라 다르지만, 평균적으로 10년 정도입니다. 감오리는 인간과 친밀한 관계를 맺을 수 있는 동물로, 그들은 상호 의존적인 관계에서 존재합니다."
 
    val pattern = Regex("[^.!?]+[.!?]")        
    val matches = pattern.findAll(text)
 
    val sentences = matches.map { it.value.trim() }.toList()
 
    println(sentences)
}
cs


Share:
Read More
,

Text-To-Speech(TTS) not worked above Android 13

 Text-To-Speech(TTS) not worked above Android 13

Android development blog


Text-to-speech(TTS) is the underlying software used by screen readers when they convert text into spoken content..

However, you can not have TTS service basically from Android 13.

You must take action following below guide.

  1. Open your device Settings .
  2. Select Accessibility and then Text-to-speech output.
  3. Choose your preferred engine, language, speech rate, and pitch.
    • The default text-to-speech engine choices vary by device. Options can include Google's Text-to-speech engine, the device manufacturer's engine, and any third-party text-to-speech engines that you've downloaded from the Google Play Store.





Share:
Read More
,

Variety of Stream in Java(자바 스트림 종류)

Variety of Stream in Java

자바 스트림 종류

Android development blog


종류IO 대상 기준자료의 종류 기준스트림의 기능 기준
FileInputStream입력 스트림바이트 단위기반 스트림
FileReader입력 스트림문자 단위기반 스트림
BufferedInputStream입력 스트림바이트 단위보조 스트림
BufferedReader입력 스트림문자 단위보조 스트림
FileOutputStream출력 스트림바이트 단위기반 스트림
FileWriter출력 스트림문자 단위기반 스트림
BufferedOutputStream출력 스트림바이트 단위보조 스트림
BufferedWriter출력 스트림문자 단위보조 스트림


✊ 문자 기반 스트림

자바의 Char 형은 2 byte를 사용하는데, 바이트 단위는 입출력 단위가 1 byte이므로 문자를 처리하기 어려웠다.

이를 보완하기 위해 등장한 것이 바로 문자 기반 스트림이다.


✋ 보조 스트림

기반 스트림은 대상에 직접 자료를 읽고 쓰는 기능의 스트림이다.

보토 스트림은 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있는 스트림을 말한다.

오로지 보완하는 용도이기에 실제데이터를 주고 받지 않고, 데이터를 입출력하는 기능은 없다.

따라서 스트림을 먼저 생성한 후, 보조 스트림을 생성하여 사용한다.


출처: 여기











Share:
Read More
,

Get Uri from android drawable resource

 Get Uri from android drawable resource

Android development blog

1. What I have in my android drawable folder

android project drawable folder


2. Get Uri from drawable

val imgResourceId = context.resources.getIdentifier("album" + song.image.substringBefore("."), "drawable", context.packageName)
val imageUri = Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.resources.getResourcePackageName(imgResourceId))
.appendPath(context.resources.getResourceTypeName(imgResourceId))
.appendPath(context.resources.getResourceEntryName(imgResourceId))
.build()


3. Result

android.resource://net.twobeone.battlesinger/drawable/album00290

android.resource://net.twobeone.battlesinger/drawable/album01468

android.resource://net.twobeone.battlesinger/drawable/album49492


Share:
Read More
, , ,

SideEffect in Android Composable(collectAsStateWithLifecycle, LaunchedEffect, rememberCoroutineScope)

 SideEffect in Android Composable


Android developer blog

✋✋✋ SideEffect

A side-effect in Compose is a change to the state of the app that happens outside the scope of a composable function.

For example, opening a new screen when the user taps on a button, or showing a message when the app doesn't have Internet connection.


1. collectAsStateWithLifecycle

dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0"
}

First, add dependency of "androidx.lifecycle:lifecycle-runtime-compose"


@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CraneHomeContent(
onExploreItemClicked: OnExploreItemClicked,
openDrawer: () -> Unit,
modifier: Modifier = Modifier,
viewModel: MainViewModel = viewModel(),
) {
// TODO Codelab: collectAsStateWithLifecycle step - consume stream of data from the ViewModel
// val suggestedDestinations: List<ExploreModel> = remember { emptyList() }
val suggestedDestinations by viewModel.suggestedDestinations.collectAsStateWithLifecycle()

val onPeopleChanged: (Int) -> Unit = { viewModel.updatePeople(it) }
var tabSelected by remember { mutableStateOf(CraneScreen.Fly) }

BackdropScaffold(
modifier = modifier,
scaffoldState = rememberBackdropScaffoldState(BackdropValue.Revealed),
frontLayerScrimColor = Color.Unspecified,
appBar = {
HomeTabBar(openDrawer, tabSelected, onTabSelected = { tabSelected = it })
},
backLayerContent = {
SearchContent(
tabSelected,
viewModel,
onPeopleChanged
)
},
frontLayerContent = {
when (tabSelected) {
CraneScreen.Fly -> {
ExploreSection(
title = "Explore Flights by Destination",
exploreList = suggestedDestinations,
onItemClicked = onExploreItemClicked
)
}
CraneScreen.Sleep -> {
ExploreSection(
title = "Explore Properties by Destination",
exploreList = viewModel.hotels,
onItemClicked = onExploreItemClicked
)
}
CraneScreen.Eat -> {
ExploreSection(
title = "Explore Restaurants by Destination",
exploreList = viewModel.restaurants,
onItemClicked = onExploreItemClicked
)
}
}
}
)
}

Second, in CrameHomeContent composable, you can consume the list of destinations as form of state using collectAsstateWithLifecycle() method.


💪💪💪 Compose also offers APIs for Android's most popular stream-based solutions:

- LiveData.observeAsState() in "androidx.compose.runtime:runtime-livedata"

- Observable.subscribeAsState() in "androidx.compose.runtime:runtime-rxjava2"


2. LaunchedEffect and rememberUpdatedState

To call suspend functions safely from inside a composable, use the LaunchedEffect API, which triggers a coroutine scoped side-effect in Compose.

When LaunchedEffect enters the Composition, it launches a coroutine with the block of code passed as a key parameter.

The coroutine will be canceled if LaunchedEffect leaves the composition.


LaunchedEffect take a variable number of keys as a parameter that are used to restart the effect whenever one of those keys changes.

@Composable
fun LandingScreen(onTimeout: () -> Unit, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
val currentOnTimeOut by rememberUpdatedState(onTimeout)    //1

LaunchedEffect(key1 = Unit) {    //2
delay(SplashWaitTime)
onTimeout.invoke()
}
Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null)
}
}

1 => The rememberUpdatedState() make us refer to the latest onTimeout function that LandingScreen was recomposed with

2 => Create an coroutine scope that matches the lifecycle of LandingScreen.

Because the key parameter is Unit that is not changed, the delay shouldn't start again.


3. rememberCoroutineScope

API to open the navigation drawer is suspend function.

How do we call suspend function in callback function?

@Composable
fun CraneHome(
onExploreItemClicked: OnExploreItemClicked,
modifier: Modifier = Modifier,
) {
val scaffoldState = rememberScaffoldState()
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier.statusBarsPadding(),
drawerContent = {
CraneDrawer()
}
) { padding ->
val scope = rememberCoroutineScope()
CraneHomeContent(
modifier = modifier.padding(padding),
onExploreItemClicked = onExploreItemClicked,
openDrawer = {
scope.launch {
scaffoldState.drawerState.open()
}
}
)
}
}

We want a CoroutineScope that follows the lifecycle of its call-site.

The rememberCoroutineScope API returns a CoroutineScope bound to the point in the Composition where you call it.

The scope will be automatically canceled once it leaves the Composition.


✊✊✊ LaunchedEffect vs rememberCoroutineScope

Using LauncedEffect is not possible to use in regular callback which is outside of the Composition.

Both can produce CoroutineScope but, LaunchedEffect can control when the side-effect would be called.


Share:
Read More
, , , , ,

How to use UnityPlayer in Android Compose

How to use UnityPlayer in Android Compose

Android Development blog

In my company, I have to use 3D avatar animation using UnityPlayer derived from Unity module.

The base code I will use is consist of Android Compose.

So, I want to use UnityPlayer with Android Compose.

However, There were no information about using UnityPlayer with Compose.

This is why I write this post.


☝I introduce this post on the condition that you add UnityModule.

If you not, read this post first!!

https://antwhale94.blogspot.com/2024/03/AddingUnityModuleToAndroid.html


1. Add dependency

android {
...
buildFeatures {
viewBinding true
}     ...
}

dependencies {

implementation 'androidx.compose.ui:ui-viewbinding:1.6.0'

}

You should add viewBinding dependency.

And you should add "androidx.compose.ui:ui-viewbinding" to take xml into Compose.


2.  MainActivity.kt

class MainActivity : ComponentActivity() {
val TAG = MainActivity::class.java.simpleName
lateinit var mUnityPlayer: UnityPlayer

@ExperimentalMaterial3Api
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

mUnityPlayer = UnityPlayer(this)
val glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1)
val trueColor8888 = false
mUnityPlayer.init(glesMode, trueColor8888)

setContent {
Surface(
modifier = Modifier
.fillMaxSize()
) {
MLCChatTheme {
NavView()
}
}
}


}

override fun onDestroy() {
Log.d(TAG, "onDestroy: ")
super.onDestroy()

mUnityPlayer.quit()
}

override fun onPause() {
Log.d(TAG, "onPause: ")
super.onPause()
mUnityPlayer.pause()
}

override fun onResume() {
Log.d(TAG, "onResume: ")
super.onResume()
mUnityPlayer.resume()
}

override fun onLowMemory() {
Log.d(TAG, "onLowMemory: ")
super.onLowMemory()
mUnityPlayer.lowMemory()
}

override fun onTrimMemory(level: Int) {
Log.d(TAG, "onTrimMemory: ")
super.onTrimMemory(level)
if(level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL)
mUnityPlayer.lowMemory()
}

override fun onConfigurationChanged(newConfig: Configuration) {
Log.d(TAG, "onConfigurationChanged: ")
super.onConfigurationChanged(newConfig)
mUnityPlayer.configurationChanged(newConfig)
}

override fun onWindowFocusChanged(hasFocus: Boolean) {
Log.d(TAG, "onWindowFocusChanged: $hasFocus")
super.onWindowFocusChanged(hasFocus)
mUnityPlayer.windowFocusChanged(hasFocus)
}

}

fun Context.getActivity(): ComponentActivity? = when (this) {
is ComponentActivity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
}

In my experience, The UnityPlayer derived from activity only can play animation.

I don't know why, so I make UnityPlayer object in MainActivity using MainActivity context.

The lowest function getActivity is used to get MainActivity from Composable function.


3. xml layout to put UnityPlayer

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frameLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

</FrameLayout>

This layout file's name is "avatar_view_layout.xml"

Obviously, you can change this file name if you want.


4. Composable function to show UnityPlayer

@Composable
fun AvatarView() {
val TAG = "AvatarView"
val localContext = LocalContext.current

Log.d(TAG, "AvatarView composition")

val unityPlayer = (localContext.getActivity() as MainActivity).mUnityPlayer    //1
AndroidViewBinding(AvatarViewLayoutBinding::inflate) {     //2
if(unityPlayer.parent == null) {
Log.d(TAG,
"AvatarView added")
frameLayout.addView(unityPlayer.view)        //3
unityPlayer.resume()
}
}

DisposableEffect(key1 = localContext) {
onDispose {        //4
Log.d(TAG, "remove AvatarView")
(unityPlayer.
parent as FrameLayout).removeView(unityPlayer.view)

unityPlayer.pause()

}
}
}

1 => Get UnityPlayer object using local context

2 => AndroidViewBinding to use xml in Compose

3 => Add UnityPlayer to FrameLayout and resume UnityPlayer

4 => DisposableEffect to notice when AvatarView composable leave.

You should remove UnityPlayer from framelayout and pause it to restart when recomposing.


I don't know this is good way to use UnityPlayer with Compose, but This is only way I got this.




Share:
Read More
, , , ,

How to import Unity module to Android project

 How to import Unity module to Android project

Android develop blog

This post introduces how to import Unity module to Android project.

Before start, I assume you have Android module exported from Unity already.


1. Copy module folder into Android Project folder.

module in Android Project folder


2. Add Unity Module name in settings.gradle

pluginManagement {
repositories {
jcenter()
google()
mavenCentral()
gradlePluginPortal()
}
}

include ':unityLibrary'        //Add this line
project(':unityLibrary').projectDir=newFile('../unityLibrary')        //Add this line

dependencyResolutionManagement
{
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories
{
jcenter()
google()
mavenCentral()
flatDir
{
dirs "${project(':unityLibrary').projectDir}/libs"        //Add this line
}
}
}
rootProject.name = "MLCChat"
include ':app'

The "unityLibrary" is replaced if you have another module name.


3. Add Unity module and jar files which Unity module have in build.gradle (Module :app)

dependencies {

implementation projcet(':unityLibrary')
implementation fileTree(dir: project(':unityLibrary').getProjectDir().toString() + ('\\libs'), include: ['*.jar'])



}


4. Represent abiFilters in build.gradle(Module: app)

android {
namespace 'ai.mlc.mlcchat'
compileSdk 34

defaultConfig {
applicationId "ai.mlc.mlcchat"
minSdk 26
targetSdk 33
versionCode 1
versionName "1.0"

ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"        //Add this line
}

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}

...
}

✋ abiFilters must be same with Unity abiFilters setting.


5. Copy the options in gradle.properties from Unity module to app's gradle.properties

example)

org.gradle.jvmargs=-Xmx4096M

org.gradle.parallel=true

unityStreamingAssets=

unityTemplateVersion=5


org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true

unityStreamingAssets=zepeto_streamingassets/ZEPETO-INTERNAL
unityTemplateVersion=5


6. Sync Now!

Click Sync Now


7. Add ndk path in local.properties

## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Tue Mar 19 16:58:42 KST 2024
sdk.dir=C\:\\Users\\07062301\\AppData\\Local\\Android\\Sdk
ndk.dir=C\:\\Users\\07062301\\AppData\\Local\\Android\\Sdk\\ndk\\25.1.8937393

8. Import Unity Module Finish!!

Unity Module finished

You can find Unity module in left side Android panel.


Share:
Read More