<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های عباس اویسی</title>
        <link>https://virgool.io/feed/@abbas.oveissi</link>
        <description>یه دولوپر دیگه مثل بقیه دولوپرها. غیر از اینجا توی بلاگمم مینویسم http://abbas.oveissi.ir</description>
        <language>fa</language>
        <pubDate>2026-06-16 08:27:07</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2475/avatar/avatar.png?height=120&amp;width=120</url>
            <title>عباس اویسی</title>
            <link>https://virgool.io/@abbas.oveissi</link>
        </image>

                    <item>
                <title>پیاده‌سازی Deferred DeepLink‌ها</title>
                <link>https://virgool.io/MobileLab/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-deferred-deeplink-%D9%87%D8%A7-etpe5k2tuywg</link>
                <description>دو سه روز پیش توی شرکت به عنوان یه تسک فرعی، در مورد امکان پیاده‌سازی Deferred DeepLinkها توسط خودمون یه تحقیق یکی دو ساعته کردم، چون خروجیش رو وقتی برای بچه‌های شرکت نوشتم شبیه مقاله شد، گفتم اینجام بذاریم شاید برای فرد دیگه‌ای هم جذاب بود. اگر شمام نظری داشتید میتونید در قسمت نظرات بنویسید.عکس برای یه مقاله در سایت innovationm.co می‌باشد.
وقتی کاربر روی یه DeepLink کلیک میکنه، براساس اینکه App روی دستگاهش نصب باشه یا نه؟! دو حالت پیش میاد:۱- اپ (App) نصب هست: توی این حالت کاربر روی لینک کلیک میکنه و توسط App به صفحه‌ای مشخص شده توسط لینک منتقل میشه. بهش Direct DeepLink میگن.۲- اپ (App) نصب نیست: توی این حالت چون کاربر App رو نداره، با زدن روی لینک به Store منتقل میشه، و بعد از نصب، وقتی اپ رو اجرا کرد به همون صفحه مشخص شده توسط لینک منتقل میشه. لینک‌های این مدلی رو Deferred DeepLink میگن. نکته مهم اینه اینجوری نیست که اینجور لینک‌ها در ۱۰۰درصد موارد کار کنند! احتمال داره گاهی کاربر روی لینک بزنه و بعد نصب بره توی App ولی به جای خاصی منتقل نشه. (در ادامه دلیلشو میگم)هم Firebase و هم Branch.io دو حالت رو پشتیبانی میکنن. اگر بخوایم خودمون همه چیو پیاده‌سازی کنیم، حالت اول که سادس و میتونیم بدون دردسر خاصی پیاده کنیم، ولی پشتیبانی از Deferred DeepLink نیاز به کار و تست بیشتری داره. مخصوصا که یه چیز ۱۰۰درصدی نیست و همیشه باید تلاش کنی درصد مواردی که لینک کار میکنه رو بالاتر ببری.جزییات پیاده‌سازینحوه‌ی پیاده‌سازی Deferred DeepLinkها اینجوریه که وقتی کاربر روی لینک کلیک میکنه و توی مرورگر به سایت میره، اونجا یه سری اطلاعات ازش مثل مدل دیواس، آی‌پی، سایز اسکرین، سیستم‌عامل، نسخه‌سیستم‌عامل و … رو نگه میدارن. بعد وقتی کاربر اپ برای اولین‌بار اپ رو باز میکنه، دوباره همین اطلاعات رو توسط اپ به سرور میفرستن، اگر دیدن کسی با این اطلاعات قبلا روی لینکی کلیک کرده، میفهمن این همون یوزر هست و به اون صفحه‌ای که لینک مشخص کرده منتقلش میکنن. هرچقدر این Match شدن‌ها درست باشه، نشون میده کیفیت پیاده‌سازی بالاتر بوده. گاهی اوقات کاریش نمیشه کرد دیگه، مثلا کاربر روی لینک کلیک میکنه، بعد مثلا VPN وصل میکنه IPش عوض میشه، بعد میاد App رو باز میکنه!! توی این حالت خب اون Matching درست نمیشه با اینکه واقعا این همون کاربر هست. این چیزی که گفتم حالت کلی پیاده‌سازی بود. حالا با روش‌های کمکی و یا سری کانفیگ‌ها سعی میکنن درصد Match شدن رو بالاتر ببرن.بخوام یه مثال از کانفیگ بزنم، مثلا میتونه کم کردن یا زیاد کردن بازه‌ی Matching بین اطلاعات باشه، مثلا آخرین اطلاعاتم از فایریس اینه تا ۱ ساعت کاربر اگر بعد نصب App رو اجرا کنه، امکان داره Matching پیش بیاد و به صفحه خاصی که نیازه منتقل بشه. ولی بیشتر از این دیگه کار نمیکنه. این تایم برای Branch.io دو ساعت هست.بخوام یه مثال از روش کمکی بگم که به بالا رفتن Matching توی کمک میکنه (البته توی اندروید) اینه که گوگل‌پلی یه چیزی به اسم INSTALL_REFERRER داره و گوگل‌پلی میتونه بعد نصب یه سری پارامتر رو باهاش به App بده. مقاله زیر خیلی خوب پیاده‌سازی Deferred DeepLink رو با این قابلیت گوگل‌پلی توضیح داده.https://www.linkedin.com/pulse/android-deferred-deep-link-hamid-sedghi-n/ سرویس‌هایی مثل Branch.io ترکیب همه روش‌ها با هم استفاده میکنند. حتی Branch.io یه روش داره که فقط بکار سرویس‌هایی میاد که اپ‌های زیادی ازشون استفاده میکنن و اگر ما پیاده‌ کنیم خیلی سودی نبریم. در کل این دو تا مقاله Branch.io خیلی عالیه. اگر علاقمندید بخونید:https://blog.branch.io/the-importance-of-matching-accuracy-in-deep-linkinghttps://blog.branch.io/deferred-deep-linking-with-device-snapshotting</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Thu, 04 Mar 2021 03:23:42 +0330</pubDate>
            </item>
                    <item>
                <title>منابع آموزشی در رابطه با Style و Theme</title>
                <link>https://virgool.io/MobileLab/styles-and-themes-resource-links-da30hazr87pe</link>
                <description>همیشه توی ذهنم یه عذاب وجدانی داشتم از اینکه توی Theme و Styleهای اندروید ضعیف هستم. گاهی بنظرم بدرد نخور بود و گاهی خیلی پیچیده! تا اینکه تصمیم گرفتم بطور فشرده یه هفته‌ای در رابطه باهاش تحقیق کنم. یه سری مقاله خوندم و ارائه‌های مرتبط رو‌ نگاه کردم، الان دیگه واقعا دیدم به Style و Theme توی اندروید عوض شد، قبلا چون این چیزارو بلد نبودم، برای کاستومایز کردن بعضی چیزا راه‌های سختتری میرفتم. برای شروع سعی کردم چیزهایی که یاد گرفتمو روی اپ Wood Working Tools پیاده کنم. در آینده حتما بیشتر از Theme و Style توی اپ‌هام استفاده می‌کنم. در ادامه لیست مقاله‌‌ها و ارائه‌هایی که بنظر خوب بودن رو به همراه نکات جالبشون نوشتم. اگر شمام مثل من دوست دارید بیشتر با Style و Theme توی اندروید آشنا بشید، حداقل سعی کنید همه‌ی منابع زیر رو بررسی کنید.1- Android styling: themes vs styleshttps://medium.com/androiddevelopers/android-styling-themes-vs-styles-ebe05f917578این مقاله برای شروع خیلی خوبه. در رابطه با تفاوت Style و Theme صحبت می‌کنه. یکی از بدی‌های Theme و Style این هست که برای تعریف جفتشون باید از تگ یکسان Style استفاده کرد.توی مقاله یه مثال جالب میزنه، اونم اینه که اگر از attributeهای Theme توی پروژه استفاده کنیم (بجای استفاده مستقیم از ریسورسهایی مثل رنگ و …)، شبیه این میمونه توی کدهامون بجای اینکه مستقیما از یه کلاس استفاده کنیم از اینترفیس استفاده کرده باشیم. در نتیجه میتونیم پیاده‌سازیهای مختلفی برای حالت شب و روز و … داشته باشیم.بخش Scopeش هم جالبه.2- Android styling: common theme attributeshttps://medium.com/androiddevelopers/android-styling-common-theme-attributes-8f7c50c9eabaاگر مقاله اول رو خونده باشید، متوجه شدید که چرا بهتره از Attributeهای Theme استفاده کنیم. توی این مقاله میتونید Attributeهایی که خیلی پرکاربرد هستند رو یاد بگیرید.3- Android Styling: prefer theme attributeshttps://medium.com/androiddevelopers/android-styling-prefer-theme-attributes-412caa748774این مقاله هم در مورد استفاده از Attributeهای Theme هست. تقریبا همیشه باید در همه جا از Attributeها استفاده کنید، مگه حالت استثنایی باشه که خود مقاله براش یه مثال میزنه.در مورد استفاده از Attributeها توی ColorStateList هم صحبت میکنه.4 - Themes overlayAndroid Styling: themes overlayhttps://medium.com/androiddevelopers/android-styling-themes-overlay-1ffd57745207Refactoring Android Themes with Style: Theme Overlayshttps://ataulm.com/2020/05/28/refactoring-themes-with-style-theme-overlays.htmlهمیشه این Theme Overlayها برام گنگ بود، برای رنگ دادن به Toolbar ازشون استفاده کرده بودم ولی خب خیلی درک خاصی ازشون نداشتم. مثلا نمیدونستم خودم کجا باید Theme Overlay تعریف کنم. این دو تا مقاله واقعا دید خوبی بهم داد. مخصوصا بخش Theme overlays in default styles رو توی مقاله دوم خیلی دوست داشتم، تازه فهمیدم دلیل وجود Attributeیی به اسم materialThemeOverlay چیه.5- Refactoring Android Themes with StylePart 1: Restructuring Themeshttps://ataulm.com/2020/04/30/refactoring-themes-with-style.htmlPart 2: Default Styleshttps://ataulm.com/2020/05/07/refactoring-themes-with-style-default-styles.htmlPart 3: Theme Overlayshttps://ataulm.com/2020/05/28/refactoring-themes-with-style-theme-overlays.htmlاین سه مقاله‌ هم واقعا عالی و کاربردی هستن، همه چیز کامل و با مثال توضیح داده شده. قضیه مقاله‌ها اینه که تیم توسعه Monzo بعد دیدن یه ارائه توی سال ۲۰۱۹ (لینکش در ادامه هست)، تصمیم گرفته بخش Theme و Style اپ‌شون رو ریفکتور کنه و از Best Practiceهایی که توی ارائه گفته شده استفاده کنند. فرآیندی که طی کردن و تجربیاتی که بدست آوردن توی این سه مقاله نوشته شده.مقاله سوم رو توی یه آیتم دیگه هم معرفی کردم. ولی دلم نیومد اینجا سه تاشون رو دوباره با هم نذارم. اشکال نداره شما هم دوبار بخونیدش D:6- Android themes &amp; styles demystified - Google I/O 2016https://www.youtube.com/watch?v=TIHXGwRTMWIاین فیلم چند نکته باحال یاد میده:- اولیش مهم نیست ولی برام جالب بود، این بود که فهمیدم قضیه این res-auto توی آدرس xmlns چیه. D: تاحالا نمیدونستم دلیل اسمش چیه.- دومیش اینه که یه مثال از پیاده‌سازی Style و Theme دکمه (Button) توی متریال کامپوننت میزنه که خوبه. خوبیش اینه آدم میبینه پیاده‌سازیش چطوره، بعد اگر بخواد چیزی پیاده‌سازی کنه یا حتی مهمتر مثلا رنگ و قیافه Button رو عوض بکنه، راحت میتوجه بشه باید چیارو ست کنه یا تغییر بده.- سومیش اشتباهات رایجه که آخر ارائه میگه، دیدنشون خالی از لطف نیست. توی بخش اشتباهات رایج در مورد resolve شدن Attributeها صحبت میشه. اگر دوست داشتید این مقاله هم در همین رابطه هست. برای اطلاعات بیشتر میتونید بخوندیش.https://ataulm.com/2019/10/28/resolving-view-attributes.html7- Best Practices for Themes and Styles (Android Dev Summit &#x27;18)https://www.youtube.com/watch?v=sNSlDfaNq-0یه نکته‌ی خیلی جالب این فیلم برای من همون اول فیلمه که آموزش میده چطوری رنگ‌ها رو نامگذاری کنیم. مثلا اگر دیزاینر یه رنگ جدید استفاده که غیر از رنگ‌های Primary و PrimaryVariant هست، باید با چه اسمی براش Attribute درست کنیم.وسط‌های این ارائه در مورد تغییر دادن رنگ یه دکمه صحبت میکنه، حتما ببینیدش. من قبلا‌ فکر میکردم بعضی از اینا که توی stackoverflow سوال‌های مرتبط به اینجور چیزهارو جواب میدن، چقدر خفنن! واقعا نمیدونستم اینقدر راحت میشه فهمید چطوری باید ظاهر یه دکمه کاستومایز کرد.8- Developing themes with style (Android Dev Summit &#x27;19)https://www.youtube.com/watch?v=Owkf8DhAOSoدوباره توی این ارائه در مورد این صحبت میشه که چرا باید از Attributeها استفاده کرد. این نکته رو تقریبا توی بیشتر ارائه‌‌ها یا مقاله‌ها میشه دید. اگر هنوز اینکارو نمیکنید، مثالی که توی این ارائه زده رو نگاه کنید، شاید نظرتون عوض شد و در آینده همیشه از Attributeها استفاده کردید.یه بخش مهم دیگه ارائه اونجاش هست که در مورد نامگذاری Style و Themeها صحبت میکنه. اینکه چطور اون سلسله مراتبی نامگذاریشون کنید. این نامگذاری‌ Style و Themeها همیشه برای من سوال بود تا بالاخره توی این ارائه فهمیدم قضیه‌اش چیه (توی سه مقاله ریفکتورینگ هم در این رابطه صحبت میکنه، بنظرم اونو هم بخونید). حتی توضیح میده بهتره Themeها و Styleهارو توی فایل‌های جدا بذارید.آخر ارائه در مورد یه قضیه‌ای صحبت میکنه که من تا حالا فکر میکردم کار درست رو انجام میدم چون خود اندروید استودیو هم پروژه رو اینجوری میسازه، ولی خب اشتباه بوده!! اونم این بود که میگه اسم رنگ‌هارو نباید توی فایل colors.xml یه چیزهایی مثل priamaryColor و … بذاریم. باید دقیقا اسمشو مثل indigo_500 یا indigo_200 بنویسی. نامگذاری‌های انتزاعی مثل priamaryColor برای attributeهای Theme هست.9- The Components of Material Design (Android Dev Summit &#x27;18)https://www.youtube.com/watch?v=DPH3F0v1jB0این ارائه هم هست که در مورد متریال دیزاین صحبت میکنه. اگر داکیومنت متریال رو کامل بخونید و مقاله یا فیلم‌هایی که گفتمو دیدید، بیشتر بخش‌های این ارائه براتون تکراری میشه. ولی اگر وقت دارید ببینیدش. توی یه بخشش نشون میده اگر کاستوم ویو میسازید، چطوری باید داخلش از Theme و Style که بهش ست میشه استفاده کنید یا اصلا چطوری میشه Style پیشفرض براش تعریف کرد. یه مثال هم میزنه چطوری یه اپی که قبلا نوشته شده رو میشه Theme و Styleهاشو به متریال تغییر داد.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Sun, 29 Nov 2020 00:21:22 +0330</pubDate>
            </item>
                    <item>
                <title>ارائه‌های مرتبط به Coroutine در یوتیوب</title>
                <link>https://virgool.io/@abbas.oveissi/coroutines-learning-by-youtube-mblw2eaq8re1</link>
                <description>وقتی قرار شد توی پادکست کاتلین فارسی در مورد Coroutine صحبت کنم، تصمیم گرفتم در موردش عمیق‌تر بدونم. برای همین از یه روشی استفاده کردم که قبلا باهاش Dagger و RxJava رو یاد گرفته بودم. روش ساده‌‌ای هست، این مدلیه که موضوع رو باید انتخاب کرد، بعد باید توی یه مدت کوتاه و بصورت فشرده هرچی ویدئو و مقاله مرتبط به موضوع توی یوتیوب و مدیوم و … هست رو دید یا خوند. حتی اگر چیزی ازشون نشه متوجه شد هم اشکال نداره و باید باز ادامه داد. اوایلش یه مدت اذیت کننده هست ولی یهو از یه جایی به بعد همه اطلاعاتی که پراکنده از منابع متفاوت خونده شدن، بهم لینک میشن و شبیه کارتون‌های دوران بچگی، یه لامپ گنده توی مغز آدم روشن میشه. برای همین منم رفتم یوتیوب و هر ارائه‌ای که توی عنوانش Coroutine رو داشت دیدم، حالا چه کنفرانس قدیمی بود (مثل Kotlin Confهای گذشته) یا حتی به اندروید مرتبط نبود (مثل یکی دو تا فیلم در مورد استفاده از Coroutine توی Spring). نتیجه راضی کننده بود و کلی چیز جالب در مورد Coroutine و Flow یادگرفتم. وقتی ارائه‌هارو میدیدم، اونایی که دوست داشتمو یا یکم توضیح توی یه فایل می‌نوشتم، چون بنظرم شاید بدرد بقیه هم بخوره، در ادامه چیزهایی که برای خودم توی اون فایل نوشته بودمو با یکم تغییر مینویسم:ارائه KotlinConf 2017 - Introduction to Coroutineshttps://www.youtube.com/watch?v=_hfBv0a09Jc برای شروع ارائه خوبیه. البته من خیلی قدیم یبار همینجوری دیده بودمش ولی بهش دقت نکرده بودم و خیلی درکش نکرده بودم. اما این سری با تمرکز بالا دیدمش و متوجه شدم که خیلی خوب توضیح میده. میتونه دریچه‌ی ورود به coroutine باشه. ارائه دهنده اسمش Roman Elizarov هست، هرچی ارائه و مقاله از این نفر دیدید بخونید، هرچی. تک تک ارائه و مقاله‌هاش فوق‌العاده هستند. ۲ تا ارائه بعدی هم از همین نفره.ارائه KotlinConf 2017 - Deep Dive into Coroutines on JVMhttps://www.youtube.com/watch?v=YrrUCSi72E8این فیلم فوق‌العاده هست. خیلی خوبه. هر چی بگم کم گفتم. توی این ارائه در مورد این صحبت میشه coroutine چطوری پیاده سازی شده و چطوری این همه کار عجیب و غریب رو میکنه، واقعا جذابه. تا قبل از این که اینو ببینم coroutine رو خیلی خوب درک نمیکردم و فقط میدونستم چیه. ولی تازه بعد دیدن این فیلم فهمیدم چطوری کار میکنه و چطوری اون کد async رو میتونیم به شکل sync بنویسیم.ارائه KotlinConf 2018 - Kotlin Coroutines in Practicehttps://www.youtube.com/watch?v=a3agLJQ6vt8توی این ارائه Elizarov یه سیستم دانلود رو با coroutine پیاده میکنه. از همه چی توش استفاده میکنه مثل channel و … . یه قسمتش در مورد پیاده سازی coroutinescope صحبت میکنه و اینکه فرق بین متدهای suspend و scope چیه.ارائه Understand Kotlin Coroutines on Android (Google I/O&#x27;19)https://www.youtube.com/watch?v=BOHK_w09pVAاین ارائه اولش از این نظر مهمه که میگه چرا با وجود Rx و Livedata گوگل این همه پیگیر coroutine شده. بعدش یکم در مورد اینکه  coroutine چطور کار میکنه میگه. در ادامه در مورد این میگه چرا اصن بهتره اگر workermanager استفاده میکنیم از اون مدلش که‌ با coroutine کار میکنه استفاده کنیم. بعدش در مورد scopeهای از پیش آماده شده توی اندروید مثل viewmodelScope و … حرف میزنه و در آخر LiveData block رو میگه که چطوری کار میکنن و lifecycleش چطورهارائه KotlinConf 2019: Asynchronous Data Streams with Kotlin Flowhttps://www.youtube.com/watch?v=tYcqn48SMT8این ارائه در مورد Flow خیلی خوبه. فرقشو با channel میگه و در ادامه توضیحش میده و با RxJava اینام مقایسه‌اش میکنه. اگر RxJava بلد باشید، خیلی راحت میتونید با این ارائه Flow رو درک کنید.ارائه LiveData with Coroutines and Flow (Android Dev Summit &#x27;19)https://www.youtube.com/watch?v=B8ppnjGPAGEاینم خیلی خوبه، اولش از coroutine شروع میکنه و ترکیبشو با livedata اینا میگه. اینکه چطور بجای اینکه خودتون اینترفیس coroutine scope رو پیاده کنید، میتونید از scopeهای اندروید مثل viewodelScope و … استفاده کنید.آخرش Flow رو میگه و میگه توی پترن‌های مختلف چطوری میشه ازش توی لایه‌های مختلف ViewModel و Repository و … استفاده کرد. برای کسایی که میخوان توی اندروید از Coroutine استفاده کنن واقعا مفیده.ارائه KotlinConf 2019: Coroutines! Gotta catch &#x27;em all!https://www.youtube.com/watch?v=w0kfnydnFWIتوی این ارايه هم در مورد coroutinescop و context و بقیه چیزا صحبت میشه. و چیزی که خیلی در موردش صحبت میشه، نحوه‌ی cancel شدن و هندل کردن exceptionها توی Coroutine هست.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Fri, 23 Oct 2020 19:19:26 +0330</pubDate>
            </item>
                    <item>
                <title>چالش پیاده‌سازی قابلیت انتخاب متن در کاستوم‌ویوها</title>
                <link>https://virgool.io/@abbas.oveissi/text-selection-in-customviews-swg7h3mrgjks</link>
                <description>چند وقت پیش با دوستان در مورد این صحبت می‌کردیم که چطور میشه قابلیت Text Selectionیی شبیه تلگرام پیاده‌سازی کرد. پیچیدگی مسئله توی اینه که اگر توی CustomView متنی draw بشه، دیگه از قابلیت TextSelection خود اندروید نمیشه استفاده کرد و دولوپر باید راهی پیدا کنه.چون بنظرم چالش جالبی میومد، آخر هفته چند ساعتی برای پیدا کردن راه حلش تحقیق کردم و این مقاله در واقع گزارشی از اون فعالیت چند ساعته برای پیدا کردن حل چالش و جواب سه سوال زیر هست:۱- چطوری میشه یه متن چند خطی رو Draw کرد؟ (اگر از متد drawText آبجکت Canvas استفاده کرده باشید، حتما میدونید که بدرد اینجور کارها نمیخوره و ساده هست).۲-  وقتی کاربر CustomView رو لمس میکنه، چطوری میشه فهمید نقطه‌ی لمسش روی چه کاراکتریه و از کجا Selection باید شروع بشه؟۳- چطوری میشه از اولین کاراکتری که کاربر لمس کرده تا جایی که الان انگشتش هست رو هایلایت کرد؟ (مثلا رنگ متنو عوض کرد که کاربر بفهمه چه بخشی از متن رو تا الان انتخاب کرده)یافتن سرنخ برای حل چالشهمیشه اولین گام برای حل یه چالش دیدن سورس‌ پروژه‌های متن‌باز مشابه با چالش هست. خوشبختانه تلگرام خودش متن‌بازه و میشه سورس خودشو بررسی کرد. البته چون پروژه‌اش خیلی بزرگه از طریق گیت‌هاب توی ریپوی تلگرام دنبال کلمه‌هایی مثل Text Selection، Selection و … گشتم تا سریعتر بتونم اون بخش‌های مرتبط به مسئله‌ی خودمو پیدا کنم. خوشبختانه به نتیجه رسیدم و توی نتایج جستجو کلاس TextSelectier.java رو پیدا کردم.ولی خب یه مشکلی بود، متاسفانه این کلاس ۲۵۰۰خط کد داشت و بررسی خط به خطش خیلی سخت بود. به همین دلیل بجای اینکه کد رو خط به خط بررسی کنم، فقط مروری کدهارو یه نگاه مینداختم تا ببینم جایی از کدی هست که از متغیرهایی مثل x و y استفاده کنه و کاراکتری چیزی پیدا کنه یا نه؟! (تقریبا مطمئن بودم اگر جایی از x و y استفاده کنه، به مختصات نقطه‌ی لمس کاربر یا همون محل انگشت کاربر روی صفحه ربط داره)تلاش‌ها نتیجه داد و یه متد به اسم getCharOffsetFromCord توی خط ۱۵۸۲ پیدا کردم. اسمش دقیقا همون بود که میخواستم. البته متوجه نشدم پارامترهای ورودیش دقیقا چی هست ولی این بخش از کدهای متد چشممو گرفت:حدسم این بود از روی مقدار y تشخیص میده که نقطه‌ی لمس کاربر روی کدوم خط از متن قرار داره. نکته‌ی مهم این کد اون آبجکت layout هست که بررسی میکنه آیا مقدار y بین مختصات بالا و پایین خط iام قرار میگیره یا نه؟! اگر بگیره یعنی کاربر یه  بخشی از اون خط رو لمس کرده.اولش فکرکردم اون آبجکت layout برای یه‌ کلاسی هست که خود تلگرام توسعه داده ولی بالاتر دیدم تایپ این آبجکت از نوع android.text.StaticLayout هست. اهمیت این کشف در این نکته هست که متوجه شدم خود اندروید یه سری API برای پیاده‌سازی چالش ما داره و لازم نیست همه کارهارو خودمون بکنیم، در ضمن بجای خوندن کدهای تلگرام میشه از داکیومنت‌های خود اندروید استفاده کرد و کار راحتتر میشه.نمایش متن چند خطی در CustomViewقبل اینکه ببینیم StaticLayout چیه، بهتره در مورد والدش یعنی android.text.Layout صحبت کنیم. اونجور که من فهمیدم، بیشتر زحمت‌هارو این کلاس برای نمایش متن توی اندروید میکشه. وظیفه‌اش مدیریت نمایش متن روی UI هست. یه متد به اسم draw داره که Canvas میگیره و متن رو با خصوصیاتی که براش تعریف شده draw میکنه. این کلاس سه تا فرزند با ویژگی‌های متفاوت داره که بر حسب نیاز باید از یکیشون استفاده کرد:-- کلاس BoringLayout - ساده‌ترین فرزند هست، برای متن‌های تک خطی میشه استفاده کرد که هیچ کاراکتری خاصی نداشته باشن.-- کلاس StaticLayout - باهاش میشه متن‌های چند خطی رو draw کرد. امکانات بیشتری نسبت به BoringLayout داره.-- کلاس DynamicLayout - همه‌ی قابلیت‌های StaticLayout رو داره، در ضمن اگر متن تغییر کنه، خودشو آپدیت میکنه.اگر بخوام یه مثال واقعی از نحوه‌ی استفاده‌ی دو تا کلاس اول بزنم، میتونم به کلاس ReactTextShadowNode در فریمورک ReactNative اشاره کنم. وقتی توی گوگل دنبال اطلاعات بیشتر بودم بهش رسیدم. برای اینکه این کلاس بتونه به بهترین پرفورمنس ممکن متن رو Draw کنه، اول مقدار دو متغیر زیر رو محاسبه میکنه و بعد با سه تا شرط انتخاب میکنه که متن رو با BoringLayout نشون بده یا از StaticLayout استفاده کنه.متد isBoring چک میکنه آیا این متن رو میشه از طریق BoringLayout کشید یا نه، متد getDesiredWidth هم محاسبه میکنه که عرض صفحه اگر چقدر باشه میشه متن هر پاراگراف رو توی یه خط نشون داد.کلاس ReactTextShadowNode شرط‌هاشو به شکل زیر نوشته (البته ساده‌ترش کردم که راحت‌تر قابل فهم باشه): https://gist.github.com/abbas-oveissi/92ffdb3ad2caf07e4df96af695f4836b مثلا شرط دوم رو اگر چک کنید اینجوریه که اگر نتیجه isBoring نال نبود (متن قابلیت نمایش توی BoringLayout رو داره) و عرض صفحه‌ای که میخوایم متن رو توش نشون بدیم از مقدار desiredWidth بیشتر بود (یعنی متن رو میشه توی یه خط در صفحه نشون داد)، برای کشیدن متن از BoringLayout استفاده کرده.راستی توی سیستم‌عامل‌های قدیمی‌تر از M برای ساخت یه Instance از StaticLayout باید از Constructor کلاس و در سیستم‌عامل‌های جدیدتر از Builderش استفاده کرد. بازم توی کد کلاس ReactNative میشه مثال واقعیشو دید:برای ساخت یه Instance از کلاس BoringLayoutهم باید از متد استیتک Make استفاده کرد. مثل زیر:برای اینکه دقیقتر بدونید هر پارامتر برای چیه، بهتره داکیومنت اندروید رو نگاه کنید، البته این مقاله توی مدیوم هم در رابطه با StaticLayout خیلی خوبه. تقریبا همه نکات مهم برای draw کردن متن با StaticLayout رو میگه.تا اینجا پاسخ سوال اول مشخص شد. پس با استفاده از StaticLayout میشه متن‌های چند خطی رو توی یه CustomView کشید. در ادامه باید تشخیص بدیم که اگر کاربر CustomView رو لمس میکنه، نقطه‌ی لمس شده روی کدوم کاراکتر هست.تشیخص کاراکتر لمس شدهبرای اینکه تشخیص بدیم کاربر کدوم کلمه رو لمس کرده به سه چیز نیاز داریم.۱- پیدا کردن مختصات X و Y نقطه‌ی لمس کاربر۲- پیدا کردن خطی که توسط کاربر لمس شده۳- پیدا کردن مکان کاراکتر لمس شده در خط پیدا شده در مرحله دوممختصات نقطه‌ی لمس کاربر رو صفحه گوشی رو میشه با override کردن onTouch پیدا کرد. یه کد ساده در حد زیر هم برای کار ما کافی هست. https://gist.github.com/abbas-oveissi/5ba0ee345acc5110fdfa1bb828ad3a11 وقتی کاربر انگشتش رو روی صفحه میذاره، مختصات زیر انگشتش به عنوان نقطه‌ی شروع Selection متن در دو متغیر startX و startY نگهداری میشه. بعد همزمان با حرکت دست کاربر روی صفحه دو متغییر currentX و currentY مقداردهی میشن و همیشه نقطه‌ی پایان Selection رو توی خودشون نگه میدارن.ولی برای پیدا کردن اینکه چه خطی رو لمس کرده، باید یکم بیشتر تلاش کرد. با الگو گرفتم از کد تلگرام، متد زیر رو درست کردم. https://gist.github.com/abbas-oveissi/9df1af6baf92464f9cb65311ece26816 دونه دونه چک میکنه ببینه y نقطه‌ی لمس شده توسط کاربر توی محدوده‌ی کدوم خط قرار میگیره و Index اون خط رو return میکنه.حالا در آخرین گام برای اینکه تشخیص دادن اینکه کدوم کاراکتر در خط لمس شده میشه از متد getOffsetForHorizontal استفاده کرد. کارش اینه میگه توی خط iام و مختصات x، کدوم کاراکتر قرار داره و Index کاراکتر توی رشته رو به ما میده.با ترکیب استفاده از دو متد getTouchedLine و getTouchedChar و دو متغیر startX و startY میشه مکان کاراکتری که کاربر برای شروع  Selection متن لمس کرده رو پیدا کرد. مثل زیر:نقطه‌ی پایانی Selection با جا به جا کردن انگشت کاربر روی صفحه جا به جا میشه. با همین دو متد بالا و دو متغیر currentX و currentY میشه نقطه‌ی پایانی رو هم پیدا کرد.هایلایت کردن متنبرای هایلایت کردن دو روش پیدا کردم. روش اول ساده‌تر هست و نیاز نیست هیچ محاسبه‌ای براش انجام داد ولی یه ضعفی داره که در ادامه نشونش میدم، روش دوم یه سری محاسبات داره اما خب خیلی شبیه‌تر به قابلیت TextSelection تلگرام هست. در ادامه هر روش رو توضیح میدم.روش اول - استفاده از متد getSelectionPathاین روش رو تصادفی وقتی داکیومنت‌های مرتبط به کلاس Layout رو میخوندم پیدا کردم. این متد از شما مکان کاراکتر شروع و پایان Selection رو به همراه یه آبجکت از نوع Path میگیره و بعدش اون مسیری که باید برای Selection روی صفحه هایلایت بشه رو توی اون آبجکت Path پر میکنه. من یه همچین متدی برای استفاده ازش نوشتم.بعدم خیلی راحت میتونید این Path رو بدید به Canvas تا مثل زیر براتون هایلایت رو انجام بده:نتیجه کار این شکلی میشه.مزیت این روش اینه شما هیچ محاسبه‌ای انجام نمیدید و خیلی ساده هست اما مشکلی که داره اینه حتی فضاهای خالی رو هم داره هایلایت میکنه و خیلی جالب نیست.روش دوم - استفاده از روش کاستومتوی این روش باید بدون از استفاده از متدهای کلاس Layout و براساس مکان اولین کاراکتر لمس شده در خط شروع Selection تا آخرین کاراکتر در خط پایان Selection اینکه از هر خط چقدر باید هایلایت بشه رو محاسبه کنیم.از حالت ساده‌اش شروع میکنیم، فرض کنید کاربر از وسط خط ۲ شروع کرده و تا وسط خط ۵ Selection رو ادامه داده. برای خط ۳ و ۴ محاسبه‌ی زیادی نیاز نیست، فقط باید بدونیم کاراکتر شروع و پایان این خط‌ها توی چه مختصاتی هست و روش یه مستطیل draw کنیم. برای اینکه بتونم محدوده‌ی یه خط رو پیدا کنم متد زیر رو درست کردم:شماره خط رو میگیره و rect درست میکنه که دور خط هست و کل خط رو شامل میشه. با این متد هم روی خط رو هایلایت میکنم:حالا سراغ حالت بعدی میریم که محاسبات بیشتری نیاز داره، فرض کنید کاربر از اوایل خط ۲ Selection رو شروع کرده و تا اواخر همون خط ادامه داده. توی این حالت باید مختصات کاراکتر شروع Selection و کاراکتر پایان Selection رو در خط رو پیدا کنیم و بعد هایلایت کنیم. با استفاده از متد getPrimaryHorizontal میشه مختصات یه کاراکتر رو پیدا کرد، Index کاراکتر در رشته رو میگیره و مختصات رو میده.از getPrimaryHorizontal استفاده کردم و متد زیر رو برای پیدا کردن مختصات شروع و پایان هایلایت نوشتم.برای نقطه‌ی شروع فقط از متد getPrimaryHorizontal استفاده کردم ولی برای نقطه پایان چک کردم اگر مکان کاراکتر از آخر خط بیشتر بود (افتاده بوده توی خط بعدی)، بجای استفاده از getPrimaryHorizontal همون مختصات آخر خط رو که از getRectText گرفتن به عنوان پایان گذاشتم. در آخر هم از draw استفاده کردم.از همین متد میشه در حالتی که کاربر چند خط رو میخواد هایلایت کنه برای هایلایت کردن اولین خط و آخرین خط استفاده کرد (برای خط‌‌های وسط قبلا drawFullSelection رو توضیح دادم). فقط برای خط شروع ورودی‌های متد میشه مکان کاراکتر شروع لمس و مکان کاراکتر پایان خط (توی خط شروع Selection باید تا آخرین کاراکتر خط هایلایت بشه) و توی خط پایان ورودی‌های متد میشه کاراکتر اول خط تا مکان آخرین کاراکتری که کاربر لمس کرده (توی خط پایان Selection باید تا اول خط تا زیر کاراکتری که کاربر انگشتش روش هست هایلایت بشه).کد کل متد هایلایت کردن اینجوری میشه: https://gist.github.com/abbas-oveissi/43616c72637db17959e15b4508ccee20 نتیجه نهایی هم اینجوری میشه:توی این حالت نتیجه خیلی شبیه‌تر به تلگرام میشه و یجورایی چالش حل میشه. هووراااا D:نکته‌ی مهم: در نظر داشته باشید که موقع نوشتن این مقاله نکات پرفورمنسی برام مهم نبوده و هدفم توضیح دادن متدها و نحوه‌ی استفاده‌شون در حل جالش بوده. به عنوان نمونه توی متد onDraw نباید آبجکتی رو new بکنید ولی برای اینکه کد تمیزتر باشه اینکارو کردم تا درکش راحت‌تر باشه. در ضمن توی این مقاله حالت‌های خاص هم بررسی نشده، مثلا اینکه متن RTL باشه یا کاربر بخواد از پایین Selection رو شروع کنه و به سمت بالا دستشو ببره، اگر بخواید واقعا همچین قابلیتی رو توی پروژه‌ اصلیتون پیاده کنید، حتما باید حواستون به اینجور چیزها باشه. این مقاله فقط شروعی برای پیاده‌سازی هست.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Fri, 28 Feb 2020 19:04:56 +0330</pubDate>
            </item>
                    <item>
                <title>راهنمای پیاده‌سازی خرید درون برنامه‌ای کافه‌بازار</title>
                <link>https://virgool.io/@abbas.oveissi/%D8%B1%D8%A7%D9%87%D9%86%D9%85%D8%A7%DB%8C-%D8%AE%D8%B1%DB%8C%D8%AF-%D8%AF%D8%B1%D9%88%D9%86-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87%D8%A7%DB%8C-%DA%A9%D8%A7%D9%81%D9%87%D8%A8%D8%A7%D8%B2%D8%A7%D8%B1-zqd1ngqukg5y</link>
                <description>اگر تازه برنامه‌نویسی اندروید رو شروع کرده باشید، حتما دوست دارید در کنار یادگیری از راه ساخت برنامه‌ی موبایل پول هم در بیارید. خرید درون برنامه‌ای یکی از راه‌هایی هست که میشه باهاش از طریق برنامه‌نویسی اندروید کسب درآمد کرد. کاربر توی این روش برنامه رو رایگان نصب می‌کنه ولی در صورت علاقه میتونه محصولاتی که توی برنامه برای فروش گذشته شدن رو بخره. محصول میتونه دسترسی به محتوای بیشتر، اضافه شدن یه سری قابلیت‌ها به برنامه یا دریافت خدمات گوناگون باشه. در ادامه بیشتر در مورد پرداخت درون برنامه‌ای صحبت میکنیم، اینکه چطوری کار میکنه، چه روش‌هایی داره و در آخر خرید درون برنامه‌ای رو به یه پروژه‌ی اندروید که از قبل نوشته شده اضافه می‌کنیم.نحوه‌ی کار خرید درون برنامه‌ایبرای اینکه نحوه‌ی کار خرید درون برنامه‌ای رو بهتر درک کنید با یه مثال شروع می‌کنیم، فرض کنید یه کاربر برنامه‌ی شما رو باز میکنه و روی دکمه‌ی خرید محصول y در برنامه کلیک میکنه. برنامه شما، به برنامه‌ی کافه‌بازار بر روی گوشی کاربر اعلام میکنه که کاربر x میخواد محصول y رو بخره (شماره ۱ در تصویر زیر)، بعد برنامه‌ی کافه‌بازار با سرور خودش صحبت میکنه و سرور کافه‌بازار همه مسائلی که برای انجام خرید کاربر نیازه (مثل اعتبارسنجی کاربر، داشتن اعتبار کافی برای خرید و …) رو چک میکنه و خرید رو ثبت میکنه (شماره ۲)، بعد نتیجه رو به برنامه‌ی کافه‌بازار روی گوشی کاربر خبر میده که خرید با موفقیت انجام شده یا به مشکل خورده (شماره ۳)، برنامه‌ی کافه‌بازار هم به برنامه‌ی شما خبر میده که نتیجه خرید کاربر چی شده (شماره ۴) و شما میتونید متناسب با جوابی که از برنامه کافه‌بازار میگیرید، هر کاری که میخواید بکنید.فرآیند خرید درون برنامه‌ایبا توجه به توضیحات بالا سه نکته رو در نظر داشته باشید:۱- توی خرید درون برنامه‌ای برنامه‌ی شما نیاز به اینترنت نداره ولی با توجه به اینکه برنامه کافه‌بازار باید با سرور خودش در ارتباط باشه، گوشی در زمان خرید باید به اینترنت وصل باشه.۲- برنامه‌ی کافه‌بازار باید روی گوشی کاربر نصب باشه. پس قبل از شروع فرآیند خرید درون برنامه از نصب بودن برنامه کافه‌بازار روی گوشی کاربر مطمئن بشید و در صورت نصب نبودنش به کاربر پیغام مناسب نشون بدید.۳- اطلاعات خرید کاربر روی حساب کاربری کافه‌بازارش ثبت میشه. یعنی اگر کاربر برنامه‌ی شمارو پاک و دوباره نصب کنه، نیاز نیست محصولاتی که قبلا خریده رو دوباره بخره.روش‌های مختلف خرید درون برنامه‌ایبه چند صورت میشه محصولات رو از طریق خرید درون برنامه‌ای برای خرید در اختیار کاربر گذاشت. در ادامه چهار روش ساده‌شو با هم بررسی می‌کنیم.۱- فروش دائمی (محصولات غیر مصرفی)- در این حالت کاربر با یه بار خرید محصول، برای همیشه میتونه به اون محصول دسترسی داشته باشه و مالکش باشه. برای مثال کاربر میتونه بخش‌هایی از برنامه رو به رایگان استفاده کنه (Demo Version) ولی برای دسترسی به همه امکانات باید از طریق خرید درون برنامه‌‌ای، برنامه خود رو به نسخه‌ی کامل (Full Version) تبدیل بکنه.۲- فروش موقت (محصولات مصرفی)- در این حالت کاربر وقتی محصولی رو میخره، فقط یکبار میتونه ازش استفاده بکنه و بعد از استفاده اگر دوباره بهش نیاز داشته باشه باید بخرتش. مثلا فرض کنید دیدن آنلاین فیلم‌ها در برنامه‌ای رایگانه ولی اگر کاربر بخواد فیلمی رو دانلود کنه، باید محصول «دانلود» رو بخره. بعد از اینکه خرید با موفقیت انجام شد، باید محصول مصرف بشه تا کاربر بتونه برای دانلود فیلم بعدی هم این محصول رو بخره. اصطلاح مصرف کردن به صدا زدن یه متد به اسم consumeAsync توسط برنامه‌نویس اشاره میکنه. اگر برنامه‌نویس محصول رو با این متد مصرف نکنه، کاربر دیگه نمیتونه اون محصول رو بخره، چون یه بار خریده و هنوز مالکش هست. برای اینکه این مشکل پیش نیاد، برنامه‌نویس باید بلافاصله بعد اینکه خرید کاربر موفق بود، متد consumeAsync رو صدا بزنه و محصول رو مصرف کنه تا کاربر در آینده اگر به اون قابلیت نیاز داشت، بتونه دوباره بخرتش.۳- فروش ماهیانه (اشتراک)- در این حالت کاربر وقتی محصولی رو میخره برای حداقل یکماه میتونه بهش دسترسی داشته باشه و ازش استفاده کنه. در صورتی که اشتراکش رو لغو نکنه، بصورت خودکار در آخر ماه دوباره پول اشتراک ماه بعدی از حسابش کم میشه و اشتراکش تمدید میشه. اگر وسط ماه اشتراکش رو لغو کنه، تا پایان ماه جاری هنوز میتونه از اشتراکی که خریده استفاده کنه و پولی بهش پرداخت نمیشه، فقط در پایان اون ماه دیگه اشتراک بصورت خودکار تمدید نمیشه. در صورتی که از این روش استفاده می‌کنید، حتما به این نکته دقت کنید که اگر حتی یه نفر از شما اشتراک خریده باشه، باید تا اتمام مدت اشتراک اون فرد، خدماتی که در ازاش اشتراک رو فروختید ارائه کنید وگرنه کافه‌بازار با شما برخورد میکنه.۴- فروش سالیانه (اشتراک)- تنها فرق این روش با روش قبلی توی مدت زمان دوره اشتراک هست که بجای ماهیانه، دوره‌اش سالیانه هست.برنامه‌ی Moviesبرای درک بهتره اینکه پیاده‌سازی خرید درون برنامه‌ای چطوریه و چه مراحلی داره، روش‌های بالا رو بصورت قدم به قدم در یک برنامه‌ی اندروید به اسم Movies پیاده‌سازی می‌کنیم. این برنامه قابلیت‌های زیر رو داره:۱- دسترسی به اطلاعات ۱۰ فیلم برتر IMDB مثل نام کارگردان، امتیاز IMDB و خلاصه داستان۲- امکان اضافه کردن فیلم‌ به لیست علاقمندی‌۳- امکان دانلود فیلم‌‌ها۴- امکان دیدن فیلم‌ها بصورت آنلاینالبته این قابلیت‌ها بصورت واقعی توی برنامه پیاده‌سازی نشدن و فقط برای اینکه مثال مقاله به پروژه‌های واقعی شبیه‌تر باشه، این قابلیت‌ها بصورت ظاهری توی برنامه گذاشته شدن. برنامه Movies خیلی ساده هست و از ۳ صفحه درست شده. صفحه اول و اصلی، لیست ۱۰ فیلم رو نشون میده. صفحه دوم، برای نمایش جزییات هر فیلم استفاده میشه. صفحه سوم هم وظیفه‌اش نمایش آنلاین فیلم هست.صفحه اصلی برنامهصفحه نمایش جزییات فیلمسورس برنامه رو بدون کدهای خرید درون برنامه‌ای میتونید از طریق این لینک دانلود کنید یا توی این لینک در گیت‌هاب ببینید.فرض کنید شما مسئول پیاده‌سازی پیشنهادات تیم بازاریابی برای برنامه‌‌ی Movies هستید. تیم بازاریابی پیشنهاد داده که بعضی از قابلیت‌های برنامه پولی بشه. تیم بازاریابی پیشنهادهای زیر رو دادن:قابلیت لیست علاقمندی‌ها- برنامه دو نسخه‌ی پیش نمایش و نسخه‌ی کامل داشته باشه. کاربر باید بتونه نسخه‌ی پیش نمایش رو رایگان روی گوشیش نصب کنه و از اطلاعات فیلم‌ها استفاده بکنه ولی برای اینکه بتونه فیلمی رو به لیست علاقمندی‌هاش اضافه کنه، باید نسخه‌ی کامل برنامه رو بخره.قابلیت دانلود فیلم‌ها- کاربر هر بار میخواد فیلمی رو دانلود کنه، باید مبلغی رو به عنوان هزینه دانلود پرداخت کنه.قابلیت پخش آنلاین فیلم‌ها- قابلیت پخش آنلاین فیلم‌ها فقط برای کاربرانی فعال بشه که اشتراک ماهیانه بخرن. کاربر در زمانی که اشتراک ماهیانه خریده میتونه هر چقدر که خواست فیلم‌های مختلف رو آنلاین ببینه.پیاده‌سازی روش‌های خرید درون برنامه‌ایخب با توجه به اینکه قراره از خرید درون برنامه‌ای برای پیاده‌سازی پیشنهادات تیم بازاریابی استفاده کنید، توی این بخش سه مرحله‌ای که برای اینکار نیاز هست رو مرور کنیم.مرحله اول- اضافه کردن مجوز و کلاس‌های پیش‌نیاز به پروژهمرحله دوم- دریافت کد RSA و تعریف محصولات توی پنل کافه‌بازارمرحله سوم- پیاده‌سازی روش‌های خرید درون برنامه‌ای در پروژهدر ادامه این مرحله‌هارو بیشتر بررسی می‌کنیم و همزمان تغییرات مورد نیاز رو روی پروژه Movies اعمال می‌کنیم.مرحله اول - مقدماتهمانطور که در تصویر اول مقاله نشون داده شده بود، توی فرآیند خرید درون برنامه‌ای نیاز هست که بین برنامه‌ای که خرید داخلش انجام میشه و برنامه‌ی کافه‌بازار ارتباط برقرار بشه. برای این ارتباط، نیاز به یه سری کلاس کمکی و یه فایل AIDL هست. خوشبختانه لازم به دونستن جزییات این کلاس‌ها و فایل AIDL نیست.برای اضافه کردن فایل AIDL باید یه فولدر مخصوص توی پروژه ساخته بشه. برای ساخت فولدر روی app کلیک راست کنید.مسیر زیر رو توی منوی باز شده برید و روی AIDL Folder بزنید.بعد یه فولدر شبیه تصویر زیر اضافه میشه که البته خالی هست.داخلش یه پکیج به اسم com.andorid.vending.billing و داخل پکیج یه کلاس به IInAppBillingService.aidl بسازید، سورس رو از لینک زیر داخل کلاس کپی کنید.IInAppBillingService.aidlکلاس‌های زیر رو هم از لینک‌ها برداشته و مثل بقیه‌ی کلاس‌های معمولی توی پروژه کپی و پیست کنید. میتونید کنار کلاس اکتیویتیون بذاریدشون یا برای اینکه ساختار پروژه تمیزتر باشه یه پکیج به اسم iabhelpers بسازید.Base64.java - Base64DecoderException.java - IabException.java - IabHelper.java - IabResult.java - Inventory.java - Purchase.java - Security.java - SkuDetails.javaدر ضمن برای ارتباط با برنامه کافه‌بازار باید مجوز PAY_THROUGH_BAZAAR رو شبیه زیر توی مانیفست کپی و پیست کنید.AndroidManifest.xml https://gist.github.com/abbas-oveissi/1571d20471fa4d2753ab0d6cbaa7355e مرحله دوم - دریافت RSA و ثبت محصولبرای دریافت کلید RSA و ثبت محصول باید به داشبورد برنامه Movies در پنل توسعه‌دهندگان کافه‌بازار برید. اگر هنوز برنامه‌ی Movies رو به پنل توسعه‌دهندگان کافه‌بازار اضافه نکردید، اول باید اینکارو انجام بدید. برای اینکار از منوی Build اندروید استودیو روی Generate signed build apk کلید کنید.توی پنجره‌ای که باز میشه Apk رو انتخاب و روی Next کلیک کنید. در پنجره‌ی بعدی یه کلید جدید بسازید یا از یه کلیدی که قبلا داشتید برای امضا استفاده کنید (حواستون باشه در آپدیت‌های بعدی باید APK رو با همین کلید امضا کنید و دیگه نمیشه تغییرش داد). توی تصویر زیر من از یه کلید قدیمی استفاده کردم.اگر قبلا کلید نساختید، روی Create New کلیک کنید و طبق راهنمای زیر کلید خودتون رو بسازید.راهنمای ساخت کلید که در صفحه خطای Play Protect کافه‌بازار گذاشتهبعد ساخت کلید جدید یا انتخاب کلید قدیمی روی Next بزنید و بقیه مرحله‌هارو طی کنید تا به Finish برسید، بعد از زدن Finish اندروید استودیو به شما خروجی APK پروژه‌ رو تحویل میده. حالا به پنل توسعه‌دهندگان کافه‌بازار برید و روی «ثبت برنامه جدید» بزنید. پنجره‌ای شبیه تصویر زیر باز میشه.دسته‌بندی برنامه رو انتخاب (راهنمای انتخاب دسته‌بندی) و APK رو آپلود کنید. اگر تیک سبز رو دیدید، یعنی همه چی بدون مشکل پیش رفته و روی دکمه «ایجاد برنامه» بزنید. حالا برنامه‌ی Movies به لیست برنامه‌هاتون توی پنل اضافه میشه و میتونید به داشبورد برنامه برید.توی داشبورد برنامه‌ از بین تب‌های موجود روی «خرید درون برنامه‌ای» کلیک کنید. توی صفحه جدید کلید RSA به شما نمایش داده می‌شود.برای ثبت محصولات روی دکمه «ثبت محصول جدید» بزنید.توی این پروژه ما سه محصول داریم. محصول FULLVERSION که خریدش باعث میشه برنامه تبدیل به نسخه‌ی کامل بشه. محصول DOWNLOAD که کاربر هر بار بخواد فیلمی رو دانلود کنه باید بخرتش. و آخرین محصول STREAMING که کاربر با خریدش میتونه حداقل تا یه ماه، هر فیلمی رو خواست آنلاین ببینه.فرم اطلاعات محصول رو شبیه زیر برای هر سه محصول پر کنید، نوع دو محصول FULLVERSION و DOWNLOAD رو خریدنی و STREAMING رو اشتراک یه ماهه بذارید. برای اینکه بتونید به صورت رایگان روش‌های خرید درون برنامه‌ای رو تست کنید، قیمت‌ همه محصولات رو صفر بذارید. توجه کنید بعد از ساخت محصول امکان تغییر نوع یا شناسه‌ی اون وجود نداره.محصول‌های ساخته شده به جدول محصولات اضافه میشن.شناسه محصولات و کلید RSA رو جایی یادداشت کنید، چون در ادامه بهشون نیاز دارید.مرحله سوم - پیاده‌سازی روش‌های خرید درون برنامه‌ایبرای اتصال به برنامه‌کافه‌بازار از کلاس کمکی IabHelper استفاده میشه که در مرحله اول به پروژه اضافه‌شون کردید. توی متد OnCreate اکتیویتی DetailActivity با فراخوانی متد startSetup ارتباط برقرار میشه و در متد OnDestroy اکتویتی با متد dispose ارتباط قطع میشه.سازنده کلاس IabHelper دو تا پارامتر میگیره، اولی از نوع Context و دومی از نوع رشته هست. این رشته همون کلید RSA هست که توی مرحله قبلی از توی پنل یادداشتشون کردید. کد زیر رو توی onCreate اضافه کنید.InAppPurchase_Code1.java https://gist.github.com/abbas-oveissi/d15cefaed0569624646d7db84366b1fa برای شروع ارتباط با برنامه‌ی کافه‌بازار، کد زیر رو بعد از کد ساخت آبجکت IabHelper بذارید.InAppPurchase_code2.java https://gist.github.com/abbas-oveissi/930a0f4d1e34fde502ba576d8fd252f6 کالبک متد startSetup نتیجه‌ی ارتباط با برنامه‌ی کافه‌بازار خبر میده. اگر result.isSuccess() مقدارش True بود یعنی ارتباط به درستی برقرار شده و میشه از همه‌ی متدهای خرید درون برنامه‌ای استفاده کرد.برای اینکه کالبک‌هایی که ست میکنید درست کار کنند، تیکه کد زیر رو حتما توی onActivityResult اکتیویتی DetailActivity بذارید.InAppPurchase_code3.java https://gist.github.com/abbas-oveissi/e6e268c73bbad2bf349dbe293cd1e121 متد زیر رو هم توی اکتیویتی کپی کنید تا باهاش بتونید از اینکه برنامه‌ی کافه‌بازار روی گوشی کاربر نصب هست، مطمئن بشید.InAppPurchase_code4.java https://gist.github.com/abbas-oveissi/d8d0d05643e65743f2c66209e9fbf58d برای اینکه کد تمیزتر باشه، به ازای سه تا محصولی که توی پنل ساخته شده، سه تا متغییر ثابت به شکل زیر تعریف می‌کنیم. در ادامه اگر جایی به شناسه محصول‌ها نیاز بود، از این متغیرها استفاده می‌کنیم.InAppPurchase_Constants.java https://gist.github.com/abbas-oveissi/af289cba4988f61a1620865c60d2bd4f خب حالا نوبت پیاده‌سازی روش‌های خرید درون برنامه‌ای هست که از محصول FULLVERSION شروع می‌کنیم.فروش دائمی FULLVERSIONاگر کاربر این محصول رو خریده باشه باید بتونه از همه قابلیت‌هایی برنامه که مخصوص نسخه‌ی کامل هست، استفاده کنه. توی پروژه‌ی Movies قراره اضافه کردن یه فیلم به لیست علاقمندی‌ها، جزء قابلیت‌های نسخه‌ی کامل باشه.وقتی کاربر روی دکمه‌ی علاقمندی‌ها کلیک کرد، برای اینکه بدونید آیا کاربر نسخه‌ی کامل رو خریده یا نه، یه متد به اسم isUserBuyFullVersion با کدهای زیر بسازید و صداش کنید.InAppPurchase_code5.java https://gist.github.com/abbas-oveissi/e91ba4d3a541b902669f73166446ea6c داخل این متد اول از طریق متد isCafeInstalled چک شده که روی گوشی کاربر کافه‌بازار نصب هست یا نه، اگر نصب بود با استفاده از متد QueryInventoryFinishedListener کلاس IabHelper چک شده که آیا کاربر محصول FULLVERSION رو خریده یا نه. داخل کالبک این متد، شناسه محصول رو اگر به متد hasPurchase آبجکت Inventory بدید، بهتون میگه آیا کاربر این محصول رو خریده یا نه.اگر خروجی HasPurchase برای شناسه محصول True بود، متد bookmarkMovie صدا زده میشه و توی این متد باید فیلم به لیست علاقمندی‌ها اضافه بشه (البته بصورت نمادین دیگه!)، اگر خروجی False بود، متد buyFullVersion صدا زده میشه تا فرآیند خرید نسخه‌ی کامل آغاز بشه.توی متد buyFullVersion باید متد launchPurchaseFlow آبجکت mHelper رو مثل کد زیر صدا بزنید. این متد ۴ پارامتر داره، پارامتر اول اکتیویتی رو میخواد که this بهش بدید، پارامتر دوم شناسه محصول رو میخواد، پارامتر سوم یه عدد صحیح به عنوان request code بدید و در نهایت کالبک رو ست کنید.InAppPurchase_code6.java https://gist.github.com/abbas-oveissi/588b733e926037570f532d43bd583874 زمانی که خرید موفق یا ناموفق نتیجه‌اش مشخص بشه، متد onIabPurchaseFinished صدا زده میشه. توی این متد از طریق اطلاعاتی که توی آبجکت info از کلاس Purchase میده، میتونیم متوجه بشید خرید موفق بوده یا نه. اگر موفق بود متد bookmarkMovie رو صدا بزنید تا فیلم به لیست علاقمندی‌ها اضافه بشه (باز تاکید میکنم بصورت نمادین!).خب پیاده‌سازی این روش تموم شد، برای اطمینان از اینکه همه مراحل رو درست رفتید، یه بار تست کنید تا ببینید فرآیند خرید بدون خطا انجام میشه یا نه. فروش موقت DOWNLOADهر بار کاربر میخواد فیلمی رو دانلود کنه باید محصول DOWNLOAD رو بخره. کد پیاده‌سازی خرید درون برنامه‌ای برای این محصول با محصول قبلی دو تا تفاوت داره. اول اینکه بعد از اتمام فرآیند خرید، باید متد consumeAsync صدا زده بشه، دوم اینکه نیازی نیست چک کنید کاربر قبلا این مصحول رو خریده یا نه، چون هر بار باید بخره.یه متد به اسم buyDownload بسازید و هر بار کاربر روی دکمه‌ی دانلود کلیک کرد، صداش بزنید. کدهای زیر رو داخلش کپی کنید.InAppPurchase_code7.java https://gist.github.com/abbas-oveissi/4eaa5d91bd94087b538216da59eea348 کد داخل این متد شبیه همون متد buyFullVersion هست، اول چک میشه روی گوشی برنامه‌ی کافه‌بازار هست یا نه، اگر نصب بود متد launchPurchaseFlow صدا زده میشه.فقط توی این روش وقتی خرید با موفقیت تموم شد، باز از طریق متد consumeAsync محصولی رو که کاربر خریده مصرف شده. پارامتر اول یه آبجکت از کلاس Purchase هست که توی کالبک متد launchPurchaseFlow بهمون داده میشه. کد مصرف کردنش به شکل زیره:InAppPurchase_code8.java https://gist.github.com/abbas-oveissi/d799168cddb0dcf0b777f772f1479e09 اگر مقدار result.isFailure() برابر با false بود، مشخص میشه که محصول به درستی مصرف شده و با صدا زدن متد startDownloading دانلود فیلم شروع میشه (باز اینم بصورت نمادینه).فروش اشتراک STREAMINGکاربر برای دیدن آنلاین فیلم‌ها باید اشتراک دیدن آنلاین فیلم (محصول STREAMING) رو خریده باشه. وقتی این محصول رو بخره، تا زمانی که لغوش نکرده باشه، هر ماه اشتراکش تمدید میشه و هرچقدر بخواد میتونه فیلم‌های مختلف رو بصورت آنلاین توی برنامه Movies ببینه.یه متد به اسم isUserBuyStreaming بسازید و وقتی کاربر روی دکمه بخش فیلم کلیک کرد صداش بزنید. کدهای زیر رو داخلش کپی کنید.InAppPurchase_code9.java https://gist.github.com/abbas-oveissi/8314028c31a5e89e9b9ef3308eda2beb اگر روی گوشی کاربر برنامه‌ی کافه‌بازار نصب بود، متد queryInventoryAsync صدا زده میشه تا مشخص بشه که کاربر اشتراک STREAMING رو خریده یا نه. اگر خریده بود به صفحه نمایش فیلم منتقل میشه و اگر نخریده بود متد buyStreaming صدا زده میشه. توی buyStreaming مثل زیر متد launchPurchaseFlow صدا زده بزنید تا فرآیند خرید اشتراک شروع بشه.InAppPurchase_Code10.java https://gist.github.com/abbas-oveissi/f26176873955a97bbcd1bfe242cf63b8 اگر خرید اشتراک با موفقیت انجام شد، یعنی مقدار result.isFailure() برابر false بود و مطمئن شدیم info برای محصول STREAMING هست، کاربر رو به صفحه نمایش فیلم منتقل میشه.وب‌سرویس‌های (API) کافه‌بازار کافه‌بازار برای بخش خرید درون برنامه‌ای یه سری وب‌سرویس درست کرده که باهاش میشه خریدها و اشتراک‌های کاربران رو چک کرد. البته در بیشتر مواقع همون پنل توسعه‌دهندگان کافه‌بازار برای دیدن این اطلاعات کافی هست ولی اگر شمابرای خودتون یه پنل مدیریتی وب دارید، شاید بخواید اونجا گزارش چندتا برنامه رو با هم ببینید یا صحت خریدهای کاربران رو قبل از ثبت در دیتابیس از طریق کافه‌بازار چک کنید.بطور مثال فرض کنید میخواید قبل از اینکه خرید کاربر رو در دیتابیس سرورتون ثبت کنید، میخواید مطمئن بشید که خرید حتما انجام شده و درخواست یه درخواست تقلبی نیست. برای اینکار سه مرحله به فرآیند خرید درون برنامه‌ای که اول مقاله در موردش صحبت کردیم اضافه میشه (فرآیند Authorization رو در نظر نگرفتم، ابتدا باید اینکارو انجام بدین، توی مستندات کافه‌بازار جزییاتش هست). وقتی خرید با موفقیت انجام شد، برنامه باید به سرورتون اعلام کنه کاربر x محصول y رو خریده (شماره ۵)، بعد سرور از طریق APIهای کافه‌بازار چک میکنه واقعا همچین خریدی توسط کاربر x انجام شده یا نه؟! (شماره ۶). اگر جواب وب‌سرویس کافه‌بازار این بود که خرید انجام شده (شماره ۷)، شما با اطمینان خاطر میتونید خرید رو توی دیتابیس ذخیره کنید و به برنامه خبر بدید که خرید کاربر در دیتابیس سرورتون اعمال شده.فرآیند خرید درون برنامه‌ی + صحت سنجی خرید از طریق APIبدلیل اینکه مقاله طولانی نشه، اینجا در مورد جزییات پیاده‌سازی کار با APIهای کافه‌بازار صحبت نمی‌کنیم ولی خود مستندات API کافه‌بازار در این رابطه کامل هست، اگر روزی خواستید ازش استفاده کنید به اون مستندات مراجعه کنید.وضعیت لاگین بودن کاربر در حساب کافه‌بازاربرای اینکه بتونید لیست خریدهای کاربر رو از اپ کافه‌بازار بگیرید یا خریدی رو مصرف کنید، کاربر باید در برنامه‌ی کافه‌بازار لاگین کرده باشه.اگر کاربر لاگین نکرده باشه، توی کالبک این متدها result.isFailure() مقدار True رو برمیگردونه و نشون میده مشکلی پیش اومده. در این حالت برنامه کافه‌بازار با یه ناتیکفییشن (شبیه تصویر زیر) کاربر رو متوجه میکنه که باید وارد حسابش در کافه‌بازار بشه.ولی راه بهتر اینه شما خودتون قبل از اینکه متد queryInventoryAsync یا consumeAsync رو صدا بزنید، از لاگین بودن کاربر مطمئن بشید. توی این مقاله در مورد جزییات پیاده‌سازی این قضیه صحبت نمی‌کنیم ولی توی پروژه Movies پیاده‌سازیش انجام شده و اول دو متد isUserBuyStreaming و isUserBuyFullVersion وضعیت لاگین کاربر چک میشه. برای خوندن جزییات پیاده‌سازی به این لینک در مستندات کافه‌بازار مراجعه کنید.و بالاخره پایان!فکرکنم خیلی زودتر از این‌ها منتظر این بخش مقاله بودید D: ببخشید اگر مقاله طولانی شد، امیدوارم از اینکه خوندینش پشیمون نشده باشید و جواب سوال‌هاتون رو گرفته باشید. حتما مستندات خود کافه‌بازار رو هم بخونید، اونجا خیلی کاملتر همه‌ی جزییات رو توضیح دادند. من فقط سعی کردم همون نکات رو ساده‌ و خلاصه‌تر با یه مثال شبه واقعی توی این مقاله بگم.در ضمن سورس کامل پروژه که همه روش‌های خرید درون برنامه‌ای داخل پیاده شدن رو میتونید توی ریپوی گیت‌هاب زیر ببینید.https://github.com/abbas-oveissi/Movies-InAppBillingلینک بالا به آخرین کامیت برنچ Master اشاره می‌کنه که شامل پیاده‌سازی همه‌ی روش‌ها به همراه کدهای چک کردن وضعیت لاگین بودن کاربر هست.اگر پیاده‌سازی همه‌ی روش‌هارو بدون چک کردن لاگین بودن کاربر میخواهید از این لینک استفاده کنید.اگر پیاده‌سازی هر روش رو جدا میخواهید ببینید از برنچ‌های دیگه به نام‌های consumable، non-consumable و subscription استفاده کنید.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Sat, 07 Dec 2019 20:24:01 +0330</pubDate>
            </item>
                    <item>
                <title>اخلاقیات و مسئولیت‌پذیری در توسعه نرم‌افزار</title>
                <link>https://virgool.io/@abbas.oveissi/%D8%A7%D8%AE%D9%84%D8%A7%D9%82%DB%8C%D8%A7%D8%AA-%D9%88-%D9%85%D8%B3%D8%A6%D9%88%D9%84%DB%8C%D8%AA%D9%BE%D8%B0%DB%8C%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%AA%D9%88%D8%B3%D8%B9%D9%87-%D9%86%D8%B1%D9%85%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-audu4lvtklqi</link>
                <description> منبع عکس مقاله Hiring Great Software Engineers در مدیومامروز بالاخره فرصت شد تا قسمت Smithereens از فصل جدید سریال Black Mirror رو ببینم. خیلی ذهنمو درگیر خودش کرد و باعث شد تا بیام اینجا در مورد چیزهایی که گاهی بهشون فکر می‌کنم صحبت کنم?. البته الان هدفم این نیست که خودم نتیجه گیری کنم و بیشتر دنبال این هستم که یه سری موضوع مطرح بشن تا بشه بیشتر در موردشون صحبت کرد.نکته: شاید باعث اسپویل قسمت Smithereens بشهآقای رابرت مارتین (عمو باب) یه سوگند برای برنامه‌نویسا نوشته با عنوان The Programmer&amp;amp;#x27;s Oath که خیلی جالبه اما چیزهای بیشتری از فقط یه کد خوب مهم هستند. اینکه یه کد بدون باگ بنویسیم یا Code Coverage کدمون بالا باشه، دلیل نمیشه یه توسعه‌دهنده‌/مهندس/… نرم‌افزار عالی باشیم. آدم باید در کنار افزایش مهارت‌های فنی، حتما یه نیم نگاهی به مهارت‌های نرم (Soft Skills) داشته باشه. هر چقدر کدت خوب باشه ولی نتونی ۴ کلمه با همکارت تعامل داشته باشی یا توی یه همفکری فنی شرکت کنی یا حتی هنوز جنسیت/سن برات مهمتر از دانش فرد باشه، اون کدت هم ارزش خاصی نداره.حالا غیر از تعامل با بقیه، چندتا موضوع دیگه هم هست که بنظرم نیاز به بحث دارند:۱- چطوری باید یه پروژه رو قیمت گذاری کنیم؟ اگر پروژه‌ای رو به یکی دادیم، با چقدر تغییر مجازیم به یکی دیگه بفروشیم؟ آیا اگر پروژه‌ای رو به کسی میدیم باید بهش بگیم از چیزی مثل وردپرس استفاده کردیم یا نه؟ اگر از صفر یه هسته‌ی نرم‌افزاری برای خودمون درست کرده باشیم و باهاش چندتا پروژه بزنیم، آیا باید قیمت ارزونتر بشه یا اینکه همه رو به قیمت همون ساخت یه پروژه باید فروخت؟۲- معمولا خیلی از برنامه‌نویسا حقوق خودشون رو با توسعه دهنده‌های خارجی مقایسه می‌کنند و بنظرشون خیلی حقشون خورده میشه. آیا این مقایسه درسته؟ آیا باید حقوق رو با کار آزاد یا دلالی مقایسه کرد؟ بر اساس میزان رفاه زندگی سنجید؟ آیا تیم فنی مهمترین تیم در یه شرکت هست و باید بالاترین حقوق رو بگیره؟۳- شاید از همشون مهمتر این مورد باشه، اینکه مسئولیت ما در قبال کاربرهایی که از نرم‌افزارمون استفاده می‌کنند چی هست. کاری به بحث حفاظت از داده ندارم که بدیهی هست. مثال‌های دیگه رو بررسی میکنیم. یه مثالش همین قسمت از سریال Black Mirror بود. یه شرکتی سعی کرده بود تا میتونه کاربرهارو مجذوب نرم‌افزارش کنه که همیشه ازش استفاده کنند (تقریبا یا شاید همه ماها دوست داریم که کاربرها زمان بیشتری رو توی ا‌پ‌مون بگذرونن). در نهایت همین قضیه باعث شده بود که راننده حواسش به اپ پرت بشه و تصادفی رخ بده و داستان سریال درست بشه. چقدر حواسمون به همچین چیزهایی هست؟ من تنها اپی که دیدم هشدار میده، اپ Waze هست که زمان استفاده از اپ میپرسه مسافر هستید یا راننده؟ اگر بگید مسافرید، میذاره با اپ کار کنید.یه مثال دیگه از این مسئولیت‌ها میتونه این باشه که ما چقدر مسئول روابطی هستیم که آدم‌ها توی اپ‌مون دارن. یه بار نظرات یه اپ توی کافه‌بازار رو میخوندم که بنظر اپ از یه تاریخی غیر فعال شده بود و تعداد زیادی آدم نظر گذاشته بودن که دوستایی که توی اپ داشتیم رو از دست دادیم و راه ارتباطی باهاشون نداریم. توی نگاه اول شاید هرکی حق داشته باشه که اپ‌شو غیر فعال کنه ولی خب شاید همین غیرفعال کردن، تاثیر زیادی روی بعضی از کاربرها بذاره.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Wed, 26 Jun 2019 21:17:16 +0430</pubDate>
            </item>
                    <item>
                <title>چالش RxJavaیی - یافتن شهرهای هر استان</title>
                <link>https://virgool.io/devAndroid/%DA%86%D8%A7%D9%84%D8%B4-rxjava%DB%8C%DB%8C-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D8%B4%D9%87%D8%B1%D9%87%D8%A7%DB%8C-%D9%87%D8%B1-%D8%A7%D8%B3%D8%AA%D8%A7%D9%86-phra8x9x2rit</link>
                <description>بهترین راه یادگیری RxJava بعد از اینکه مفاهیم اولیه‌اش رو خوندید، حل مسئله‌های متفاوت با اون هست. اینکار باعث میشه به مرور تسلطتون روی RxJava و اپراتورهاش زیادتر بشه. امروز یه مسئله‌ای رو با هم بررسی میکنیم که تاحالا چند نفری در موردش ازم سوال پرسیدن. سه تا نکته رو فقط در نظر داشته باشید.شاید بدون RxJava بشه این مسئله رو راحت‌تر حل کرد ولی اینجا هدفمون RxJava هست.احتمال داره از چند روش بشه این مسئله‌ رو حل کرد، اصراری نیست که فقط راه‌حل من درسته.برای اینکه کد تمیزتر باشه کاتلین استفاده کردم ولی خب میشه همینو با جاوا پیاده کرد.خب مقدمه دیگه بسه، بریم ببینیم سوال یا مسئلمون چیه.تعریف مسئلهدو تا کلاس City (شهر) و Province (استان) بصورت زیر داریم، از اون دو طرف هم دو تا متد داریم که یکیشون لیست استان‌ها و اون یکی لیست شهرهای یه استان رو از اینترنت بهمون میدن. توی برناممون نیاز داریم که لیست استان‌هارو رو با شهرهاشون از اینترنت بگیریم. چطوری میشه اینکارو با یه زنجیره RxJava انجام داد؟؟؟ (راهنمایی: میتونید به کلاس Province فیلد اضافه کنید) https://gist.github.com/abbas-oveissi/25f0922846386f13a831e7f39fd573cf حتما سعی کنید قبل اینکه کد راه‌حل رو بخونید، تلاش کنید تا خودتون پیاده‌سازیش کنید. راه‌حل (هشدار اسپویل)اگر با کد راحت‌تر هستید، این توضیحات رو نیاز نیست بخونید و بجاش مستقیم نمونه کد رو اینجا یا در آخر مقاله بررسی کنید. در ضمن توی این توضیحات فرض کردم که شما با RxJava و اپراتورهایی مثل flatmap و map و ... آشنایی دارید. پس اگر باهاشون آشنا نیستید، بهتره قبلش توی گوگل یه جستجویی در موردشون بکنید.خب اولین کار برای حل مسئله اینه که لیست استان‌هایی که از متد getProvinces میگیریم رو تبدیل به دونه‌های Province بکنیم. اینکار رو میشه با Observable.from انجام داد. فقط چون خروجی این اپراتور از نوع observable هست، باید از flatmap استفاده کرد.حالا در ادامه باید لیست شهرهای این استان‌هارو تک تک بگیریم. اینجا هم باید از flatmap استفاده کنیم ولی یه نکته داره. بذارید با این نکته رو با یه مثال توضیح بدم. توی بخش اول با flatmap لیست Province رو تبدیل به دونه‌های Province کردیم. اینجوری:List&lt;Province&gt;  ========FlatMap==========&gt; Provinceالان اگر به همون مدل از flatmap استفاده کنیم، اینجوری میشه:Province  ========FlatMap==========&gt; List&lt;City&gt;توی این حالت درسته لیست شهرهارو گرفتیم!! ولی خب دیگه به آبجکت Province دسترسی ندارید. در نتیجه بدردمون نمیخوره. چاره‌ی کار اینه که نگاهی به داکیومنت‌ اپراتور flatmap توی RxJava بندازیم. غیر از flatmapیی که بالاتر استفاده کردیم، یه flatmap دیگه هست که میشه دو ورودی بهش داد و این شکلی هست:flatMap(final Func1&lt;&gt; collectior, final Func2&lt;&gt; resultSelector)توضیح پارامتر اول collectior - این همون تابعی هست که داخلش بهمون یه Province میده و ما خروجی متد getCityByProvinceId که یه observable هست رو return میکنیم.توضیح پارامتر دوم resultSelector - داخل این تابع اصل کارو انجام میدیم. داخلش بهمون یه آبجکت Province و نتیجه‌ی اون Observable که توی collectior براش return کرده بودیم رو میده. در نتیجه تنها کاری که باید بکنیم اینه یه فیلد به Province اضافه کنیم و این لیست رو بهش ست کنیم.در آخر زنجیره‌مون هم از toList استفاده میکنیم تا دوباره دونه‌های Province رو یه لیست بکنه و بهمون تحویل بده. نمونه کد راه‌حل رو میتونید در ادامه مشاهده کنید. https://gist.github.com/abbas-oveissi/170036ed900e5d148c113038669bc2ce اگر شمام راه‌حل بهتری به ذهنتون میرسه توی بخش نظرات لینک gistشو بفرستید.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Thu, 13 Jun 2019 19:24:29 +0430</pubDate>
            </item>
                    <item>
                <title>چالش ساخت اپ اندروید برای گرفتن قیمت بلیت!</title>
                <link>https://virgool.io/MobileLab/%DA%86%D8%A7%D9%84%D8%B4-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%A7%D9%BE-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%AF%D8%B1%D9%81%D8%AA%D9%86-%D9%82%DB%8C%D9%85%D8%AA-%D8%A8%D9%84%DB%8C%D8%AA-rsucffllvzpx</link>
                <description>عید امسال رو به خوردن و فیلم دیدن میگذروندم تا اینکه یه وظیفه‌ای رو بهم محول کردن?. اونم این بود که قیمت بلیت‌ هواپیمارو توی یه سایتی چک کنم تا وقتی که خیلی ارزون شد، ازش بخریم. چند دفعه‌‌ای توی سایت رفتم و قیمت‌هارو نگاه کردم ولی بعدش گفتم چکاریه که خودم چک کنم، بجاش یه اپ اندروید درست میکنم تا قیمت بلیت رو هر ۱۵ دقیقه بگیره و اگر از یه حدی ارزون‌تر بود، خبرم کنه.چون پروژه برای خودم بود و وقت هم داشتم، هرچیزی که این یه مدت میخواستم امتحان کنم ولی نشده بود رو توی این پروژه استفاده کردم!? دو روزی ازم وقت گرفت ولی تجربه‌ی خوبی بود، در ادامه چندتا نکته از این تجربه رو میگم. در ضمن سورس پروژه رو هم توی گیت‌هاب منتشر کردم تا شاید به عنوان نمونه سورس به کار کسی بیاد یا حتی بعدا خودم چیزهای دیگه‌ای رو روش تست کنم. لینک ریپوی گیت‌هاب پروژه:https://github.com/abbas-oveissi/Charterتصاویر اپنکاتی که میخواستم بگم اینا هست:گرفتن اطلاعات بلیتبرای گرفتن اطلاعات بلیت از WorkerManager استفاده کردم. APIشو خیلی خوب درست کردن و کار باهاش خیلی ساده هست. برای این پروژه از هر دو مدل PeriodicWorkRequest و OneTimeWorkRequest استفاده کردم. برای PeriodicWorkRequest از حداقل intervalش یعنی ۱۵ دقیقه استفاده کردم ولی اگر گوشی به حالت Doze بره، این interval دیگه رعایت نمیشه و هر زمان گوشی اجازه بده، عملیات سینک انجام میشه.ثبت تاریخ و زمان در Roomقبلا از Room استفاده کرده بودم ولی خب این سری میخواستم تاریخ و زمان (یا همون DateTime) رو داخلش ذخیره کنم. دنبال یه روش اصولی بودم، خود Room برای ذخیره DateTime تایپ نداره ولی یه چیزی به اسم TypeConverter که میشه ازش توی این قضیه کمک گرفت. توی سرچ‌هام به یه مقاله‌ی عالی از chris bans رسیدم که عنوانش Room + Time هست. پیاده‌سازی رو از روی این مقاله انجام دادم. حتی اگر هنوز SQLite استفاده میکنید باز توصیه میکنم که حتما بخونیدش. Room + Time - Chris Banesاستفاده از کتابخونه‌ی Pagingخیلی وقت بود که دلم میخواست از Paging استفاده کنم ولی فرصتش نبود. این پروژه باعث شد که سراغ این کتابخونه‌ برم و برای نمایش لیست تاریخچه ازش استفاده کنم. یه چیزی که در مورد Paging میخوندم و برام جالب بود، ایدشون در مورد Placeholderها بود که توی لینک زیر توضیح دادن. خودشون معایب و مزایاشو گفتن.PagedList - Placeholdersکتابخونه‌ی Paging به سه مدل معماری میتونه کار کنه (شکل زیر). من از مدل وسطی استفاده کردم. باید توی یه پروژه‌ی دیگه روش ترکیبی Network و Databaseش رو امتحان کنم، بنظر خیلی باحاله. اینجوری هست که از دیتابیس اطلاعات رو توی لیست نمایش میده، وقتی دیگه داخل دیتابیس چیزی نمونده باشه، خبرتون میکنه تا از Network اطلاعات جدید رو بگیرید و دوباره توی دیتابیس بریزید تا Paging دوباره نشونشون بده.وابستگی UI به دیتابیسترکیب استفاده از WorkerManager و Room و LiveData و Paging باعث شد که یجورایی اپ بصورت offline first بشه. برنامه اینطوری کار میکنه که UIش اطلاعات رو بوسیله‌ی LiveData از Room میگیره و نشون میده، یعنی هرموقع برنامه رو باز کنید، آخرین اطلاعات رو میبنید. از اونطرف WorkerManager هر بار اطلاعات جدید رو از وب‌سرویس میگیره، دیتابیس رو آپدیت میکنه و کاری با UI نداره. اما چون از LiveData استفاده شده، خود Room به UI اطلاعات به روز شده رو میده تا UI هم آپدیت بشه.وب‌سرویس کاتلینی!اول که برنامه رو نوشته بودم، مستقیم اطلاعات رو از سایت اصلی بلیت میگرفت. بعد یکی از دوستان سایت liara.ir رو بهم معرفی کرد. سرویسشون اینقدر جذاب بود که تصمیم گرفتم برای تست کردنش یه وب‌سرویس درست کنم و روی سرویسشون دیپلوی کنم تا ببینم کارشون چطوره. وب‌سرویس هم اینجوری کار میکنه که یه واسط بین اپ اندروید و سایت اصلی هست. اطلاعات رو از سایت اصلی میگیره و با فرمت بهتری به اپ میده. برای ساخت وب‌سرویس از spring boot و gradle و kotlin استفاده  کردم. فکر نمیکردم روزی برسه که با دست‌های خودم به پروژه gradle wrapper اضافه کنم تا کارم راحت‌تر بشه :)) وب‌سرویس رو از روی لینک زیر پیاده کردمHow to build a Simple REST API with Kotlin and Spring Bootمزیت کاتلین و گریدل بودن وب‌سرویس این بود که یه سری کدهارو همراه با کتابخونه‌ها (مثل Gson و OkHttp) کپی کردم و عینا توی وب‌سرویس استفاده کردم. خیلی حال داد.البته برای اینکه وب‌سرویس رو روی liara دیپلوی کنم، براش Dockerfile ساختم. توضیحات بیشتر در مورد داکرایز کردن رو میتونید از پست قبلیم توی ویرگول بخونید. پینوشت ۱: بخش نمایش ناتیفیکیشن رو فرصت نکردم پیاده کنم، خیلی کار سختی نیست، فقط کافیه یه شرط گذاشته بشه تا هرموقع بلیت از فلان قیمت پایینتر بود، یه ناتیفیکیشن نشون داده بشه.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Mon, 01 Apr 2019 18:15:32 +0430</pubDate>
            </item>
                    <item>
                <title>داکرایز کردن و دیپلوی وب اپلیکیشن Spring Bootیی روی فندق</title>
                <link>https://virgool.io/@abbas.oveissi/%D8%AF%D8%A7%DA%A9%D8%B1%D8%A7%DB%8C%D8%B2-%DA%A9%D8%B1%D8%AF%D9%86-%D9%88-%D8%AF%DB%8C%D9%BE%D9%84%D9%88%DB%8C-%D9%88%D8%A8-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D8%A7%D8%B3%D9%BE%D8%B1%DB%8C%D9%86%DA%AF-%D8%A8%D9%88%D8%AA-%DB%8C-%D8%B1%D9%88%DB%8C-%D9%81%D9%86%D8%AF%D9%82-wsbqqvefxyyc</link>
                <description>این مقاله بیشتر از اینکه یه مقاله‌ی تخصصی از یه متخصص اسپرینگ و داکر باشه، داستان آخر هفته‌ایه که به کلنجار رفتن با داکر و پلتفرم فندق گذشت. قضیه از اینجا شروع شد که قبلا برای پایان‌نامه‌ام یه وب اپلیکیشن با اسپرینگ بوت  (و دیتابیس PostgreSQL) درست کرده بودم و از VPSها برای هاستش استفاده می‌کردم. با توجه به اینکه هزینه‌های VPS خیلی زیاده، همیشه به مشکل میخوردم. چند وقت پیش توی توییتر دیدم یه پلتفرم Paas به اسم فندق توی ایران درست شده که سرویس رایگان هم داره. واسه همین کنجکاو شدم تا ببینم میشه ازش برای کارم استفاده کنم یا نه.  برای شروع فکرکنم بهتر باشه اول یکم در مورد کلمه‌های توی این مقاله مثل داکرایز، فندق، اسپرینگ بوت و ... صحبت کنیم.داکر (Docker): خانم پیرو توی بلاگش داکر رو اینجوری توضیح داده &quot;داکر ابزاری‌ست برای ساخت، ارسال و اجرای آسان اپلیکیشن. یکی از مهم‌ترین مفاهیم در داکر، کانتینر است. کانتینر با استفاده از مجازی‌سازی در لایه سیستم‌ عامل و ایزوله کردن منابع به این امر کمک می‌کنه. میشه گفت کانتینر و ماشین مجازی تقریبا شبیه هم عمل می‌کنند با این تفاوت که کانتینر در لایه سیستم‌عامل و ماشین مجازی در لایه سخت‌افزار این ایزوله‌سازی را انجام می‌دهند که همین باعث تفاوت‌هایی در عملکرد و پرفورمنسشان می‌شود.&quot;داکرایز کردن (Dockerizing): به فرآیند تغییر برنامه بصورتی که بتونه توی کانتینر داکر اجرا بشه، داکرایز کردن میگن.داکر هاب (Docker Hub): یه خدمت از شرکت داکر هست تا شرکت‌ها و برنامه‌نویس‌ها بتونن ایمیج‌هاشون رو با هم تیمیشون یا بصورت عمومی به اشتراک بذارن.فندق: توی سایتشون نوشته &quot; فندق یه PaaS است که به شما این امکان رو میده که سرویس‌های خودتون رو بر روی سرورهای ابری مستقر کنید و نگران مدیریت سرورها و سرویس‌هایی که نیاز دارید نباشید.&quot; البته اگر اشتباه نکنم به اینجور پلتفرم‌ها Container as a Service هم میگن.اسپرینگ بوت (Spring Boot): اسپرینگ بوت یه پروژه‌ای هست که بر روی فریمورک اسپرینگ نوشته شده و کمک میکنه مراحل راه‌اندازی، تنظیمات و اجرای وب اپلیکیشن ساده‌تر و سریعتر انجام بشه. اگر بخواید فقط از خود اسپرینگ استفاده کنید، باید تمام تنظیمات رو خودتون انجام بدید.خب حالا بریم سراغ اصل قضیه و تعریف جزییات کارهایی که آخر هفته‌ انجام دادم.داکرایز کردن وب اپلیکیشن اسپرینگ بوتتا حالا هیچوقت از خود فریمورک اسپرینگ به تنهایی استفاده نکردم ولی اینجور که میگن اسپرینگ بوت خیلی کار رو آسون کرده و حتی به ما کمک کرده تا بتونیم راحت‌تر وب اپلیکیشن‌هامون رو توی یه کانتینر اجرا کنیم. با روش‌های مختلفی میشه برای وب اپلیکیشن اسپرینگ بوت، ایمیج ساخت. فکرکنم ساده‌ترین راه این باشه که با maven یه پکیج از وب اپلیکیشن بسازیم و بعد به ایمیج منتقلش کنیم. برای ساخت ایمیج، این مراحل زیر رو طی کردم.۱- با استفاده از maven یه پکیج از وب اپلیکیشن ساختم. وقتی کار maven تموم شد، یه فایل با پسوند war در پوشه‌ی target پروژه ساخته شد.۲- توی فولدر پروژه یه فایل با اسم Dockerfile ساختم و دستورات زیر رو داخلش نوشتم. اینارو از یه پروژه‌ی آماده کپی کردم و تغییر دادم.FROM openjdk:8-jdk-alpine
ADD target/crowdsourcing.war target/crowdsourcing.war
EXPOSE 8082
ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;-Dspring.profiles.active=prod&quot;,&quot;target/crowdsourcing.war&quot;]خط اول میگه که این ایمیج براساس یه ایمیج دیگه ساخته بشه که روش JDK 8 نصب هست. خط سوم war ساخته شده توسط maven رو به ایمیج اضافه میکنه. خط پنجم پورت 8082 رو باز میکنه تا از بیرون کانتینر بشه به سایت دسترسی داشت. در نهایت خط آخر مشخص میکنه وقتی کانتینری از این ایمیج ساخته شد، داخلش فایل war با پروفایل prod اجرا بشه.۳- اول مطمئن بشید داکر رو بدرستی روی سیستم نصب کردید. خود سایت داکر نحوه‌ی نصب داکر روی سیستم عامل‌های مختلف رو نوشته، بطور مثال این لینک آموزش نصب داکر روی اوبونتو هست. ترمینال رو باز کنید و به مسیر پروژه برید. با دستور زیر براساس Dockerfileیی که ساخته بودم، ایمیج‌ درست شد.$ docker build .وقتی فرآیند ساخته شدن ایمیج با موفقیت تموم شد، در آخرین خط جزییات بیلد، شناسه‌ی ایمیج نوشته بود.Successfully built d4ef33685011رشته‌ی &quot;d4ef33685011&quot; شناسه‌ی ایمیج ساخته شده هست، بعدا هرجا خواستم از این ایمیج استفاده کنم با این شناسه میتونم بهش اشاره کنم.۴- برای تست ایمیج دستور زیر رو اجرا کردم تا متوجه بشم که آیا کانتینر به درستی از روی ایمیج اجرا میشه یا نه؟!$ docker run d4ef33685011کانتینر اجرا شد ولی چون پروژه نیاز به دیتابیس PostgreSQL داشت، بعد از اجرا شدن خطا داد و کانتینر بسته شد.با دستور زیر میشه از کانتینرهایی که الان در حال اجرا هستند با خبر بشید$ docker ps۵- برای دیتابیس PostgreSQL دنبال ایمیج آماده گشتم که اینجارو پیدا کردم، داکیومنت نحوه‌ی استفاده‌اش رو هم نوشتن.  چون ایمیج توی داکر هاب هست با این دستور زیر یه کانتینر postgres با تنظیماتی که نیاز داشتم ساختم:$ docker run --name mypostgres -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=crowdsourcing -d postgresاین ایمیج &quot;postgres&quot; پورت 5432 رو expose میکنه و بقیه کانتینرها اگر بهش لینک بشن، میتونن از این پورت به دیتابیس متصل بشن. توی connection stringم از شناسه کانتینر برای اتصال به دیتابیس استفاده کردم، مثل زیر:jdbc:postgresql://mypostgres:5432/crowdsourcingالبته اگر میخواید از سیستم عامل میزبان و با ابزارهای مدیریت دیتابیس به دیتابیستون وصل بشید باید توی دستور run آپشن زیر رو هم اضافه کنید.-p 8081:5432اینجوری همه‌ی برنامه‌های روی سیستم عامل میزبان از طریق پورت 8081 میتونن به دیتابیس داخل کانتینر وصل بشن.۶- حالا با دستور زیر دوباره کانتینتر وب اپلیکیشن رو اجرا کردم. توی دستور زیر کانتینر وب اپلیکیشن رو به کانتینر postgres لینک کردم تا مشکل دسترسی به دیتابیس حل بشه.$ docker run --link mypostgres d4ef33685011نتیجه رضایت بخش بود و لاگ‌ها نشون دادن وب اپلیکیشن به درستی اجرا و به دیتابیس متصل شده.پوش کردن ایمیج روی داکر هابخب تا اینجا همه چی بصورت لوکال به درستی انجام شده ولی برای اینکه بعدا بتونم از این ایمیج وب اپلیکیشنم در پلتفرم فندق استفاده کنم، باید ایمیج رو روی داکر هاب میذاشتم. خوشبختانه اینکار رایگان هست و فقط لازم بود یه اکانت در سایت داکر هاب به همراه یه ریپوی بسازم. اگر نمیخواید همه بتونن از ایمیج استفاده کنند باید ریپوی خصوصی بسازید.  در ضمن هر ریپو میتونه شامل نسخه‌های مختلف یه ایمیج باشه که با Tag از هم متمایز شدن. برای Tag گذاشتن روی یه ایمیج‌ها میشه موقع بیلد از دستور زیر استفاده کرد. $ docker build -t &lt;hub-user&gt;/&lt;repo-name&gt;[:&lt;tag&gt;] یا اینکه هر موقع نیاز داشتید، یه ایمیج لوکالتون رو دوباره اینجوری Tagگذاری کنید. $ docker tag &lt;existing-image&gt; &lt;hub-user&gt;/&lt;repo-name&gt;[:&lt;tag&gt;] من از روش دوم استفاده کردم، فرض کنید اسم اکانتم در داکر هاب abbasoveissi هست و اسم ریپو رو webcorwd گذاشتم. شناسه ایمیج هم که d4ef33685011 بود. با دستور زیر ایمیج تگ‌دار میشه. $ docker tag d4ef33685011 abbasoveissi/webcrowd:v1بعد Tag گذاشتن، با دستور زیر به داکر هاب پوش کردمش.$ docker push abbasoveissi/webcrowd:v1سایت داکر هاب رو چک کردم و مطمئن شدم عملیات پوش کردن با موفقیت انجام شده. ایمیج postgres هم که از قبل روی داکر هاب ساخته شده بود و نیاز نبود در رابطه با اون کاری انجام بدم.دیپلوی وب اپلیکیشن روی فندقبه بخش نهایی رسیدیم. مثل داکر هاب، توی فندق هم یه اکانت ساختم (موقع ثبت‌نام یه namespace پرسیده میشه که بعدا توی آدرس سرویس تاثیر داره). پلن رایگانش اینجوری هست که میذاره دو تا کانتینر داشته باشید که رم‌هاشون ۲۰۰ مگ هست. توضیحات بیشتر رو میتونید توی سایتش بخونید.فندق سه مدل سرویس داره، خودشون اینجوری توضیح دادن: سرویس خارجی (ExternalService)-  اگر تحت شرایطی بخواهید به یک سرویس بیرون از محیط namespace دسترسی داشته باشید می توانید از سرویس های خارجی استفاده کنید. مثلا سرویس Front از پروژه شما که قرار است از بیرون مشاهده شود باید از نوع سرویس خارجی باشد. سرویس داخلی (InternalService)- این سرویس ها تنها از داخل namespace قابل دسترسی هستند و فقط سرویس هایی که داخلی این فضا نام باشند می توانند با سرویس های داخلی ارتباط برقرار کنند و هیچ راهی برای برقراری ارتباط با این سرویس ها از خارج از namespace وجود ندارد. سرویس‌های مدیریت شده (ManagedService)-   برخی از سرویس ها مانند MySql یا Postgresql و خیلی موارد دیگر٬ بسیار پر‌کاربرد هستند٬ لذا هسته فندق به صورت خودکار در صورت درخواست کاربر این سرویس ها را می‌سازد تا دیگر شما درگیر تنظیمات آن نشوید و سرعت روند کاری کاهش پیدا نکند. ما به این سرویس ها Managed Service یا سرویس مدیریت شده می‌گوییم. برای ساخت سرویس ابتدا CLI فندق رو از روی آموزشش نصب کردم و بعد با دستور زیر توی ترمینال لاگین کردم.$  fandogh  loginاجرا شدن وب اپلیکیشنم به دو تا سرویس نیاز داره:۱- سرویس خارجی برای وب اپلیکیشن که کاربرها بتونن از طریق اینترنت ازش استفاده کنن۲- سرویس مدیریت شده برای دیتابیس PostgreSQLبرای ساخت سرویس توی فندق باید مانیفست درست کرد، فندق از تنظیمات توی مانیفست برای ساخت سرویس‌ها استفاده می‌کنه.برای سرویس خارجی مانیفست زیر رو درست کردم و توی یه فایل با پسوند yaml ذخیره کردم. برخلاف Dockerfile، اسم این فایل مهم نیست.kind: ExternalService
name: web
spec:
 image: abbasoveissi/webcrowd:v1
 image_pull_policy: Always
 image_pull_secret: &quot;mydockerhub&quot;
 replicas: 1
 port: 8082
 allow_http: true
 env:
    - name: _JAVA_OPTIONS
    value: -Dspring.profiles.active=dev -Xmx160M -Xms130M
 resources:
 memory: 400Mi
توی داکیومنت فندق، تک تک آیتم‌های مانیفست توضیح داده شده، فقط چندتا نکته سریع بگم:چون ریپوی من توی داکر هاب خصوصی هست، برای اینکه فندق بهش دسترسی داشته باشه باید از secretها استفاده می‌کردم. یدونه به اسم mydockerhub ساختم و اسمشو توی مانیفست نوشتم. فندق از طریق این secret میتونه به ریپوی خصوصیم دسترسی داشته باشه و ایمیج رو ازش بگیره. قبلا با دستور زیر secret رو ساختم fandogh  secret create --name mydockerhub -t docker-registry -f server=registry.hub.docker.com -f username=JohnKane -f password=J0hnKane از طریق مانیفست میشه متغیر محیطی ست کرد. مثلا من متغیر _JAVA_OPTIONS رو ست کردم تا هم پروفایلی که وب اپلیکیشن باهاش اجرا میشه رو تغییر بدم و هم مقدار استفاده از رم رو با Xmx و Xms کنترل کنم.با استفاده از port میشه مشخص کرد درخواست‌هایی که به پورت 80 یا 443 سرویستون میرسه به چه پورتی از کانتینر منتقل بشه. توی خط آخر مشخص کردم که میخوام این سرویس ۴۰۰مگ رم استفاده کنه (باید سرویس غیر رایگان باشه وگرنه خطا میده)، دلیلشم اینه نتونستم با کمتر از این مقدار رم، وب اپلیکیشن رو بالا بیارم. به دلیل کم بودن رم، سرویس بسته میشد.با دستور زیر توی ترمینال، سرویس رو در فندق ساختم. fandogh service apply -f manifestname.yamlآدرس سرویس از ترکیب اسم سرویس و namespace درست میشه. مثل زیر:service_name-namespace.fandogh.cloudآدرس سرویس من این شکلی شد:http://web-abbas.fandogh.cloudهر درخواستی به آدرس بالا بره، به پورت 8082 کانتینر منتقل میشه.با دستور زیر ممیشه لاگ کانتینر رو توی فندق دید:$ fandogh service logsبرای ساخت سرویس مدیریت شده‌ی PostgreSQL به جای اینکه مانیفست بسازم، مستقیما از دستور زیر استفاده کردم.fandogh managed-service deploy postgresql 10.4 -c service_name=myservicename  -c adminer_enabled=true -c postgres_password=123456البته قبلا connection string رو توی وب اپلیکیشن به عبارت زیر تغییر داده بودم.jdbc:postgresql://myservicename/dbnameو تماااام!در نهایت سرویس‌هارو چک کردم و دیدم جفتشون به خوبی دارن کار می‌کنند. آدرس وب اپلیکیشن رو توی مرورگر نوشتم و دیدم بدون مشکل بالا میاد. اینجا نقطه‌ای بود که دیگه کلی خوشحال شدم D:ببخشید دیگه مقاله خیلی طولانی شد ولی امیدوارم که هم بعدا بدرد کسی بخوره و هم خودم در آینده اگر خواستم باز از اینکارها کنم، کمتر نیاز باشه براش سرچ کنم. در آخر از دوستم علیرضا و پشتیبانی فندق برای کمک‌هاشون در حل گیر‌ها تشکر میکنم :)</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Mon, 04 Feb 2019 18:57:17 +0330</pubDate>
            </item>
                    <item>
                <title>منابع الهام بخش برای ساخت رابط‌ کاربری برنامه موبایل (قسمت دوم)</title>
                <link>https://virgool.io/@abbas.oveissi/%D9%85%D9%86%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%87%D8%A7%D9%85-%D8%A8%D8%AE%D8%B4-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%B1%D8%A7%D8%A8%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%85%D9%88%D8%A8%D8%A7%DB%8C%D9%84-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-aut0lne4mwac</link>
                <description> سال پیش توی بلاگم یه پست در رابطه با سایت‌هایی نوشتم که میشه ازشون برای ساخت رابط‌کاربری کمک گرفت، یکی دو تا سایت رو اون موقع یادم رفت بنویسم و توی این مدت هم با یه سری سایت جدید آشنا شدم. برای همین تصمیم گرفتم تا یه پست جدید به عنوان مکمل پست قبلی بنویسم. در ادامه میتونید لیست سایت‌‌های جدید رو ببینید.۱- سایت Ui Screenshots: بالاخره چون لیست رو خودم دارم مینویسم، به عنوان اولین سایت، سایت خودمون رو معرفی میکنمD: گاهی بدلیل تفاوت فونت فارسی با انگلیسی نمیشه از طرح‌های انگلیسی الگو گرفت، به همین دلیل با دوستان این سایت رو درست کردیم. در حال حاضر بیشتر از ۵۰۰ تصویر از اپ‌های ایرانی داخل سایت گذاشتیم که از طریق تگ‌ها یا اسم اپ میتونید بهشون دسترسی داشته باشید. http://uiscreenshots.ir/۲- سایت Mobbin: این سایت بیشتر از ۱۵۰تا اپ iOS که از لحاظ دیزاین خوب بودن رو انتخاب کرده و تصاویرشون رو توی سایت گذاشته. چیزی در حدود ۸۰۰۰ الگو میشه. میتونید تصاویر رو براساس اپ یا الگو ببنیید.https://mobbin.design۳- سایت UI Movement: این سایت هر روز طرح‌های مختلفی رو روی سایتش میذاره. تمرکزشون فقط UI هست. یه newsletter داره که میتونید مشترکش بشید و براتون طرح‌هارو میفرستن.https://uimovement.com۴- سایت Pinterest: پینترست یک شبکه‌ی اجتماعی هست و داخلش همه چی پیدا میشه. ولی اگر موقع جستجو از عبارت‌هایی استفاده کنید که داخلش کلمات ui، design، mobile باشه، طرح‌های UIیی زیادی پیدا کنید.https://pinterest.com/explore/material-design/۵- سایت Behance: شبیه سایت dribbble هست، فقط تا اونجایی که من میدونم، طراحان توی dribbble فقط با دعوتنامه میتونند عضو بشن ولی اینجا عضویت براشون راحت‌تر هست. گاهی طرح‌های خوبی میشه توش پیدا کرد.https://www.behance.net۶- سایت Collect UI: از اسمش معلومه کارش چیه. بیش از ۱۴۰۰۰ تصویر از UIهای متفاوت داخلش هست. یه دسته‌بندی خیلی کاملی هم داره که از طریق اون، میتونید فقط تصاویر مربوط به همون دسته‌بندی رو ببینید.http://collectui.com/۷- سایت UI Garage: توی این سایت هم طرح‌های مرتبط با رابط کاربری اپ‌ها و سایت‌ها هست. تعدادش مثل منابع قبلی زیاد نیست ولی خب باز گاهی به کار میاد.https://uigarage.net/پ.ن۱: از آقای یوسف کثیری (توییتر @UsefKasiri) تشکر میکنم چون تعدادی از این سایت‌هارو ایشون به مرور توی توییترشون معرفی کردن و من اونجا باهاشون آشنا شدم.پ.ن۲: لینک قسمت اول منابع الهام بخشhttp://abbas.oveissi.ir/2017/05/27/inspiring-sites-for-designing-ui/</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Mon, 10 Dec 2018 11:23:38 +0330</pubDate>
            </item>
                    <item>
                <title>یادگیری پروژه محور یا آب در هاون کوبیدن؟!</title>
                <link>https://virgool.io/@abbas.oveissi/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D9%85%D8%AD%D9%88%D8%B1-%DB%8C%D8%A7-%D8%A2%D8%A8-%D8%AF%D8%B1-%D9%87%D8%A7%D9%88%D9%86-%DA%A9%D9%88%D8%A8%DB%8C%D8%AF%D9%86-eomgdqhsv2iq</link>
                <description>فرمول موفقیت!متاسفانه نظام آموزشی کشور ما در مدرسه و دانشگاه (به جزء دانشگاه‌ها و مدارس خاص) جوری بوده که یادگیری هر موضوعی رو به دو بخش کاملا متفاوت تئوری و عملی تقسیم کرده. در ذهن بیشتر افراد، یادگیری تئوری یه روش خسته‌کننده و بی فایده هست اما یادگیری عملی روشی جذاب و مفید به حساب میاد. این شکاف توی کارهایی مثل برنامه‌نویسی خیلی عمیق‌تره! خیلی از افراد فقط آموزش‌هایی رو مطالعه میکنند که توی هر پاراگراف حداقل یه خط کد بهشون یاد بده وگرنه خوندن اون آموزش یا کتاب رو بی‌ارزش و وقت تلف کردن میدونن.این طرز تفکر باعث میشه که بیشتر افراد برای یادگیری برنامه‌نویسی روش پروژه محور رو انتخاب کنند. این انتخاب به خودی خود مشکلی نداره ولی مشکل اونجاست که بنظر من درک درستی از یادگیری پروژه محور ندارند. معمولا یادگیری پروژه محورشون شبیه فرآیند زیر هست:۱- تعریف پروژه: بیشتر افراد در شروع کار، توانایی تعریف پروژه برای خودشون رو ندارند. در نتیجه با پرس و جو از بقیه یه پروژه مناسب پیدا میکنند.۲- انجام پروژه: درصد کمی از افراد پیاده‌سازی پروژه رو با سرچ عبارت‌هایی شبیه How blah blah شروع می‌کنند و بعد هر تیکه کدی که پیدا میکنند رو داخل پروژه کپی میکنند. بقیه از همون فردی که پروژه رو براشون تعریف کرده، اینقدر سوال میپرسند تا ذره ذره یه سری نمونه کد ازش بگیرند و عینا داخل پروژه کپی کنند.۳- عیب‌یابی: با توجه به اینکه هر دو گروه کدها رو بدون اینکه دانشی ازش داشته باشن توی پروژه کپی کردن، با هزاران مشکل روبرو میشن. اینجاست که باز آدم‌ها دو دسته میشن، یه سری اینقدر سرچ میکنند و کدهای مختلف رو دوباره کپی پیست میکنن تا مشکل حل بشه و یه سری هم به کسی که برنامه‌نویسی بیشتر ازشون بلده گیر میدن تا بالاخره اون یه نفر بیاد براشون مشکل پروژه رو حل کنه.تا پروژه کامل بشه، مراحل ۲ و ۳ بینهایت بار تکرار میشه. اینجوری یاد گرفتن برنامه‌نویسی به شدت اتلاف انرژی و وقت داره. چون کسی که پروژه رو ساخته چیز زیادی رو یاد نگرفته، حتی اگر قرار باشه دوباره یه پروژه مشابه انجام بده، سر هر تفاوت پروژه‌ی جدید با قبلی، دوباره باید بارها مرحله‌ی ۲ و۳ رو تکرار کنه. تازه نکته‌ی مهم اینه به سازنده‌ی همچین پروژه‌ای نباید برنامه‌نویس گفت. چون در اینصورت همه افراد دنیا هم معمار میشدن، چون به هر کسی چهارتا آجر و یه کیسه سیمان (معادل کدهای آماده توی برنامه‌نویسی) بدی، بالاخره با سعی و خطا میتونه یه خونه برای خودش بسازه ولی این فرد هم معمار حساب نمیشه!! فقط یه سرپناه موقت برای خودش ساخته که احتمالا در آینده‌ای نزدیک خراب میشه.باز تاکید میکنم یادگیری پروژه محور بد نیست، مزایای خودشو داره ولی به شرطی که درست انجام بشه. از دید من تفاوت یادگیری پروژه محور با حالت معمولی (گام به گام) در اینه که بجای خوندن فصل‌های یه کتاب آموزش برنامه‌نویسی بصورت فصل به فصل، موضوعاتی که برای ساخت پروژه نیازه رو خوند. این روش با اون فرآیند ۳ مرحله‌ای که بالاتر گفتم خیلی فرق داره. فرض کنید قراره برنامه‌ای به اسم X برای نمایش نقشه در اندروید ساخته بشه، باید بجای اینکه دنبال نمونه کد نمایش نقشه گشت، توی داکیومنت‌های رسمی اندروید سر فصل‌های مرتبط با نقشه‌اش رو خوند یا مثلا به کتاب‌های رفرنس مثل busy coder رجوع کرد و فصل پیاده‌سازی نقشه‌ی اون رو نگاه کرد. حالا بعد از آشنا شدن با مفاهیم مربوط به نقشه، از روی نمونه مثال‌های موجود در اینترنت، کد برنامه X نوشته بشه.در این روش  چون برنامه‌نویس بعد از یادگرفتن مفاهیم اولیه به دنبال پیاده‌سازی میره، مرحله‌ی کشف خطا براش خیلی راحت‌تر میشه. چون میدونه هر کد به چه موضوعی ربط داره و وظیفه‌اش چیه. در نهایت بعد از کامل شدن پروژه هم، سازنده برنامه میتونه به راحتی هر تغییری که دوباره نیاز داره رو بده. با همین روش، وقتی تعدادی پروژه متنوع انجام بده، بخش‌های مختلف برنامه‌نویسی رو یاد میگیره.ختم کلام اینکه یادگیری پروژه محور به این معنا نیست که فقط باید کپی-پیست کرد و هیچ مطالعه‌ای نداشت. یادگیری پروژه محور ترکیبی از یادگیری تئوری و عملی هست، فقط در ترتیب خوندن موضوعات با یادگیری گام  به گام تفاوت داره. بعد اینکه آموزش‌‌های تئوری به دانش ما عمق میدن و یادگیری عملی تجربه‌ی ما رو برای پیاده‌سازی اون دانش تئوری بالاتر میبره و مکمل یکدیگر هستند. اگر کسی بتونه بالانس رو در مطالعه تئوری و کار کردن بصورت عملی حفظ کنه، قطعا بیشتر از بقیه توی مسیر حرفه‌ایش موفق میشه.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Sun, 25 Nov 2018 20:08:42 +0330</pubDate>
            </item>
                    <item>
                <title>نتیجه توسعه و تحویل مداوم در پروژه‌های نرم‌افزاری!</title>
                <link>https://virgool.io/@abbas.oveissi/%D9%86%D8%AA%DB%8C%D8%AC%D9%87-%D8%AA%D9%88%D8%B3%D8%B9%D9%87-%D9%88-%D8%AA%D8%AD%D9%88%DB%8C%D9%84-%D9%85%D8%AF%D8%A7%D9%88%D9%85-%D8%AF%D8%B1-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D9%87%D8%A7%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1%DB%8C-pf6ro8ruwyul</link>
                <description>قدیما اگر کسی میخواست یک پروژه‌ی نرم‌افزاری رو بی‌نقص انجام بده، زمان زیادی رو برای تحلیل و توسعه صرف میکرد تا در انتها نرم‌افزاری که از نظر خودش عالی بود رو بتونه تحویل بده. ولی دیگه مشخص شده که این روش خیلی خوب نتیجه نمیده، بهتره پروژه بصورت چرخه‌ی تکرار شونده (مثلا توسعه،تست،تحویل) انجام بشه که در آخر هر چرخه هم یک محصول قابل ارائه وجود داشته باشه.در همین راستا امروز یک کتاب میخوندم، آزمایش جالبی رو تعریف کرده بود. آزمایش اینطوری بوده که افراد شرکت‌کننده‌ در کلاس سفالگری رو به دو گروه A و B تقسیم کرده بودند. استاد کلاس به گروه A میگه که تا آخر ترم فقط یکبار فرصت دارند که یک سفال رو تحویل بدن و نمره‌ی پایانیشون رو بگیرن، و به گروه B میگه نمره‌ی پایانیشون براساس وزن همه سفال‌هایی که تا آخر ترم تحویل میدن محاسبه میشه (یعنی مهم نیست که خوشگله یا زشت، فقط وزنش مهمه).نتیجه آزمایش خیلی جالب شده، برخلاف انتظار سفال‌هایی که گروه B ساختن بهتر از گروه A شده. به دلیل اینکه افراد گروه B برای گرفتن نمره‌ی بیشتر بصورت پیوسته در حال ساخت سفال بودند، بعضی از سفال‌ها خوب میشدند و بعضیشون مشکل داشتند. هربار که افراد گروه B، این چرخه ساخت سفال رو انجام میدادن، تجربه‌های جدیدی بدست میاوردن و دانششون بیشتر میشده. در نتیجه به مرور تونستن به هدف کلاس که ساخت سفال با کیفیت بوده برسن.برخلاف گروه B، افراد گروه A بجای اینکه بخوان تلاش کنند و سفال بسازن، کل ترم رو درگیر مسائل تئوری بودن که چطور میشه بهترین سفال ممکن رو ساخت! در نهایت چون شبیه افراد گروه B نتونستن ساخت سفال رو خیلی تمرین کنن، نتونستن سفال عالی که در ذهنشون بوده رو در واقعیت بسازن.توی ساخت نرم‌افزار هم میشه همین نتیجه رو دید. بجای اینکه از طرف کاربر تصمیم بگیرید یا از پیاده‌سازی بعضی چیزها بترسید. بهتره که در یک فرآیند تکرار شونده تصمیماتمون رو پیاده کنیم و ذره ذره کارمون رو بهبود بدیم.</description>
                <category>عباس اویسی</category>
                <author>عباس اویسی</author>
                <pubDate>Sun, 28 Oct 2018 18:40:21 +0330</pubDate>
            </item>
            </channel>
</rss>