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

Commit 7b6b93b4 authored by govenliu's avatar govenliu
Browse files

[Wi-Fi] Add one network request case for adding Wi-Fi for apps feature.

Handle adding one network case, add UI components and process saving/connecting flow.

Bug: 136472483
Test: Add unit test cases to test one network in list case and null list case, and also check if the SSID set correct or not.
Change-Id: I99f804a967575538c9dc3f1faabb2b507e92e558
parent 40785974
Loading
Loading
Loading
Loading
+37 −1
Original line number Diff line number Diff line
@@ -84,6 +84,42 @@

        <include layout="@layout/horizontal_divider"/>

        <LinearLayout
            android:id="@+id/single_network"
            android:layout_width="match_parent"
            android:layout_height="144dp"
            android:paddingTop="24dp"
            android:paddingBottom="16dp"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/signal_strength"
                android:layout_width="48dp"
                android:layout_height="48dp"
                android:layout_gravity="center_horizontal"/>

            <TextView
                android:id="@+id/single_ssid"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingTop="8dp"
                android:singleLine="true"
                android:gravity="center"
                android:textAppearance="?android:attr/textAppearanceListItem"
                android:ellipsize="marquee"/>

            <TextView
                android:id="@+id/single_status"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingTop="2dp"
                android:gravity="center"
                android:accessibilityLiveRegion="polite"
                android:textColor="?android:attr/textColorSecondary"/>
        </LinearLayout>

        <include layout="@layout/horizontal_divider"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
+8 −2
Original line number Diff line number Diff line
@@ -2342,6 +2342,12 @@
    <string name="wifi_add_app_single_network_title">Save this network?</string>
    <!-- Summary for the panel of add Wi-Fi network from APP [CHAR LIMIT=NONE] -->
    <string name="wifi_add_app_single_network_summary"><xliff:g id="appName" example="ThirdPartyAppName">%1$s</xliff:g> would like to save a network to your phone</string>
    <!-- Summary for saving status when saving single network [CHAR LIMIT=30] -->
    <string name="wifi_add_app_single_network_saving_summary">Saving\u2026</string>
    <!-- Summary for saved status when saving single network   [CHAR LIMIT=30] -->
    <string name="wifi_add_app_single_network_saved_summary">Saved</string>
    <!-- Summary for save failed status when saving single network [CHAR LIMIT=50] -->
    <string name="wifi_add_app_single_network_save_failed_summary">Couldn\u2019t save. Try again.</string>
    <!-- Do not translate. Used for diagnostic screens, precise translation is not necessary
         Wi-Fi Testing on the diagnostic screen-->
+11 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settings.wifi.addappnetworks;

import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
@@ -53,6 +54,12 @@ public class AddAppNetworksActivity extends FragmentActivity {
        setContentView(R.layout.settings_panel);
        showAddNetworksFragment();
        getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));

        // Move the window to the bottom of screen, and make it take up the entire screen width.
        final Window window = getWindow();
        window.setGravity(Gravity.BOTTOM);
        window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.WRAP_CONTENT);
    }

    @Override
@@ -64,14 +71,11 @@ public class AddAppNetworksActivity extends FragmentActivity {

    @VisibleForTesting
    void showAddNetworksFragment() {
        // Move the window to the bottom of screen, and make it take up the entire screen width.
        final Window window = getWindow();
        window.setGravity(Gravity.BOTTOM);
        window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.WRAP_CONTENT);

        // TODO: check the new intent status
        // TODO: Check the new intent status.
        mBundle.putString(KEY_CALLING_PACKAGE_NAME, getCallingPackage());
        mBundle.putParcelableArrayList(Settings.EXTRA_WIFI_CONFIGURATION_LIST,
                getIntent().getParcelableArrayListExtra(Settings.EXTRA_WIFI_CONFIGURATION_LIST));

        final FragmentManager fragmentManager = getSupportFragmentManager();
        if (fragmentManager.findFragmentByTag(TAG) == null) {
            final AddAppNetworksFragment fragment = new AddAppNetworksFragment();
+295 −16
Original line number Diff line number Diff line
@@ -16,13 +16,22 @@

package com.android.settings.wifi.addappnetworks;

import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;

import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,6 +50,7 @@ import com.android.settings.Utils;
import com.android.settings.core.InstrumentedFragment;

import java.util.ArrayList;
import java.util.List;

/**
 * The Fragment list those networks, which is proposed by other app, to user, and handle user's
@@ -49,10 +59,36 @@ import java.util.ArrayList;
public class AddAppNetworksFragment extends InstrumentedFragment {
    public static final String TAG = "AddAppNetworksFragment";

    private TextView mTitleView;
    private TextView mSummaryView;
    private ImageView mIconView;
    // Security types of a requested or saved network.
    private static final String SECURITY_NO_PASSWORD = "nopass";
    private static final String SECURITY_WEP = "wep";
    private static final String SECURITY_WPA_PSK = "wpa";
    private static final String SECURITY_SAE = "sae";

    // Possible result values in each item of the returned result list, which is used
    // to inform the caller APP the processed result of each specified network.
    private static final int RESULT_NETWORK_INITIAL = -1;  //initial value
    private static final int RESULT_NETWORK_SUCCESS = 0;
    private static final int RESULT_NETWORK_ADD_ERROR = 1;
    private static final int RESULT_NETWORK_ALREADY_EXISTS = 2;

    // Handler messages for controlling different state and delay showing the status message.
    private static final int MESSAGE_START_SAVING_NETWORK = 1;
    private static final int MESSAGE_SHOW_SAVED_AND_CONNECT_NETWORK = 2;
    private static final int MESSAGE_SHOW_SAVE_FAILED = 3;
    private static final int MESSAGE_FINISH = 4;

    // Signal level for the constant signal icon.
    private static final int MAX_RSSI_SIGNAL_LEVEL = 4;

    // Duration for showing different status message.
    private static final long SHOW_SAVING_INTERVAL_MILLIS = 500L;
    private static final long SHOW_SAVED_INTERVAL_MILLIS = 1000L;

    @VisibleForTesting
    FragmentActivity mActivity;
    @VisibleForTesting
    View mLayoutView;
    @VisibleForTesting
    Button mCancelButton;
    @VisibleForTesting
@@ -60,45 +96,251 @@ public class AddAppNetworksFragment extends InstrumentedFragment {
    @VisibleForTesting
    String mCallingPackageName;

    private TextView mSummaryView;
    private TextView mSingleNetworkProcessingStatusView;
    private int mSavingIndex;
    private List<WifiConfiguration> mAllSpecifiedNetworksList;
    private ArrayList<Integer> mResultCodeArrayList;
    private WifiManager.ActionListener mSaveListener;
    private WifiManager mWifiManager;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_START_SAVING_NETWORK:
                    mSaveButton.setEnabled(false);
                    // Set the initial text color for status message.
                    mSingleNetworkProcessingStatusView.setTextColor(
                            com.android.settingslib.Utils.getColorAttr(mActivity,
                                    android.R.attr.textColorSecondary));
                    mSingleNetworkProcessingStatusView.setText(
                            getString(R.string.wifi_add_app_single_network_saving_summary));
                    mSingleNetworkProcessingStatusView.setVisibility(View.VISIBLE);

                    // Save the proposed network.
                    saveNetworks();
                    break;

                case MESSAGE_SHOW_SAVED_AND_CONNECT_NETWORK:
                    mSingleNetworkProcessingStatusView.setText(
                            getString(R.string.wifi_add_app_single_network_saved_summary));

                    // For the single network case, we need to call connection after saved.
                    connectNetwork();

                    sendEmptyMessageDelayed(MESSAGE_FINISH,
                            SHOW_SAVED_INTERVAL_MILLIS);
                    break;

                case MESSAGE_SHOW_SAVE_FAILED:
                    mSingleNetworkProcessingStatusView.setText(
                            getString(R.string.wifi_add_app_single_network_save_failed_summary));
                    // Error message need to use colorError attribute to show.
                    mSingleNetworkProcessingStatusView.setTextColor(
                            com.android.settingslib.Utils.getColorAttr(mActivity,
                                    android.R.attr.colorError));
                    mSaveButton.setEnabled(true);
                    break;

                case MESSAGE_FINISH:
                    finishWithResult(RESULT_OK, mResultCodeArrayList);
                    break;

                default:
                    // Do nothing.
                    break;
            }
        }
    };

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        mActivity = getActivity();
        mWifiManager = mActivity.getSystemService(WifiManager.class);

        return inflater.inflate(R.layout.wifi_add_app_networks, container, false);
    }

    // TODO: Makesure function work correctly after rotate.
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // init local variable.
        mTitleView = view.findViewById(R.id.app_title);
        mIconView = view.findViewById(R.id.app_icon);
        // Initial UI variable.
        mLayoutView = view;
        mCancelButton = view.findViewById(R.id.cancel);
        mSaveButton = view.findViewById(R.id.save);
        mSummaryView = view.findViewById(R.id.app_summary);

        // Assigns button listeners.
        mSingleNetworkProcessingStatusView = view.findViewById(R.id.single_status);
        // Assigns button listeners and network save listener.
        mCancelButton.setOnClickListener(getCancelListener());
        mSaveButton.setOnClickListener(getSaveListener());
        prepareSaveResultListener();

        // Prepare the non-UI variables.
        final Bundle bundle = getArguments();
        createContent(bundle);
    }

    private void createContent(Bundle bundle) {
        final FragmentActivity activity = getActivity();
        mAllSpecifiedNetworksList =
                bundle.getParcelableArrayList(Settings.EXTRA_WIFI_CONFIGURATION_LIST);

        // If there is no networks in the request intent, then just finish activity.
        if (mAllSpecifiedNetworksList == null || mAllSpecifiedNetworksList.isEmpty()) {
            finishWithResult(RESULT_CANCELED, null /* resultArrayList */);
            return;
        }

        // Initial the result arry.
        initializeResultCodeArray();

        // Filter out the saved networks, don't show saved networks to user.
        checkSavedNetworks();

        if (mAllSpecifiedNetworksList.size() == 1) {
            // If the only one requested network is already saved, just return with existence.
            if (mResultCodeArrayList.get(0) == RESULT_NETWORK_ALREADY_EXISTS) {
                finishWithResult(RESULT_OK, mResultCodeArrayList);
                return;
            }

        // Assigns caller app icon and summary.
            // Show signal icon for single network case.
            setSingleNetworkSignalIcon();
            // Show the SSID of the proposed network.
            ((TextView) mLayoutView.findViewById(R.id.single_ssid)).setText(
                    mAllSpecifiedNetworksList.get(0).SSID);
            // Set the status view as gone when UI is initialized.
            mSingleNetworkProcessingStatusView.setVisibility(View.GONE);
        } else {
            // TODO: Add code for processing multiple networks case.
        }

        // Assigns caller app icon, title, and summary.
        mCallingPackageName =
                bundle.getString(AddAppNetworksActivity.KEY_CALLING_PACKAGE_NAME);
        assignAppIcon(activity, mCallingPackageName);
        assignTitleAndSummary(activity, mCallingPackageName);
        assignAppIcon(mActivity, mCallingPackageName);
        assignTitleAndSummary(mActivity, mCallingPackageName);
    }

    private void initializeResultCodeArray() {
        final int networksSize = mAllSpecifiedNetworksList.size();
        mResultCodeArrayList = new ArrayList<>();

        for (int i = 0; i < networksSize; i++) {
            mResultCodeArrayList.add(RESULT_NETWORK_INITIAL);
        }
    }

    /**
     * Classify security type into following types:
     * 1. {@Code SECURITY_NO_PASSWORD}: No password network or OWE network.
     * 2. {@Code SECURITY_WEP}: Traditional WEP encryption network.
     * 3. {@Code SECURITY_WPA_PSK}: WPA/WPA2 preshare key type.
     * 4. {@Code SECURITY_SAE}: SAE type network.
     */
    private String getSecurityType(WifiConfiguration config) {
        if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
            return SECURITY_SAE;
        }
        if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
            return SECURITY_NO_PASSWORD;
        }
        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK) || config.allowedKeyManagement.get(
                KeyMgmt.WPA2_PSK)) {
            return SECURITY_WPA_PSK;
        }
        return (config.wepKeys[0] == null) ? SECURITY_NO_PASSWORD : SECURITY_WEP;
    }

    /**
     * For the APP specified networks, need to filter out those saved ones and mark them as existed.
     */
    private void checkSavedNetworks() {
        final List<WifiConfiguration> privilegedWifiConfigurations =
                mWifiManager.getPrivilegedConfiguredNetworks();
        boolean foundInSavedList;
        int networkPositionInBundle = 0;
        for (WifiConfiguration specifiecConfig : mAllSpecifiedNetworksList) {
            foundInSavedList = false;
            final String ssidWithQuotation = addQuotationIfNeeded(specifiecConfig.SSID);
            final String securityType = getSecurityType(specifiecConfig);

            for (WifiConfiguration privilegedWifiConfiguration : privilegedWifiConfigurations) {
                final String savedSecurityType = getSecurityType(privilegedWifiConfiguration);

                // If SSID or security type is different, should be new network or need to updated
                // network.
                if (!ssidWithQuotation.equals(privilegedWifiConfiguration.SSID)
                        || !securityType.equals(savedSecurityType)) {
                    continue;
                }

                //  If specified network and saved network have same security types, we'll check
                //  more information according to their security type to judge if they are same.
                switch (securityType) {
                    case SECURITY_NO_PASSWORD:
                        foundInSavedList = true;
                        break;
                    case SECURITY_WEP:
                        if (specifiecConfig.wepKeys[0].equals(
                                privilegedWifiConfiguration.wepKeys[0])) {
                            foundInSavedList = true;
                        }
                        break;
                    case SECURITY_WPA_PSK:
                    case SECURITY_SAE:
                        if (specifiecConfig.preSharedKey.equals(
                                privilegedWifiConfiguration.preSharedKey)) {
                            foundInSavedList = true;
                        }
                        break;
                    default:
                        break;
                }
            }

            if (foundInSavedList) {
                // If this requested network already in the saved networks, mark this item in the
                // result code list as existed.
                mResultCodeArrayList.set(networkPositionInBundle, RESULT_NETWORK_ALREADY_EXISTS);
            } else {
                // TODO: for multiple networks case, need to add to adapter for present list to user
            }
            networkPositionInBundle++;
        }
    }

    private void setSingleNetworkSignalIcon() {
        // TODO: Check level of the network to show signal icon.
        final Drawable wifiIcon = mActivity.getDrawable(
                Utils.getWifiIconResource(MAX_RSSI_SIGNAL_LEVEL)).mutate();
        final Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
        wifiIconDark.setTintList(
                Utils.getColorAttr(mActivity, android.R.attr.colorControlNormal));
        ((ImageView) mLayoutView.findViewById(R.id.signal_strength)).setImageDrawable(wifiIconDark);
    }

    private String addQuotationIfNeeded(String input) {
        if (TextUtils.isEmpty(input)) {
            return "";
        }

        if (input.length() >= 2 && input.startsWith("\"") && input.endsWith("\"")) {
            return input;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("\"").append(input).append("\"");
        return sb.toString();
    }

    private void assignAppIcon(Context context, String callingPackageName) {
        final Drawable drawable = loadPackageIconDrawable(context, callingPackageName);
        mIconView.setImageDrawable(drawable);
        ((ImageView) mLayoutView.findViewById(R.id.app_icon)).setImageDrawable(drawable);
    }

    private Drawable loadPackageIconDrawable(Context context, String callingPackageName) {
@@ -114,7 +356,7 @@ public class AddAppNetworksFragment extends InstrumentedFragment {

    private void assignTitleAndSummary(Context context, String callingPackageName) {
        // Assigns caller app name to title
        mTitleView.setText(getTitle());
        ((TextView) mLayoutView.findViewById(R.id.app_title)).setText(getTitle());

        // Set summary
        mSummaryView.setText(getAddNetworkRequesterSummary(
@@ -132,23 +374,60 @@ public class AddAppNetworksFragment extends InstrumentedFragment {
    View.OnClickListener getCancelListener() {
        return (v) -> {
            Log.d(TAG, "User rejected to add network");
            finishWithResult(RESULT_CANCELED, null /* resultArrayList */);
        };
    }

    View.OnClickListener getSaveListener() {
        return (v) -> {
            Log.d(TAG, "User agree to add networks");
            // Start to process saving networks.
            final Message message = mHandler.obtainMessage(MESSAGE_START_SAVING_NETWORK);
            message.sendToTarget();
        };
    }

    private void prepareSaveResultListener() {
        mSaveListener = new WifiManager.ActionListener() {
            @Override
            public void onSuccess() {
                mResultCodeArrayList.set(mSavingIndex, RESULT_NETWORK_SUCCESS);
                Message nextState_Message = mHandler.obtainMessage(
                        MESSAGE_SHOW_SAVED_AND_CONNECT_NETWORK);
                // Delay to change to next state for showing saving mesage for a period.
                mHandler.sendMessageDelayed(nextState_Message, SHOW_SAVING_INTERVAL_MILLIS);
            }

            @Override
            public void onFailure(int reason) {
                mResultCodeArrayList.set(mSavingIndex, RESULT_NETWORK_ADD_ERROR);
                Message nextState_Message = mHandler.obtainMessage(MESSAGE_SHOW_SAVE_FAILED);
                // Delay to change to next state for showing saving mesage for a period.
                mHandler.sendMessageDelayed(nextState_Message, SHOW_SAVING_INTERVAL_MILLIS);
            }
        };
    }

    private void saveNetworks() {
        final WifiConfiguration wifiConfiguration = mAllSpecifiedNetworksList.get(0);
        wifiConfiguration.SSID = addQuotationIfNeeded(wifiConfiguration.SSID);
        mWifiManager.save(wifiConfiguration, mSaveListener);
    }

    private void connectNetwork() {
        final WifiConfiguration wifiConfiguration = mAllSpecifiedNetworksList.get(0);
        // Don't need to handle the connect result.
        mWifiManager.connect(wifiConfiguration, null /* ActionListener */);
    }

    private void finishWithResult(int resultCode, ArrayList<Integer> resultArrayList) {
        if (resultArrayList != null) {
            Intent intent = new Intent();
            intent.putIntegerArrayListExtra(Settings.EXTRA_WIFI_CONFIGURATION_RESULT_LIST,
                    resultArrayList);
            getActivity().setResult(resultCode, intent);
            mActivity.setResult(resultCode, intent);
        }
        getActivity().finish();
        mActivity.finish();
    }

    @Override
+74 −11

File changed.

Preview size limit exceeded, changes collapsed.