A reliable/fast way to know an APK file's api level

I googled quite a while and found the following way is the best:

If you have Android Studio installed, using the "Build > Analyze APK" menu command is indeed a convenient way to quickly determine the supported API level of an APK file. Here are the steps:

Open Android Studio.

Go to "Build" in the top menu.

Choose "Analyse APK..."

Navigate to and select the APK file you want to analyze.

Android Studio will display information about the APK, including the minimum and target API levels.

Surely if you don't have Android Studio installed, then this way cannot help.

Moq Tips 2 - check if a log message with a specific prefix/suffix and call a method with an object having a specific field value

  1. To check if a log message with a specific prefix is logged, you can use Moq's Verify method along with an appropriate assertion.
_logger.Verify(logger => logger.logInformation(
    It.Is<string>(s => s.StartsWith("specific prefix"))), Times.Once);
  1. To verify that a method is called with an object containing a specific property value, you can use Moq's It.Is<T> matcher along with a lambda expression. Here's an example:
_mockObject.Verify(x => x.MyMethod(It.Is<MyObject>(
    obj => obj.PropertyValue == expectedPropertyValue)), Times.Once);
  1. Correctly returns null: Sometimes we need to set up a Moq mock to return null but cannot directly do it as Moq doesn't allow. Please check the following way that you should use:
_bookStoreAccountRepository.Setup(
    b => b.GetBookStoreAccountByUserName(It.IsAny<string>()))
    .Returns((BookStoreAccount)null);

The key is to use (BookStoreAccount)null to explicitly cast null to the expected return type.

My New 'Toy' - E-ink Screen E-reader BOYUE LikeBook S61

As a software developer, I spend my working hours glued to screens, and in my downtime, it's either scrolling through social media or reading e-books, all on screens big and small. Often, before it's even time for a break, my eyes are already dry and strained. I can't help but reminisce about my old Kindle from years ago, lost goodness knows where. I need to get myself an e-ink device solely for reading, and it must run on Android so I can use my beloved KOReader on the ink screen.

With a limited budget and primarily for reading, my expectations aren't high: the screen doesn't need to be large, the CPU doesn't need to be powerful, but it'd be nice to have ample storage for books.

A search on AliExpress caught my eye with this BOYUE e-reader.

BOYUE LikeBook S61, 6-inch, 4-core 1.6G CPU, 1GB RAM, 16GB Storage NZ$ 102.97, with shipping $4.25 plus 15% GST, totaling $122.42.

Surely, this is a clearance sale of old machines. Because the description stated that the system is Android 4.4, quite outdated. But optimistically, I thought, there should be third-party ROMs for flashing, so I placed the order. The logistics were quite efficient this time, and after only about a dozen days, I received it on March 1st. The system is not 4.4 but even older, 4.2.2. After searching online, I couldn't find a suitable ROM for flashing. So, in the days after receiving the device, I had to make do and find software online that still supports Android 4.2.2 and meets my needs. This process wasn't smooth, and at one point, I even considered giving up and returning it. However, after the initial setbacks, things started to go more smoothly.

After some tinkering, using this device has become quite comfortable, dare I say, even enjoyable!

I'll spare you the details of the hardships endured during repeated attempts. Today's main purpose is to share my findings. If it helps any fellow buyer of this brand-new old machine, then I couldn't be happier.

  • Main reading program: koreader-v2023-06-1.apk
  • Launcher: Simple.ink.launcher.apk
  • SSH server: SimpleSSHD-27.apk
  • Browser: com.UCMobile.intl_11.4.apk
  • File Manager es-file-explorer-4-2.apkrelaunchx_200106000.apk
  • Mastodon client: tusky.apk

I'll find some time to bundle these APKs and throw them onto a rickety VPS for everyone to download. After all, finding these installation packages took quite a bit of my time.


2024-03-24 Update I've revised this list, packaged these APK files, and written a brief explanation. The download link is also in this explanation, so feel free to download if needed. Link to the explanation


我的新“玩具” - 电子墨水屏阅读器 BOYUE LikeBook S61 (Chinese Version)

身为软件开发者,我工作时盯着屏幕,工作之余除了刷社交媒体就是看电子书,还是大大小小的屏幕。往往不等到休息时间,眼睛就干涩得不行。我不禁怀念起我那台多年前就不知道丢到哪里的老Kindle。还是要整一台e-ink 设备专门读书,而且一定得是安卓系统,这样我就能在墨水屏上用我最爱的 KOReader阅读器读书了。

预算不多,而且主要是看书,我的期望值并不高:屏幕不必大,CPU也不求多块,但存书的空间最好能大一点。

AliExpress 上一通搜索,这台BOYUE 阅读器吸引住我的眼球。

BOYUE LikeBook S61 6 inch 4核1.6G CPU 1G RAM 16G Storage NZ$ 102.97 运费 $4.25 再加上 15% 的GST,到手价才 $122.42。

这定然是老机器清仓甩卖。因为描述里写系统是Android 4.4,相当老了。但乐观的我想,应该总能找到第三方ROM刷机吧,于是乎就下单了。这次物流蛮给力,只等了十几天,我3月1日就收到货了。系统不是 4.4, 而是更老的 4.2.2。网上一通搜索,并没有找到合适的ROM刷机。于是乎拿到机器这几天,我只能因陋就简,在网上找能满足我需要的尚支持 Android 4.2.2 版本的软件。这个过程并不顺利,我甚至一度考虑别折腾了,要不干脆退货算了。然而在最初的挫折之后,后面却是越来越顺利。

经过一番调教之后,这台机器用起来已经相当顺手,甚至可以说是舒适了!

中间反复尝试的艰辛略去不提,今天这篇主要是分享一下我的成果, 如果能帮到也买了这台全新的老机器的网友,那我就再开心不过了。

  • 主阅读程序 koreader-v2023-06-1.apk
  • Launcher Simple.ink.launcher.apk
  • SSH server, SimpleSSHD-27.apk
  • Browser, com.UCMobile.intl_11.4.apk
  • File Manager es-file-explorer-4-2.apkrelaunchx_200106000.apk
  • Mastodon client tusky.apk

抽时间我会将这些apk打个包扔我一台破VPS上供大家下载,毕竟找这些安装包花了我不少时间。


2024-03-24 Update 我修订了这个列表打包了这些apk文件,并写了一个简要的说明, 下载链接也在这个说明里,欢迎需要的朋友下载。

English Learning Memo 1

My boss sent a message in our office channel, which said she was WFH today, as "she have a bloke coming round to fix the garage door".

BLOKE is a new word to me. so I learned this word today.

Other learnings

  • "得不偿失" in English can be translated as "not worth the loss" or "the gains do not justify the losses."
  • "蒜苔" in English is "garlic scape" or simply "garlic shoots."
  • "In and out promotion" 广告术语中指的是一种快速进行的促销活动,通常是短期的、迅速产生效果的宣传推广。这可能包括限时折扣、特价促销等手段,旨在迅速吸引顾客。
  • Every so often it'd rain. “Every so often"是固定搭配,表示“偶尔”或者“时不时的”。
  • My brother loved playing tag. 这句里的 tag 指的是一种游戏,即“追逐游戏”。通常几个人一起玩,其中一个人被标记 (tag)为追逐者,其他人则努力躲避追击者,避免被他/她触及到。

…more

Moq tips 1 - learn how to write unit tests from ChatGPT

When should I use mockObject.Reset() and when should I use mockObject.Invocations.Clear()?

In the context of using a mocking framework like Moq in C# for unit testing, the choice between mockObject.Reset() and mockObject.Invocations.Clear() depends on what you want to achieve.

  1. mockObject.Reset():

    • This is specific to the Moq library.
    • It resets the mock object, removing any setups or custom behavior you might have defined for that mock.
    • It does not clear recorded invocations; you can still access them after calling Reset().

    Example:

    var mock = new Mock<MyClass>();
    mock.Setup(x => x.MyMethod()).Returns("Original");
    
    // ... code using the mock ...
    
    // Reset the mock, removing setups
    mock.Reset();
    
    // Now, if you call the method, it won't use the previous setup
    var result = mock.Object.MyMethod();
    
  2. mockObject.Invocations.Clear():

    • This clears the recorded invocations on the mock object.
    • It doesn't affect setups or custom behavior. It just removes the history of invocations.
    • This can be useful when you want to verify interactions or check the number of invocations without interference from previous test runs.

    Example:

   var mock = new Mock<MyClass>();
   mock.Setup(x => x.MyMethod()).Returns("Original");
   
   // ... code using the mock ...
   
   // Clear recorded invocations
   mock.Invocations.Clear();
   
   // Now, if you verify invocations, it won't consider previous calls
   mock.Verify(x => x.MyMethod(), Times.Never());

In summary:

  • Use mockObject.Reset() when you want to clear setups and start fresh with the mock.
  • Use mockObject.Invocations.Clear() when you want to clear the history of invocations, but keep the existing setups and behaviors.

If I set up a method with the same parameters multiple times, the last setup will take effect, right?

In most mocking frameworks, including Moq for C#, the answer is YES. Each new setup for the same method and parameters overrides any previous setups.

Here's an example using Moq:


// Setting up the method with the same parameters but different returns
mock.Setup(x => x.MyMethod()).Returns("FirstSetup");
mock.Setup(x => x.MyMethod()).Returns("SecondSetup");

// Now, if you call the method, it will return "SecondSetup"
var result = mock.Object.MyMethod();