Merge "Implemented nearby button that displays wifi sharing intent and long press button that opens wifi qr share fragment"
diff --git a/res/layout/wifi_dpp_qrcode_generator_fragment.xml b/res/layout/wifi_dpp_qrcode_generator_fragment.xml
index 1f8b37c..756465a 100644
--- a/res/layout/wifi_dpp_qrcode_generator_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_generator_fragment.xml
@@ -42,6 +42,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
+ android:id="@+id/wifi_dpp_layout"
android:orientation="vertical">
<ImageView
@@ -54,8 +55,11 @@
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginBottom="24dp"
android:textAlignment="center"
+ android:textIsSelectable="true"
+ android:focusable="true"
+ android:longClickable="true"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java
index 79f48fb..8fbe293 100644
--- a/src/com/android/settings/wifi/WifiSettings2.java
+++ b/src/com/android/settings/wifi/WifiSettings2.java
@@ -103,6 +103,7 @@
@VisibleForTesting
static final int MENU_ID_FORGET = Menu.FIRST + 3;
static final int MENU_ID_MODIFY = Menu.FIRST + 4;
+ static final int MENU_ID_SHARE = Menu.FIRST + 5;
// Max age of tracked WifiEntries
private static final long MAX_SCAN_AGE_MILLIS = 15_000;
@@ -485,7 +486,8 @@
}
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);
}
@@ -524,6 +526,10 @@
case MENU_ID_FORGET:
forget(mSelectedWifiEntry);
return true;
+ case MENU_ID_SHARE:
+ WifiDppUtils.showLockScreen(getContext(),
+ () -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
+ return true;
case MENU_ID_MODIFY:
showDialog(mSelectedWifiEntry, WifiConfigUiBase2.MODE_MODIFY);
return true;
@@ -558,6 +564,23 @@
return true;
}
+ 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);
+ }
+ }
+
private void showDialog(WifiEntry wifiEntry, int dialogMode) {
if (WifiUtils.isNetworkLockedDown(getActivity(), wifiEntry.getWifiConfiguration())
&& wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
@@ -698,8 +721,8 @@
*/
private void updateWifiEntryPreferencesDelayed() {
// Safeguard from some delayed event handling
- if (getActivity() != null && !mIsRestricted &&
- mWifiPickerTracker.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
+ if (getActivity() != null && !mIsRestricted
+ && mWifiPickerTracker.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
final View view = getView();
final Handler handler = view.getHandler();
if (handler != null && handler.hasCallbacks(mUpdateWifiEntryPreferencesRunnable)) {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index 603ea10..2ec960e 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -16,9 +16,17 @@
package com.android.settings.wifi.dpp;
+import android.annotation.Nullable;
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.drawable.Drawable;
import android.os.Bundle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -27,9 +35,13 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
import android.widget.ImageView;
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.wifi.qrcode.QrCodeGenerator;
@@ -45,6 +57,15 @@
private ImageView mQrCodeView;
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
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
@@ -56,10 +77,12 @@
// setTitle for TalkBack
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
- if (wifiNetworkConfig.isHotspot()) {
- getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
- } else {
- getActivity().setTitle(R.string.wifi_dpp_share_wifi);
+ if (getActivity() != null) {
+ if (wifiNetworkConfig.isHotspot()) {
+ getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
+ } else {
+ getActivity().setTitle(R.string.wifi_dpp_share_wifi);
+ }
}
}
@@ -112,10 +135,130 @@
}
}
+ 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();
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) {
+ final 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;
+ final Button button = createActionButton(ti.getDisplayIcon(getContext()),
+ ti.getDisplayLabel(), r);
+ button.setAllCaps(false);
+ return button;
+ }
+
private void setQrCode() {
try {
final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 7a502c9..71301d5 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -149,7 +149,7 @@
return wifiConfiguration.preSharedKey;
}
- private static String removeFirstAndLastDoubleQuotes(String str) {
+ static String removeFirstAndLastDoubleQuotes(String str) {
if (TextUtils.isEmpty(str)) {
return str;
}
diff --git a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragmentTest.java
new file mode 100644
index 0000000..b36d7f5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragmentTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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();
+ }
+}