Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d254d460 authored by Xiangyu/Malcolm Chen's avatar Xiangyu/Malcolm Chen Committed by Android (Google) Code Review
Browse files

Merge "MSIM support for Wi-Fi calling setting."

parents 9e1570c5 13d50cb0
Loading
Loading
Loading
Loading
+45 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tabs_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.android.settings.widget.SwitchBar
        android:id="@+id/switch_bar"
        android:layout_height="?android:attr/actionBarSize"
        android:layout_width="match_parent"
        android:background="@drawable/switchbar_background"
        android:theme="?attr/switchBarTheme" />

    <FrameLayout
        android:id="@android:id/tabcontent"
        android:layout_width="0dip"
        android:layout_height="0dip" />

    <FrameLayout
        android:id="@+id/prefs_container"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:smoothScrollbar="false" />

</LinearLayout>
+36 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tabs_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.android.settings.widget.SlidingTabLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none"
            android:fillViewport="true"/>

        <com.android.settings.widget.RtlCompatibleViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

</LinearLayout>
+87 −382
Original line number Original line Diff line number Diff line
@@ -16,190 +16,38 @@


package com.android.settings;
package com.android.settings;


import android.app.Activity;
import android.app.Fragment;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v7.preference.ListPreference;
import android.telephony.SubscriptionInfo;
import android.support.v7.preference.Preference;
import android.telephony.SubscriptionManager;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.support.v7.preference.PreferenceScreen;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.widget.Switch;
import android.view.LayoutInflater;
import android.widget.TextView;
import android.view.View;
import android.view.ViewGroup;


import com.android.ims.ImsConfig;
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.ims.ImsManager;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.Phone;
import com.android.settings.widget.RtlCompatibleViewPager;
import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.SlidingTabLayout;

import java.util.List;


/**
/**
 * "Wi-Fi Calling settings" screen.  This preference screen lets you
 * "Wi-Fi Calling settings" screen. This is the container fragment which holds
 * enable/disable Wi-Fi Calling and change Wi-Fi Calling mode.
 * {@link WifiCallingSettingsForSub} fragments.
 */
 */
public class WifiCallingSettings extends SettingsPreferenceFragment
public class WifiCallingSettings extends SettingsPreferenceFragment {
        implements SwitchBar.OnSwitchChangeListener,
        Preference.OnPreferenceChangeListener {

    private static final String TAG = "WifiCallingSettings";
    private static final String TAG = "WifiCallingSettings";

    private List<SubscriptionInfo> mSil;
    //String keys for preference lookup
    private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
    private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
    private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";

    private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;

    public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";

    public static final int LAUCH_APP_ACTIVATE = 0;
    public static final int LAUCH_APP_UPDATE = 1;


    //UI objects
    //UI objects
    private SwitchBar mSwitchBar;
    private RtlCompatibleViewPager mViewPager;
    private Switch mSwitch;
    private WifiCallingViewPagerAdapter mPagerAdapter;
    private ListPreference mButtonWfcMode;
    private SlidingTabLayout mTabLayout;
    private ListPreference mButtonWfcRoamingMode;
    private Preference mUpdateAddress;
    private TextView mEmptyView;

    private boolean mValidListener = false;
    private boolean mEditableWfcMode = true;
    private boolean mEditableWfcRoamingMode = true;

    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
        /*
         * Enable/disable controls when in/out of a call and depending on
         * TTY mode and TTY support over VoLTE.
         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
         * java.lang.String)
         */
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            final SettingsActivity activity = (SettingsActivity) getActivity();
            boolean isNonTtyOrTtyOnVolteEnabled = ImsManager
                    .isNonTtyOrTtyOnVolteEnabled(activity);
            final SwitchBar switchBar = activity.getSwitchBar();
            boolean isWfcEnabled = switchBar.getSwitch().isChecked()
                    && isNonTtyOrTtyOnVolteEnabled;

            switchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE)
                    && isNonTtyOrTtyOnVolteEnabled);

            boolean isWfcModeEditable = true;
            boolean isWfcRoamingModeEditable = false;
            final CarrierConfigManager configManager = (CarrierConfigManager)
                    activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
            if (configManager != null) {
                PersistableBundle b = configManager.getConfig();
                if (b != null) {
                    isWfcModeEditable = b.getBoolean(
                            CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
                    isWfcRoamingModeEditable = b.getBoolean(
                            CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
                }
            }

            Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
            if (pref != null) {
                pref.setEnabled(isWfcEnabled && isWfcModeEditable
                        && (state == TelephonyManager.CALL_STATE_IDLE));
            }
            Preference pref_roam = getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
            if (pref_roam != null) {
                pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
                        && (state == TelephonyManager.CALL_STATE_IDLE));
            }
        }
    };

    private final OnPreferenceClickListener mUpdateAddressListener =
            new OnPreferenceClickListener() {
                /*
                 * Launch carrier emergency address managemnent activity
                 */
                @Override
                public boolean onPreferenceClick(Preference preference) {
                    final Context context = getActivity();
                    Intent carrierAppIntent = getCarrierActivityIntent(context);
                    if (carrierAppIntent != null) {
                        carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
                        startActivity(carrierAppIntent);
                    }
                    return true;
                }
    };

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        final SettingsActivity activity = (SettingsActivity) getActivity();

        mSwitchBar = activity.getSwitchBar();
        mSwitch = mSwitchBar.getSwitch();
        mSwitchBar.show();

        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
        setEmptyView(mEmptyView);
        String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation)
                + activity.getString(R.string.wifi_calling_off_explanation_2);
        mEmptyView.setText(emptyViewText);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mSwitchBar.hide();
    }

    private void showAlert(Intent intent) {
        Context context = getActivity();

        CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
        CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(message)
                .setTitle(title)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setPositiveButton(android.R.string.ok, null);
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    private IntentFilter mIntentFilter;

    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) {
                // If this fragment is active then we are immediately
                // showing alert on screen. There is no need to add
                // notification in this case.
                //
                // In order to communicate to ImsPhone that it should
                // not show notification, we are changing result code here.
                setResultCode(Activity.RESULT_CANCELED);

                // UX requirement is to disable WFC in case of "permanent" registration failures.
                mSwitch.setChecked(false);

                showAlert(intent);
            }
        }
    };


    @Override
    @Override
    public int getMetricsCategory() {
    public int getMetricsCategory() {
@@ -207,242 +55,81 @@ public class WifiCallingSettings extends SettingsPreferenceFragment
    }
    }


    @Override
    @Override
    public void onCreate(Bundle savedInstanceState) {
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        super.onCreate(savedInstanceState);
            Bundle savedInstanceState) {

        final View view = inflater.inflate(R.layout.wifi_calling_settings_tabs, container, false);
        addPreferencesFromResource(R.xml.wifi_calling_settings);

        mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
        mButtonWfcMode.setOnPreferenceChangeListener(this);

        mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
        mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);

        mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
        mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR);

        CarrierConfigManager configManager = (CarrierConfigManager)
                getSystemService(Context.CARRIER_CONFIG_SERVICE);
        boolean isWifiOnlySupported = true;
        if (configManager != null) {
            PersistableBundle b = configManager.getConfig();
            if (b != null) {
                mEditableWfcMode = b.getBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
                mEditableWfcRoamingMode = b.getBoolean(
                        CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
                isWifiOnlySupported = b.getBoolean(
                        CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
            }
        }

        if (!isWifiOnlySupported) {
            mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
            mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
            mButtonWfcRoamingMode.setEntries(
                    R.array.wifi_calling_mode_choices_v2_without_wifi_only);
            mButtonWfcRoamingMode.setEntryValues(
                    R.array.wifi_calling_mode_values_without_wifi_only);
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        final Context context = getActivity();

        // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
        boolean wfcEnabled = ImsManager.isWfcEnabledByUser(context)
                && ImsManager.isNonTtyOrTtyOnVolteEnabled(context);
        mSwitch.setChecked(wfcEnabled);
        int wfcMode = ImsManager.getWfcMode(context, false);
        int wfcRoamingMode = ImsManager.getWfcMode(context, true);
        mButtonWfcMode.setValue(Integer.toString(wfcMode));
        mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode));
        updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);


        if (ImsManager.isWfcEnabledByPlatform(context)) {
        mTabLayout = view.findViewById(R.id.sliding_tabs);
            TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        mViewPager = (RtlCompatibleViewPager) view.findViewById(R.id.view_pager);
            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

            mSwitchBar.addOnSwitchChangeListener(this);

            mValidListener = true;
        }


        context.registerReceiver(mIntentReceiver, mIntentFilter);
        mPagerAdapter = new WifiCallingViewPagerAdapter(getChildFragmentManager(), mViewPager);
        mViewPager.setAdapter(mPagerAdapter);


        Intent intent = getActivity().getIntent();
        return view;
        if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
            showAlert(intent);
        }
    }
    }


    @Override
    @Override
    public void onPause() {
    public void onCreate(Bundle icicle) {
        super.onPause();
        super.onCreate(icicle);

        // TODO: besides in onCreate, we should also update subList when SIM / Sub status
        final Context context = getActivity();
        // changes.

        updateSubList();
        if (mValidListener) {
            mValidListener = false;

            TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);

            mSwitchBar.removeOnSwitchChangeListener(this);
        }

        context.unregisterReceiver(mIntentReceiver);
    }
    }


    /**
     * Listens to the state change of the switch.
     */
    @Override
    @Override
    public void onSwitchChanged(Switch switchView, boolean isChecked) {
    public void onStart() {
        final Context context = getActivity();
        super.onStart();
        Log.d(TAG, "onSwitchChanged(" + isChecked + ")");


        if (!isChecked) {
        if (mSil != null && mSil.size() > 1) {
            updateWfcMode(context, false);
            mTabLayout.setViewPager(mViewPager);
            return;
        }

        // Call address management activity before turning on WFC
        Intent carrierAppIntent = getCarrierActivityIntent(context);
        if (carrierAppIntent != null) {
            carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
            startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
        } else {
        } else {
            updateWfcMode(context, true);
            mTabLayout.setVisibility(View.GONE);
        }
        }
    }
    }


    /*
    private final class WifiCallingViewPagerAdapter extends FragmentPagerAdapter {
     * Get the Intent to launch carrier emergency address management activity.
        private final RtlCompatibleViewPager mViewPager;
     * Return null when no activity found.
     */
    private static Intent getCarrierActivityIntent(Context context) {
        // Retrive component name from carrirt config
        CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class);
        if (configManager == null) return null;

        PersistableBundle bundle = configManager.getConfig();
        if (bundle == null) return null;

        String carrierApp = bundle.getString(
                CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
        if (TextUtils.isEmpty(carrierApp)) return null;


        ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
        public WifiCallingViewPagerAdapter(FragmentManager fragmentManager,
        if (componentName == null) return null;
                RtlCompatibleViewPager viewPager) {

            super(fragmentManager);
        // Build and return intent
            mViewPager = viewPager;
        Intent intent = new Intent();
        intent.setComponent(componentName);
        return intent;
        }
        }


    /*
        @Override
     * Turn on/off WFC mode with ImsManager and update UI accordingly
        public CharSequence getPageTitle(int position) {
     */
            return String.valueOf(mSil.get(position).getDisplayName());
    private void updateWfcMode(Context context, boolean wfcEnabled) {
        Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
        ImsManager.setWfcSetting(context, wfcEnabled);

        int wfcMode = ImsManager.getWfcMode(context, false);
        int wfcRoamingMode = ImsManager.getWfcMode(context, true);
        updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);
        if (wfcEnabled) {
            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode);
        } else {
            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1);
        }
        }
        }


        @Override
        @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        public Fragment getItem(int position) {
        super.onActivityResult(requestCode, resultCode, data);
            Log.d(TAG, "Adapter getItem " + position);

            final Bundle args = new Bundle();
        final Context context = getActivity();
            args.putInt(WifiCallingSettingsForSub.FRAGMENT_BUNDLE_SUBID,

                    mSil.get(position).getSubscriptionId());
        if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
            WifiCallingSettingsForSub fragment = new WifiCallingSettingsForSub();
            Log.d(TAG, "WFC emergency address activity result = " + resultCode);
            fragment.setArguments(args);


            if (resultCode == Activity.RESULT_OK) {
            return fragment;
                updateWfcMode(context, true);
        }
        }
        }
    }

    private void updateButtonWfcMode(Context context, boolean wfcEnabled,
                                     int wfcMode, int wfcRoamingMode) {
        mButtonWfcMode.setSummary(getWfcModeSummary(context, wfcMode));
        mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
        // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
        mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);


        final PreferenceScreen preferenceScreen = getPreferenceScreen();
        @Override
        boolean updateAddressEnabled = (getCarrierActivityIntent(context) != null);
        public Object instantiateItem(ViewGroup container, int position) {
        if (wfcEnabled) {
            Log.d(TAG, "Adapter instantiateItem " + position);
            if (mEditableWfcMode) {
            return super.instantiateItem(container,
                preferenceScreen.addPreference(mButtonWfcMode);
                    mViewPager.getRtlAwareIndex(position));
            } else {
                // Don't show WFC (home) preference if it's not editable.
                preferenceScreen.removePreference(mButtonWfcMode);
            }
            if (mEditableWfcRoamingMode) {
                preferenceScreen.addPreference(mButtonWfcRoamingMode);
            } else {
                // Don't show WFC roaming preference if it's not editable.
                preferenceScreen.removePreference(mButtonWfcRoamingMode);
            }
            if (updateAddressEnabled) {
                preferenceScreen.addPreference(mUpdateAddress);
            } else {
                preferenceScreen.removePreference(mUpdateAddress);
            }
        } else {
            preferenceScreen.removePreference(mButtonWfcMode);
            preferenceScreen.removePreference(mButtonWfcRoamingMode);
            preferenceScreen.removePreference(mUpdateAddress);
        }
        }
        }


        @Override
        @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        public int getCount() {
        final Context context = getActivity();
            if (mSil == null) {
        if (preference == mButtonWfcMode) {
                Log.d(TAG, "Adapter getCount null mSil ");
            mButtonWfcMode.setValue((String) newValue);
                return 0;
            int buttonMode = Integer.valueOf((String) newValue);
            } else {
            int currentWfcMode = ImsManager.getWfcMode(context, false);
                Log.d(TAG, "Adapter getCount " + mSil.size());
            if (buttonMode != currentWfcMode) {
                return mSil.size();
                ImsManager.setWfcMode(context, buttonMode, false);
                mButtonWfcMode.setSummary(getWfcModeSummary(context, buttonMode));
                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
            }
            if (!mEditableWfcRoamingMode) {
                int currentWfcRoamingMode = ImsManager.getWfcMode(context, true);
                if (buttonMode != currentWfcRoamingMode) {
                    ImsManager.setWfcMode(context, buttonMode, true);
                    // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value
                }
            }
        } else if (preference == mButtonWfcRoamingMode) {
            mButtonWfcRoamingMode.setValue((String) newValue);
            int buttonMode = Integer.valueOf((String) newValue);
            int currentMode = ImsManager.getWfcMode(context, true);
            if (buttonMode != currentMode) {
                ImsManager.setWfcMode(context, buttonMode, true);
                // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
            }
            }
        }
        }
        return true;
    }
    }


    public static int getWfcModeSummary(Context context, int wfcMode) {
    public static int getWfcModeSummary(Context context, int wfcMode) {
@@ -464,4 +151,22 @@ public class WifiCallingSettings extends SettingsPreferenceFragment
        }
        }
        return resId;
        return resId;
    }
    }

    private void updateSubList() {
        mSil = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList();

        // Only config Wfc if it's enabled by platform.
        if (mSil == null) {
            return;
        }
        for (int i = 0; i < mSil.size();) {
            ImsManager imsManager = ImsManager.getInstance(getActivity(),
                    mSil.get(i).getSimSlotIndex());
            if (!imsManager.isWfcEnabledByPlatform()) {
                mSil.remove(i);
            } else {
                i++;
            }
        }
    }
}
}
Loading