<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های حسین صفدری</title>
        <link>https://virgool.io/feed/@021hossein</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-06-21 11:46:13</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/61087/avatar/0tzNZI.jpeg?height=120&amp;width=120</url>
            <title>حسین صفدری</title>
            <link>https://virgool.io/@021hossein</link>
        </image>

                    <item>
                <title>دیگه توکن فایربیس در سمت کلاینت جنریت نمیشه</title>
                <link>https://virgool.io/@021hossein/%D8%AF%DB%8C%DA%AF%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D9%81%D8%A7%DB%8C%D8%B1%D8%A8%DB%8C%D8%B3-%D8%AF%D8%B1-%D8%B3%D9%85%D8%AA-%DA%A9%D9%84%D8%A7%DB%8C%D9%86%D8%AA-%D8%AC%D9%86%D8%B1%DB%8C%D8%AA-%D9%86%D9%85%DB%8C%D8%B4%D9%87-btztl3aylpiz</link>
                <description>در ایران ما فقط از سرویس پوش نوتیفیکیشن فایربیس یا (Firebase Cloud Messaging) میتونستیم استفاده کنیم که اونم بدلیل عدم ایجاد FCM Token غیرقابل استفاده شده.(‏FCM Token) یا Firebase Cloud Messaging Token چیه؟برای دریافت پوش نوتیفیکیشن در سمت کلاینت لازم هستش که فایربیس برای هر دیوایس یک توکن جنریت کند که به ما این امکان را می‌دهد که بتوانیم از سمت سرور برای کاربران خاص پوش نوتیفیکیشن ارسال کنیم.مشکلی که الان پیش امده اینه که دیگه توکن جدید برای کاربران با IP ایران ایجاد نمیشه ولی همچنان برای آن دسته از کاربران که قبلا براشون توکن ایجاد شده، پوش نوتیفیکیشن‌ها رو دریافت میکنند.اما بعضی وقتا برای کاربرانی که از قبل براشون توکن ایجاد شده لازم هست توکن جدید ایجاد بشه. مثلا زمانی که کاربر برنامه را حذف و یا دوباره نصب میکند و یا داده‌های برنامه را پاک میکند توکن قبلی منقضی میشه و لازم یک توکن جدید ایجاد شود. از چه زمانی این مشکل ایجاد شدهمن اولین بار دیروز یعنی شنبه ۱۵ مرداد با متوجه وجود این مشکل شدم این در حالیه که روز قبلش این مشکل وجود نداشت این یعنی احتمالا این مشکل از ۱۴ یا ۱۵ مرداد ایجاد شده.سرویس‌های جایگزین FCMاکثر سرویس‌های موجود برای ارسال پوش نوتیفیکیشن چه داخلی چه خارجی، از FCM برای ارسال پوش نوتیفیکیشن در اندروید اسفاده می‌کنند. که با توجه به این که خود FCM غیرقابل استفاده شده، نمی‌توانند جایگزین FCM باشند.بعضی از سرویس‌هایی خود از FCM استفاده می‌کنند:پوشهنجوا‏Kumulos ‏OneSignal‏Yandex AppMetrica‏Pusherاما سرویس‌هایی هم هستند که از FCM برای ارسال پوش نوتیفیکیشن استفاده نمی‌کنند و می‌توانند جایگزین خوبی برای FCM باشند. Pushy یکی از آن‌هاست.‏Pushy یک سرویس تخصصی ارسال پوش‌ نوتیفیکیشن هست. که میتونه جایگزین خوبی برای FCM در ایران باشه.از وبسایت Pushy:آیا Pushy به FCM وابسته است؟راهنمای کوچ از FCM به PUSHY در اندرویدپی نوشت:لطفا تجربه خودتون را در مواجه با این مشکل کامنت کنید. اگه راه‌حلی به نظرتون میرسه یا جایگزین مناسب دیگری برای FCM سراغ دارید هم کامنت کنید.</description>
                <category>حسین صفدری</category>
                <author>حسین صفدری</author>
                <pubDate>Tue, 09 Aug 2022 00:28:58 +0430</pubDate>
            </item>
                    <item>
                <title>کارکتر اتصال مجازی در یونیکد و کاربرد آن در توسعه نرم‌افزار</title>
                <link>https://virgool.io/@021hossein/zero-width-joiner-gsfcccykt6wq</link>
                <description>کارکتر اتصال مجازی یا (Zero-Width Joiner) یک کارکتر کنترلی در یونیکد هستش و در برخی از خط‌های پیچیده مثل فارسی، عربی و هندی کاربرد داره. اضافه کردن این کارکتر به حروف متصل باعث می‌شود، حرف به شکل کوچکش نمایش داده شود.کد این کارکتر در یونیکد (U+200D) است و در جاهای دیگر به این شکل میشه ازش استفاد کرد:Unicode Character &#039;ZERO WIDTH JOINER&#039;:  (U+200D)
UTF-8 (hex):  e2808d
UTF-8 (binary):  11100010:10000000:10001101
UTF-16 (hex):  0x200D
UTF-16 (decimal):  8205
C/C++/Java/\u200D&amp;quot
Python3: u&amp;quot\u200D&amp;quot

و اما کاربردالان دیگه تقریبا در اکثر برنامه‌ها قابلیت جستجو دیده میشه، شما هم اگر قبلا در برنامه هایتان قابلیت جستجو را پیاده‌سازی کرده باشید احتمالا متوجه همچین مشکلی شده‌اید که امکان توپر کردن &quot;بخشی از یک کلمه&quot; وجود ندارد.اسکرین شات از وبسایت موتور جستحوی DuckDuckGoبرای مثال به اسکرین شات بالا توجه کنید کلمه &quot;برای&quot; به شکل &quot;ب‌رای&quot; نمایش داده شده. به همین دلیل هستش که هیچ‌کدام از سایت‌ها و برنامه‌های فارسی از این قابلیت در نمایش پیشنهادهای جستجو استفاده نمی‌کنند. حتی در گوگل هم این امکان را در جستجوی عبارت های فارسی و عربی غیرفعال شده است.راه حل:قبل از این که سراغ راه حل بریم،‌ ابتدا دلیل به وجود آمدن این مشکل را برسی میکنم.مثال ب&lt;b&gt;رای تشبیه&lt;/b&gt;
خروجی:
مثال ب‌رای تشبیه
همان طور که قطعه کد بالا مشاهده می‌کنید برای تغییر استایل بخشی از متن عبارتی به قبل و بعد آن اضافه می‌شود این عبارت‌ها در نهایت از دید کاربر پنهان می‌ماند اما باعث می‌شوند کارکترهای متصل، جدا از هم نمایش داده شوند. اضافه کردن کارکتر اتصال مجازی (ZWJ) در جاهای مناسب می‌تونه این مشکل به راحتی حل کند.پیاده سازی در کاتلیندر ادامه پیاده‌سازی این راه‌حل را با زبان برنامه‌نویسی کاتلین در اندروید رو برسی می‌کنیم.اسکرین‌شات خروجی نهایی برنامهتابع Char.isConnectable() امکان اتصال کارکتر مورد نظر به کارکتر بعدی را مشخص می‌کند.نکته: این تابع فقط از کارکترهای قابل اتصال خط فارسی و عربی استاندارد پشتیبانی می‌کند در صورت نیاز به پشتیبانی خط‌هایی مثل کوردی می‌توانید کارکترهای قابل اتصال آن خط‌ها را به تابع اضافه کنید.  private fun Char.isConnectable(): Boolean {
    return &amp;quotيبپتثجچحخسشصضطظعغفقکگلمنهیكيئ&amp;quot.indexOf(this) != -1
}تابع getSpannableString() دو پارامتر از نوع String را از ورودی دریافت می‌کند یکی [query] که بخشی از متن که قرار است توپر نمایش داده شود و دیگری [text] که کل متن می‌باشد. خروجی تابع نمونه از کلاس SpannableString می‌باشد. نکته: در تابع زیر فرض بر این گرفته شده که مقدار پارامتر [query] همیشه از از شروع کلمه آغاز میشه. در غیر این صورت هیچ قسمتی از متن توپر نخواهد شد. fun getSpannableString(text: String, query: String): SpannableString {
    val zwj = Char(0x200D).toString() // Zero Width Joiner Character

    if (query.length &gt; text.length) return SpannableString(text)

    val firstMatch = &amp;quot\\b$query&amp;quot.toRegex().find(text)?: return SpannableString(text)

    val startIndex = firstMatch.range.first
    var endIndex = startIndex + query.length

    val s = StringBuilder(text)

    if (s[endIndex-1].isConnectable2() &amp;&amp; s[endIndex].isWhitespace().not()) {
            s.insert(endIndex, zwj + zwj)
            endIndex += 1
    }

    return SpannableString(s.toString()).apply {
        setSpan(
            StyleSpan(Typeface.BOLD),
            startIndex,
            endIndex,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        setSpan(
            ForegroundColorSpan(Color.BLACK),
            startIndex,
            endIndex,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
}مثال برای نحوه استفاده از تابع بالا:textview.text = getSpannableString(text = &amp;quotحسین صفدری&amp;quot, query= &amp;quotحسی&amp;quot)</description>
                <category>حسین صفدری</category>
                <author>حسین صفدری</author>
                <pubDate>Mon, 11 Oct 2021 20:56:21 +0330</pubDate>
            </item>
            </channel>
</rss>