【问题标题】:Using PhoneNumberFormattingTextWatcher without typing country calling code使用 PhoneNumberFormattingTextWatcher 而不输入国家/地区呼叫代码
【发布时间】:2015-12-16 03:42:39
【问题描述】:

在我的应用程序的登录面板中,我将国家/地区呼叫代码和剩余号码划分为两个可编辑的 TextView,如下所示:

我想在右侧的 TextView 中使用国际格式标准。如果电话号码为 +905444444444 的用户在这些框中输入号码,我希望在左侧的框中看到“90”,在右侧的框中看到“544 444 4444”。

出于这个原因,我尝试使用以下使用 libphonenumber 的实现:

/**
 * Watches a {@link android.widget.TextView} and if a phone number is entered
 * will format it.
 * <p>
 * Stop formatting when the user
 * <ul>
 * <li>Inputs non-dialable characters</li>
 * <li>Removes the separator in the middle of string.</li>
 * </ul>
 * <p>
 * The formatting will be restarted once the text is cleared.
 */
public class PhoneNumberFormattingTextWatcher implements TextWatcher {

    /**
     * Indicates the change was caused by ourselves.
     */
    private boolean mSelfChange = false;

    /**
     * Indicates the formatting has been stopped.
     */
    private boolean mStopFormatting;

    private AsYouTypeFormatter mFormatter;

    private String code;

    /**
     * The formatting is based on the current system locale and future locale changes
     * may not take effect on this instance.
     */
    public PhoneNumberFormattingTextWatcher() {
        this(Locale.getDefault().getCountry());
    }

    /**
     * The formatting is based on the given <code>countryCode</code>.
     *
     * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
     * where the phone number is being entered.
     */
    public PhoneNumberFormattingTextWatcher(String countryCode) {
        if (countryCode == null) throw new IllegalArgumentException();
        mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
                                  int after) {
        if (mSelfChange || mStopFormatting) {
            return;
        }
        // If the user manually deleted any non-dialable characters, stop formatting
        if (count > 0 && hasSeparator(s, start, count)) {
            stopFormatting();
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (mSelfChange || mStopFormatting) {
            return;
        }
        // If the user inserted any non-dialable characters, stop formatting
        if (count > 0 && hasSeparator(s, start, count)) {
            stopFormatting();
        }
    }

    @Override
    public synchronized void afterTextChanged(Editable s) {

        if (mStopFormatting) {
            // Restart the formatting when all texts were clear.
            mStopFormatting = !(s.length() == 0);
            return;
        }
        if (mSelfChange) {
            // Ignore the change caused by s.replace().
            return;
        }
        String formatted = reformat(s, Selection.getSelectionEnd(s));
        if (formatted != null) {
            int rememberedPos = mFormatter.getRememberedPosition();
            mSelfChange = true;
            s.replace(0, s.length(), formatted, 0, formatted.length());
            // The text could be changed by other TextWatcher after we changed it. If we found the
            // text is not the one we were expecting, just give up calling setSelection().
            if (formatted.equals(s.toString())) {
                Selection.setSelection(s, rememberedPos);
            }
            mSelfChange = false;
        }
        // PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length());
    }

    /**
     * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
     * nearest dialable char to the left. For instance, if the number is  (650) 123-45678 and '4' is
     * removed then the cursor should be behind '3' instead of '-'.
     */
    private String reformat(CharSequence s, int cursor) {
        // The index of char to the leftward of the cursor.
        int curIndex = cursor - 1;
        String formatted = null;
        mFormatter.clear();
        char lastNonSeparator = 0;
        boolean hasCursor = false;
        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (PhoneNumberUtils.isNonSeparator(c)) {
                if (lastNonSeparator != 0) {
                    formatted = getFormattedNumber(lastNonSeparator, hasCursor);
                    hasCursor = false;
                }
                lastNonSeparator = c;
            }
            if (i == curIndex) {
                hasCursor = true;
            }
        }
        if (lastNonSeparator != 0) {
            formatted = getFormattedNumber(lastNonSeparator, hasCursor);
        }
        return formatted;
    }

    private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
        return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
                : mFormatter.inputDigit(lastNonSeparator);
    }

    private void stopFormatting() {
        mStopFormatting = true;
        mFormatter.clear();
    }

    private boolean hasSeparator(final CharSequence s, final int start, final int count) {
        for (int i = start; i < start + count; i++) {
            char c = s.charAt(i);
            if (!PhoneNumberUtils.isNonSeparator(c)) {
                return true;
            }
        }
        return false;
    }
}

但是,此 TextWatcher 格式化数字包括调用代码。换句话说,它成功格式化“+905444444444”,但无法格式化“54444444444”。当输入的电话号码在右侧的 TextView 中包含国家代码时,如何获得相同的结果?不用说,但我想得到以下输出:

  • 5
  • 54
  • 544
  • 544 4
  • 544 44
  • 544 444
  • 544 444 4
  • 544 444 44 ...

【问题讨论】:

  • 您可能使用的是默认语言环境,而不是土耳其的语言环境。在 api 级别 21 及更高版本上,每次选择一个国家/地区时,您都可以使用新的 Locale 动态删除和重新设置 PhoneNumberFormattingTextWatcher。 developer.android.com/reference/android/telephony/…
  • 不。我使用了土耳其的语言环境,而不是默认语言。最低 API 级别应该是 14,所以我不能直接使用该方法。因此,我使用了一个外部库来实现这一点。此外,此库的工作方式与您指定的方法相同。但是,问题是要获得国际格式的号码,您需要在开头输入国家/地区呼叫代码。我想在不以某种方式在框上键入调用代码的情况下获取格式化文本。只有在开头写“+90”才能得到预期的结果。我想要的是相同的格式但没有国家/地区呼叫代码。
  • 实际上,PhoneNumberFormattingTextWatcher(countryCode) 适用于土耳其和美国,但不适用于德国和瑞士。只有在开头添加调用代码,我才能得到格式化的德语和瑞典数字。

标签: android phone-number textwatcher libphonenumber


【解决方案1】:

我编辑了reformat(charSequence, cursor)方法,终于可以得到没有国家区号的国际格式电话号码。如果你想得到同样的结果,你可以看下面的编辑代码:

/**
 * Watches a {@link android.widget.TextView} and if a phone number is entered
 * will format it.
 * <p>
 * Stop formatting when the user
 * <ul>
 * <li>Inputs non-dialable characters</li>
 * <li>Removes the separator in the middle of string.</li>
 * </ul>
 * <p>
 * The formatting will be restarted once the text is cleared.
 */
public class PhoneNumberFormattingTextWatcher implements TextWatcher {

    /**
     * Indicates the change was caused by ourselves.
     */
    private boolean mSelfChange = false;

    /**
     * Indicates the formatting has been stopped.
     */
    private boolean mStopFormatting;

    private AsYouTypeFormatter mFormatter;

    private String countryCode;

    /**
     * The formatting is based on the current system locale and future locale changes
     * may not take effect on this instance.
     */
    public PhoneNumberFormattingTextWatcher() {
        this(Locale.getDefault().getCountry());
    }

    /**
     * The formatting is based on the given <code>countryCode</code>.
     *
     * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
     * where the phone number is being entered.
     *
     * @hide
     */
    public PhoneNumberFormattingTextWatcher(String countryCode) {
        if (countryCode == null) throw new IllegalArgumentException();
        mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
        this.countryCode = countryCode;
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
                                  int after) {
        if (mSelfChange || mStopFormatting) {
            return;
        }
        // If the user manually deleted any non-dialable characters, stop formatting
        if (count > 0 && hasSeparator(s, start, count)) {
            stopFormatting();
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (mSelfChange || mStopFormatting) {
            return;
        }
        // If the user inserted any non-dialable characters, stop formatting
        if (count > 0 && hasSeparator(s, start, count)) {
            stopFormatting();
        }
    }

    @Override
    public synchronized void afterTextChanged(Editable s) {
        if (mStopFormatting) {
            // Restart the formatting when all texts were clear.
            mStopFormatting = !(s.length() == 0);
            return;
        }
        if (mSelfChange) {
            // Ignore the change caused by s.replace().
            return;
        }
        String formatted = reformat(s, Selection.getSelectionEnd(s));
        if (formatted != null) {
            int rememberedPos = formatted.length();
            Log.v("rememberedPos", "" + rememberedPos);
            mSelfChange = true;
            s.replace(0, s.length(), formatted, 0, formatted.length());



            // The text could be changed by other TextWatcher after we changed it. If we found the
            // text is not the one we were expecting, just give up calling setSelection().
            if (formatted.equals(s.toString())) {
                Selection.setSelection(s, rememberedPos);
            }
            mSelfChange = false;
        }
    }

    /**
     * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
     * nearest dialable char to the left. For instance, if the number is  (650) 123-45678 and '4' is
     * removed then the cursor should be behind '3' instead of '-'.
     */
    private String reformat(CharSequence s, int cursor) {
        // The index of char to the leftward of the cursor.
        int curIndex = cursor - 1;
        String formatted = null;
        mFormatter.clear();
        char lastNonSeparator = 0;
        boolean hasCursor = false;

        String countryCallingCode = "+" + CountryCodesAdapter.getCode(countryCode);
        s = countryCallingCode + s;
        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (PhoneNumberUtils.isNonSeparator(c)) {
                if (lastNonSeparator != 0) {
                    formatted = getFormattedNumber(lastNonSeparator, hasCursor);
                    hasCursor = false;
                }
                lastNonSeparator = c;
            }
            if (i == curIndex) {
                hasCursor = true;
            }
        }
        if (lastNonSeparator != 0) {
            Log.v("lastNonSeparator", "" + lastNonSeparator);
            formatted = getFormattedNumber(lastNonSeparator, hasCursor);
        }

        if (formatted.length() > countryCallingCode.length()) {
            if (formatted.charAt(countryCallingCode.length()) == ' ')
                return formatted.substring(countryCallingCode.length() + 1);
            return formatted.substring(countryCallingCode.length());
        }

        return formatted.substring(formatted.length());
    }

    private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
        return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
                : mFormatter.inputDigit(lastNonSeparator);
    }

    private void stopFormatting() {
        mStopFormatting = true;
        mFormatter.clear();
    }

    private boolean hasSeparator(final CharSequence s, final int start, final int count) {
        for (int i = start; i < start + count; i++) {
            char c = s.charAt(i);
            if (!PhoneNumberUtils.isNonSeparator(c)) {
                return true;
            }
        }
        return false;
    }
}

【讨论】:

    【解决方案2】:

    感谢@Dorukhan Arslan 和@NixSam 的回答。接受的答案运行良好,但是当用户在中间某处更改数字时会出现问题。另一个答案在那里有所帮助,但对于某些极端情况,它的行为并不如我所愿。所以我想以不同的方式解决它。该解决方案每次都使用“digitsBeforeCursor”来保持正确的光标位置[希望:-)]。

    对于所有面临此问题的人,您有两种选择来解决此问题。


    1。简单易用的选项

    如果您打算使用国际电话输入,您可以使用CCP Library,它可以轻松灵活地为您提供full international number 的全部功能。它可以让你做这样的事情。它将与国家选择器(奖金)一起处理格式。

    2。自定义选项

    如果您想从头开始实施,请点击此处。

     dependencies {
       compile 'io.michaelrocks:libphonenumber-android:8.9.0'
     }
    
    • 创建一个名为InternationalPhoneTextWatcher的新类

    将以下代码添加到该类。 CCP 使用这个类here。然后将此类的对象用于editText。这将在构造函数中采用国家/地区名称代码和电话代码。并且会在调用 updateCountry() 更改国家/地区时自动更新格式。

    public class InternationalPhoneTextWatcher implements TextWatcher {
        // Reference https://stackoverflow.com/questions/32661363/using-phonenumberformattingtextwatcher-without-typing-country-calling-code to solve formatting issue
        // Check parent project of this class at https://github.com/hbb20/CountryCodePickerProject
    
        private static final String TAG = "Int'l Phone TextWatcher";
        PhoneNumberUtil phoneNumberUtil;
        /**
         * Indicates the change was caused by ourselves.
         */
        private boolean mSelfChange = false;
        /**
         * Indicates the formatting has been stopped.
         */
        private boolean mStopFormatting;
        private AsYouTypeFormatter mFormatter;
        private String countryNameCode;
        Editable lastFormatted = null;
        private int countryPhoneCode;
    
        //when country is changed, we update the number.
        //at this point this will avoid "stopFormatting"
        private boolean needUpdateForCountryChange = false;
    
    
        /**
         * @param context
         * @param countryNameCode  ISO 3166-1 two-letter country code that indicates the country/region
         *                         where the phone number is being entered.
         * @param countryPhoneCode Phone code of country. https://countrycode.org/
         */
        public InternationalPhoneTextWatcher(Context context, String countryNameCode, int countryPhoneCode) {
            if (countryNameCode == null || countryNameCode.length() == 0)
                throw new IllegalArgumentException();
            phoneNumberUtil = PhoneNumberUtil.createInstance(context);
            updateCountry(countryNameCode, countryPhoneCode);
        }
    
        public void updateCountry(String countryNameCode, int countryPhoneCode) {
            this.countryNameCode = countryNameCode;
            this.countryPhoneCode = countryPhoneCode;
            mFormatter = phoneNumberUtil.getAsYouTypeFormatter(countryNameCode);
            mFormatter.clear();
            if (lastFormatted != null) {
                needUpdateForCountryChange = true;
                String onlyDigits = phoneNumberUtil.normalizeDigitsOnly(lastFormatted);
                lastFormatted.replace(0, lastFormatted.length(), onlyDigits, 0, onlyDigits.length());
                needUpdateForCountryChange = false;
            }
        }
    
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                                      int after) {
            if (mSelfChange || mStopFormatting) {
                return;
            }
            // If the user manually deleted any non-dialable characters, stop formatting
            if (count > 0 && hasSeparator(s, start, count) && !needUpdateForCountryChange) {
                stopFormatting();
            }
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (mSelfChange || mStopFormatting) {
                return;
            }
            // If the user inserted any non-dialable characters, stop formatting
            if (count > 0 && hasSeparator(s, start, count)) {
                stopFormatting();
            }
        }
    
        @Override
        public synchronized void afterTextChanged(Editable s) {
            if (mStopFormatting) {
                // Restart the formatting when all texts were clear.
                mStopFormatting = !(s.length() == 0);
                return;
            }
            if (mSelfChange) {
                // Ignore the change caused by s.replace().
                return;
            }
    
            //calculate few things that will be helpful later
            int selectionEnd = Selection.getSelectionEnd(s);
            boolean isCursorAtEnd = (selectionEnd == s.length());
    
            //get formatted text for this number
            String formatted = reformat(s);
    
            //now calculate cursor position in formatted text
            int finalCursorPosition = 0;
            if (formatted.equals(s.toString())) {
                //means there is no change while formatting don't move cursor
                finalCursorPosition = selectionEnd;
            } else if (isCursorAtEnd) {
                //if cursor was already at the end, put it at the end.
                finalCursorPosition = formatted.length();
            } else {
    
                // if no earlier case matched, we will use "digitBeforeCursor" way to figure out the cursor position
                int digitsBeforeCursor = 0;
                for (int i = 0; i < s.length(); i++) {
                    if (i >= selectionEnd) {
                        break;
                    }
                    if (PhoneNumberUtils.isNonSeparator(s.charAt(i))) {
                        digitsBeforeCursor++;
                    }
                }
    
                //at this point we will have digitsBeforeCursor calculated.
                // now find this position in formatted text
                for (int i = 0, digitPassed = 0; i < formatted.length(); i++) {
                    if (digitPassed == digitsBeforeCursor) {
                        finalCursorPosition = i;
                        break;
                    }
                    if (PhoneNumberUtils.isNonSeparator(formatted.charAt(i))) {
                        digitPassed++;
                    }
                }
            }
    
            //if this ends right before separator, we might wish to move it further so user do not delete separator by mistake.
            // because deletion of separator will cause stop formatting that should not happen by mistake
            if (!isCursorAtEnd) {
                while (0 < finalCursorPosition - 1 && !PhoneNumberUtils.isNonSeparator(formatted.charAt(finalCursorPosition - 1))) {
                    finalCursorPosition--;
                }
            }
    
            //Now we have everything calculated, set this values in
            if (formatted != null) {
                mSelfChange = true;
                s.replace(0, s.length(), formatted, 0, formatted.length());
                mSelfChange = false;
                lastFormatted = s;
                Selection.setSelection(s, finalCursorPosition);
            }
    
        }
    
        /**
         * this will format the number in international format (only).
         */
        private String reformat(CharSequence s) {
    
            String internationalFormatted = "";
            mFormatter.clear();
            char lastNonSeparator = 0;
    
            String countryCallingCode = "+" + countryPhoneCode;
    
            //to have number formatted as international format, add country code before that
            s = countryCallingCode + s;
            int len = s.length();
    
            for (int i = 0; i < len; i++) {
                char c = s.charAt(i);
                if (PhoneNumberUtils.isNonSeparator(c)) {
                    if (lastNonSeparator != 0) {
                        internationalFormatted = mFormatter.inputDigit(lastNonSeparator);
                    }
                    lastNonSeparator = c;
                }
            }
            if (lastNonSeparator != 0) {
                internationalFormatted = mFormatter.inputDigit(lastNonSeparator);
            }
    
            internationalFormatted = internationalFormatted.trim();
            if (internationalFormatted.length() > countryCallingCode.length()) {
                if (internationalFormatted.charAt(countryCallingCode.length()) == ' ')
                    internationalFormatted = internationalFormatted.substring(countryCallingCode.length() + 1);
                else
                    internationalFormatted = internationalFormatted.substring(countryCallingCode.length());
            } else {
                internationalFormatted = "";
            }
            return TextUtils.isEmpty(internationalFormatted) ? "" : internationalFormatted;
        }
    
        private void stopFormatting() {
            mStopFormatting = true;
            mFormatter.clear();
        }
    
        private boolean hasSeparator(final CharSequence s, final int start, final int count) {
            for (int i = start; i < start + count; i++) {
                char c = s.charAt(i);
                if (!PhoneNumberUtils.isNonSeparator(c)) {
                    return true;
                }
            }
            return false;
        }
    }
    

    【讨论】:

    • 怎么会有“libphonenumber的优化Android端口”,如果libphonenumber说“Java版本是为在智能手机上运行而优化的,并且从4.0开始被Android框架使用(冰淇淋三明治) 。” ?也许它不再需要了?
    【解决方案3】:

    CCP 库的完整示例:

    布局:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
            <com.hbb20.CountryCodePicker
                android:id="@+id/ccp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:ccp_textSize="20sp"
                android:layout_gravity="center"
                app:ccp_flagBorderColor="@color/colorPrimary"
                />
    
            <EditText
                android:id="@+id/phone"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:autofillHints="Enter phone number"
                android:inputType="phone|numberDecimal"
                android:hint="@string/your_phone"
                tools:text="9000000000"
                />
    </LinearLayout>
    

    活动/片段(在我的例子中 - 片段):

    package app.my.fragments;
    
    import android.os.Bundle;
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.EditText;
    
    import androidx.annotation.NonNull;
    import androidx.fragment.app.Fragment;
    
    import com.hbb20.CountryCodePicker;
    import com.hbb20.InternationalPhoneTextWatcher;
    
    import java.util.Locale;
    
    import app.my.R;
    import app.my.util.Logger;
    import app.my.util.TextHelper;
    
    public class LoginEnterPhoneFragment extends Fragment {
    
        private final static String TAG = LoginEnterPhoneFragment.class.getSimpleName();
    
        private EditText phoneNumberView;
        private CountryCodePicker ccp;
        private InternationalPhoneTextWatcher internationalPhoneTextWatcher;
    
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
            View view = inflater.inflate(R.layout.fragment_login_phone, container, false);
    
            phoneNumberView = view.findViewById(R.id.phone);
            ccp = view.findViewById(R.id.ccp);
    
            // Setting up ccp
            ccp.setDefaultCountryUsingNameCode(Locale.getDefault().getCountry());
            ccp.showNameCode(false);
            ccp.setOnCountryChangeListener(new CountryCodePicker.OnCountryChangeListener() {
                @Override
                public void onCountrySelected() {
                    if (internationalPhoneTextWatcher != null) {
                        phoneNumberView.removeTextChangedListener(internationalPhoneTextWatcher);
                    }
                    internationalPhoneTextWatcher = new InternationalPhoneTextWatcher(getContext(), ccp.getSelectedCountryNameCode(), ccp.getSelectedCountryCodeAsInt());
                    phoneNumberView.addTextChangedListener(internationalPhoneTextWatcher);
                    // Triggering phoneNumberView.TextChanged to reformat phone number
                    if (TextHelper.isNotEmpty(phoneNumberView.getText().toString())) {
                        phoneNumberView.setText(String.format("+%s", phoneNumberView.getText()));
                    }
                }
            });
    
            // Triggering ccp.CountryChanged to add InternationalPhoneTextWatcher to phoneNumberView
            ccp.setCountryForNameCode(Locale.getDefault().getCountry());
    
            // Setting up phoneNumberView
            phoneNumberView.addTextChangedListener(new TextWatcher() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {}
    
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    
                @Override
                public void afterTextChanged(Editable s) {
                    String original = s.toString().replaceAll("[^\\d+]", "");
                    String result = original;
                    if (result.startsWith(ccp.getDefaultCountryCodeWithPlus())) {
                        result = result.substring(ccp.getDefaultCountryCodeWithPlus().length());
                    }
                    if (result.startsWith("+")) {
                        result = result.substring(1);
                    }
                    if (!original.equals(result)) {
                        phoneNumberView.setText(result);
                    }
                }
            });
    
            return view;
        }
    
    }
    

    【讨论】:

    • 你知道如何根据验证限制输入的数字吗(比如印度电话号码有 10 位数字)
    【解决方案4】:

    工作正常,但... 光标未设置在正确位置。当用户在编辑文本内更改光标并输入数字时,光标会移动到末尾。我添加了保存格式化数字和位置的类,并从重新格式化方法返回它。

    return new InputFormatted(TextUtils.isEmpty(formatted) ? "" : formatted,
                    mFormatter.getRememberedPosition());
    

    之后只设置

    Selection.setSelection(s, formatted.getPosition());
    

    【讨论】:

    • 这是一个很大的帮助。你能发布整个类的观察者和InputFormatted吗?我按照您的建议实施,但在某些情况下,它无法正常工作。
    猜你喜欢
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多