Setting up Android Studio on a freshly reformatted MacBook, I got this error:Git is not installed
xcode-select: note: No developer tools were found, requesting install. If developer tools are located at a non-default location on disk, use sudo xcode-select --switch path/to/Xcode.app to specify the Xcode that you wish to use for command line developer tools, and cancel the installation dialog. See man xcode-select for more details.
Tag: programming
-
Installing git on a macbook
-
Spoofing Battery and Forcing Sleep (Android Development)
I wanted to do something with my Android phone using ADB commands, while keeping it plugged in to run the said commands. However, the thing I wanted to test was related to the idle (dozing or asleep) mode of Android devices. The workaround was to spoof the battery state to bypass Android’s charging restrictions.
Spoofing Battery Charging Status and Forcing Android Device to Sleep (Idle Mode)
- Turn off the device screen using the physical power button.
- Trick Android into thinking the device is running on battery power:
adb shell dumpsys battery unplug - Force the device to step into deep Doze mode:
adb shell dumpsys deviceidle force-idle - Verify the device is successfully spoofed and asleep:
adb shell dumpsys deviceidle get deep(This should returnIDLE).
Can also do:
adb shell dumpsys battery unplug && adb shell dumpsys deviceidle force-idle && adb shell dumpsys deviceidle get deepDevice Cleanup for Spoofed Battery and Forced Sleep (Crucial!)
- Once testing is done, you must tell the OS to start reading the actual battery state again, otherwise it won’t charge properly:
adb shell dumpsys battery reset - Bring the device out of idle:
adb shell dumpsys deviceidle unforce
Can also do:
adb shell dumpsys battery reset && adb shell dumpsys deviceidle unforce && adb shell dumpsys deviceidle get deep -
A random dev brain dump
I don’t need a reason to randomly post a brain dump on my blog, no?
- If an Android dev wishes to implement a precise “notification ID” that they want to be able to precisely cancel later (imagine that it plays an annoying alarm tone!) they can go about it either by
- Using a unique ID tied to that notification’s base entity (e.g., an app’s incoming message) so that NOTIFS WILL STACK, or
- Using a unique, dedicated ID that is defined as a constant and is defined without ties to the notification’s base entity so that ONLY THE LAST NOTIF WILL BE SHOWN
- I gotta update my site’s, something..
- If an Android dev’s soft keyboard covers a text input field when it’s displayed, particularly in Android 15 devices, they may wanna try to add an
imePaddingmodifier to the root element (e.g., Box, Column) because this modifier will take care of spacings whenever the keyboard appears, avoiding issues such as covering the text input field.
- If an Android dev wishes to implement a precise “notification ID” that they want to be able to precisely cancel later (imagine that it plays an annoying alarm tone!) they can go about it either by
-
Android emulator shell list fails: Permission Denied in app directory
Today I learned I should include the app package name after
shellin my ADB command, if for example I want to list files under the “subdirectory/media” directory of an app, like so:adb -s emulator-5556 shell run-as com.domain.my.app ls -al /data/user/0/com.domain.my.app/subdirectory/mediaFirst try’s command below failed with a Permission Denied error message:
adb -s emulator-5556 shell ls -al /data/user/0/com.domain.my.app/subdirectory/mediaMakes sense, right? Because part of Android OS’ security is limiting access to app directories. But if you “run-as” the package owner app itself, it should grant you access.
-
Android: Broadcast sent, receiver registered, but receiver doesn’t receive broadcast
I should blog more often about things I learn about.
In a native Android app written in Kotlin, I have a LifecycleService class with below code to send a broadcast:
val eventIntent = Intent(CUSTOM_EVENT_ACTION_NAME).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } applicationContext.sendBroadcast(eventIntent) Log.d("AppTag", "Broadcast sent!")
Based on the logcat, the broadcast is being sent.
However, below BroadcastReceiver in an Activity doesn’t receive the broadcast, even while it is visible:private val customEventReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Log.d("AppTag", "Custom event receiver triggered") if (intent?.action == GlobalConstants.CUSTOM_EVENT_ACTION_NAME) { Log.d("AppTag", "Confirmed action match, proceeding with task.") performSpecificTask() } } }The above BroadcastReceiver is registered from the Activity’s onCreate function, like so:
val filter = IntentFilter(GlobalConstants.CUSTOM_EVENT_ACTION_NAME) ContextCompat.registerReceiver( this, customEventReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED )Attempt #1: LocalBroadcastManager – it worked, but…
After browsing the web for potential solutions I came across LocalBroadcastManager. The broadcast was successfully received by the receiver after I changed the sendBroadcast’s
applicationContextand the registerBroadcast’s ContextCompat into LocalBroadcastManager.getInstance(Context).That is,
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(eventIntent)andLocalBroadcastManager.getInstance(this).registerReceiver(customEventReceiver, filter).The problem with this approach is that LocalBroadcastManager has been deprecated:
Important changes since 1.0.0
https://developer.android.com/jetpack/androidx/releases/localbroadcastmanagerandroidx.localbroadcastmanagerhas been fully deprecated. There will be no further releases of this library. Developers should replace usages ofLocalBroadcastManagerwith other implementations of the observable pattern. Depending on the use case, suitable options may beLiveDataor reactive streams.Ideally, the solution should not be a deprecated one.
As to why LocalBroadcastManager made my receiver work, I think it’s because it “bridged” 2 components that weren’t really supposed to be able to communicate. Because from the Android docs:
LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app: any component may listen events from any other.
Why it works, and why it’s been deprecated, apparently have the same explanation. 😅
Attempt #2: Use applicationContext on the sender and receiver — Nope!
I reverted my code to the original, and just made sure they all used applicationContext, matching the context used in the service’s broadcast sender
applicationContext.sendBroadcast(eventIntent).So that my registration became:
val filter = IntentFilter(GlobalConstants.CUSTOM_EVENT_ACTION_NAME) ContextCompat.registerReceiver( applicationContext, customEventReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED ) And the code to unregister became:applicationContext.unregisterReceiver(customEventReceiver)Hmm… Nope! that didn’t let my receiver get the broadcast.
Attempt #3: Make it an explicit broadcast — YES, IT WORKS!
I reverted my code again to try another approach, which is to make the broadcast an explicit one.
So, in my service, I simply set the package name as shown in code below:
val eventIntent = Intent(CUSTOM_EVENT_ACTION_NAME).apply { setPackage(applicationContext.packageName) //...And with this 1 line changed in code, my BroadcastReceiver now receives the broadcast! Plus, there’s no deprecated class used here so we prevented 1 new technical debt. :3
Yeah, I think I’ll stick to the 3rd attempt’s approach!
Why does this approach work, though? It’s because starting from Android API level 26, there’s been restriction on implicit broadcasts.
As part of the Android 8.0 (API level 26)Â background execution limits, apps that target the API level 26 or higher can’t register broadcast receivers for implicit broadcasts in their manifest unless the broadcast is sent specifically to them. However, several broadcasts are exempted from these limitations.Â
https://developer.android.com/develop/background-work/background-tasks/broadcasts/broadcast-exceptionsMy original code’s broadcast Intent was an implicit one, but specifying a package name converted it into an explicit one, consequently lifting the limitation applied to implicit broadcasts!
-
Run command automatically after updating a file on Mac
I get a kick out of automating things! Especially those seemingly easy extra steps that we do numerous times a day, everyday.
There’s this configuration file that I frequently have to
adb pushinto my test Android devices. And the cycle of editing the file, saving it, and then going to the terminal to run the push command (sometimes having to press the up ↑ key a few times) , is repeated as many times as needed.Last week, while rapidly becoming exhausted from the extreme summer heat, I was becoming frustrated at the bug I was trying to fix. Having to repeat the edit-save-push cycle on the file a countless number of times, along with the discomfort from sweaty armpits, wasn’t helpful in fixing the bug.
I wondered, how do I automatically push this configuration file when unix detects a change in the file after editing and saving?
What I did was rely on “entr”.
I first installed it on my mac like so:
brew install entrAnd then ran below command in my terminal:
ls /Users/catz/Documents/MyConfigFile.json | | entr adb -e push /Users/catz/Documents/MyConfigFile.json /sdcard/Android/data/com.example.app/files/config/MyConfigFile.json/Users/catz/Documents/MyConfigFile.jsonis the configuration file I frequently edit and push./sdcard/Android/data/com.example.app/files/config/MyConfigFile.jsonis the expected location of the file in my test Android device. The app reads the configuration from this location.Now, with this setup, I simply have to hit Save after editing the configuration file, and I just watch it automatically get pushed to my test Android device. 😎 My younger self probably would have tried to code her own script for automation, but my old, cranky present self knows better than waste time like that. Heh.
Automating little things, such as having to push a changed file each and every time it’s edited, can have a big impact in one’s workflow. Streamlining mine this way allows me to use my little energy on what really matters.
So, how did the bug squashing go?
A quick background on the bug: StateFlow.update was not updating a value inside a list of data class model. I thought that a deep copy was what I needed, but it turned out that the data class contained an override function of
equalsand it was lacking a necessary logic to return the correct Boolean value. StateFlow.compareAndSet, which is called byStateFlow.updatebehind the scenes, does not proceed with updating the value, because the old and new value are seen as “equal” due to the overriddenequalsfunction not returning the correct result.I didn’t notice the faulty
equalslogic until I regained my calm focus on that hot workday — all thanks to a quick cool shower, and of course, the kick I got from setting up my command to automatically run after updating the configuration file~And yes, remember to try to feel comfortable as you do problem solving tasks! I think that feeling comfortable helps us keep a clear head, which consequently helps us solve problems more effectively.
-
Make a RecyclerView expand its height when new item is added //Android Dev Diary
It seems that simply using
(more…)android:layout_height="wrap_content"on your RecyclerView won’t make its height automatically expand when new items are added in it.