diff --git a/res/layout/wifi_button_preference_widget.xml b/res/layout/wifi_button_preference_widget.xml new file mode 100644 index 00000000000..55078c28ecf --- /dev/null +++ b/res/layout/wifi_button_preference_widget.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml index bb2946d9202..2a395b677c4 100644 --- a/res/layout/wifi_dialog.xml +++ b/res/layout/wifi_dialog.xml @@ -50,13 +50,28 @@ android:text="@string/wifi_ssid" android:textDirection="locale" /> - + + + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_margin="5dp" + android:background="@null" + android:src="@drawable/ic_qrcode_24dp" + android:visibility="gone" + android:contentDescription="@string/wifi_add_network" /> + - + + + + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_margin="5dp" + android:background="@null" + android:src="@drawable/ic_qrcode_24dp" + android:visibility="gone" + android:contentDescription="@string/wifi_add_network" /> + { + // Launch QR code scanner to join a network. + getContext().startActivity( + WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null)); + }); + } + } + return rootView; } diff --git a/src/com/android/settings/wifi/ButtonPreference.java b/src/com/android/settings/wifi/ButtonPreference.java new file mode 100644 index 00000000000..62e8caf1e20 --- /dev/null +++ b/src/com/android/settings/wifi/ButtonPreference.java @@ -0,0 +1,123 @@ +/* + * 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; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; + +import com.android.settings.R; + +import androidx.annotation.VisibleForTesting; +import androidx.annotation.DrawableRes; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * This preference provides one button layout with Settings style. + * It looks like below + * + * -------------------------------------------------------------- + * | icon | title | button | + * -------------------------------------------------------------- + * + * User can set icon / click listener for button. + * By default, the button is invisible. + */ +public class ButtonPreference extends Preference { + + private static final String TAG = "ButtonPreference"; + + private ImageButton mImageButton; + private Drawable mButtonIcon; + private View.OnClickListener mClickListener; + + // Used for dummy pref. + public ButtonPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setWidgetLayoutResource(R.layout.wifi_button_preference_widget); + mImageButton = null; + mButtonIcon = null; + mClickListener = null; + } + + public ButtonPreference(Context context) { + this(context, /* attrs */ null); + } + + @Override + public void onBindViewHolder(final PreferenceViewHolder view) { + super.onBindViewHolder(view); + initButton(view); + } + + @Override + public void setOrder(int order) { + super.setOrder(order); + setButtonVisibility(); + } + + @VisibleForTesting + protected void initButton(final PreferenceViewHolder view) { + if (mImageButton == null) { + mImageButton = (ImageButton) view.findViewById(R.id.button_icon); + } + if (mImageButton != null) { + mImageButton.setImageDrawable(mButtonIcon); + mImageButton.setOnClickListener(mClickListener); + } + setButtonVisibility(); + } + + private void setButtonVisibility() { + if(mImageButton != null) { + mImageButton.setVisibility(mButtonIcon == null ? View.GONE : View.VISIBLE); + } + } + + /** + * Sets the drawable to be displayed in button. + */ + public ButtonPreference setButtonIcon(@DrawableRes int iconResId) { + if (iconResId == 0) { + return this; + } + + try { + mButtonIcon = getContext().getDrawable(iconResId); + notifyChanged(); + } catch (Resources.NotFoundException exception) { + Log.e(TAG, "Resource does not exist: " + iconResId); + } + return this; + } + + /** + * Register a callback to be invoked when button is clicked. + */ + public ButtonPreference setButtonOnClickListener(View.OnClickListener listener) { + if (listener != mClickListener) { + mClickListener = listener; + notifyChanged(); + } + return this; + } +} diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java index 2b8fb2d3236..0e2ca608b5b 100644 --- a/src/com/android/settings/wifi/WifiDialog.java +++ b/src/com/android/settings/wifi/WifiDialog.java @@ -21,10 +21,13 @@ import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.widget.Button; +import android.widget.ImageButton; import androidx.appcompat.app.AlertDialog; import com.android.settings.R; +import com.android.settings.wifi.dpp.WifiDppUtils; + import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.wifi.AccessPoint; @@ -77,7 +80,18 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase, @Override protected void onCreate(Bundle savedInstanceState) { - mView = getLayoutInflater().inflate(R.layout.wifi_dialog, null); + mView = getLayoutInflater().inflate(R.layout.wifi_dialog, /* root */ null); + if (WifiDppUtils.isSharingNetworkEnabled(getContext())) { + final ImageButton scannerButton = mView.findViewById(R.id.password_scanner_button); + if (scannerButton != null) { + scannerButton.setVisibility(View.VISIBLE); + scannerButton.setOnClickListener((View v) -> { + // Launch QR code scanner to join a network. + getContext().startActivity( + WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null)); + }); + } + } setView(mView); mController = new WifiConfigController(this, mView, mAccessPoint, mMode); super.onCreate(savedInstanceState); diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 1c9a5e136c0..54de28d9de4 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -66,6 +66,7 @@ import com.android.settings.search.SearchIndexableRaw; import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; import com.android.settings.widget.SwitchBarController; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; +import com.android.settings.wifi.dpp.WifiDppUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.search.SearchIndexable; @@ -175,7 +176,7 @@ public class WifiSettings extends RestrictedSettingsFragment private PreferenceCategory mConnectedAccessPointPreferenceCategory; private PreferenceCategory mAccessPointsPreferenceCategory; - private Preference mAddPreference; + private ButtonPreference mAddPreference; @VisibleForTesting Preference mConfigureWifiSettingsPreference; @VisibleForTesting @@ -235,9 +236,17 @@ public class WifiSettings extends RestrictedSettingsFragment mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS); Context prefContext = getPrefContext(); - mAddPreference = new Preference(prefContext); + mAddPreference = new ButtonPreference(prefContext); mAddPreference.setIcon(R.drawable.ic_menu_add); mAddPreference.setTitle(R.string.wifi_add_network); + if (WifiDppUtils.isSharingNetworkEnabled(getContext())) { + mAddPreference.setButtonIcon(R.drawable.ic_qrcode_24dp); + mAddPreference.setButtonOnClickListener((View v) -> { + // Launch QR code scanner to join a network. + getContext().startActivity( + WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null)); + }); + } mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE); mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager()); diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java index 70ef3a84a39..ae144bad32a 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java +++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java @@ -16,7 +16,10 @@ package com.android.settings.wifi.dpp; +import android.content.Context; import android.content.Intent; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; /** * Here are the items shared by both WifiDppConfiguratorActivity & WifiDppEnrolleeActivity @@ -54,4 +57,46 @@ public class WifiDppUtils { * H true Optional. True if the network SSID is hidden. */ public static final String EXTRA_QR_CODE = "qrCode"; + + /** + * Returns whether the user can share the network represented by this preference with QR code. + */ + public static boolean isSharingNetworkEnabled(Context context) { + return FeatureFlagUtils.isEnabled(context, + com.android.settings.core.FeatureFlags.WIFI_SHARING); + } + + /** + * Returns an intent to launch QR code scanner. + * + * @param ssid The data corresponding to {@code WifiConfiguration} SSID + * @return Intent for launching QR code scanner + */ + public static Intent getConfiguratorQRCodeScannerIntent(String ssid) { + final Intent intent = new Intent( + WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_SCANNER); + if (!TextUtils.isEmpty(ssid)) { + intent.putExtra(EXTRA_WIFI_SSID, ssid); + } + return intent; + } + + /** + * Returns an intent to launch QR code generator. + * + * @param ssid The data corresponding to {@code WifiConfiguration} SSID + * @param Security The data is from {@code AccessPoint.securityToString} + * @return Intent for launching QR code generator + */ + public static Intent getConfiguratorQRCodeGeneratorIntent(String ssid, String Security) { + final Intent intent = new Intent( + WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR); + if (!TextUtils.isEmpty(ssid)) { + intent.putExtra(EXTRA_WIFI_SSID, ssid); + } + if (!TextUtils.isEmpty(Security)) { + intent.putExtra(EXTRA_WIFI_SECURITY, Security); + } + return intent; + } } diff --git a/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java new file mode 100644 index 00000000000..3dc109f9078 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java @@ -0,0 +1,82 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.View; +import android.widget.ImageButton; + +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import androidx.preference.PreferenceViewHolder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ButtonPreferenceTest { + + private Context mContext; + private View mRootView; + private ButtonPreference mPref; + private PreferenceViewHolder mHolder; + private boolean mClicked; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mPref = new ButtonPreference(mContext); + mRootView = View.inflate(mContext, R.layout.wifi_button_preference_widget, /* parent */ + null); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + } + + @Test + public void initButton_noIcon_shouldInvisible() { + mPref.initButton(mHolder); + assertThat(mRootView.findViewById(R.id.button_icon).getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void initButton_withIcon_shouldVisible() { + mPref.setButtonIcon(R.drawable.ic_qrcode_24dp); + mPref.initButton(mHolder); + assertThat(mRootView.findViewById(R.id.button_icon).getVisibility()).isEqualTo( + View.VISIBLE); + } + + @Test + public void initButton_whenClick_shouldCallback() { + mClicked = false; + mPref.setButtonIcon(R.drawable.ic_qrcode_24dp); + mPref.setButtonOnClickListener((View v) -> { + mClicked = true; + }); + mPref.initButton(mHolder); + ImageButton button = (ImageButton) mRootView.findViewById(R.id.button_icon); + button.performClick(); + assertThat(mClicked).isTrue(); + } +}