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

Commit 65c319a7 authored by Alex Salo's avatar Alex Salo Committed by Android (Google) Code Review
Browse files

Merge "Implemented nearby button that displays wifi sharing intent and long...

Merge "Implemented nearby button that displays wifi sharing intent and long press button that opens wifi qr share fragment"
parents 67a9908d 8881f913
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@
            android:layout_width="match_parent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:gravity="center"
            android:id="@+id/wifi_dpp_layout"
            android:orientation="vertical">
            android:orientation="vertical">


            <ImageView
            <ImageView
+24 −1
Original line number Original line Diff line number Diff line
@@ -106,6 +106,7 @@ public class WifiSettings extends RestrictedSettingsFragment
    @VisibleForTesting
    @VisibleForTesting
    static final int MENU_ID_FORGET = Menu.FIRST + 3;
    static final int MENU_ID_FORGET = Menu.FIRST + 3;
    static final int MENU_ID_MODIFY = Menu.FIRST + 4;
    static final int MENU_ID_MODIFY = Menu.FIRST + 4;
    static final int MENU_ID_SHARE = Menu.FIRST + 5;


    // Max age of tracked WifiEntries
    // Max age of tracked WifiEntries
    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
@@ -499,7 +500,8 @@ public class WifiSettings extends RestrictedSettingsFragment
        }
        }


        if (mSelectedWifiEntry.canDisconnect()) {
        if (mSelectedWifiEntry.canDisconnect()) {
            menu.add(Menu.NONE, MENU_ID_DISCONNECT, 0 /* order */,
            menu.add(Menu.NONE, MENU_ID_SHARE, 0 /* order */, R.string.share);
            menu.add(Menu.NONE, MENU_ID_DISCONNECT, 1 /* order */,
                    R.string.wifi_disconnect_button_text);
                    R.string.wifi_disconnect_button_text);
        }
        }


@@ -538,6 +540,10 @@ public class WifiSettings extends RestrictedSettingsFragment
            case MENU_ID_FORGET:
            case MENU_ID_FORGET:
                forget(mSelectedWifiEntry);
                forget(mSelectedWifiEntry);
                return true;
                return true;
            case MENU_ID_SHARE:
                WifiDppUtils.showLockScreen(getContext(),
                        () -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
                return true;
            case MENU_ID_MODIFY:
            case MENU_ID_MODIFY:
                showDialog(mSelectedWifiEntry, WifiConfigUiBase2.MODE_MODIFY);
                showDialog(mSelectedWifiEntry, WifiConfigUiBase2.MODE_MODIFY);
                return true;
                return true;
@@ -1115,6 +1121,23 @@ public class WifiSettings extends RestrictedSettingsFragment
                .launch();
                .launch();
    }
    }


    private void launchWifiDppConfiguratorActivity(WifiEntry wifiEntry) {
        final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(getContext(),
                mWifiManager, wifiEntry);

        if (intent == null) {
            Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!");
        } else {
            mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
                    SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE,
                    SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
                    /* key */ null,
                    /* value */ Integer.MIN_VALUE);

            startActivity(intent);
        }
    }

    /** Helper method to return whether a WifiEntry is disabled due to a wrong password */
    /** Helper method to return whether a WifiEntry is disabled due to a wrong password */
    private static boolean isDisabledByWrongPassword(WifiEntry wifiEntry) {
    private static boolean isDisabledByWrongPassword(WifiEntry wifiEntry) {
        WifiConfiguration config = wifiEntry.getWifiConfiguration();
        WifiConfiguration config = wifiEntry.getWifiConfiguration();
+144 −4
Original line number Original line Diff line number Diff line
@@ -16,9 +16,17 @@


package com.android.settings.wifi.dpp;
package com.android.settings.wifi.dpp;


import android.annotation.Nullable;
import android.app.settings.SettingsEnums;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
@@ -27,9 +35,13 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.TextView;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.wifi.qrcode.QrCodeGenerator;
import com.android.settings.wifi.qrcode.QrCodeGenerator;


@@ -45,6 +57,15 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
    private ImageView mQrCodeView;
    private ImageView mQrCodeView;
    private String mQrCode;
    private String mQrCode;


    private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
    private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
    private static final String EXTRA_WIFI_CREDENTIALS_BUNDLE =
            "android.intent.extra.WIFI_CREDENTIALS_BUNDLE";
    private static final String EXTRA_SSID = "android.intent.extra.SSID";
    private static final String EXTRA_PASSWORD = "android.intent.extra.PASSWORD";
    private static final String EXTRA_SECURITY_TYPE = "android.intent.extra.SECURITY_TYPE";
    private static final String EXTRA_HIDDEN_SSID = "android.intent.extra.HIDDEN_SSID";

    @Override
    @Override
    public int getMetricsCategory() {
    public int getMetricsCategory() {
        return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
        return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
@@ -56,12 +77,14 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {


        // setTitle for TalkBack
        // setTitle for TalkBack
        final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
        final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
        if (getActivity() != null) {
            if (wifiNetworkConfig.isHotspot()) {
            if (wifiNetworkConfig.isHotspot()) {
                getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
                getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
            } else {
            } else {
                getActivity().setTitle(R.string.wifi_dpp_share_wifi);
                getActivity().setTitle(R.string.wifi_dpp_share_wifi);
            }
            }
        }
        }
    }


    @Override
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
@@ -112,10 +135,127 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
            }
            }
        }
        }


        final Intent intent = new Intent().setComponent(getNearbySharingComponent());
        addActionButton(view.findViewById(R.id.wifi_dpp_layout), createNearbyButton(intent, v -> {
            intent.setAction(Intent.ACTION_SEND);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

            Bundle wifiCredentialBundle = new Bundle();

            String ssid = WifiDppUtils.removeFirstAndLastDoubleQuotes(wifiNetworkConfig.getSsid());

            String passwordExtra = wifiNetworkConfig.getPreSharedKey();
            String securityType = wifiNetworkConfig.getSecurity();
            boolean hiddenSsid = wifiNetworkConfig.getHiddenSsid();

            wifiCredentialBundle.putString(EXTRA_SSID, ssid);
            wifiCredentialBundle.putString(EXTRA_PASSWORD, passwordExtra);
            wifiCredentialBundle.putString(EXTRA_SECURITY_TYPE, securityType);
            wifiCredentialBundle.putBoolean(EXTRA_HIDDEN_SSID, hiddenSsid);

            intent.putExtra(EXTRA_WIFI_CREDENTIALS_BUNDLE, wifiCredentialBundle);
            startActivity(intent);
        }));

        mQrCode = wifiNetworkConfig.getQrCode();
        mQrCode = wifiNetworkConfig.getQrCode();
        setQrCode();
        setQrCode();
    }
    }


    @VisibleForTesting ComponentName getNearbySharingComponent() {
        String nearbyComponent = Settings.Secure.getString(
                getContext().getContentResolver(),
                Settings.Secure.NEARBY_SHARING_COMPONENT);
        if (TextUtils.isEmpty(nearbyComponent)) {
            nearbyComponent = getString(
                    com.android.internal.R.string.config_defaultNearbySharingComponent);
        }
        if (TextUtils.isEmpty(nearbyComponent)) {
            return null;
        }
        return ComponentName.unflattenFromString(nearbyComponent);
    }

    private TargetInfo getNearbySharingTarget(Intent originalIntent) {
        final ComponentName cn = getNearbySharingComponent();
        if (cn == null) return null;

        final Intent resolveIntent = new Intent(originalIntent);
        resolveIntent.setComponent(cn);
        PackageManager pm = getContext().getPackageManager();
        final ResolveInfo resolveInfo = pm.resolveActivity(
                resolveIntent, PackageManager.GET_META_DATA);
        if (resolveInfo == null || resolveInfo.activityInfo == null) {
            Log.e(TAG, "Device-specified nearby sharing component (" + cn
                    + ") not available");
            return null;
        }

        // Allow the nearby sharing component to provide a more appropriate icon and label
        // for the chip.
        CharSequence name = null;
        Drawable icon = null;
        final Bundle metaData = resolveInfo.activityInfo.metaData;
        if (metaData != null) {
            try {
                final Resources pkgRes = pm.getResourcesForActivity(cn);
                final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
                name = pkgRes.getString(nameResId);
                final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
                icon = pkgRes.getDrawable(resId);
            } catch (Resources.NotFoundException ex) {
            } catch (PackageManager.NameNotFoundException ex) {
            }
        }
        if (TextUtils.isEmpty(name)) {
            name = resolveInfo.loadLabel(pm);
        }
        if (icon == null) {
            icon = resolveInfo.loadIcon(pm);
        }

        final DisplayResolveInfo dri = new DisplayResolveInfo(
                originalIntent, resolveInfo, name, "", resolveIntent, null);
        dri.setDisplayIcon(icon);
        return dri;
    }

    private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
        Button b = (Button) LayoutInflater.from(getContext()).inflate(
                com.android.internal.R.layout.chooser_action_button, null);
        if (icon != null) {
            final int size = getResources()
                    .getDimensionPixelSize(
                            com.android.internal.R.dimen.chooser_action_button_icon_size);
            icon.setBounds(0, 0, size, size);
            b.setCompoundDrawablesRelative(icon, null, null, null);
        }
        b.setText(title);
        b.setOnClickListener(r);
        return b;
    }

    private void addActionButton(ViewGroup parent, Button b) {
        if (b == null) return;
        final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        final int gap = getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.resolver_icon_margin) / 2;
        lp.setMarginsRelative(gap, 0, gap, 0);
        parent.addView(b, lp);
    }

    @VisibleForTesting
    @Nullable
    Button createNearbyButton(Intent originalIntent, View.OnClickListener r) {
        final TargetInfo ti = getNearbySharingTarget(originalIntent);
        if (ti == null) return null;

        return createActionButton(ti.getDisplayIcon(getContext()), ti.getDisplayLabel(), r);
    }

    private void setQrCode() {
    private void setQrCode() {
        try {
        try {
            final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
            final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
+1 −1
Original line number Original line Diff line number Diff line
@@ -145,7 +145,7 @@ public class WifiDppUtils {
        return wifiConfiguration.preSharedKey;
        return wifiConfiguration.preSharedKey;
    }
    }


    private static String removeFirstAndLastDoubleQuotes(String str) {
    static String removeFirstAndLastDoubleQuotes(String str) {
        if (TextUtils.isEmpty(str)) {
        if (TextUtils.isEmpty(str)) {
            return str;
            return str;
        }
        }
+161 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

package com.android.settings.wifi.dpp;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;

import androidx.fragment.app.FragmentTransaction;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;

@RunWith(AndroidJUnit4.class)
public class WifiDppQrCodeGeneratorFragmentTest {

    private WifiDppConfiguratorActivity mActivity;
    private WifiDppQrCodeGeneratorFragment mFragment;
    private Context mContext;


    @Before
    public void setUp() {
        Intent intent =
                new Intent(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
        intent.putExtra(WifiDppUtils.EXTRA_WIFI_SSID, "GoogleGuest");
        intent.putExtra(WifiDppUtils.EXTRA_WIFI_SECURITY, "WPA");
        intent.putExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY, "\\012345678,");

        MockitoAnnotations.initMocks(this);
        mActivity = Robolectric.setupActivity(WifiDppConfiguratorActivity.class);
        mActivity.setWifiNetworkConfig(WifiNetworkConfig.getValidConfigOrNull(intent));
        mActivity.startActivity(intent);

        mFragment = spy(new WifiDppQrCodeGeneratorFragment());
        FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
        ft.add(mFragment, null);
        ft.commit();

        mContext = spy(InstrumentationRegistry.getTargetContext());
        when(mFragment.getContext()).thenReturn(mContext);
    }

    @Test
    public void rotateScreen_shouldNotCrash() {
        mActivity.setRequestedOrientation(
                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        mActivity.setRequestedOrientation(
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    @Test
    public void createNearbyButton_returnsNull() {
        assertThat(mFragment.createNearbyButton(new Intent(), v -> {
        })).isNull();
    }

    private static ResolveInfo createResolveInfo(int userId) {
        final ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.activityInfo = createActivityInfo();
        resolveInfo.targetUserId = userId;
        return resolveInfo;
    }

    private static ActivityInfo createActivityInfo() {
        ActivityInfo ai = new ActivityInfo();
        ai.name = "activity_name";
        ai.packageName = "foo_bar";
        ai.enabled = true;
        ai.exported = true;
        ai.permission = null;
        ai.applicationInfo = new ApplicationInfo();
        ai.applicationInfo.packageName = "com.google.android.gms";

        Bundle metadata = mock(Bundle.class);
        when(metadata.getInt(anyString())).thenReturn(1);
        ai.metaData = metadata;
        return ai;
    }

    @Test
    public void createNearbyButtonFromSetting_notNull()
            throws PackageManager.NameNotFoundException {
        doReturn(ComponentName.unflattenFromString(
                "com.google.android.gms/com.google.android.gms.nearby.sharing.ShareSheetActivity"))
                .when(mFragment).getNearbySharingComponent();
        PackageManager packageManager = mock(PackageManager.class);
        doReturn(createResolveInfo(0)).when(packageManager).resolveActivity(any(), anyInt());

        Resources resources = mock(Resources.class);
        when(resources.getString(anyInt())).thenReturn("Nearby");
        Drawable drawable = mock(Drawable.class);
        when(resources.getDrawable(anyInt())).thenReturn(drawable);

        when(packageManager.getResourcesForActivity(any())).thenReturn(resources);

        when(mContext.getPackageManager()).thenReturn(packageManager);


        assertThat(mFragment.createNearbyButton(new Intent(), v -> {
        })).isNotNull();
    }

    @Test
    public void createNearbyButtonFromConfig_notNull() throws PackageManager.NameNotFoundException {
        doReturn(
                "com.google.android.gms/com.google.android.gms.nearby.sharing.ShareSheetActivity")
                .when(mFragment).getString(anyInt());
        PackageManager packageManager = mock(PackageManager.class);
        doReturn(createResolveInfo(0)).when(packageManager).resolveActivity(any(), anyInt());

        Resources resources = mock(Resources.class);
        when(resources.getString(anyInt())).thenReturn("Nearby");
        Drawable drawable = mock(Drawable.class);
        when(resources.getDrawable(anyInt())).thenReturn(drawable);

        when(packageManager.getResourcesForActivity(any())).thenReturn(resources);

        when(mContext.getPackageManager()).thenReturn(packageManager);


        assertThat(mFragment.createNearbyButton(new Intent(), v -> {
        })).isNotNull();
    }
}