Skip to content

Miscellaneous hints for developers

Never replace what was already distributed

Replacing an already published release, or APKs attached to it, can come with several side effects. To mention the most obvious:

  • folks who manually install from your releases, might not notice replaced APKs if they see no new releases
  • whatever goes by tags/releases to fetch the latest update, would not process the same tag/release again (e.g. our repo updater, our builders)
  • independent builders checking for Reproducible Builds, having processed the original tag/release/APK, will not process the replaced one – so their confirmation would be void (as checksums of the APKs no longer match)

If something went wrong, instead consider the following:

  • remove the APK from an existing release if it e.g. has serious issues (e.g. security problems, instant crashes)
  • make a new release, with versionCode increased, and versionName having a higher „patch level“ (see semantic versioning). The tag name should correspond to the new versionName then, of course.

Implied permissions

Sometimes, various permissions show up in the final AndroidManifest.xml of your Android app, and you wonder where they come from. Apart from dependencies having them declared, there are also permissions which are implied, based on SDK versions:

Importing a library Declares this permission in the merged application manifest
minSdk < 2 WRITE_EXTERNAL_STORAGE
minSdk < 4 WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE
Declared WRITE_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE
minSdk < 16 and
Declared READ_CONTACTS
READ_CALL_LOG
minSdk < 16 and
Declared WRITE_CONTACTS
WRITE_CALL_LOG

(source: this article, via Warren Bank; also see aapt/Command.cpp)

Android libraries declare a minSdk, but they do not declare a targetSdk (the com.android.library gradle plugin deprecated and later removed the targetSdk property). When an application is compiled and the manifests for its libraries are merged:

  • the targetSdk of a library is set to its minSdk
  • depending of that targetSdk, implied permissions are then added to the application, as outlined in above table.

So if e.g. android.permission.READ_PHONE_STATE shows up unexpectedly with your app, check if any of your libraries has no minSdk declared (which would be considered equal to minSdk:0).

Fastlane and Translation Services

Translators are often unaware of consequences when exceeding character limits (hint: all text behind the limit will simply be truncated, even mid-word), and thus commit „longer strings“ even when told not to. Luckily, some translation services provide means to enforce such limits.

Weblate

Weblate allows to set „enforcement checks“ for string lengths. In your Weblate component, go to Operations › Settings › Translations › Enforced checks and make sure „Maximum length of translation“ is on the right side. Then, block ignored checks from getting committed.

Despite what you would think from the description of „enforced“, users can still just hit submit a second time to force-submit a translation. So you need to prevent those from being committed. For this, in your Weblate project, go to Operations › Settings › Workflow › Translation quality filter and make sure it is set to „Skip translations marked as needing editing“. This keeps them from being committed to git.

After you've done both it should work as intended to prevent new incorrect submissions.

(source: this issue comment by Sylvia)

Crowdin

Crowdin also allows you to set a maximum translation length, see Adding Strings and String Editing in their documentation.