• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.wifi.dpp;
18 
19 import android.app.settings.SettingsEnums;
20 import android.content.ComponentName;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.drawable.Drawable;
27 import android.os.Bundle;
28 import android.provider.Settings;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.LayoutInflater;
32 import android.view.Menu;
33 import android.view.MenuInflater;
34 import android.view.MenuItem;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.Button;
38 import android.widget.ImageView;
39 import android.widget.TextView;
40 
41 import androidx.annotation.Nullable;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.app.chooser.DisplayResolveInfo;
45 import com.android.internal.app.chooser.TargetInfo;
46 import com.android.settings.R;
47 import com.android.settings.flags.Flags;
48 import com.android.settingslib.qrcode.QrCodeGenerator;
49 
50 import com.google.zxing.WriterException;
51 
52 /**
53  * After sharing a saved Wi-Fi network, {@code WifiDppConfiguratorActivity} start with this fragment
54  * to generate a Wi-Fi DPP QR code for other device to initiate as an enrollee.
55  */
56 public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
57     private static final String TAG = "WifiDppQrCodeGeneratorFragment";
58 
59     private ImageView mQrCodeView;
60     protected String mQrCode;
61 
62     private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
63     private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
64     private static final String EXTRA_WIFI_CREDENTIALS_BUNDLE =
65             "android.intent.extra.WIFI_CREDENTIALS_BUNDLE";
66     private static final String EXTRA_SSID = "android.intent.extra.SSID";
67     private static final String EXTRA_PASSWORD = "android.intent.extra.PASSWORD";
68     private static final String EXTRA_SECURITY_TYPE = "android.intent.extra.SECURITY_TYPE";
69     private static final String EXTRA_HIDDEN_SSID = "android.intent.extra.HIDDEN_SSID";
70 
71     @Override
getMetricsCategory()72     public int getMetricsCategory() {
73         if (Flags.enableWifiSharingRuntimeFragment()) {
74             return SettingsEnums.SETTINGS_WIFI_DPP_QR_SHARING;
75         }
76 
77         return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
78     }
79 
80     @Override
onActivityCreated(Bundle savedInstanceState)81     public void onActivityCreated(Bundle savedInstanceState) {
82         super.onActivityCreated(savedInstanceState);
83 
84         // setTitle for TalkBack
85         final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
86         if (getActivity() != null) {
87             if (wifiNetworkConfig.isHotspot()) {
88                 getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
89             } else {
90                 getActivity().setTitle(R.string.wifi_dpp_share_wifi);
91             }
92         }
93     }
94 
95     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)96     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
97         final MenuItem menuItem = menu.findItem(Menu.FIRST);
98         if (menuItem != null) {
99             menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
100         }
101 
102         super.onCreateOptionsMenu(menu, inflater);
103     }
104 
105     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)106     public final View onCreateView(LayoutInflater inflater, ViewGroup container,
107             Bundle savedInstanceState) {
108         return inflater.inflate(R.layout.wifi_dpp_qrcode_generator_fragment, container,
109                 /* attachToRoot */ false);
110     }
111 
112     @Override
onViewCreated(View view, Bundle savedInstanceState)113     public void onViewCreated(View view, Bundle savedInstanceState) {
114         super.onViewCreated(view, savedInstanceState);
115 
116         mQrCodeView = view.findViewById(R.id.qrcode_view);
117         mQrCodeView.setContentDescription(getString(R.string.qr_code_content_description));
118 
119         final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
120         if (wifiNetworkConfig.isHotspot()) {
121             setHeaderTitle(R.string.wifi_dpp_share_hotspot);
122         } else {
123             setHeaderTitle(R.string.wifi_dpp_share_wifi);
124         }
125 
126         final String password = wifiNetworkConfig.getPreSharedKey();
127         TextView passwordView = view.findViewById(R.id.password);
128         if (TextUtils.isEmpty(password)) {
129             mSummary.setText(getString(
130                     R.string.wifi_dpp_scan_open_network_qr_code_with_another_device,
131                     wifiNetworkConfig.getSsid()));
132 
133             passwordView.setVisibility(View.GONE);
134         } else {
135             mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
136                     wifiNetworkConfig.getSsid()));
137 
138             if (wifiNetworkConfig.isHotspot()) {
139                 passwordView.setText(getString(R.string.wifi_dpp_hotspot_password, password));
140             } else {
141                 passwordView.setText(getString(R.string.wifi_dpp_wifi_password, password));
142             }
143         }
144 
145         final Intent intent = new Intent().setComponent(getNearbySharingComponent());
146         addActionButton(view.findViewById(R.id.wifi_dpp_layout), createNearbyButton(intent, v -> {
147             intent.setAction(Intent.ACTION_SEND);
148             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
149             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
150 
151             Bundle wifiCredentialBundle = new Bundle();
152 
153             String ssid = WifiDppUtils.removeFirstAndLastDoubleQuotes(wifiNetworkConfig.getSsid());
154 
155             String passwordExtra = wifiNetworkConfig.getPreSharedKey();
156             String securityType = wifiNetworkConfig.getSecurity();
157             boolean hiddenSsid = wifiNetworkConfig.getHiddenSsid();
158 
159             wifiCredentialBundle.putString(EXTRA_SSID, ssid);
160             wifiCredentialBundle.putString(EXTRA_PASSWORD, passwordExtra);
161             wifiCredentialBundle.putString(EXTRA_SECURITY_TYPE, securityType);
162             wifiCredentialBundle.putBoolean(EXTRA_HIDDEN_SSID, hiddenSsid);
163 
164             intent.putExtra(EXTRA_WIFI_CREDENTIALS_BUNDLE, wifiCredentialBundle);
165             startActivity(intent);
166         }));
167 
168         mQrCode = wifiNetworkConfig.getQrCode();
169         setQrCode();
170     }
171 
172     @VisibleForTesting
getNearbySharingComponent()173     ComponentName getNearbySharingComponent() {
174         String nearbyComponent = Settings.Secure.getString(
175                 getContext().getContentResolver(),
176                 Settings.Secure.NEARBY_SHARING_COMPONENT);
177         if (TextUtils.isEmpty(nearbyComponent)) {
178             nearbyComponent = getString(
179                     com.android.internal.R.string.config_defaultNearbySharingComponent);
180         }
181         if (TextUtils.isEmpty(nearbyComponent)) {
182             return null;
183         }
184         return ComponentName.unflattenFromString(nearbyComponent);
185     }
186 
getNearbySharingTarget(Intent originalIntent)187     private TargetInfo getNearbySharingTarget(Intent originalIntent) {
188         final ComponentName cn = getNearbySharingComponent();
189         if (cn == null) return null;
190 
191         final Intent resolveIntent = new Intent(originalIntent);
192         resolveIntent.setComponent(cn);
193         PackageManager pm = getContext().getPackageManager();
194         final ResolveInfo resolveInfo = pm.resolveActivity(
195                 resolveIntent, PackageManager.GET_META_DATA);
196         if (resolveInfo == null || resolveInfo.activityInfo == null) {
197             Log.e(TAG, "Device-specified nearby sharing component (" + cn
198                     + ") not available");
199             return null;
200         }
201 
202         // Allow the nearby sharing component to provide a more appropriate icon and label
203         // for the chip.
204         CharSequence name = null;
205         Drawable icon = null;
206         final Bundle metaData = resolveInfo.activityInfo.metaData;
207         if (metaData != null) {
208             try {
209                 final Resources pkgRes = pm.getResourcesForActivity(cn);
210                 final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
211                 name = pkgRes.getString(nameResId);
212                 final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
213                 icon = pkgRes.getDrawable(resId);
214             } catch (Resources.NotFoundException ex) {
215             } catch (PackageManager.NameNotFoundException ex) {
216             }
217         }
218         if (TextUtils.isEmpty(name)) {
219             name = resolveInfo.loadLabel(pm);
220         }
221         if (icon == null) {
222             icon = resolveInfo.loadIcon(pm);
223         }
224 
225         final DisplayResolveInfo dri = new DisplayResolveInfo(
226                 originalIntent, resolveInfo, name, "", resolveIntent, null);
227         dri.setDisplayIcon(icon);
228         return dri;
229     }
230 
createActionButton(Drawable icon, CharSequence title, View.OnClickListener r)231     private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
232         final Button b = (Button) LayoutInflater.from(getContext()).inflate(
233                 R.layout.action_button, null);
234         if (icon != null) {
235             final int size = getResources().getDimensionPixelSize(R.dimen.action_button_icon_size);
236             icon.setBounds(0, 0, size, size);
237             b.setCompoundDrawablesRelative(icon, null, null, null);
238         }
239         b.setText(title);
240         b.setOnClickListener(r);
241         return b;
242     }
243 
addActionButton(ViewGroup parent, Button b)244     private void addActionButton(ViewGroup parent, Button b) {
245         if (b == null) return;
246         final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
247                 ViewGroup.LayoutParams.WRAP_CONTENT,
248                 ViewGroup.LayoutParams.WRAP_CONTENT
249         );
250         final int gap = getResources().getDimensionPixelSize(
251                 com.android.internal.R.dimen.resolver_icon_margin) / 2;
252         lp.setMarginsRelative(gap, 0, gap, 0);
253         parent.addView(b, lp);
254     }
255 
256     @VisibleForTesting
257     @Nullable
createNearbyButton(Intent originalIntent, View.OnClickListener r)258     Button createNearbyButton(Intent originalIntent, View.OnClickListener r) {
259         final TargetInfo ti = getNearbySharingTarget(originalIntent);
260         if (ti == null) return null;
261         final Button button = createActionButton(ti.getDisplayIcon(getContext()),
262                 ti.getDisplayLabel(), r);
263         button.setAllCaps(false);
264         return button;
265     }
266 
setQrCode()267     protected void setQrCode() {
268         try {
269             final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
270                     R.dimen.qrcode_size);
271             final Bitmap bmp = QrCodeGenerator.encodeQrCode(mQrCode, qrcodeSize);
272             mQrCodeView.setImageBitmap(bmp);
273         } catch (WriterException e) {
274             Log.e(TAG, "Error generating QR code bitmap " + e);
275         }
276     }
277 
getWifiNetworkConfigFromHostActivity()278     private WifiNetworkConfig getWifiNetworkConfigFromHostActivity() {
279         final WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
280                 .getWifiNetworkConfig();
281         if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
282             throw new IllegalStateException("Invalid Wi-Fi network for configuring");
283         }
284 
285         return wifiNetworkConfig;
286     }
287 
288     @Override
isFooterAvailable()289     protected boolean isFooterAvailable() {
290         return false;
291     }
292 }
293