دریافت اطلاعات از json در اندروید بوسیله کتابخانه volley
در این آموزش قصد داریم نحوه اتصال به وب سرویس Rest با استفاده از کتابخانه volley و پردازش اطلاعات دریافتی از json در برنامه نویسی اندروید را آموزش بدهیم.
Volley کتابخانه برنامه نویسی شبکه برای آندروید است که اولین بار در کنفرانس گوگل IO سال ۲۰۱۳ معرفی شد. دسترسی به منابع شبکه مانند ارسال و دریافت اطلاعات و یا آپلود و دانلود فایل از کارهایی است که Volley انجام میدهد. خب تا قبل از معرفی Volley برنامه نویسان چطور برنامه های تحت شبکه و اینترنت می نوشتند؟
کتابخانه ها و روشهایی که قبل از معرفی Volley استفاده میشدند همگی با رابط کاربری تداخل داشتند و به نوعی کند عمل می کردند. تا قبل از معرفی Volley کلاس استاندارد جاوا java.net.HttpUrlConnection و کلاس org.apache.http.client که توسط بنیاد آپاچی معرفی شده بود تنها راههای دسترسی به منابع شبکه و اینترنت و توسعه برنامه های REST بودند.
پرواضح است که هر دو کلاس فوق باگ داشتند و متدهایی مانند caching را بطور پیشفرض پشتیبانی نمی کردند و برای بعضی کارهای ساده مانند استفاده از cache برای تصاویر باید کلی کد نوشته میشد و بارها ناچار بودیم چرخ را دوباره اختراع کنیم.
دلایل استفاده از Volley :
دلیل اول: منسوخ کردن استفاده از دو کلاس HttpUrlConnetcion و HttpClient.
در نسخه های اولیه Android API مانند GingerBread و Froyo این دو کلاس بسیار مشکل ساز بودند و کلی باگ شناخته شده داشتند که هرگز برطرف نشدند. از API نسخه ۲۲ کلاس HttpClient منسوخ اعلام شد یعنی در نسخه های آتی حذف خواهد شد.
دلیل دوم : منسوخ کردن استفاده از AsyncTask.
از زمان معرفی Honeycomb یعنی API 11 استفاده از AsyncTask برای تعاملات بین کلاینت و سرور اجباری شد.AsyncTask درخواست های بین کلاینت و سرور را در یک ترید جداگانه انجام میداد تا ترید اصلی برنامه دچار اختلال نشود. این تغییر بنیادی منجر به استفاده کسترده از مشخصه زیر گردید.
حال به سراغ آموزش نحوه اتصال به وب سرویس Rest در اندروید و پردازش اطلاعات از json با استفاده از کتابخانه volley می رویم:
مراحل پیاده سازی پروژه :
۱. پروژه جدید ایجاد نمایید.
ما اسم پروژه را VolleyJson و نام پکیج برنامه را ir.mobileprogram.volleyjson در نظر گرفتیم.
۲. حال یک فولدر جدید (Package ) در مسیر src ⇒ New ⇒ Package بنام app ایجاد نمایید. در نتیجه نام پکیج این قسمت ir.mobileprogram.volleyjson.app خواهد شد.
۳٫ کتابخانه volley.jar را دانلود کنید و در پوشه libs پروژه قراردهید.
۴٫ در مسیر فولدر app یک کلاس جدید به نام AppController.java ایجاد نمایید
این کلاس تمامی object های مورد نیاز کتابخانه volley را ایجاد می کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package ir.mobileprogram.volleyjson.app; import android.app.Application; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; public class AppController extends Application { public static final String TAG = AppController.class.getSimpleName(); private RequestQueue mRequestQueue; private static AppController mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized AppController getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } public <T> void addToRequestQueue(Request<T> req, String tag) { req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public <T> void addToRequestQueue(Request<T> req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (mRequestQueue != null) { mRequestQueue.cancelAll(tag); } } } |
۵٫ کلاس AppController.java در زمان launch شدن برنامه اجرا خواهد شد. بنابراین این کلاس را در AndroidManifest.xml در تگ <application> به صورت زیر اضافه نمایید :
1 2 |
<application android:name="ir.mobileprogram.volleyjson.app.AppController"> |
همچنین به مجوز Internet نیاز داریم بنابراین این permisson را در منیفیست اضافه کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ir.mobileprogram.volleyjson" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:name="ir.mobileprogram.volleyjson.app.AppController"> <activity android:name="ir.mobileprogram.volleyjson.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
۶٫ کد های زیر را به activity_main.xml اضافه نمایید :
اینصفحه شامل دو Buttonاست که یکی برای json object request بکار می رود و دیگری برای json array request . اطلاعات parse شده از از json در Textview نمایش داده می شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" > <Button android:id="@+id/btnObjRequest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:paddingLeft="15dp" android:paddingRight="15dp" android:text="Make JSON Object Request" /> <Button android:id="@+id/btnArrayRequest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:paddingLeft="15dp" android:paddingRight="15dp" android:text="Make JSON Array Request" android:layout_below="@id/btnObjRequest" /> <TextView android:id="@+id/txtResponse" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/btnArrayRequest" android:layout_marginTop="40px" android:padding="20dp" /> </RelativeLayout> |
۷. در MainActivity.java کد های زیر را اضافه نمایید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
package ir.mobileprogram.volleyjson; import ir.mobileprogram.volleyjson.R; import ir.mobileprogram.volleyjson.app.AppController; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.android.volley.Request.Method; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.VolleyLog; import com.android.volley.toolbox.JsonArrayRequest; import com.android.volley.toolbox.JsonObjectRequest; public class MainActivity extends Activity { // json object response url private String urlJsonObj = "http://api.androidhive.info/volley/person_object.json"; // json array response url private String urlJsonArry = "http://api.androidhive.info/volley/person_array.json"; private static String TAG = MainActivity.class.getSimpleName(); private Button btnMakeObjectRequest, btnMakeArrayRequest; // Progress dialog private ProgressDialog pDialog; private TextView txtResponse; // temporary string to show the parsed response private String jsonResponse; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnMakeObjectRequest = (Button) findViewById(R.id.btnObjRequest); btnMakeArrayRequest = (Button) findViewById(R.id.btnArrayRequest); txtResponse = (TextView) findViewById(R.id.txtResponse); pDialog = new ProgressDialog(this); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); btnMakeObjectRequest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // making json object request makeJsonObjectRequest(); } }); btnMakeArrayRequest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // making json array request makeJsonArrayRequest(); } }); } /** * Method to make json object request where json response starts wtih { * */ private void makeJsonObjectRequest() { } /** * Method to make json array request where response starts with [ * */ private void makeJsonArrayRequest() { } private void showpDialog() { if (!pDialog.isShowing()) pDialog.show(); } private void hidepDialog() { if (pDialog.isShowing()) pDialog.dismiss(); } } |
مهم :
خروجی json در حالت استاندارد میتواند یکی از این ۲ نوع باشد :
۱- JSON Object : اگر json با } شروع شده باشد از نوع JsonObject است.
۲- JSON Array : اگر json با ] شروع شده باشد از نوع JsonArray است.
حال فرامیگیریم که چگونه هر کدام از درخواست ها را پردازش کنیم و فیلد های مورد نیاز را از درونشان استخراج کنیم:
Making JSON Object Request
۸٫ کتابخانه volley کلاس JsonObjectRequest را برای دریافت اطلاعات وب سرویس که از نوع
json object request است (یعنی با { شروع و بسته می شود ) در اختیار ما قرار می دهد. ما از این کلاس استفاده میکنیم و اطلاعات خروجی json را تبدیل به String کرده و در Textview نمایش می دهیم:
یک نمونه خروجی JsonObject :
URL: http://api.androidhive.info/volley/person_object.json
1 2 3 4 5 6 7 8 9 |
{ "name" : "Mobile Program", "email" : "info@mobileprogram.ir", "phone" : { "home" : "08947 000000", "mobile" : "9999999999" } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/** * Method to make json object request where json response starts wtih { * */ private void makeJsonObjectRequest() { showpDialog(); JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET, urlJsonObj, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d(TAG, response.toString()); try { // Parsing json object response // response will be a json object String name = response.getString("name"); String email = response.getString("email"); JSONObject phone = response.getJSONObject("phone"); String home = phone.getString("home"); String mobile = phone.getString("mobile"); jsonResponse = ""; jsonResponse += "Name: " + name + "\n\n"; jsonResponse += "Email: " + email + "\n\n"; jsonResponse += "Home: " + home + "\n\n"; jsonResponse += "Mobile: " + mobile + "\n\n"; txtResponse.setText(jsonResponse); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } hidepDialog(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); // hide the progress dialog hidepDialog(); } }); // Adding request to request queue AppController.getInstance().addToRequestQueue(jsonObjReq); } |
Making JSON Array Request
۹٫ همچنین کتابخانه volley کلاس JsonArrayRequest را برای دریافت اطلاعات وب سرویس که از نوع
json Array request است (یعنی با [ شروع و بسته می شود ) در اختیار ما قرار می دهد. ما از این کلاس استفاده میکنیم و اطلاعات خروجی json را تبدیل به String کرده و در Textview نمایش می دهیم:
یک نمونه خروجی JsonArray :
URL: http://api.androidhive.info/volley/person_array.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[ { "name" : "Mobile Program", "email" : "info@mobileprogram.ir", "phone" : { "home" : "08947 000000", "mobile" : "9999999999" } }, { "name" : "MobiPro", "email" : "info@mobipro.ir", "phone" : { "home" : "08946 000000", "mobile" : "0000000000" } } ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/** * Method to make json array request where response starts with [ * */ private void makeJsonArrayRequest() { showpDialog(); JsonArrayRequest req = new JsonArrayRequest(urlJsonArry, new Response.Listener<JSONArray>() { @Override public void onResponse(JSONArray response) { Log.d(TAG, response.toString()); try { // Parsing json array response // loop through each json object jsonResponse = ""; for (int i = 0; i < response.length(); i++) { JSONObject person = (JSONObject) response .get(i); String name = person.getString("name"); String email = person.getString("email"); JSONObject phone = person .getJSONObject("phone"); String home = phone.getString("home"); String mobile = phone.getString("mobile"); jsonResponse += "Name: " + name + "\n\n"; jsonResponse += "Email: " + email + "\n\n"; jsonResponse += "Home: " + home + "\n\n"; jsonResponse += "Mobile: " + mobile + "\n\n\n"; } txtResponse.setText(jsonResponse); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } hidepDialog(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); hidepDialog(); } }); // Adding request to request queue AppController.getInstance().addToRequestQueue(req); } |
حال پروژه را اجرا کنید و خروجی هر کدام از درخواست ها را ببینید :
کد کامل MainActivity.java :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
package ir.mobileprogram.volleyjson; import ir.mobileprogram.volleyjson.R; import ir.mobileprogram.volleyjson.app.AppController; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.android.volley.Request.Method; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.VolleyLog; import com.android.volley.toolbox.JsonArrayRequest; import com.android.volley.toolbox.JsonObjectRequest; public class MainActivity extends Activity { // json object response url private String urlJsonObj = "http://api.androidhive.info/volley/person_object.json"; // json array response url private String urlJsonArry = "http://api.androidhive.info/volley/person_array.json"; private static String TAG = MainActivity.class.getSimpleName(); private Button btnMakeObjectRequest, btnMakeArrayRequest; // Progress dialog private ProgressDialog pDialog; private TextView txtResponse; // temporary string to show the parsed response private String jsonResponse; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnMakeObjectRequest = (Button) findViewById(R.id.btnObjRequest); btnMakeArrayRequest = (Button) findViewById(R.id.btnArrayRequest); txtResponse = (TextView) findViewById(R.id.txtResponse); pDialog = new ProgressDialog(this); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); btnMakeObjectRequest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // making json object request makeJsonObjectRequest(); } }); btnMakeArrayRequest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // making json array request makeJsonArrayRequest(); } }); } /** * Method to make json object request where json response starts wtih { * */ private void makeJsonObjectRequest() { showpDialog(); JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET, urlJsonObj, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d(TAG, response.toString()); try { // Parsing json object response // response will be a json object String name = response.getString("name"); String email = response.getString("email"); JSONObject phone = response.getJSONObject("phone"); String home = phone.getString("home"); String mobile = phone.getString("mobile"); jsonResponse = ""; jsonResponse += "Name: " + name + "\n\n"; jsonResponse += "Email: " + email + "\n\n"; jsonResponse += "Home: " + home + "\n\n"; jsonResponse += "Mobile: " + mobile + "\n\n"; txtResponse.setText(jsonResponse); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } hidepDialog(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); // hide the progress dialog hidepDialog(); } }); // Adding request to request queue AppController.getInstance().addToRequestQueue(jsonObjReq); } /** * Method to make json array request where response starts with [ * */ private void makeJsonArrayRequest() { showpDialog(); JsonArrayRequest req = new JsonArrayRequest(urlJsonArry, new Response.Listener<JSONArray>() { @Override public void onResponse(JSONArray response) { Log.d(TAG, response.toString()); try { // Parsing json array response // loop through each json object jsonResponse = ""; for (int i = 0; i < response.length(); i++) { JSONObject person = (JSONObject) response .get(i); String name = person.getString("name"); String email = person.getString("email"); JSONObject phone = person .getJSONObject("phone"); String home = phone.getString("home"); String mobile = phone.getString("mobile"); jsonResponse += "Name: " + name + "\n\n"; jsonResponse += "Email: " + email + "\n\n"; jsonResponse += "Home: " + home + "\n\n"; jsonResponse += "Mobile: " + mobile + "\n\n\n"; } txtResponse.setText(jsonResponse); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } hidepDialog(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); hidepDialog(); } }); // Adding request to request queue AppController.getInstance().addToRequestQueue(req); } private void showpDialog() { if (!pDialog.isShowing()) pDialog.show(); } private void hidepDialog() { if (pDialog.isShowing()) pDialog.dismiss(); } } |
سلام ممنون از آموزش خوبتون
بنده با روش json دارم اطلاعات رو از پایگاه داده میگیرم ولی یه مشکلی داره اطلاعات درج شده در پایگاه داده اگر انگلیسی باشه که مشکلی پیش نمیاد ولی اگر فارسی باشه یه سری کاراکتر وشماره چاپ میشه به جای اینکه متن فارسی مثلا یه موضوعی
لطفا اگر اطلاعاتی در این مورد دارید برام ایمیل بزنید خیلی خوش حال میشم
در پناه حق
التماس دعا
با سلام
به زودی آموزش در رابطه با این مشکل در سایت قرار خواهیم داد .
با عرض سلام مجدد
خیلی سپاسگذارم
ببخشید میشه دقیقا بفرمایید چه زمانی؟
آخه میدونید من عجله ایم الان هم کارم لنگ شده در حال انجام پروژه ای هستم و میخوام که با json کار کنم اما با این مشکل مواجه شدم
پس خواهشمندم اگه امکانش هست تا فردا آموزش رو قرار بدید خیلی ممنون میشم
در پناه حق باشید
التماس دعا
تشکر از لطف شما
آموزش حل مشکل نمایش حروف فارسی json در برنامه نویسی اندروید در سایت قرار گرفت و از طریق لینک زیر قابل دسترسی است:
مشکل نمایش حروف فارسی json اندروید
موفق باشید
این قسمت از کد کلاس appcontroller مشکل داره علتش چیه؟
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
اون قسمت که نوشته شده
public <T>
اوکی شد اگه دوست داشتید پیام رو پاک کنید
سلام خسته نباشید
آقا ما این برنامه رو نوشتم ولی موقع اجرا کرش میکنه
مشکل چیه؟
واسه وارد کردن کتابخانه روش خاصی داره؟
همه چیز اوکیه ولی کرش میکنه
سلام دوستان کسی میدونه چطور میشه پارامتر ها رو به این صورت به Volley ارسال کنیم
مثلا کد Volley رو در یک کلاس دیگه قرار بدیم و با این دستور بهش URL و پارامتر بدیم.
String result =Webservice.readUrl(“http://192.168.56.1/note/index.php”,params);
سلام و خسته نباشید خیلی عالی بود
فقط من میخام کارمتد makeJsonObjectRequest که تمام بشه یک لیست به من بده که مشکلی که الان دارم اینه که این لیست رو هنوز کامل نکرده برام ارسال میکنه و برنامه به خطا میخوره
سلام دوستان عزیز و بزرگوار
یه سوال دارم از محضرتون
من دارم با متد پست از دیتا بیس چند تا فیلد اطلاعات دریافت میکنم اما برخلاف این مثال فیلد ها بصورت فارسی هستند.
هم در ثبت اطلاعات و هم در دریافت اطلاعات توسط volley و post مشکل دارم
فارسی هارو درست نشون نمیده
چطوری حلش کنم؟؟؟؟؟؟؟؟؟؟؟
واقعا درگیرم کرده
سلام
واقعا عالی بود
خیلی ممنون