ป้องกันไม่ให้ WebView แสดง“ หน้าเว็บไม่พร้อมใช้งาน”


88

ฉันมีแอปที่ใช้ประโยชน์จาก WebView ได้อย่างกว้างขวาง เมื่อผู้ใช้แอปนี้ไม่มีการเชื่อมต่ออินเทอร์เน็ตจะมีหน้าว่า "ไม่สามารถใช้งานหน้าเว็บได้" และข้อความอื่น ๆ จะปรากฏขึ้น มีวิธีที่จะไม่แสดงข้อความทั่วไปนี้ใน WebView ของฉันหรือไม่? ฉันต้องการจัดการข้อผิดพลาดของตัวเอง

private final Activity activity = this;

private class MyWebViewClient extends WebViewClient
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
  // I need to do something like this:
  activity.webView.wipeOutThePage();
  activity.myCustomErrorHandling();
  Toast.makeText(activity, description, Toast.LENGTH_LONG).show();
 }
}

ฉันพบว่าWebView-> clearViewไม่ได้ล้างมุมมอง


3
ทำไมคุณไม่ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตก่อนที่จะแสดง webView และหากไม่มีสิ่งอำนวยความสะดวกอินเทอร์เน็ตคุณสามารถข้ามการแสดง WebView และคุณสามารถแสดงการแจ้งเตือนหรือขนมปังปิ้งโดยไม่มีข้อความอินเทอร์เน็ตแทนได้
Andro Selva

@ JoJo คุณติ๊กคำตอบได้ไหมว่าถูกต้อง น่าจะเป็นของฉัน: P
Sherif elKhatib

คำตอบ:


102

ขั้นแรกให้สร้างหน้าข้อผิดพลาดของคุณเองใน HTML และวางไว้ในโฟลเดอร์สินทรัพย์ของคุณเรียกว่า myerrorpage.html จากนั้นด้วย onReceivedError:

mWebView.setWebViewClient(new WebViewClient() {
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        mWebView.loadUrl("file:///android_asset/myerrorpage.html");

    }
});

27
หน้าข้อผิดพลาด "ไม่ได้โหลดหน้าเว็บ" แบบเก่าแสดงให้เห็นเพียงเสี้ยววินาทีก่อนที่จะถูกแทนที่ด้วยหน้าข้อผิดพลาดที่กำหนดเองจากเนื้อหา
Cheenu Madan

1
เห็นด้วยกับชีนุ. จะต้องมีทางออกที่ดีกว่า
Jozua

3
1. คุณสามารถโหลดเนื้อหาเว็บในขณะที่มองไม่เห็น WebView (และอาจแสดงความคืบหน้าไม่ถูกต้อง) และแสดงให้เห็นหากโหลดหน้าสำเร็จ / โหลด "myerrorpage.html" โดยมีข้อผิดพลาด 2. คุณควรทราบว่า onReceiveError จะถูกทริกเกอร์หากมีปัญหาในการโหลดปัญหา OR ใน JS ที่ทำงานหลังจากโหลดเพจแล้ว (เช่นฟังก์ชัน jQuery ที่ทำงานในเหตุการณ์ document.ready ()) เบราว์เซอร์เดสก์ท็อป - จะอนุญาตข้อผิดพลาด JS โดยไม่แจ้งให้ผู้ใช้ทราบเช่นเดียวกับเบราว์เซอร์มือถือที่ควรทำ
FunkSoulBrother

6
เพิ่มview.loadUrl("about:blank");ก่อน loadUrl เพื่อป้องกันไม่ให้แสดงหน้าข้อผิดพลาดเป็นเวลาเสี้ยววินาที
Aashish

2
จะวางไฟล์ myerrorpage.html ไว้ที่ไหน โฟลเดอร์สินทรัพย์ของ Android นั้นอยู่ที่ไหน
Nived Kannada

34

ทางออกที่ดีที่สุดที่ฉันพบคือการโหลดหน้าว่างในเหตุการณ์ OnReceivedError ดังนี้:

@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);

    view.loadUrl("about:blank");
}

ความคิดที่ดี! ในกรณีของฉันฉันเพิ่มview.loadUrl(noConnectionUrl);สองครั้ง (โดยที่ noConnectionUrl เป็นไฟล์ในเครื่องที่มีข้อความแสดงข้อผิดพลาด) นอกจากนี้ยังช่วยไม่ให้ "เห็น" หน้าข้อผิดพลาดในตัวเป็นเวลาเสี้ยววินาที
hQuse

4
โปรดทราบว่าหากคุณกำลังจัดการwebView.goBack()ตรวจสอบให้แน่ใจว่าได้จัดการเงื่อนไขสำหรับหน้าว่างนี้แล้ว มิฉะนั้นหน้าก่อนหน้าของคุณจะถูกโหลดและหากล้มเหลวอีกครั้งคุณจะเห็นหน้าว่างนี้อีกครั้ง
Chintan Shah

ดังนั้นวิธีที่ดีที่สุดในการจัดการคืออะไร?
Mostafa Soliman

@ MoSoli ทุกอย่างขึ้นอยู่กับความต้องการของคุณ อีกทางเลือกหนึ่งในการแสดงหน้าว่างคือการแสดง UI ที่กำหนดเองหรือหน้าเว็บที่แคชไว้
7heViking

10

ในที่สุดฉันก็แก้ไขสิ่งนี้ (ใช้ได้จนถึงปัจจุบัน .. )

ทางออกของฉันเป็นแบบนี้ ...

  1. จัดเตรียมเค้าโครงเพื่อแสดงเมื่อเกิดข้อผิดพลาดแทนเว็บเพจ (ข้อความ 'ไม่พบเพจสกปรก') เค้าโครงมีปุ่มเดียว "RELOAD" พร้อมข้อความแนะนำบางส่วน

  2. หากเกิดข้อผิดพลาดโปรดจำไว้ว่าใช้บูลีนและแสดงเค้าโครงที่เราเตรียมไว้

  3. หากผู้ใช้คลิกปุ่ม "RELOAD" ให้ตั้งค่า mbErrorOccured เป็น false และตั้งค่า mbReloadPressed เป็น true
  4. ถ้า mbErrorOccured เป็นเท็จและ mbReloadPressed เป็นจริงแสดงว่าโหลดหน้าเว็บวิวสำเร็จแล้ว เพราะหากเกิดข้อผิดพลาดอีกครั้ง mbErrorOccured จะถูกตั้งค่าเป็นจริงใน onReceivedError (... )

นี่คือแหล่งข้อมูลทั้งหมดของฉัน ลองดู.

public class MyWebViewActivity extends ActionBarActivity implements OnClickListener {

    private final String TAG = MyWebViewActivity.class.getSimpleName();
    private WebView mWebView = null;
    private final String URL = "http://www.google.com";
    private LinearLayout mlLayoutRequestError = null;
    private Handler mhErrorLayoutHide = null;

    private boolean mbErrorOccured = false;
    private boolean mbReloadPressed = false;

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);

        ((Button) findViewById(R.id.btnRetry)).setOnClickListener(this);
        mlLayoutRequestError = (LinearLayout) findViewById(R.id.lLayoutRequestError);
        mhErrorLayoutHide = getErrorLayoutHideHandler();

        mWebView = (WebView) findViewById(R.id.webviewMain);
        mWebView.setWebViewClient(new MyWebViewClient());
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);
        mWebView.setWebChromeClient(getChromeClient());
        mWebView.loadUrl(URL);
    }

    @Override
    public boolean onSupportNavigateUp() {
        return super.onSupportNavigateUp();
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();

        if (id == R.id.btnRetry) {
            if (!mbErrorOccured) {
                return;
            }

            mbReloadPressed = true;
            mWebView.reload();
            mbErrorOccured = false;
        }
    }

    @Override
    public void onBackPressed() {
        if (mWebView.canGoBack()) {
            mWebView.goBack();
            return;
        }
        else {
            finish();
        }

        super.onBackPressed();
    }

    class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return super.shouldOverrideUrlLoading(view, url);
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }

        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if (mbErrorOccured == false && mbReloadPressed) {
                hideErrorLayout();
                mbReloadPressed = false;
            }

            super.onPageFinished(view, url);
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            mbErrorOccured = true;
            showErrorLayout();
            super.onReceivedError(view, errorCode, description, failingUrl);
        }
    }

    private WebChromeClient getChromeClient() {
        final ProgressDialog progressDialog = new ProgressDialog(MyWebViewActivity.this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(false);

        return new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
            }
        };
    }

    private void showErrorLayout() {
        mlLayoutRequestError.setVisibility(View.VISIBLE);
    }

    private void hideErrorLayout() {
        mhErrorLayoutHide.sendEmptyMessageDelayed(10000, 200);
    }

    private Handler getErrorLayoutHideHandler() {
        return new Handler() {
            @Override
            public void handleMessage(Message msg) {
                mlLayoutRequestError.setVisibility(View.GONE);
                super.handleMessage(msg);
            }
        };
    }
}

เพิ่มเติม: นี่คือเค้าโครง ....

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rLayoutWithWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<WebView
    android:id="@+id/webviewMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<LinearLayout
    android:id="@+id/lLayoutRequestError"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"
    android:background="@color/white"
    android:gravity="center"
    android:orientation="vertical"
    android:visibility="gone" >

    <Button
        android:id="@+id/btnRetry"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minWidth="120dp"
        android:text="RELOAD"
        android:textSize="20dp"
        android:textStyle="bold" />
</LinearLayout>


คุณช่วยแชร์ไฟล์ทรัพยากรของคุณได้ไหม ฉันเป็นมือใหม่
Rajan Rawal

@RajanRawal ฉันแก้ไขและเพิ่มตัวอย่างเค้าโครง :)
cmcromance

ฉันไม่เห็นแถบความคืบหน้าใด ๆ แต่ฉันเห็นรหัสที่นั่น :-(
Rajan Rawal

8

เมื่อ Webview ฝังอยู่ในมุมมองที่กำหนดเองบางอย่างซึ่งผู้ใช้เกือบจะเชื่อว่าเขากำลังเห็นมุมมองแบบเนทีฟไม่ใช่มุมมองเว็บในสถานการณ์ดังกล่าวที่แสดงข้อผิดพลาด "ไม่สามารถโหลดหน้าเว็บได้" จะเกิดขึ้นล่วงหน้า สิ่งที่ฉันมักจะทำในสถานการณ์เช่นนี้คือฉันโหลดหน้าว่างและแสดงข้อความขนมปังปิ้งด้านล่าง

webView.setWebViewClient(new WebViewClient() {

            @Override
            public void onReceivedError(WebView view, int errorCode,
                    String description, String failingUrl) {
                Log.e(TAG," Error occured while loading the web page at Url"+ failingUrl+"." +description);     
                view.loadUrl("about:blank");
                Toast.makeText(App.getContext(), "Error occured, please check newtwork connectivity", Toast.LENGTH_SHORT).show();
                super.onReceivedError(view, errorCode, description, failingUrl);
            }
        });

7

ตรวจสอบการอภิปรายที่Android WebView onReceivedError () มันค่อนข้างยาว แต่ดูเหมือนว่าจะเห็นพ้องกันว่า a) คุณไม่สามารถหยุดไม่ให้หน้าเว็บ "ไม่พร้อมใช้งาน" ปรากฏขึ้น แต่ b) คุณสามารถโหลดหน้าว่างได้ตลอดเวลาหลังจากที่คุณได้รับ onReceivedError


พวกนี้พูดถึงonReceivedErrorไม่จุดชนวนเลย มันทริกเกอร์อย่างถูกต้องในรหัสของฉันเมื่อไม่มีการเชื่อมต่ออินเทอร์เน็ต
JoJo

2

คุณสามารถใช้GETคำขอเพื่อรับเนื้อหาของเพจจากนั้นแสดงข้อมูลนั้นโดยใช้Webviewวิธีนี้คุณไม่ได้ใช้การเรียกเซิร์ฟเวอร์หลายครั้ง ทางเลือกอื่นที่คุณสามารถใช้Javascriptเพื่อตรวจสอบDOMความถูกต้องของวัตถุ


นั่นเป็นความคิดที่ดี แต่โชคไม่ดีจริงๆที่เราต้องทำอะไรแบบนี้ ...
Jeffrey Blattman

2

บางทีฉันอาจเข้าใจผิดในคำถาม แต่ดูเหมือนว่าคุณกำลังบอกว่าคุณได้รับข้อผิดพลาดที่ได้รับการติดต่อกลับและคุณแค่ถามว่าอะไรคือวิธีที่ดีที่สุดในการไม่แสดงข้อผิดพลาด ทำไมคุณไม่ลบมุมมองเว็บออกจากหน้าจอและ / หรือแสดงมุมมองอื่นที่ด้านบน


2

ที่นี่ฉันพบวิธีแก้ปัญหาที่ง่ายที่สุด ลองดูสิ ..

   @Override
        public void onReceivedError(WebView view, int errorCode,
                                    String description, String failingUrl) {
//                view.loadUrl("about:blank");
            mWebView.stopLoading();
            if (!mUtils.isInterentConnection()) {
                Toast.makeText(ReportingActivity.this, "Please Check Internet Connection!", Toast.LENGTH_SHORT).show();
            }
            super.onReceivedError(view, errorCode, description, failingUrl);
        }

และนี่คือ isInterentConnection () วิธีการ ...

public boolean isInterentConnection() {
    ConnectivityManager manager = (ConnectivityManager) mContext
            .getSystemService(Context.CONNECTIVITY_SERVICE);
    if (manager != null) {
        NetworkInfo info[] = manager.getAllNetworkInfo();
        if (info != null) {
            for (int i = 0; i < info.length; i++) {
                if (info[i].getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
    }
    return false;
}

1

ฉันคิดว่าถ้าคุณยืนยันที่จะทำสิ่งนี้คุณสามารถตรวจสอบได้ว่ามีทรัพยากรอยู่ที่นั่นหรือไม่ก่อนที่จะเรียกใช้ฟังก์ชัน loadURL เพียงแค่แทนที่ฟังก์ชันและทำการตรวจสอบก่อนที่จะเรียก super ()

ข้อควรจำ (อาจไม่ตรงประเด็น): ใน http มีวิธีการที่เรียกว่า HEAD ซึ่งอธิบายได้ดังนี้:

เมธอด HEAD เหมือนกับ GET ยกเว้นว่าเซิร์ฟเวอร์ต้องไม่ส่งคืนข้อความ - เนื้อหาในการตอบสนอง

วิธีนี้อาจมีประโยชน์ อย่างไรก็ตามคุณใช้มันอย่างไร ... ตรวจสอบรหัสนี้:

import java.util.Map;

import android.content.Context;
import android.webkit.WebView;

public class WebViewPreLoad extends WebView{

public WebViewPreLoad(Context context) {
super(context);
}
public void loadUrl(String str){
if(//Check if exists)
super.loadUrl(str);
else
//handle error
}
public void loadUrl(String url, Map<String,String> extraHeaders){
if(//Check if exists)
super.loadUrl(url, extraHeaders);
else
//handle error
}
}

คุณสามารถลองใช้การตรวจสอบนี้โดยใช้

if(url.openConnection().getContentLength() > 0)

6
การตรวจสอบว่าทรัพยากรอยู่ที่นั่นหรือไม่ก่อนที่จะไปจริงจะทำให้โหลดบนเซิร์ฟเวอร์เพิ่มขึ้นเป็นสองเท่าเนื่องจากจะมีคำขอทางกายภาพ 2 คำขอต่อคำขอเสมือน ดูเหมือนจะเกินความจำเป็นเล็กน้อย
JoJo

1
อย่างไรก็ตามคุณยังสามารถโอเวอร์โหลดและรับเนื้อหา html ได้ตรวจสอบว่ามีข้อผิดพลาดหรือไม่ หากไม่แยกวิเคราะห์และแสดง
Sherif elKhatib

จะนำสิ่งนี้ไปใช้ได้จริงอย่างไร
JoJo

@JoJo จริงๆแล้ววิธีนี้จะช่วยลดภาระบนเซิร์ฟเวอร์ในกรณีที่ URL ไม่ถูกต้องเนื่องจากการส่งคืนการตอบกลับ HEAD มีค่าใช้จ่ายที่น้อยกว่ามากเมื่อเทียบกับการตอบกลับ 304 หรือ 500
Justin Shield

สิ่งนี้ไม่ได้แก้ไขปัญหาหากการเชื่อมต่อขาดหายไปในขณะที่ดาวน์โหลดหน้า
Pedro Loureiro

0

ฉันจะเปลี่ยนหน้าเว็บเป็นอะไรก็ได้ที่คุณใช้เพื่อจัดการข้อผิดพลาด:

getWindow().requestFeature(Window.FEATURE_PROGRESS);  
webview.getSettings().setJavaScriptEnabled(true);  
final Activity activity = this;  
webview.setWebChromeClient(new WebChromeClient() {  
public void onProgressChanged(WebView view, int progress) {  
 // Activities and WebViews measure progress with different scales.  
 // The progress meter will automatically disappear when we reach 100%  
 activity.setProgress(progress * 1000);  
 }  
});  
webview.setWebViewClient(new WebViewClient() {  
public void onReceivedError(WebView view, int errorCode, String description, String 
failingUrl) {  
 Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();  
}  
});  
webview.loadUrl("http://slashdot.org/");

ทั้งหมดนี้สามารถพบได้ในhttp://developer.android.com/reference/android/webkit/WebView.html


0

ฉันได้แก้ไขปัญหานี้ในการทิ้งหน้าข้อผิดพลาดของ Google ที่ระคายเคืองเหล่านี้ เป็นไปได้ด้วยโค้ดตัวอย่าง Android ที่เห็นด้านบนและในฟอรัมอื่น ๆ มากมาย (ถามว่าฉันรู้ได้อย่างไร):

wv.setWebViewClient(new WebViewClient() {
    public void onReceivedError(WebView view, int errorCode,
                            String description, String failingUrl) {
       if (view.canGoBack()) {
          view.goBack();
        }
       Toast.makeText(getBaseContext(), description, Toast.LENGTH_LONG).show();
       }
    }
});

หากคุณใส่ไว้ใน shouldOverrideUrlLoading () เป็นเว็บไคลเอ็นต์เพิ่มเติม อย่างน้อยสิ่งนี้ก็ใช้ได้กับฉันในอุปกรณ์ 2.3.6 ของฉัน เราจะดูว่าที่อื่นใช้งานได้ในภายหลัง นั่นจะทำให้ฉันหดหู่ตอนนี้ฉันแน่ใจ goBack bit เป็นของฉัน คุณอาจไม่ต้องการมัน


0

ลองทำตามนี้

 @SuppressWarnings("deprecation")
 @Override
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
      // Your handling
 }

 @Override
 public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
      }
 }

0

เราสามารถตั้งค่าการมองเห็นของ webView เป็น 0 (view.INVISIBLE) และแสดงข้อความ รหัสนี้ใช้ได้กับแอปของฉันที่ทำงานบน lolipop

@SuppressWarnings("deprecation")
    @Override
    public void onReceivedError(WebView webView, int errorCode, String description, String failingUrl) {
        // hide webView content and show custom msg
        webView.setVisibility(View.INVISIBLE);
        Toast.makeText(NrumWebViewActivity.this,getString(R.string.server_not_responding), Toast.LENGTH_SHORT).show();
    }

    @TargetApi(android.os.Build.VERSION_CODES.M)
    @Override
    public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
        // Redirect to deprecated method, so you can use it in all SDK versions
        onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
    }

0

ฉันต้องเผชิญกับปัญหานี้และพยายามแก้ไขจากมุมมองที่แตกต่างกัน ในที่สุดฉันก็พบวิธีแก้ไขโดยใช้แฟล็กเดียวเพื่อตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นหรือไม่

... extends WebViewClient {

    boolean error;

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {

        showLoading(true);
        super.onPageStarted(view, url, favicon);

        error  = false; // IMPORTANT
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);

        if(!error) {
            Observable.timer(100, TimeUnit.MICROSECONDS, AndroidSchedulers.mainThread())
                    .subscribe((data) -> view.setVisibility(View.VISIBLE) );
        }

        showLoading(false);
    }


    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

        view.stopLoading();

        view.setVisibility(View.INVISIBLE) 
        error  = true;  

        // Handle the error
    }


     @Override
    @TargetApi(android.os.Build.VERSION_CODES.M)
    public void onReceivedError(WebView view,
                                WebResourceRequest request,
                                WebResourceError error) {

        this.onReceivedError(view, error.getErrorCode(),
                error.getDescription().toString(),
                request.getUrl().toString());
    }
 }

ด้วยวิธีนี้ฉันจะซ่อนหน้าทุกครั้งที่มีข้อผิดพลาดและแสดงเมื่อหน้าโหลดอย่างถูกต้องอีกครั้ง

นอกจากนี้ยังเพิ่มความล่าช้าเล็กน้อยในกรณี

ฉันหลีกเลี่ยงวิธีแก้ปัญหาในการโหลดหน้าว่างเนื่องจากไม่อนุญาตให้คุณทำ webview.reload () ในภายหลังเนื่องจากเพิ่มหน้าใหม่นั้นในประวัติการนำทาง


-1

ลองใช้shouldOverrideUrlLoadingก่อนที่จะเปลี่ยนเส้นทางไปยังการตรวจสอบ URL อื่นสำหรับการเชื่อมต่ออินเทอร์เน็ตตามหน้านั้นควรโหลดหรือไม่


-1

แทนที่เมธอด WebViewClient ของคุณ

@Override
public void onReceivedError(WebView view, int errorCode,
    String description, String failingUrl) {
    view.clearView();
}

view.loadUrl('about:blank') มีผลข้างเคียงเนื่องจากจะยกเลิกการโหลด URL เดิม


view.clearView () เลิกใช้งานแล้วโปรดดูstackoverflow.com/questions/3715482/clear-webview-content
silva96
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.