Slide 1

Slide 1 text

migrating to Android Julien Salvi - Android GDE | Lead Android Engineer @ Aircall @JulienSalvi One does not simply:

Slide 2

Slide 2 text

Julien Salvi Lead Android Engineer @ Aircall Android GDE PAUG, Punk & IPAs! @JulienSalvi Hello!

Slide 3

Slide 3 text

⚠ Disclaimer ⚠ ● Android 12 ● Migrate to a new Android version ● API changes ● Tricks & tips to successfully migrate your app ● Go deep in the implementation ● Lord of the Rings 😅

Slide 4

Slide 4 text

Discovering Android New version, new life!

Slide 5

Slide 5 text

Android 12 roadmap Avril. Jui. Août Oct. 2022 Developer Previews Beta Releases Android 12 migration deadline nov. 2023 Fev. 2021 2023 Android 12 is stable 🥳 ! New API level requirements 1st Nov. source: https://android-developers.googleblog.com/2022/04/expanding-plays-target-level-api-requirements-to-strengthen-user-security.html

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Personal Introduces Material 3 and Material You New design for components Theme and color can be personalized New API to build widgets SplashScreen API for all apps

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Safe Indicator for the microphone and the camera

Slide 12

Slide 12 text

Safe Indicator for the microphone and the camera Share your location: precise or approximate

Slide 13

Slide 13 text

Safe Indicator for the microphone and the camera Share your location: precise or approximate Bluetooth Permission at runtime! List of permissions used by your apps Permissions are removed for apps that have not been used for a while

Slide 14

Slide 14 text

well… not so simple for the developers!

Slide 15

Slide 15 text

Story of a migration… …and one does not simply migration to Android 12

Slide 16

Slide 16 text

Is it that complicated? Really? - Me now

Slide 17

Slide 17 text

So what do we need check at first? - me again right now

Slide 18

Slide 18 text

💡Tip 1 Documentation 📚

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Gradle configuration ● Update the gradle build files and target the API 31 ● Update the build tools ● Let’s compile… 💥💥💥💥💥💥💥 android { compileSdkVersion 31 buildToolsVersion "31.0.0" defaultConfig { targetSdkVersion 31 } }

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Manifest configuration ● For Activity, Service et BroadcastReceiver which use IntentFilter, the field android:exported must be defined for security reasons. ● Let’s compile… 💥💥💥💥💥💥💥

Slide 23

Slide 23 text

Manifest configuration ● Same error but with an external library. ● Let’s check if there is any updates, otherwise we update the manifest. ● Let’s compile… 💥💥💥💥💥💥💥

Slide 24

Slide 24 text

Manifest configuration ● Every compilation will tell which component has not been exported! ● Gradle to the rescue to find the missing components! https://bit.ly/3M9Nw4q application.childNodes.each { child -> def childNodeName = child.nodeName if (childNodeName == "activity" || childNodeName == "activity-alias" || childNodeName == "service" || childNodeName == "receiver") { def attributes = child.getAttributes() if (attributes.getNamedItem("android:exported") == null) { def intentFilters = child.childNodes.findAll { it.nodeName == "intent-filter" } if (intentFilters.size() > 0) { def exportedAttrAdded = false for (def i = 0; i < intentFilters.size(); i++) { def intentFilter = intentFilters[i] def actions = intentFilter.childNodes.findAll { it.nodeName == "action" } for (def j = 0; j < actions.size(); j++) { def action = actions[j] def actionName = action.getAttributes().getNamedItem("android:name").nodeValue if (actionName == "com.google.firebase.MESSAGING_EVENT") { ((Element) child).setAttribute("android:exported", "false") exportedAttrAdded = true } } } if (!exportedAttrAdded) { ((Element) child).setAttribute("android:exported", "true") } nodesFromDependencies.add(child) } } } }

Slide 25

Slide 25 text

Manifest configuration https://bit.ly/3M9Nw4q application.childNodes.each { child -> def childNodeName = child.nodeName if (childNodeName == "activity" || childNodeName == "activity-alias" || childNodeName == "service" || childNodeName == "receiver" ) { ... } } ● Every compilation will tell which component has not been exported! ● Gradle to the rescue to find the missing components!

Slide 26

Slide 26 text

Manifest configuration https://bit.ly/3M9Nw4q def attributes = child.getAttributes() if (attributes.getNamedItem("android:exported") == null) { def intentFilters = child.childNodes.findAll { it.nodeName == "intent-filter" } if (intentFilters.size() > 0) { println "found ${childNodeName} ${attributes.getNamedItem("android:name").nodeValue} with intent filters but without android:exported attribute" ... } } ● Every compilation will tell which component has not been exported! ● Gradle to the rescue to find the missing components!

Slide 27

Slide 27 text

Manifest configuration ● Every compilation will tell which component has not been exported! ● Gradle to the rescue to find the missing components! ● Let’s compile… ✅✅✅✅✅✅✅ Safer exporting ✅ https://bit.ly/3M9Nw4q application.childNodes.each { child -> def childNodeName = child.nodeName if (childNodeName == "activity" || childNodeName == "activity-alias" || childNodeName == "service" || childNodeName == "receiver") { def attributes = child.getAttributes() if (attributes.getNamedItem("android:exported") == null) { def intentFilters = child.childNodes.findAll { it.nodeName == "intent-filter" } if (intentFilters.size() > 0) { def exportedAttrAdded = false for (def i = 0; i < intentFilters.size(); i++) { def intentFilter = intentFilters[i] def actions = intentFilter.childNodes.findAll { it.nodeName == "action" } for (def j = 0; j < actions.size(); j++) { def action = actions[j] def actionName = action.getAttributes().getNamedItem("android:name").nodeValue if (actionName == "com.google.firebase.MESSAGING_EVENT") { ((Element) child).setAttribute("android:exported", "false") exportedAttrAdded = true } } } if (!exportedAttrAdded) { ((Element) child).setAttribute("android:exported", "true") } nodesFromDependencies.add(child) } } } }

Slide 28

Slide 28 text

java.lang.IllegalArgumentException: com.azerty: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles After using the app for a while… 💥 🤔(ok something’s missing)

Slide 29

Slide 29 text

PendingIntent mutability ● Important changes for the Notifications. ● A mutability flag now must be specified to properly handle notification PendingIntent. PendingIntent.getActivity( context, randomRequestCode(), this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) PendingIntent.getBroadcast( context, notificationId, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE ) Available since Android 12

Slide 30

Slide 30 text

PendingIntent mutability ● Update to the latest WorkManager stable version! It uses PendingIntent. ● And check other 3rd party libraries which can affect your apps! 💥🥲 dependencies { def work_version = "2.7.1" // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" }

Slide 31

Slide 31 text

System dialogs ● Before Android 12, ability to close system dialogs but you get a warning in the Logcat ● From Android 12, calling this function will throw a SecurityException 💥🥲 sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) E ActivityTaskManager Permission Denial: \ android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from \ com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, \ dropping broadcast.

Slide 32

Slide 32 text

Don’t be afraid to run Lint analysis 💡Half way Tip

Slide 33

Slide 33 text

Bluetooth ● 2 new permissions to manage Bluetooth! ● Major change: Bluetooth permissions are now runtime! ● Runtime… so now we must ask the users while using the app… 😅 override fun requestBluetoothPermission(requestCode: Int) { if (osVersionProvider.isAtLeastS) { requestPermission( requestCode, BLUETOOTH_CONNECT ) } }

Slide 34

Slide 34 text

Bluetooth 🤯 ● Context: we want to connect to the user bluetooth headset ● We use Bluetooth API to get the device name… but not to route the audio to the device 😮 ● A bit complex isn’t it?! 😅 👀 Let’s go to Android Studio

Slide 35

Slide 35 text

Don’t be afraid to explore the Android source code and associated libraries 💡New tip!

Slide 36

Slide 36 text

Subtle but impactful improvements! Android 12 and performances 📊

Slide 37

Slide 37 text

Trampoline notification ● Android 12 restricts the trampoline effect with notifications ● It’s now forbidden to open an Activity from a Service or a BroadcastReceiver Notification Service / BroadcastReceiver Activity

Slide 38

Slide 38 text

Trampoline notification // We define a Receiver which can start an Activity class NotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // Trampoline effect context.startActivity( Intent(context, RandomActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK } ) } } ● Android 12 restricts the trampoline effect with notifications ● It’s now forbidden to open an Activity from a Service or a BroadcastReceiver

Slide 39

Slide 39 text

Trampoline notification // Let’s build the notification with the Broadcast val broadcastIntent = Intent(context, NotificationReceiver::class.java) val actionIntent = PendingIntent.getBroadcast( context, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.icon) .setContentTitle("Android12") .setContentText("Notification trampoline") .addAction(R.drawable.icon, "Open activity", actionIntent) .build() notificationManager.notify(getUniqueId(), notification) Indirect notification activity start (trampoline) from PACKAGE_NAME, this should be avoided for performance reasons. ● Android 12 restricts the trampoline effect with notifications ● It’s now forbidden to open an Activity from a Service or a BroadcastReceiver

Slide 40

Slide 40 text

Trampoline notification ● Android 12 restricts the trampoline effect with notifications ● It’s now forbidden to open an Activity from a Service or a BroadcastReceiver ● And to fix it? 🤔 val activityIntent = Intent(context, RandomActivity::class.java) // Let’s create our PendingIntent val resultPendingIntent = TaskStackBuilder.create(context).run { addNextIntentWithParentStack(activityIntent) getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } Trampoline notification ✅

Slide 41

Slide 41 text

Foreground service ● It’s forbidden to launch Services while the app is in background ● Alternatively, you can use expedited work with WorkManager Fatal Exception: android.app.ForegroundServiceStartNotAllowedException startForegroundService() not allowed due to mAllowStartForeground false: service com.helge.droiddashcam/.service.RecorderService // Build the intent for the service val intent = Intent(...) applicationContext.startForegroundService(intent)

Slide 42

Slide 42 text

Test, Test, Test, Test, Test, Test to avoid surprises 🥲 💡Last tip ! (and sometimes bad surprises 🙃)

Slide 43

Slide 43 text

Ladies and gentlemen, the telephony stack 🥁🥁🥁🥁🥁🥁

Slide 44

Slide 44 text

Telephony stack ● Nothing in the migration guide for Android 12 ● No Lint errors when launching the analysis ● But some methods are deprecated… 👀 Let’s go to Android Studio

Slide 45

Slide 45 text

Things to keep in mind while migrating to Android

Slide 46

Slide 46 text

Key points 🔬Test beta versions as soon as possible to prepare the migration ⚠ Beware 3rd party libraries and deprecated methods ! 💾 Go check the Android source code from time to time

Slide 47

Slide 47 text

Resources Android 12 documentation https://www.android.com/android-12 https://developer.android.com/about/versions/12 Android Code Search https://cs.android.com/ Play’s Target API Requirements https://android-developers.googleblog.com/2022/04/expanding-plays-target-level-api-re quirements-to-strengthen-user-security.html

Slide 48

Slide 48 text

????

Slide 49

Slide 49 text

Thanks! … and good luck 😅 Julien Salvi - Android GDE @JulienSalvi