<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Meisam Beiranvand</title>
        <link>https://virgool.io/feed/@softrun</link>
        <description>[برنامه نویس و توسعه دهنده ی نرم افزار]</description>
        <language>fa</language>
        <pubDate>2026-06-28 11:13:34</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/80439/avatar/dDbLNU.png?height=120&amp;width=120</url>
            <title>Meisam Beiranvand</title>
            <link>https://virgool.io/@softrun</link>
        </image>

                    <item>
                <title>تست نویسی و استفاده از دیزاین پترن Builder</title>
                <link>https://virgool.io/Rocket/%D8%AA%D8%B3%D8%AA-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D9%88-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AF%DB%8C%D8%B2%D8%A7%DB%8C%D9%86-%D9%BE%D8%AA%D8%B1%D9%86-builder-iols3budwmjr</link>
                <description>سلام به همگیاستفاده ی عملی از دیزاین پترن Builderداشتم برای نرم افزاری که در حال توسعه با زبان Java هستم Integration Test می نوشتم و برای تست ثبت سفارش به یک کاربر با حالت های مختلف از وضعیت اون کاربر نیاز داشتمسه تا از وضعیت های مختلف یک کاربر از قرار زیر هستند:آیا کاربر ثبت نام شده است یا خیر؟ایا اکانت کاربر تایید شده است یا خیر؟آیا کاربر قرارداد استفاده از نرم افزار را تایید کرده است یا خیر؟البته حالت های دیگه ای هم هست اما خب من به همین 3 مورد برای شلوغ نشدن متنم کفایت میکنمخب با همین 3 مورد به تستمون برسیمچندتا از تست هایی که می نویسیم از قرار زیر هستن:اگر کاربر لاگین نشده بود (یعنی توکن احراز هویت ارسال نکرده بود) خطای 401 برگردونهاگر اکانت کاربر تایید نشده بود خطای 403 برگردونهاگر کاربر قرارداد استفاده از نرم افزار رو تایید نکرده خطای 403 برگردونهاگر اکانت کاربر تایید شده بود ولی کاربر قرارداد استفاده از نرم افزار رو تایید نکرده بود خطای 403 برگردونهاگر اکانت کاربر تایید نشده بود ولی کاربر قرارداد رو تایید کرده بود خطای 403 برگردونهو تست های بعدی که مربوط به سفارش و اکانت هستهمینطور که می بینید ما 5 تا تست برای کاربر با حالت های مختلف نیاز داریماول اومدم و خواستم با چند تا متد انجامش بدم ولی دیدم که چقدر ساختن کاربر با وضعیت های مختلف با دیزاین پترن Builder راحت ترهیک کلاس به اسم UserApi دارم که کار های مربوط به کاربر رو انجام میدم. حالا کافیه کلاس Builderام  را داخل همین کلاس بنویسم به این صورت:public static class Builder {
   private String mobile = &amp;quot09122221122&quot;
  private boolean authenticated = false;
  private boolean acceptContract = false;
 
  public Builder setMobile(String mobile) {
  this.mobile = mobile;
  return this;
  }
 
  public Builder authenticated() {
  this.authenticated = true;
  return this;
  }
 
  public Builder acceptContract() {
  this.acceptContract = true;
  return this;
  }
 
  public String buildToken() {
  User user = // create user by mobile …
  if (authenticated)
   // set user authentication status to true
 
  if (acceptContract)
  // set user contract status to truereturn // token generated by user details
  }
}نکته: چون من در نهایت Token کاربر را نیاز دارم Token رو در کلاس Builder می سازمکلاس Buidler بالا اسکلت بندی مربوط به Builder هست و شما میتونید بر مبنای نیازتون مقادیرش رو کامل کنید.برای استفاده ازش یک  static methodاز کلاس UserApi ساختم:public static Builder get() {
  return new Builder();
}البته شما می توانید نیاز ساخت کاربر و Token را به عنوان آرگومانت های ورودی برای کلاس Builder ارسال کنید.اما مهمترین بخش یعنی استفاده کردن از کلاس Builder که ساختیمحالا کافیه برای هر تست من کاربر رو بسازم:الان من یک کاربر با شماره ی پیشفرض دارم که میتونم ازش استفاده کنم:UserApi.get().buildToken(); یک کاربر با شماره جدید:UserApi.get().setMobile(&quot;09123332211&quot;).buildToken();یک کاربر که ثبت نام کرده و تنها اکانت کاربری اش تایید شده است:UserApi.get().authenticated().buildToken();یک کاربر که تنها قرارداد استفاده از نرم افزار را تایید کرده است:UserApi.get().acceptContract().buildToken();یک کاربر که علاوه بر اینکه اکانتش تایید شده است، قرارداد استفاده از نرم افزار را نیز تایید کرده است:UserApi.get().authenticated().acceptContract().buildToken();همونطور که میبینید به یک یا چند کاربر با وضعیت های مختلف دسترسی داریم و به راحتی می توانیم تستمون رو انجام بدیم.پ.ن: من توی این نوشته دیزاین پترن رو آموزش ندادم. تنها نحوه و مزیت استفاده از اون رو توی این مثال خاص توضیح دادم</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Wed, 17 Aug 2022 13:05:26 +0430</pubDate>
            </item>
                    <item>
                <title>تایید خودکار پیامک اعتبار سنجی در اندروید با SMS Retriever API</title>
                <link>https://virgool.io/MobileLab/%D8%AA%D8%A7%DB%8C%DB%8C%D8%AF-%D8%AE%D9%88%D8%AF%DA%A9%D8%A7%D8%B1-%D9%BE%DB%8C%D8%A7%D9%85%DA%A9-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%86%D8%AC%DB%8C-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-%D8%A8%D8%A7-sms-retriever-api-rumtvmd2dpn5</link>
                <description>سلامتوی این پست در مورد نحوه ی پیاده سازی تایید خودکار پیامک اعتبار سنجی در اندروید با استفاده از SMS Retriever API توضیحاتی ارائه میدم. که امیدوارم مفید باشه براتونروند سابق تایید کد اعتبار سنجی به این صورت بود که یک اپلیکیشن درخواست خواندن پیامک های گوشی رو ارسال میکرد به کاربر و اگر کاربر تایید میکرد اپلیکیشن به صورت خودکار پیام های دریافتی رو میخوند و کد رو درمیاورد و توی یه EditText قرار میداد.مشکل این روند اینه که خب وقتی شما دسترسی به پیامک ها رو دادین چه تضمینی وجود داره که بقیه ی پیام های شما رو هم نخونه و امکان نقض حریم خصوصی وجود داره. گوگل برای رفع این مشکل اولا که اپ هایی که از روش سابق استفاده کردن رو انگار میخواد از گوگل پلی حذف کنه (یا کرده!) و یک روش جایگزین قرار داده برای اینکار.گوگل گفته شمایی که میخواید توی اپلیکیشنتون از اعتبار سنجی با شماره ی موبایل استفاده کنید و میخواید که این اعتبار سنجی به صورت خودکار باشه دیگه دسترسی پیامک رو نگیرید و بجاش با روشی که در زیر میگم یک هش به پیامک اعتبار سنجیتون اضافه کنید و با این کار دیگه نیازی به دسترسی پیامک ها ندارین!پیامک اعتبار سنجی شما که برای کاربر ارسال میشه چیزی شبیه به اینه:کد تایید نرم افزار: 52236GDse38df452که با استفاده از این کد هش شده (که باید طولش 11 باشه حتما) اجازه ی خوندن این پیامک رو به شما میده!و شما با خوندن و استخراج کد میتونید اعتبار سنجی رو انجام بدید.در ادامه این پیاده سازی رو انجام میدیم.به طور خلاصه روند کار به این صورته که ابتدا شما: 1. شماره ی کاربر رو به سمت سرور خودتون ارسال میکنید 2. همزمان توی اپلیکیشن باید SMS Retriever API رو صدا بزنید تا به پیامک های دریافتی از سرور گوش بده3. زمانی که سرور پیامک رو برای شماره ی کاربر فرستاد حالا سرویس های گوگل پلی با استفاده از او hash که توی پیامک دریافتی هست تعیین میکنن که آیا این پیامک برای برنامه ی مورد نظر هست یا خیر4. و در مرحله ی آخر اپلیکیشن پیامک رو میخونه و مجدد پیامک رو همراه با شماره برای سرور برنامه ارسال میکنه پیاده سازی SMS Retriever APIمرحله ی 1 (اضافه کردن کتابخانه های مورد نیاز):dependencies {     
          implementation &#039;com.google.android.gms:play-services-base:11.6.0&#039;   
          implementation &#039;com.google.android.gms:play-services-auth-api-phone:11.6.0&#039;   
}مرحله 2 (شروع SmsRetriever):بعد از اینکه کاربر شماره رو وارد کرد شما باید SmsRetriever رو در صفحه ی وارد کردن کد تایید پیاده سازی کنید:private void smsListener() {
      SmsRetrieverClient client = SmsRetriever.getClient(this);
      client.startSmsRetriever();
}مرحله 3 (پیاده سازی BroadcastReceiver برای دریافت پیامک):public class AppSMSBroadcastReceiver extends BroadcastReceiver {
    private OnSmsReceiveListener onSmsReceiveListener;
    public void setOnSmsReceiveListener(OnSmsReceiveListener onSmsReceiveListener) {
        this.onSmsReceiveListener = onSmsReceiveListener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

            switch (status.getStatusCode()) {
                case CommonStatusCodes.SUCCESS:
                    String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
                    onSmsReceiveListener.onReceive(message);
                    break;
                case CommonStatusCodes.TIMEOUT:
                    break;
            }
        }
    }

    public interface OnSmsReceiveListener {
        void onReceive(String code);
    }
}همینطور که میبینید یک لیستنر هم قرار دادیم که وقتی کد دریافت شد کد رو به اکتیویتیمون یا هرجا که وصل کردیم ارسال کنه.حالا باید BroadcastReceiver رو به برنامه معرفی کنیم تا ازش استفاده کنیم. برای اینکار یا میتونیم اون رو توی Manifest قرار بدیم یا اینکه مستقیم با کد جاوا ثبت کنیم.من چون میخوام با لیستنر بهش دسترسی داشته باشم به صورت زیر توی کد جاوا پیاده میکنم:IntentFilter intentFilter = new IntentFilter(&amp;quotcom.google.android.gms.auth.api.phone.SMS_RETRIEVED&amp;quot);

AppSMSBroadcastReceiver appSMSBroadcastReceiver = new AppSMSBroadcastReceiver();
appSMSBroadcastReceiver.setOnSmsReceiveListener(new AppSMSBroadcastReceiver.OnSmsRecieveListener() {
    @Override
    public void onReceive(String code) {
       // کد دریافتی از برودکست 
    }
});

@Override
protected void () {
    super.();
    registerReceiver(appSMSBroadcastReceiver, intentFilter);
}

@Override
protected void () {
    super.();
    unregisterReceiver(appSMSBroadcastReceiver);
}مرحله 4 (ساختن hash):حالا باید کد hash مربوط به اپلیکیشن رو بسازیم و به سرور بگیم که این کد رو توی پیامک دریافتی ما قرار بده تا سرویس های گوگل پلی بدونن که این پیامک دریافتی برای اپلیکیشن ماست.با استفاده از کلاس زیر میتونیم این کد رو بسازیم:public class AppSignatureHelper  extends ContextWrapper {
    public static final String TAG = AppSignatureHelper.class.getSimpleName();

    private static final String HASH_TYPE = &amp;quotSHA-256&amp;quot
    public static final int NUM_HASHED_BYTES = 9;
    public static final int NUM_BASE64_CHAR = 11;

    public AppSignatureHelper(Context context) {
        super(context);
    }

    /**
     * Get all the app signatures for the current package
     *
     * @return
     */
    public ArrayList&lt;String&gt; getAppSignatures() {
        ArrayList&lt;String&gt; appCodes = new ArrayList&lt;&gt;();

        try {
            // Get all package signatures for the current package
            String packageName = getPackageName();
            PackageManager packageManager = getPackageManager();
            Signature[] signatures = packageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES).signatures;

            // For each signature create a compatible hash
            for (Signature signature : signatures) {
                String hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appCodes.add(String.format(&amp;quot%s&amp;quot, hash));
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.v(TAG, &amp;quotUnable to find package to obtain hash.&amp;quot, e);
        }
        return appCodes;
    }

    private static String hash(String packageName, String signature) {
        String appInfo = packageName + &amp;quot &amp;quot + signature;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
            messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
            byte[] hashSignature = messageDigest.digest();

            // truncated into NUM_HASHED_BYTES
            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            // encode into Base64
            String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

            Log.v(TAG + &amp;quotsms_sample_test&amp;quot, String.format(&amp;quotpkg: %s -- hash: %s&amp;quot, packageName, base64Hash));
            return base64Hash;
        } catch (NoSuchAlgorithmException e) {
            Log.v(TAG+ &amp;quotsms_sample_test&amp;quot, &amp;quothash:NoSuchAlgorithm&amp;quot, e);
        }
        return null;
    }
}حالا باید توی کلاس Application برنامه متد ()getAppSignatures رو اجرا کنید که لیستی از کد ها برمیگردونه که شما میتونید اولی رو بردارید و اون میشه کد hash شما که باید توی پیامک قرار داده بشه:AppSignatureHelper appSignature = new AppSignatureHelper(this);
for (String signature : appSignature.getAppSignatures()) {
    Log.e(TAG, &amp;quotonCreate: &amp;quot + signature );
}نکاتی در مورد این کلاس:بعد از اینکه نسخه رو آماده کردید این کلاس رو حذف کنیدحتما در زمان ریلیز هم کد هش رو بگیرید و از اون کد استفاده کنید چون ممکنه در زمان دیباگ کد hash با زمان ریلیز متفاوت باشهتمام. کد نمونه رو در گیت هاب زیر میتونید ببینید:https://github.com/softrunapp/SMS-Retriever-APIرفرنس: https://medium.com/android-dev-hacks/autofill-otp-verification-with-latest-sms-retriever-api-73c788636783</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Mon, 27 Jul 2020 17:46:24 +0430</pubDate>
            </item>
                    <item>
                <title>پرداخت درون برنامه ای کافه بازار - مثل آب خوردن :)</title>
                <link>https://virgool.io/@softrun/%D9%BE%D8%B1%D8%AF%D8%A7%D8%AE%D8%AA-%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-%D9%85%D8%AB%D9%84-%D8%A2%D8%A8-%D8%AE%D9%88%D8%B1%D8%AF%D9%86-umcmwrj4qlgj</link>
                <description>پرداخت درون برنامه ای کافه بازارسلام، در این پست بدون مقدمه میخوام با استفاده از کتابخانه ای که نوشتم با چند مرحله ی ساده پرداخت درون برنامه ای کافه بازار رو پیاده سازی کنیم.البته بهتره که یک بار مستندات کافه بازار رو هم با دقت بخونید تا با اصطلاحات و روند کار آشنا بشید.پیاده سازی پرداخت درون برنامه ای کافه بازارمرحله 1. اضافه کردن کتابخانه ی CafebazaarInAppBilling به برنامه1.1- اضافه کردن خطوط زیر به build.gradle روت پروژه:allprojects {
    repositories {
        ...
        maven { url &#039;https://jitpack.io&#039; }
    }
}2.1- اضافه کردن کتابخانه به برنامه:dependencies {
    implementation &#039;com.github.softrunapp:CafeBazaarInAppBilling:1.0.3&#039;
}3.1- اضافه کردن Java8 به برنامه:android {
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}بسیار خب! الان کتابخانه به برنامه اضافه شدمرحله 2. دریافت RSA و SKU از کافه بازار1.2- از برنامه خروجی ریلیز بگیرید و در پنل کافه بازار آپلود کنید2.2- پس از بارگزاری برنامه در پنل کافه بازار در تب پرداخت درون برنامه ای کلید RSA رو خواهید دید و به تعدادی که نیاز دارید با دکمه ی محصول جدید SKU بسازید. دقت داشته باشید منظور از SKU شناسه ی کالا می باشد.این هم از مرحله ی دوم. مرحله 3. پیاده سازی پرداخت درون برنامه ای کافه بازار در اپلیکیشن کد های کلاس اکتیویتی را به شکل زیر تغییر بدیدpublic class MainActivity extends AppCompatActivity implements CafebazaarBillingListener {
     
     private CafebazaarBilling cafebazaarBilling;
     @Override
      protected void onCreate(Bundle savedInstanceState) {
                       super.onCreate(savedInstanceState);
                       setContentView(R.layout.activity_main);
                       
                        cafebazaarBilling = new CafebazaarBilling.Builder(this)
                               .setRsaKey(rsaKey) // کلید RSA که در مرحله ی قبل دریافت کردید
                               .setBillingListener(this)
                               .build();

                  Button purchase = findViewById(R.id.purchase);
                  purchase.setOnClickListener(view -&gt; 
                          cafebazaarBilling.purchase(SKU)); // این متد با استفاده از اس کا یو که از مرحله قبل دریافت کردید اقدام به ارتباط با کافه بازار و خرید محصول میکنه و دیالوگ خرید کافه بازار رو به کاربر نشون میده
       }

    @Override
     protected void onDestroy() {
         super.onDestroy();
         cafebazaarBilling.onDestroy(); 
      }

     @Override
     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
          cafebazaarBilling.onActivityResult(requestCode, resultCode, data);
          super.onActivityResult(requestCode, resultCode, data);
       }

  @Override
   public void ConnectingToBazaar() {
           // این متد زمانی صدا زده می شود که برنامه در شروع اجرای ارتباط با کافه بازار است که در این متد می توانید یک لودینگ نمایش دهید 
   }

   @Override
   public void onConnectedToBazaar() {
        // زمانی که با موفقیت به کافه بازار متصل شد این متد نمایش داده می شود
    }

    @Override
    public void onIabPurchaseFinished(Purchase purchase) {
         // زمانی که خرید با موفقیت انجام می شود خرید به این متد برگردانده می شود که میتوانید خرید را به سرور برنامه خودتون ارسال کنید 
        // اگر خرید شما از نوع مصرفی است باید خرید را مصرف کنید اگر به سرور ارسال میکنید در جواب برگشتی سرور متد زیر را صدا بزنید. یا اگر برنامه سرور ندارد همینجا متد مصرف را صدا بزنید:
              
          cafebazaarBilling.consumePurchase(purchase);
     }

   @Override
   public void onConsumeFinished() {
         // این متد زمانی صدا زده می شود که خرید از نوع مصرفی بوده و خرید مصرف شده باشد
    }

   @Override
   public void onQueryInventoryFinished(Inventory inventory) {
        // این متد خرید های کاربر را برمی گرداند که اگر خرید مصرفی مصرف نشده باشد می توانید مصرف کنید
   }

   @Override
   public void () {
          // این متد زمانی صدا زده می شود که کاربر خرید را لغو کند
   }

   @Override
   public void onFailed(String message) {
         // اگر خطایی در روند خرید رخ دهد این متد صدا زده می شود
   }
}متد هایی که صدا زده می شوند:cafebazaarBilling.purchase(SKU)); // خرید محصولcafebazaarBilling.consumePurchase(purchase); // مصرف محصول خریداری شدهcafebazaarBilling.queryInventoryAsync(); // لیست محصولات خریداری شدهبه همین راحتی خرید درون برنامه ای کافه بازار رو میشه انجام دادبرای مشاهده کد نمونه میتونید به لینک زیر مراجعه کنید:https://github.com/softrunapp/CafeBazaarInAppBillingامیدوارم مفید بوده باشه :)</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Sat, 18 Jul 2020 17:21:24 +0430</pubDate>
            </item>
                    <item>
                <title>کدهامونو کتابخونه کنیم + FastSnack</title>
                <link>https://virgool.io/@softrun/%DA%A9%D8%AF%D9%87%D8%A7%D9%85%D9%88%D9%86%D9%88-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D9%88%D9%86%D9%87-%DA%A9%D9%86%DB%8C%D9%85-fastsnack-tgkz86n8n1l8</link>
                <description>پروژه جدید که میاد اگر شبیه پروژه های قبلی بود که کل پروژه قبلی رو کپی میکنیم یا بخش هایی از کد ها رو کپی و توی پروژه ی جدید پیست میکنیم و شروع به کار روی پروژه ...اینکه این کار درسته یا غلط رو فعلا نظری ندارم اما میخوام در مورد اینکه چطور کد های قبلیمون رو توی پروژه جدید استفاده کنیم صحبت کنمزمانی که داشتم اپ/بازی اسم فامیل آنلاین رو توسعه میدادم تصمیم گرفتم که برای نمایش اعلان ها به کاربر از Snackbar استفاده کنم، قبلش Toast مرسوم بوداول (اول یعنی بعد از optimize شدن کدها :) ) یک کلاس ساختم به اسم AppSnackBar و متن و ... رو براش میفرستادم، خیلی هم عالی و لذت بخش :)توی یه پروژه دیگه که داشتم اونم اتفاقا برای اعلان هاش نیاز به Snackbar بود چکار باید میکردم؟پیدا کردن کلاس AppSnackBar -&gt; کپی کردن -&gt; پیست کردن توی پروژه ی جدید -&gt; :))من AppSnackBar رو فقط برای اسم فامیل آنلاین به صورت سفارشی درست کرده بودم و برای پروژه ی جدید باید تغییراتی میدادمتغییرات رو دادم و تمام!و چی میشه؟؟ برای پروژه ی بعد باید همون روند کپی پیست کردن رو ادامه میدادم و احتمالا تغییر بدماما این خوب نبود :(اول اومدم کلاس رو گسترش دادم و بعد ویژگی هایی بهش دادم تا بر اساس پروژه کاستومایز بشهو بعد اونو تبدیل به یک لایبرری ساده کردم اما این کار دو فایده داره:  کپی پیست کردنش راحت تر میشه و فقط لایبرری رو اضافه میکنم به پروژه جدیدماگر بروزرسانی روش انجام دادم خیلی راحت در دسترسمهالبته یه نکته مهمی هم این وسط هستاونم اینکه هر چیزی رو نمیشه لایبرری کرد و ممکنه (یعنی تقریبا حتما :) ) از اون چیزی که شما میخواید لایبرری براش بسازین کسی قبلا این کار رو کرده و ببینید که اگر به کار میاد از همون استفاده کنید FastSnackFastSnack.on(this).message(&amp;quotHello World!&amp;quot).show();لایبرری FastSnack رو توی لینک زیر میتونید ببینید https://github.com/softrunapp/fastsnackخوشحال میشم اگر ایرادی داره ببینید و رفع کنیم باهم :)</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Sun, 20 Oct 2019 16:45:07 +0330</pubDate>
            </item>
                    <item>
                <title>صفحه بندی کردن RecyclerView در اندروید</title>
                <link>https://virgool.io/@softrun/%D8%B5%D9%81%D8%AD%D9%87-%D8%A8%D9%86%D8%AF%DB%8C-%DA%A9%D8%B1%D8%AF%D9%86-recyclerview-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-sognkbifxyj0</link>
                <description>توی برنامه نویسی قطعا به لیست ها نیاز داریم و تقریبا همه ی ما لیست هامون ادامه دار هستن، توی برنامه نویسی اندروید مدت های زیادیه که بجای ListView از RecyclerView استفاده میشه که در کنارش یه دونه RecyclerView.Adapter هم هست و وظیفه ی چیدمان آیتم ها و مدیریتشون روی توی RecyclerView  بر عهده داره. تا اینجای کار همه چی خوبه ولی مشکل از اونجایی شروع میشه که لیست هامون صفحه بندی پیدا میکنن، البته شاید نشه اسمش رو مشکل گذاشت؛ چالش کلمه ی بهتریه!توی RecyclerView یک Listener ست میشه کرد که با اون متوجه میشیم که آیا به انتهای لیست رسیدیم یا نه، هرچند از طریق آداپتر هم میشه این رو فهمید و شاید راه های دیگه ...گوگل برای حل این چالش یک کامپوننت به Android JetPack اضافه کرد به اسم Paging اما خب یا هنوز خیلی جا نیوفتاده یا اینکه استقبال ازش نشده، من که خودم تست کردم ترغیب به استفاده مجدد نشدم که البته هیچ قطعیتی وجود نداره برای استفاده نکردن ازش :)اینها مقدمه بود برای معرفی یک راهکار متفاوت تر!زمانی که از سرور دیتا ها دریافت میشه معمولا صفحه های داده ای که میاد تعداد ثابتی دارند و تنها ممکنه صفحه آخری که دریافت میشه تعداد مقادیرش کمتر باشهhttps://yourdomain/api/user/?page=1یعنی اگر 32 (از 0 تا 31) کاربر داشته باشیم و از سرور بخواهیم لیست کاربر ها رو بهمون بده و سرور هم بر اساس اعلام خودمون یا تصمیم خودش مثلا هر سری 10 کاربر به ما برگردونه پس به این صورت خواهد شد:صفحه ی اول 10 کاربر -  0 تا 9صفحه دوم 10 کاربر - 10 تا 19صفحه ی سوم 10 کاربر - 20 تا 29صفحه چهارم 2 کاربر - 30 تا 31یکسری از api ها زمانی که داده رو برمیگردونن یک پارامتر مثلا با عنوان TotalPage برمیگردونن توی هر Api که تعداد کل صفحات رو اعلان میکنه و برای مثال ما 4 رو برمیگردونه که ما الکی برای صفحات 5 و بالاتر درخواست ارسال نکنیم چون داده ای وجود ندارهاگر دقت کرده باشین بدون استفاده از پارامتر TotalPage توی جواب api میتونیم بفهمیم که دیگه آیا درخواستی رو به سرور برای گرفتن صفحه 5 ارسال کنیم یا نه.وقتی ما میدونیم هر صفحه 10 تا کاربر رو برمیگردونه پس به احتمال خیلی زیاد صفحه ای که تعداد کمتری رو برگردونه اون صفحه ی آخره، یعنی وقتی صفحه 4 رو درخواست میکنیم و تنها 2 تا کاربر رو نشون میده پس اون صفحه صفحه ی آخر از لیست کاربرامون هست. جالب بود نه؟این حالت 2 نکته داره یکی مفید و یکی ممکنه مضر باشهنکته ی مفیدش اینکه یک کوئری رو از سرور کم میکنه اونم اینکه نیاز نیست هر بار که api صدا زده شد بره و تعداد همه ی کاربرا رو برگردونه، البته ممکنه خیلی به چشم نیاد توی سرور های درست طراحی شده و البته بزرگ!نکته ی منفی اینه که اگر تعداد کاربرا بخش پذیر بر تعداد کاربرای هر صفحه باشه اونوقت یک رکوئست اضافه به سرور فرستاده می شه، به چه صورت؟فرض کنید لیست کاربرا 40 عدد باشه و وقتی صفحه 4 رو صدا بزنیم 10 عدد کاربر رو برمیگردونه و چون تعداد کاربرا 10 تاست این صفحه رو صفحه ی آخر نمیدونیم و یک رکوئست به سرور می فرستیم که صفحه ی 5 رو بده!این مورد رو هم شاید بشه در نظر نگرفت چون احتمالش خیلی نیست!خب اینم از مقدمه ی دوم!حالا بر اساس مفاهیم بالا به تازگی یک لایبرری ساده ساختم که چالش صفحه بندی رو توی 3 مرحله حل میکنه بر اساس یک Listener میتونید صدا زدن Api رو مدیریت کنید. ابتدا کتابخونه رو به برنامه اضافه کنید:1- به Build.Gradle خط زیر رو اضافه کنیدallprojects {
    repositories {
        ...
        maven {url &#039;https://jitpack.io&#039; }
    }
}2- کتابخانه رو به Gradle برانامه اضافه کنیدdependencies {
                implementation &#039;com.github.softrunapp:Paginated-RecyclerView:1.0.0&#039; 
 }خب بعد از اضافه کردن کتابخانه میریم سراغ مراحل کار با کتابخانه. 1-  ابتدا کلاس Adapter رو میسازیم و به جای اینکه Extends بشه از RecyclerView.Adapter اینبار از PaginatedAdapter مشتق گرفته شده و توی پارامتر ها علاوه بر ViewHolder کلاس مدل رو هم قرار میدیم:public class MyAdapter extends PaginatedAdapter&lt;User, MyAdapter.ViewHolder&gt; {
      ...
      @Override
      public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
      User user = getItem(position); //get item with position
      }
      ...همونطور که توی کد بالا میبینید کلاس User رو هم به عنوان پارامتر به کلاس PaginatedAdapter پاس داده ایم. همینطور توی متد onBindViewHolder با استفاده از متد getItem میتونیم به مدل کنونی دسترسی داشته باشیم.2- حالا آداپترمون رو یک نمونه ازش میسازیم و بهش RecyclerView رو پاس میدیم و همینطور یک لیستنر هم ست میکنیم:MyAdapter adapter = new MyAdapter();
    adapter.setDefaultRecyclerView(this, R.id.recyclerView);  // آی دی لیست و اکتیویتی رو ست میکنیم برای این متد
    adapter.setOnPaginationListener(new PaginatedAdapter.OnPaginationListener() {
	      @Override
	      public void onCurrentPage(int page) {
		  //صفحه ی کنونی که بارگذاری شده
	      }

	      @Override
	      public void onNextPage(int page) {
		  // اینجا باید صفحه جدید رو از سرور درخواست کنیم
	      }

	      @Override
	      public void () {
		  // همه ی صفحات بارگذاری شده اند
	      }
	});توی کد بالا توضیح داده شده هر متد چه زمانی فراخوانی میشه به صورت پیشفرض صفحه اول رو 1 در نظر میگیره و اندازه هر صفحه رو 10 قرار میده که این مقادیر قابل ویرایش هستند که در ادامه قرارشون میدم3. ست کردن لیست دیتاهای دریافتی از سرورadapter.submitItems(yourListData);پس از دریافت داده ها مثلا User ها لیست User ها رو با متد بالا به آداپتر می فرستیمتمام شد!سفارشی سازی: MyAdapter adapter = new MyAdapter(); 
//setters 
adapter.setStartPage(1); //set first page of data. default value is 1. 
adapter.setPageSize(10); //set page data size. default value is 10. adapter.setRecyclerView(recyclerView); // set your RecyclerView with options      

//getters 
adapter.getStartPage(); // return start page 
adapter.getCurrentPage(); // return last page which loaded 
adapter.getRecyclerView(); // return recycler viewهمینطور برای دیدن مثال کامل از گیت هاب روی لینک زیر کلیک کنید:https://github.com/softrunapp/Paginated-RecyclerViewاگر سوال یا انتقاد یا پیشنهادی دارین خوشحال میشم باهام در میون بزاریدهمینطور میتونید پروژه رو توی گیت هاب فورک کنید و توی بهبود کتابخونه کمک کنید امیدوارم مفید بوده باشه :)</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Thu, 17 Oct 2019 00:02:50 +0330</pubDate>
            </item>
                    <item>
                <title>برنامه نویسی رو از کجا شروع کنیم؟ چقدر کلیشه ای :)</title>
                <link>https://virgool.io/@softrun/%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D8%B1%D9%88-%D8%A7%D8%B2-%DA%A9%D8%AC%D8%A7-%D8%B4%D8%B1%D9%88%D8%B9-%DA%A9%D9%86%DB%8C%D9%85-zqmygouussc9</link>
                <description>برنامه نویسی!سالها پیش وقتی تازه شروع به برنامه نویسی کرده بودم درک کاملی از منطق برنامه نویسی نداشتم و فقط برنامه ای رو مینوشتم که کار میکرد و البته فقط کار میکرد!!!اما واقعا برنامه نویسی چیه؟! از کجا باید شروع کرد و چجوری باید فهمیدش؟همین سوال شاید مانع خیلی ها برای برنامه نویس شدن بوده و اونها الان به کاری غیر از برنامه نویسی مشغولند.یکسری ها دوست دارند برنامه اندروید بسازن٬ یکسری بازی سازی رو دوست دارند٬ طراحی سایت علاقه بعضی ها برای توسعه هست و خیلی ها هم نمیدونن چی رو میخوان!اما اینها در درجه دوم از اهمیت قرار داره٬ درجه ی اول اهمیت درک برنامه نویسیه.خیلی تعریف ها برای برنامه نویسی نوشته شده، از تعریف های کتابی و عامیانه٬ ولی آیا میتونید برای خودتون بقبولونید که دقیقا برنامه نویسی رو با اون تعریف درک کردین؟!تا منطق برنامه نویسی رو یاد نگیرید برنامه نویسی همیشه سخت خواهد موند اما زمانی که شما منطق برنامه نویسی رو یاد گرفتید دیگه خیلی مهم نیست با چه زبانی کد مینویسین چون سوئیچ کردن بین زبان ها آسون میشهبزارید با یک مثال برنامه نویسی رو از منظر خودم توضیح بدم:تصور کنید که یک شخص چینی که خیلی آدم باسواد و سریعی هست و هر سوالی که داشته باشید رو خیلی سریع جواب میده رو داشته باشید. ولی یک مشکلی هست که شما فارسی زبان هستید و هیچی از زبان چینی بلد نیستید و نمیتونید سوالتون رو ازش بپرسید و جوابتون رو بگیرید. پس باید چکار کنید؟!بله درسته باید یک نفر که هم زبان ما رو که فارسی هست بلد باشه و هم زبان چینی رو تا بتونه سوال ما رو برای اون شخص چینی ترجمه کنه و بعد ازگرفتن جواب باز جواب رو هم برای ما به فارسی ترجمه کنه.خیلی خوب شد :)اما باز یک مشکلی هست! کسی وجود نداره که هم زبان چینی بلد باشه و هم زبان فارسی! تنها مترجمی که هست و زبان چینی رو بلده انگلیسی زبانه و فارسی رو بلد نیست! (فرض رو بر این گرفتیم)حالا باید چکار کنیم؟!خب با یک حساب سرانگشتی متوجه میشیم که یاد گرفتن زبان انگلیسی از زبان چینی آسون تره پس ما باید زبان انگلیسی رو یاد بگیریم و اونوقت سوالمون رو به انگلیسی به اون مترجم بگیم و بعد مترجم جواب رو باز به انگلیسی برای ما ترجمه کنهپس برای ارتباط با اون شخص چینی ما باید زبان انگلیسی رو یاد بگیریم و دیگه برامون مهم نیست که مترجم چطوری مفهوم ما رو به اون شخص چینی منتقل میکنه فقط این رو میدونیم که هرچقدر ما به زبان انگلیسی مسلط تر باشیم و بتونیم منظورمون رو بهتر به مترجم انگلیسی زبان انتقال بدیم بالطبع جواب درست تری هم میگیریمعالی شد نه؟ :)در رابطه با برنامه نویسی هم دقیقا همینطوره یعنی کامپیوتر زبانش فقط صفر و یک هست و جز اینها چیزی رو متوجه نمیشه و ما برای ارتباط با اون و اینکه ازش بخوایم کاری رو انجام بده یک واسط و مترجمی رو ساختن که یادگیری زبانش از زبان کامپیوتر (صفر و یک) راحت تر باشه و این واسط اسمش زبان برنامه نویسیه که ما به جای اینکه بریم و زبان کامپیوتر که سخت هست رو یاد بگیریم میریم و یک زبان برنامه نویسی رو که آسون تر هست یاد میگیریم و هرچقدر بر این زبان برنامه نویسی مسلط تر باشیم تعاملمون با کامپیوتر بهترهمثل هر زبانی تو دنیا زبان های برنامه نویسی هم یکسری قوائد دارن که باید اون قواعد رو یاد گرفت هرچقد بیشتر به یک زبان مسلط باشید راحت تر میتونید با اون زبان مفهوم و منظورتون رو انتقال بدید و برای تسلط بر یک زبان باید قواعد اون زبان رو بلد باشید، اما باید از با چه زبانی شروع کرد؟!پیشنهاد خودم زبان سی پلاس پلاس هستبه چند دلیل که دو موردش رو عرض میکنم1. توی اکثر دانشگاهامون تدریس میشه 2. قواعد زبانش شبیه به بقیه زبان هاییه که بعدا باهاش نرم افزار نوشته میشه. البته نه همه شون :)اگر دو مورد بالا نبود شاید گزینه بعدی پاسکال بود که به زبان آکادمیک هم معروفه.اما باید آیا همه ی سی پلاس پلاس رو یاد گرفت؟نه نیاز نیست فقط باید با قواعد زبان اشنا شد و درک کرد برنامه نویسی رو.چون گذارد خشت اول بر زمین معمار کج            گر رساند بر فلک٬ باشد همان دیوار کج</description>
                <category>Meisam Beiranvand</category>
                <author>Meisam Beiranvand</author>
                <pubDate>Wed, 16 Oct 2019 15:32:49 +0330</pubDate>
            </item>
            </channel>
</rss>