<?xml version="1.0" encoding="utf-8"?>
			<rss version="2.0">
				<channel>
					<title>Le news di Xabaras.it</title>
					<description>Portale Web di informazione, scienza e tecnologia</description>
					<link>http://www.xabaras.it/</link>
					<language>it-IT</language>
					<lastBuildDate>Thu, 5 Mar 2026 11:28:02 +0100</lastBuildDate>
					<generator>Xabaras.it Feedgen 5.0</generator>
					<image>
						<title>Le news di Xabaras.it</title>
						<url>http://www.xabaras.it/news_img/mlogo.gif</url>
						<link>http://www.xabaras.it/</link>
						<description>Xabaras.it</description>
					</image>
		<item>
							<title><![CDATA[Toast Messages in Compose Multiplatform]]></title>
							<link>https://www.xabaras.it/posts/727</link>
							<description><![CDATA[<p class="graf graf--p">In 2025 I&rsquo;ve been shifting my focus more and more towards <strong class="markup--strong markup--p-strong">Compose Multiplatform (CMP)</strong>. The ability to share not just logic, but the entire UI across Android, iOS, Desktop, and The world Web using the technological stack you&rsquo;re used to is a game-changer.</p>
<p class="graf graf--p">However, as we move away from platform-specific APIs, we often hit those small &ldquo;quality of life&rdquo; roadblocks. One of these, although it looks pretty basic, are Toast messages.</p>
<p></p>
<p class="graf graf--p">If you&rsquo;re coming from native Android development, you&rsquo;re used to a simple <em class="markup--em markup--p-em">Toast.makeText(&hellip;).show()</em>, but, in the multiplatform world, there is no &ldquo;common&rdquo; toast.</p>
<p class="graf graf--p">While you could reach for the <em class="markup--em markup--p-em">expect/actual</em> pattern to wrap native Toast on Android and UIAlertController or custom UIViews on iOS, this approach often leads to inconsistent behavior and design headaches.</p>
<p class="graf graf--p">Today, I want to talk about how to implement a pure Compose-based toast system that works seamlessly across all targets.</p>
<h4 class="graf graf--h4">The Problem with Platform-Specific Toasts</h4>
<p class="graf graf--p">When you implement toasts via expect/actual, you face several challenges:</p>
<ul class="postList">
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Design Disparity</strong>: Android has its native Toast, but it requires a Context, iOS doesn&rsquo;t have a direct equivalent (usually requiring a custom UIView or UIAlertController), Desktop/Web need a custom Compose-based overlay</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Context Management</strong>: on Android you need a Context, which isn&rsquo;t always easy to grab from shared business logic.</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Positioning</strong>: iOS users expect notifications near the top of the screen(avoiding the Dynamic Island), while Android users expect them at the bottom.</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Flexibility</strong>: Native toasts are notoriously hard to customize beyond simple text.</li>
</ul>
<h4 class="graf graf--h4">The Pure Compose&nbsp;Approach</h4>
<p class="graf graf--p">The idea is to build the toast system entirely in Compose, using an event based implementation to handle requests to show toast messages and keep a consistent behavior while still respecting platform-specific quirks.</p>
<h4 class="graf graf--h4">The Toast</h4>
<p class="graf graf--p">First of all we need to define what a Toast is.<br />As most will already know a Toast is a short, informational message that an app displays briefly near one edge of the screen and it generally contains simple text.</p>
<p class="graf graf--p">Thus, in the most simple implementation a Toast will have:</p>
<ul class="postList">
<li class="graf graf--li">A <strong class="markup--strong markup--li-strong">message</strong></li>
<li class="graf graf--li">A <strong class="markup--strong markup--li-strong">duration</strong></li>
<li class="graf graf--li">An <strong class="markup--strong markup--li-strong">alignment</strong> on the screen (e.g. BottomCenter)</li>
</ul>
<p class="graf graf--p">This can be hold in a simple Toast class:</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">class Toast(
    val message: String,
    val durationMillis: Long,
    val alignment: Alignment
)</pre>
<p>&nbsp;</p>
<h4 class="graf graf--h4">Managing the requests to show a&nbsp;Toast</h4>
<p class="graf graf--p">To make things work globally I needed a way to manage requests to show a Toast coming from everywhere in my app, so I went for an event based solution.</p>
<p class="graf graf--p">I used a <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">MutableSharedFlow</em></strong> to handle Toast Events asynchronously and enable the UI to requesting for a toast message to be shown</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">private object ToastEvents {
    private val _events = MutableSharedFlow()
    val events = _events.asSharedFlow()

    suspend fun publish(event: Toast) {
        _events.emit(event)
    }

    suspend fun subscribe(onEvent: (Toast) -&gt; Unit) {
        events.collectLatest { event -&gt;
            currentCoroutineContext().ensureActive()
            onEvent(event)
        }
    }
}</pre>
<p>&nbsp;</p>
<p class="graf graf--p">This way I&rsquo;m able to subscribe to the toast events flow and handle Toast visualization seamlessly.</p>
<h4 class="graf graf--h4">The Container (The &ldquo;Magic&rdquo;&nbsp;Sauce)</h4>
<p class="graf graf--p">The heart of the implementation is the <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">ToastContainer</em></strong>. A component wrapping the entire application to show the toast on top of the app&rsquo;s content.</p>
<p class="graf graf--p">This is where content rendering and platform specific positioning happen.</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">@Composable 
fun App() { 
  MaterialTheme { 
    ToastContainer(modifier = Modifier.fillMaxSize()) { 
      // Put here your app's content 
    } 
  } 
}</pre>
<p>&nbsp;</p>
<p class="graf graf--p">This component is responsible for subscribing to the <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">ToastEvents</em></strong> Flow</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">LifecycleEventEffect(Lifecycle.Event.ON_START) { 
        coroutineScope.launch { 
            ToastEvents.subscribe { 
                toast = it 
 
                if ( toast?.message?.isBlank() == true ) 
                    return@subscribe 
 
                if ( toast?.durationMillis == 0L ) 
                    return@subscribe 
 
                coroutineScope.launch { 
                    alignment = toast.alignment 
                    shouldShowToast = true 
                    delay(toast?.durationMillis ?: 0L) 
                    shouldShowToast = false 
                } 
            } 
        } 
    }</pre>
<p>&nbsp;</p>
<p class="graf graf--p">handling actual toast showing logic and defining the toast layout.</p>
<p class="graf graf--p">I implemented it as a <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">BoxWithConstraints</em></strong> composable to better suit alignment needs.<br />Using <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">AnimatedVisibility</em></strong>, we can create a smooth entrance and exit for the toast message. I opted for a combination of <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">fadeIn</em></strong> and <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">expandHorizontally</em></strong> to give it a modern, &ldquo;pill-like&rdquo; feel.<br />The container also automatically adjusts colors based on isSystemInDarkTheme(), ensuring the toast is always readable:</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">BoxWithConstraints( 
        modifier.fillMaxSize() 
    ) { 
        content.invoke() 
        AnimatedVisibility( 
            visible = shouldShowToast, 
            enter = fadeIn() + expandHorizontally(expandFrom = Alignment.CenterHorizontally), 
            exit =  shrinkHorizontally(shrinkTowards = Alignment.CenterHorizontally) + fadeOut(), 
            modifier = Modifier.align(alignment) 
        ) { 
            Row( 
                modifier = Modifier 
                    .background(if ( isDarkTheme ) Color.White else Color.Black, RoundedCornerShape(24.dp)) 
                    .widthIn(ButtonDefaults.MinWidth, 300.dp) 
                    .padding(horizontal = 12.dp, vertical = 5.dp), 
            ) { 
                Text( 
                    text = toast?.message ?: "", 
                    textAlign = TextAlign.Center, 
                    color = if (isDarkTheme) Color.Black else Color.White, 
                    fontWeight = FontWeight.Medium, 
                    maxLines = 1, 
                    overflow = TextOverflow.Ellipsis, 
                    fontSize = 12.sp 
                ) 
            } 
        } 
    }</pre>
<p>&nbsp;</p>
<p class="graf graf--p">In order to handle the different platforms I had to complicate a bit the things to handle alignment based on the platform</p>
<p class="graf graf--p">&nbsp;</p>
<pre class="brush: kotlin">when(platform.name) { 
      PlatformName.IOS -&gt; Alignment.TopCenter 
      PlatformName.JVM -&gt; Alignment.BottomEnd 
      else -&gt; Alignment.BottomCenter 
}</pre>
<p>&nbsp;</p>
<p class="graf graf--p">and padding based on the positioning of the toast</p>
<pre class="brush: kotlin">private fun getPaddingValues(alignment: Alignment) : PaddingValues { 
    return when(alignment) { 
        Alignment.TopStart -&gt; PaddingValues(top = 24.dp, start = 24.dp) 
        Alignment.TopCenter -&gt; PaddingValues(top = 24.dp) 
        Alignment.TopEnd -&gt; PaddingValues(top = 24.dp, end = 24.dp) 
        Alignment.BottomStart -&gt; PaddingValues(bottom = 24.dp, start = 24.dp) 
        Alignment.BottomCenter -&gt; PaddingValues(bottom = 24.dp) 
        Alignment.BottomEnd -&gt; PaddingValues(bottom = 24.dp, end = 24.dp) 
        Alignment.CenterStart -&gt; PaddingValues(start = 24.dp) 
        Alignment.CenterEnd -&gt; PaddingValues(end = 24.dp) 
        else -&gt; PaddingValues(0.dp) 
    } 
}</pre>
<p class="graf graf--p">I thought these could be sensed defaults but you can have your own.</p>
<p class="graf graf--p">Finally, I only had to define the public API for showing a toast from inside an app&rsquo;s code:</p>
<pre class="brush: kotlin">/** 
 * Displays a text-only toast message. 
 * 
 * @param message The text message to display. 
 * @param durationMillis The duration the toast remains visible (defaults to 3000ms). 
 * @param alignment The screen alignment for the toast (defaults to platform-specific Auto). 
 */ 
fun showToast(message: String, durationMillis: Long = ToastDefaults.DURATION_DEFAULT, alignment: Alignment = ToastDefaults.DEFAULT_ALIGNMENT) { 
    val coroutineScope = CoroutineScope(Dispatchers.Default) 
    coroutineScope.launch { 
        ToastEvents.publish(Toast(message, durationMillis, alignment = alignment)) 
    } 
}</pre>
<p class="graf graf--p">and call it like this</p>
<pre class="brush: kotlin">ToastContainer { 
    Column { 
        Button(onClick = { showToast("Hello Multiplatform!") }) { 
            Text("Show Toast") 
        } 
    } 
}</pre>
<p class="graf graf--p">All of this can be extended at own will, so far I added the ability to have an icon on one side of the text or even pass a custom composable to the toast to allow maximum personalization.</p>
<h4 class="graf graf--h4">Why this&nbsp;works</h4>
<ul class="postList">
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Pure Compose</strong>: No expect/actual bridges for the UI.</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Context-Free</strong>: You can trigger a toast from a simple function call</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Adaptive</strong>: It automatically handles padding and alignment differently for iOS (Top), Android (Bottom), and Desktop (Bottom-End)</li>
<li class="graf graf--li"><strong class="markup--strong markup--li-strong">Customization</strong>: you can have your own positioning and content</li>
</ul>
<p class="graf graf--p">Building for multiplatform shouldn&rsquo;t mean sacrificing the user experience or writing messy bridges for every tiny UI element.</p>
<p class="graf graf--p">By moving the toast logic into a shared Compose container, we gain total control over the look and feel while keeping the API dead simple.</p>
<p class="graf graf--p">Hope this little description of my experience with Toasts could help you with your Compose Multiplatform apps.</p>
<p class="graf graf--p">I&rsquo;ve wrapped the whole implementation&acirc;&Acirc;Â&Acirc;Â&mdash;&acirc;&Acirc;Â&Acirc;Âincluding the custom composable support, the event system, and the platform-aware positioning&acirc;&Acirc;Â&Acirc;Â&mdash;&acirc;&Acirc;Â&Acirc;Âinto a library called <strong class="markup--strong markup--p-strong">mToast</strong>.<br />You can check out the full source code and the sample app on GitHub:</p>
<p class="graf graf--p"><a href="https://github.com/xabaras/m-toast" target="_blank"></a></p>
<p class="graf graf--p">and use it from maven central in your project</p>
<pre class="brush: kotlin">kotlin { 
    sourceSets { 
        commonMain.dependencies { 
            implementation("it.xabaras:mtoast:0.2.0") 
        } 
    } 
}</pre>
<p class="graf graf--p">Thanks for reading, your experience and opinions are welcome in the comments.</p>]]></description>
							<guid>https://www.xabaras.it/posts/727</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Thu, 5 Mar 2026 11:15:14 +0100</pubDate>
						</item>
		<item>
							<title><![CDATA[Handling Permissions in Compose Multiplatform]]></title>
							<link>https://www.xabaras.it/posts/725</link>
							<description><![CDATA[<p>Recently I&rsquo;ve been developing more Compose Multiplatform apps rather than separate apps for Android and iOS, so I&rsquo;ve been searching for an easy way to manage permissions across the platforms w/o the need for third party libraries.</p>
<p></p>
<p id="9cf3" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Someone could ask: <strong class="ld fx">Why would you need to write permission handling code on your own while there&rsquo;s many good libraries out there to do the same?</strong></p>
<p id="d707" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Actually there&rsquo;s more than one reason why one would do so.</p>
<p id="b565" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">First of all I wanted to clearly understand how permissions are handled across platforms (especially between Android and iOS), then I wanted to minimize the number of dependencies needed to develop a large app, finally I wanted to try and find my way through the permissions woods.</p>
<p id="e925" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Obviously anyone can come out with the solution which better suits their needs, what follows is just my way, but you can try and find your own.</p>
<p id="3f96" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">I went for expected/actual object classes and lambda expressions to implement my solution since they looked the most straightforward way to me, for you don&rsquo;t have to instantiate new objects in the native code of the Android and iOS apps and you don&rsquo;t need to define classes or interfaces for the callbacks, which means less boilerplate code.</p>
<p id="9170" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">What follows is an exemple wich uses the Contacts permission but it can easily be adapted to every kind of permission with proper permission handling code on both Android and iOS.</p>
<p id="8246" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Now let&rsquo;s see what and where needs to be made in a Compose Multiplatform project in order to get the permissions thing done.</p>
<p id="a073" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">In my description I assume the project to having the standard structure of a CMP project as created by the <a class="ag mn" href="https://kmp.jetbrains.com/" rel="noopener ugc nofollow" target="_blank">Kotlin Multiplatform Wizard</a>.</p>
<h1 id="e346" class="mo mp fw bf mq mr ms mt mu mv mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl bk" data-selectable-paragraph="">Common part</h1>
<p id="6fbb" class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">First of all I created a new object class (I called it ContactsPermissionHandler) in the <strong class="ld fx"><em class="nr">commonMain</em></strong> project</p>
<pre class="brush: kotlin">expect object ContactsPermissionHandler {
    fun isPermissionGranted() : Boolean
    fun isPermissionPermanentlyDenied() : Boolean
    fun requestPermission(
        callback: (isGranted: Boolean, isPermanentlyDenied: Boolean) -&gt; Unit
    )
}</pre>
<div class="fp fq fr fs ft">
<div class="ac cb">
<div class="ci bh ew ex ey ez">
<p id="4090" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">This is to define the expected API for the permission handler.</p>
<p id="6ce9" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Then I implemented the actual handlers both on Android and iOS.</p>
</div>
</div>
</div>
<div class="ac cb fk ob oc od">&nbsp;</div>
<div class="fp fq fr fs ft">
<div class="ac cb">
<div class="ci bh ew ex ey ez">
<h1 id="ec52" class="mo mp fw bf mq mr oi mt mu mv oj mx my mz ok nb nc nd ol nf ng nh om nj nk nl bk" data-selectable-paragraph=""><strong class="am">Android</strong></h1>
<p id="5632" class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">As usual in an Android project, I added the <strong class="ld fx">READ_CONTACTS</strong> permission to the <em class="nr">AndroidManifest.xml</em> file of the <strong class="ld fx"><em class="nr">androidMain</em></strong> project</p>
<pre class="brush: xml">&nbsp;</pre>
<p class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">Then added a new ContactsPermissionHandler object class to the <strong class="ld fx"><em class="nr">androidMain</em></strong> project for providing the actual implementation of the three methods defined in the common part.</p>
<pre class="brush: kotlin">actual object ContactsPermissionHandler {
  private lateinit var _activity: ComponentActivity
  private lateinit var _callback: (isGranted: Boolean, isExplicitlyDenied: Boolean) -&gt; Unit
  private lateinit var requestContactPermissionLauncher: ActivityResultLauncher

  actual fun isPermissionGranted() : Boolean {
    return ContextCompat.checkSelfPermission(
      _activity,
      Manifest.permission.READ_CONTACTS
    ) == PackageManager.PERMISSION_GRANTED
  }

  actual fun isPermissionPermanentlyDenied() : Boolean {
    return ContextCompat.checkSelfPermission(
      _activity,
      Manifest.permission.READ_CONTACTS
    ) != PackageManager.PERMISSION_GRANTED &amp;&amp;
    _activity.shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS).not()
  }

  actual fun requestPermission(callback: (isGranted: Boolean, isPermanentlyDenied: Boolean) -&gt; Unit) {
    val permission = Manifest.permission.READ_CONTACTS
    when {
      ContextCompat.checkSelfPermission(
        _activity,
        permission
      ) == PackageManager.PERMISSION_GRANTED -&gt; {
        callback.invoke(true, false)
        Log.d("ContactsPermissionHandler", "Contacts Permission granted")
      }

      else -&gt; {
        Log.d("ContactsPermissionHandler", "Asking for Contacts permission")
        _callback = callback
        requestContactPermissionLauncher.launch(permission)
      }
    }
  }

  fun setActivity(activity: ComponentActivity) {
    _activity = activity
    requestContactPermissionLauncher =
      _activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -&gt;
        if (isGranted) {
          _callback.invoke(true, false)
          Log.d("ContactsPermissionHandler", "Contacts Permission granted")
        } else {
          val isPermanentlyDenied =
            _activity.shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS).not()
          _callback.invoke(false, isPermanentlyDenied)
          Log.d("ContactsPermissionHandler", "Contacts Permission denied")
        }
      }
  }
}</pre>
<p class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">As you can see, aside from the implementation of the methods defined in the expected object class, this class has a <strong class="ld fx"><em class="nr">setActivity()</em></strong> additional method wich needs to be called in the <strong class="ld fx"><em class="nr">onCreate()</em></strong> method of the activity that will actually request the permission (e.g. <em class="nr">MainActivity.kt</em>)</p>
<pre class="brush: kotlin">override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  ContactsPermissionHandler.setActivity(this)

  ...

  setContent {
    App()
  }
}</pre>
<p id="5566" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">This happens since you need a running activity in order to register and listen for a permission request result.</p>
<h1 id="dcb9" class="mo mp fw bf mq mr ms mt mu mv mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl bk" data-selectable-paragraph="">iOS</h1>
<p id="bc2a" class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">As for the Android counterpart, on iOS I added a proper key/value pair in the <em class="nr">Info.plist</em> file of the <strong class="ld fx"><em class="nr">iosApp</em></strong> project to explain why the app needs the contacts permission (on iOS it is required in order to be able to use a given permission)</p>
<pre class="brush: xml">NSContactsUsageDescription
To send messages to the user's contacts</pre>
<p class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">After this I added a new ContactsPermissionHandler object class in the <strong class="ld fx"><em class="nr">iosMain</em></strong> project to provide the actual implementation of the three methods defined in the <strong class="ld fx"><em class="nr">commonMain</em></strong>.</p>
<pre class="brush: kotlin">actual object ContactsPermissionHandler {

    actual fun isPermissionGranted() : Boolean {
        return CNContactStore.authorizationStatusForEntityType(CNEntityType.CNEntityTypeContacts) == CNAuthorizationStatusAuthorized
    }

    actual fun isPermissionPermanentlyDenied(): Boolean {
        return CNContactStore.authorizationStatusForEntityType(CNEntityType.CNEntityTypeContacts) == CNAuthorizationStatusDenied ||
                CNContactStore.authorizationStatusForEntityType(CNEntityType.CNEntityTypeContacts) == CNAuthorizationStatusRestricted
    }

    actual fun requestPermission(callback: (isGranted: Boolean, isPermanentlyDenied: Boolean) -&gt; Unit) {
        val store = CNContactStore()
        when (CNContactStore.authorizationStatusForEntityType(CNEntityType.CNEntityTypeContacts)) {
            CNAuthorizationStatusNotDetermined -&gt; {
                store.requestAccessForEntityType(CNEntityType.CNEntityTypeContacts) { granted, error -&gt;
                    if (granted) {
                        callback.invoke(true, false)
                    } else {
                        callback.invoke(false, false)
                    }
                    error?.let {
                        NSLog("ERROR: [ContactsPermissionHandler] ${it.description}")
                    }
                }
            }
            CNAuthorizationStatusDenied -&gt; {
                callback.invoke(false, true)
                NSLog("INFO: [ContactsPermissionHandler] Permission denied")
            }
            CNAuthorizationStatusRestricted -&gt; {
                callback.invoke(false, true)
                NSLog("INFO: [ContactsPermissionHandler] Permission restricted")
            }
            CNAuthorizationStatusAuthorized -&gt; {
                callback.invoke(true, false)
                NSLog("INFO: [ContactsPermissionHandler] Permission granted")
            }
            else -&gt; {
                NSLog("ERROR: [ContactsPermissionHandler] An unknown error occurred while handling contacts permission")
            }
        }
    }
}</pre>
</div>
</div>
</div>
<p id="f301" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">The first thing you can notice is that the class is completely written in Kotlin, so there&rsquo;s no need to write native <strong class="ld fx"><em class="nr">swift</em></strong> code, plus, you don&rsquo;t need to implement anything else but the three methods defined in the <strong class="ld fx"><em class="nr">commonMain</em></strong> and you&rsquo;re all set.</p>
<h1 id="b90f" class="mo mp fw bf mq mr ms mt mu mv mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl bk" data-selectable-paragraph="">Compose</h1>
<p id="e57c" class="pw-post-body-paragraph lb lc fw ld b le nm lg lh li nn lk ll lm no lo lp lq np ls lt lu nq lw lx ly fp bk" data-selectable-paragraph="">Let&rsquo;s now se the <strong class="ld fx"><em class="nr">Compose</em></strong> part and how to actually resquest permissions in your app.</p>
<p id="9459" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Based on the code written so far you just need to call the proper methods from the ContractsPermissionHandler object class in you Compose code, as you would usually do for a class implemented in the same project, and KMP will do all the magic of executing the actual native code on each platform.</p>
<p id="1232" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Here follows a small example of how a simple Composable would look like</p>
<p class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">&nbsp;</p>
<p id="27bf" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Hope this little walkthrough on permissions could help you achieve what you want in your Compose Multiplatform apps when it comes to permissions.</p>
<p id="03ed" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Your experience and opinions are welcome in the comments.</p>
<pre class="brush: kotlin">@Composable
@Preview
fun ExamplePermissionScreen(modifier: Modifier)
{
    var isPermissionGranted by remember { mutableStateOf(ContactsPermissionHandler.isPermissionGranted())}
    Column(
        modifier = modifier.fillMaxWidth()
    ) {
        if ( isPermissionGranted ) {
            Text(
                text = "Thanks for granting the permission to access your contacts!"
            )
        } else {
            Text(
                text = "In order to send messages to your friends we will need your permission to read contacts"
            )
            Button(
                onClick = {
                        ContactsPermissionHandler.requestPermission() { isGranted, _ -&gt;
                            isPermissionGranted = isGranted
                        }
                    })
            {
                Text("Give permission")
            }
        }
    }
}</pre>
<p id="27bf" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Hope this little walkthrough on permissions could help you achieve what you want in your Compose Multiplatform apps when it comes to permissions.</p>
<p id="03ed" class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">Your experience and opinions are welcome in the comments.</p>
<p class="pw-post-body-paragraph lb lc fw ld b le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly fp bk" data-selectable-paragraph="">This post is also available on <a href="https://xabaras.medium.com/handling-permissions-in-compose-multiplatform-1fb9bb964628?sk=0aee9ec4fad0f2a1e79cb48694e47f66" target="_blank">Medium</a>.</p>]]></description>
							<guid>https://www.xabaras.it/posts/725</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Mon, 12 May 2025 11:49:31 +0200</pubDate>
						</item>
		<item>
							<title><![CDATA[Android Studio crashes on MacBook Pro 2012 after Sonoma Update [SOLVED]]]></title>
							<link>https://www.xabaras.it/posts/721</link>
							<description><![CDATA[<p>I've got a 2012 MacBook Pro which still works great for everyday use and mobile devlopment. I kept it successfully up and running all these years thanks to <a href="https://dortania.github.io/OpenCore-Legacy-Patcher/" target="_blank">OpenCore Legacy Patcher</a> project.</p>
<p>But since updating to the latest macOS iteration I started experiencing Android Studio unexpected crashes.</p>
<p>At the beginning I wasn't able to detect the cause but eventually I managed to understand the problem: Android Studio would't start at all with an exernal monitor attached to my MacBook and would crash when connecting one.</p>
<p>From there to solve the issue I had to to go past several tries, reading system logs and searching on the internet but in the end I succeeded in fixing it.</p>
<p>The crashes were due to the Metal GPU in my MacBook Pro so I had to start Android Studio via command line adding the following JVM parameter: <em><strong>-Dsun.java2d.metal=false</strong></em></p>
<p>You will agree that starting Android Studio from the command line everytime you need to use is at least annoying but you'll find that the solution is close at hand.</p>
<p>Just find the studio.vmoptions file for your Android Studio version, mine was at the following path</p>
<pre class="brush: bash">/Users/[my_username]/Library/Application Support/Google/AndroidStudio2024.1/</pre>
<p>and add the said JVM parameter at the top of the file:</p>
<p></p>
<p>Piece of cake! </p>
<p>&nbsp;</p>
<p>Your experience and opinions are welcome in the comments.</p>
<p>&nbsp;</p>
<p>This post is also available on <a title="Android Studio crashes on MacBook Pro 2012 after Sonoma Update [SOLVED]" href="https://xabaras.medium.com/android-studio-crashes-on-macbook-pro-2012-after-sonoma-update-solved-3fa7c42998fb" target="_blank">Medium</a>.</p>]]></description>
							<guid>https://www.xabaras.it/posts/721</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Mon, 17 Jun 2024 14:57:50 +0200</pubDate>
						</item>
		<item>
							<title><![CDATA[Setting a custom content description on composables with no corresponding parameter]]></title>
							<link>https://www.xabaras.it/posts/720</link>
							<description><![CDATA[<p>In Jetpack Compose a Composition describes the UI of your app and is produced by running composables. It is a tree-structure that consists of the composables that describe your UI.</p>
<p></p>
<p>Next to the Composition, there&rsquo;s another three, called the Semantics tree, which describes your UI in a different way that is understandable for Accessibility services.<br /><br />Thus everyone using Jetpack Compose to define the UI of an Android app has had to deal with contentDescription parameters to improve accessibility of said app and providing a proper description for a given portion of the UI.<br /><br />However, there are some commonly used composables (say Box or Text for instance) which don&rsquo;t have a content description parameter.<br /><br />I came into this while developing my first all compose app, when play console reported an issue with a UI componet not properly labelled for accessibility.<br />Thus I had to figure out how to set a contentDescription on that composable (it was a Box composable).<br /><br />So, what should one do when it comes to composables which don&rsquo;t allow to set a contentDescription in a straightforward way?<br /><br />The &ldquo;semantics&rdquo; modifier, which provides &ldquo;key = value&rdquo;-style setters for any Semantics property key, may come in handy.<br /><br />Suppose you have your Box with its properties and modifiers:</p>
<p>&nbsp;</p>
<pre class="brush: kotlin">Box (<br />&nbsp; contentAlignment = Alignment.CenterStart,<br />&nbsp; modifier = Modifier<br />&nbsp; .fillMaxWidth()<br />&nbsp; .background(Color.Red)<br />)</pre>
<p>&nbsp;</p>
<p>and you want to add a custom content description for it.<br /><br />As stated before, since it has not a contentDescription property, a &ldquo;semantics&rdquo; modifier can be used as follows</p>
<p>&nbsp;</p>
<pre class="brush: kotlin">Box (<br />&nbsp; contentAlignment = Alignment.CenterStart,<br />&nbsp; modifier = Modifier<br />&nbsp; .fillMaxWidth()<br />&nbsp; .background(Color.Red)<br />&nbsp; .semantics {<br />&nbsp;&nbsp;&nbsp; this.contentDescription = "Custom content description"<br />&nbsp; }<br />)</pre>
<p>&nbsp;</p>
<p>Hope the above may be of any help for those who struggle to find an answer to this issue.<br /><br />Your experience and opinions are welcome in the comments.<br /><br />This post is also available on <a title="Settings a custom content description on composables with no corresponding parameter" href="https://medium.com/@xabaras/settings-a-custom-content-description-on-composables-with-no-corresponding-parameter-9788327ddd57" target="_blank">Medium</a>.</p>]]></description>
							<guid>https://www.xabaras.it/posts/720</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Wed, 6 Dec 2023 11:24:23 +0100</pubDate>
						</item>
		<item>
							<title><![CDATA[Including an AdMob Anchored Adaptive Banner in Jetpack Compose]]></title>
							<link>https://www.xabaras.it/posts/719</link>
							<description><![CDATA[<p id="9639" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">As of now there&rsquo;s still no native support for <a class="ay hp" href="https://developer.android.com/jetpack/compose" rel="noopener ugc nofollow" target="_blank">Jetpack Compos</a>e in the official <a class="ay hp" href="https://developers.google.com/admob/android/quick-start" rel="noopener ugc nofollow" target="_blank">AdMob SDK</a> by Google.</p>
<p id="6512" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Therefore when it comes to adding Ad banners to our Jetpack Compose project we necessarily need to get back to the classic Android View system.</p>
<p></p>
<p>Fotunately there&rsquo;s a composable which comes in handy, the AndroidView composable.<br /><br />It is designed to include an Android View hierarchy in a Compose UI.<br /><br />This makes pretty straightforward to include a classic BANNER size Ad in a Compose UI</p>
<pre class="brush: kotlin">AndroidView (
  modifier = Modifier.fillMaxWidth(),
  factory = { context -&gt;
      AdView(context).apply {
          // Set the banner size          
          setAdSize(AdSize.BANNER)

          // Set the Ad unit ID
          adUnitId = "ca-app-pub-3940256099942544/6300978111"
          
          // Load the Ad
          loadAd(AdRequest.Builder().build())
      }
  }
)</pre>
<p>Basically we can<br /><br />&nbsp;&nbsp;&nbsp; add the AndroidView composable to the UI definition<br />&nbsp;&nbsp;&nbsp; create a new instace of the AdMob AdView class in the &ldquo;factory&rdquo; block of the AndroidView composable<br />&nbsp;&nbsp;&nbsp; set the AdSize<br />&nbsp;&nbsp;&nbsp; set the adUnit ID<br />&nbsp;&nbsp;&nbsp; load the Ad<br /><br />We would most likely want to use adaptive banners to maximising performance and optimizing ad size for each device.<br /><br />Suppose we want to include and adaptive banner in our UI, we will need to set the AdSize properly, according to the screen size and orientation.<br /><br />In order to do so, the AdMob SDK provides a set of methods in the AdSize class wich take in a context and a width as parameters and return an adSize.<br /><br />Let&rsquo;s assume we are including an anchored adaptive banner to our UI, what we will need to do is something like</p>
<pre class="brush: kotlin">val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
    context,
    currentScreenWidth
)</pre>
<p>&nbsp;We can use the context from the <em><strong>factory</strong></em> block, as you can see from the previous code sample about classic banners.<br /><br />For the screen width Jetpack Compose provides a simple solution via the LocalConfiguration class so we will be able to write</p>
<pre class="brush: kotlin">val currentScreenWidth = LocalConfiguration.current.screenWidthDp</pre>
<p>And to wrap it all up we can define our custom composable like this&nbsp;</p>
<p>&nbsp;</p>
<pre class="brush: kotlin">@Composable
fun AnchoredAdaptiveBanner(modifier: Modifier) {
    val currentScreenWidth = LocalConfiguration.current.screenWidthDp

    AndroidView(
        modifier = modifier,
        factory = { context -&gt;
            AdView(context).apply {
                // Compute the AdSize based on current screen width
                val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
                    context,
                    currentScreenWidth
                )
                // Set the adaptive banner size
                setAdSize(adSize)

                // Set the Ad unit ID
                adUnitId = "ca-app-pub-4440099591868517/3269775877"

                // Load the Ad
                loadAd(AdRequest.Builder().build())
            }
        }
    )
}</pre>
<p>&nbsp;Now we can include an anchored adaptive banner into our Compose UI by simply using the <em><strong>AnchoredAdaptiveBanner</strong></em> composable.<br /><br />Hope the above may be of help for those who struggle with including AdMob ads into their Compose UIs.<br /><br />Your experience and opinions are welcome in the comments.</p>
<p>&nbsp;</p>
<p>This post is also available on <a href="https://medium.com/@xabaras/including-an-admob-anchored-adaptive-banner-in-jetpack-compose-bc3fa0f6d6e3" target="_blank">Medium</a>.</p>]]></description>
							<guid>https://www.xabaras.it/posts/719</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Thu, 25 May 2023 12:51:22 +0200</pubDate>
						</item>
		<item>
							<title><![CDATA[Universally Unique IDs as a Primary Key in a Room Database]]></title>
							<link>https://www.xabaras.it/posts/718</link>
							<description><![CDATA[<p></p>
<p>&nbsp;</p>
<p>The default choice for primary key identifiers in a Room Database is usually an auto-generated Integer field, e.g.:</p>
<pre class="brush: kotlin">@PrimaryKey(autoGenerate = true) val id: Int</pre>
<p><br />Integer IDs are plain and straightforward to implement but not so easy to handle when it comes to apps wich can run on multiple devices at the same time or when you have remotely stored data with all its consequences (e.g. coping with collisions is a literal hell).</p>
<p>In many cases like the foresaid ones having Universally Unique Identifiers maight come in handy.</p>
<p id="792f" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Here follows a brief summary of how I managed to have UUIDs as primary keys in my Room Database based apps.</p>
<p id="b200" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Let&rsquo;s assume we have a simple Room entity we use to store our app&rsquo;s users, like the one below:</p>
<pre class="brush: kotlin">@Entity data class User(
  @PrimaryKey(autoGenerate = true) val id: Int = 0,
  val firstName: String,
  val lastName: String
)</pre>
<p>Room will auto-generate and auto-increment user IDs when you save it to the database.<br /><br />Now let&rsquo;s see how to achieve the same with UUIDs.<br /><br />First of all we will need to convert our id to an UUID, like this:</p>
<pre class="brush: kotlin">@Entity
data class User(
  @PrimaryKey val id: UUID,
  val firstName: String,
  val lastName: String
)</pre>
<p id="f93a" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">But, since we don&rsquo;t have a UUID field type in SQLite we will need to put some measures in place.</p>
<p id="33a9" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">In the classic method we had a default zero value for our integer id, we&rsquo;ll do the same for UUIDs, e.g.</p>
<pre class="brush: kotlin">@Entity(tableName = "users")
data class User(
  @PrimaryKey val id: UUID = UUID.fromString(DEFAULT_UUID),
  val firstName: String,
  val lastName: String
) {

    companion object {
        const val DEFAULT_UUID = "00000000-0000-0000-0000-000000000000"
    }
}</pre>
<p id="36ce" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Now that we have our entity let&rsquo;s see how to define the app&rsquo;s database.</p>
<p id="adad" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Usually you would do so</p>
<pre class="brush: kotlin">@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}</pre>
<p class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">&nbsp;Where UserDao can be defined as follows</p>
<pre class="brush: kotlin">@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll() : List

    @Query("SELECT * FROM users WHERE id = :id")
    fun getById(id: UUID) : User?

    @Insert
    fun insert(user: User)

    @Update
    fun update(user: User)

    @Delete
    fun delete(user: User)
}</pre>
<p id="7f66" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">&nbsp;But then again, since UUIDs are not natively supported we will need to find our solution.</p>
<p id="c090" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Here&rsquo;s where TypeConverters come in handy.</p>
<p id="be5c" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">We can define a proper TypeConverter to let Room convert UUIDs to/from strings</p>
<pre class="brush: kotlin">class Converters {
    
    @TypeConverter
    fun fromUUID(uuid: UUID): String? {
        if ( uuid.toString() == User.DEFAULT_UUID )
            return UUID.randomUUID().toString()

        return uuid.toString()
    }

    @TypeConverter
    fun uuidFromString(string: String?): UUID {
        string?.let {
            return UUID.fromString(string)
        }
        return UUID.fromString(User.DEFAULT_UUID)
    }

}</pre>
<p id="175b" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">As you can see the &ldquo;fromUUID&rdquo; Type Converter will generate a new UUID for User instances with default identifier.</p>
<p id="7064" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">In order for these TypeConverters to do their job you will need to add it to the AppDatabase like this</p>
<pre class="brush: kotlin">@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}</pre>
<p class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Once you did so, Room will automatically convert UUIDs to/from Strings making it all work seamlessly.</p>
<pre class="brush: kotlin">val db = Room.databaseBuilder(
  context, AppDatabase::class.java, "example-database"
).build()

val dao = db.userDao()
val user = User(firstName = "Paolo", lastName = "Montalto")

dao.insert(user)</pre>
<p id="91dd" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">This is just a sample implementation, anyone can of course find the most suitable for own needs.</p>
<p id="98aa" class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">Your experience and opinions are welcome in the comments.</p>
<p class="pw-post-body-paragraph xm xn tt nf b xo xp xq xr xs xt xu xv mp xw xx xy mu xz ya yb mz yc yd ye yf js bp" data-selectable-paragraph="">&nbsp;</p>
<p>This post is also available on <a title="Universally Unique IDs as a Primary Key in a Room Database" href="https://medium.com/@xabaras/universally-unique-ids-as-a-primary-key-in-a-room-database-f67a78bdbf4d" target="_blank">Medium</a></p>]]></description>
							<guid>https://www.xabaras.it/posts/718</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Thu, 25 May 2023 12:22:20 +0200</pubDate>
						</item>
		<item>
							<title><![CDATA[Migrate from SharedPreferences to EncryptedSharedPreferences]]></title>
							<link>https://www.xabaras.it/posts/717</link>
							<description><![CDATA[<p>Android Developers have always been used to save little pieces of data inside their apps with SharedPreferences.<br /><br />At the same time we&rsquo;ve always struggled with securing information saved inside our apps because there was no standard way to do so and third party solutions have always been buggy or not sufficiently secure.</p>
<p><a title="Migrate from SharedPreferences to EncryptedSharedPreferences" href="https://xabaras.medium.com/migrate-from-sharedpreferences-to-encryptedsharedpreferences-4859e1dadd20" target="_blank"></a></p>
<p>Fortunately, with the advent of <a class="ae jr" href="https://developer.android.com/jetpack" rel="noopener ugc nofollow" target="_blank">Android Jetpack</a>, <a class="ae jr" href="https://developer.android.com/topic/security/data" rel="noopener ugc nofollow" target="_blank">Security library</a> finally came in to help.</p>
<p><a title="Migrate from SharedPreferences to EncryptedSharedPreferences" href="https://xabaras.medium.com/migrate-from-sharedpreferences-to-encryptedsharedpreferences-4859e1dadd20" target="_blank">[Continue on Medium]</a></p>]]></description>
							<guid>https://www.xabaras.it/posts/717</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Tue, 14 Mar 2023 10:45:32 +0100</pubDate>
						</item>
		<item>
							<title><![CDATA[Sending a photo to a Telegram channel the easy way]]></title>
							<link>https://www.xabaras.it/posts/716</link>
							<description><![CDATA[<p><span style="font-size: medium;">Today we&rsquo;ll see by practical examples how to send a picture to a Telegram channel.</span></p>
<p><a title="Sending a photo to a Telegram channel the easy way" href="https://medium.com/@xabaras/sending-a-photo-to-a-telegram-channel-the-easy-way-defbfed30dd" target="_blank"></a></p>
<p>I<span style="font-size: medium;">n order to be able to do so, you will have... <a title="Sending a photo to a Telegram channel the easy way" href="https://medium.com/@xabaras/sending-a-photo-to-a-telegram-channel-the-easy-way-defbfed30dd" target="_blank">[Continue on Medium]</a></span></p>]]></description>
							<guid>https://www.xabaras.it/posts/716</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Wed, 1 Feb 2023 12:49:00 +0100</pubDate>
						</item>
		<item>
							<title><![CDATA[How I managed to install a working IntelliJ Idea CE on an ARM Chromebook]]></title>
							<link>https://www.xabaras.it/posts/715</link>
							<description><![CDATA[<p>Ho pubblicato su Medium un post dal titolo <strong>How I managed to install a working IntelliJ Idea CE on an ARM Chromebook</strong>.</p>
<p>&nbsp;</p>
<h3><a title="How I managed to install a working IntelliJ Idea CE on an ARM Chromebook" href="https://xabaras.medium.com/how-i-managed-to-install-a-working-intellij-idea-ce-on-an-arm-chromebook-68cf06f5afa7" target="_blank"><strong>How I managed to install a working IntelliJ Idea CE on an ARM Chromebook</strong></a></h3>
<p><a title="Creating attributed text from an HTML string" href="https://xabaras.medium.com/how-i-managed-to-install-a-working-intellij-idea-ce-on-an-arm-chromebook-68cf06f5afa7" target="_blank"></a></p>]]></description>
							<guid>https://www.xabaras.it/posts/715</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Wed, 12 Jan 2022 22:07:28 +0100</pubDate>
						</item>
		<item>
							<title><![CDATA[RecyclerViewSwipeDecorator v1.3]]></title>
							<link>https://www.xabaras.it/posts/714</link>
							<description><![CDATA[<p>Ho appena pubblicato la versione 1.3 della mia libreria RecyclerViewSwipeDecorator su <a title="Xabaras @ GitHub" href="https://github.com/xabaras">GitHub</a>.</p>
<p>Finalmente supporta AndroidX! <a title="RecyclerViewSwipeDecorator 1.3" href="https://github.com/xabaras/RecyclerViewSwipeDecorator" target="_blank"></a></p>]]></description>
							<guid>https://www.xabaras.it/posts/714</guid>
							<author>webmaster@xabaras.it (Paolo Montalto)</author>

							<pubDate>Wed, 28 Jul 2021 13:59:47 +0200</pubDate>
						</item>	</channel>
</rss>