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.settingslib.qrcode.QrCodeGenerator;
48 
49 import com.google.zxing.WriterException;
50 
51 /**
52  * After sharing a saved Wi-Fi network, {@code WifiDppConfiguratorActivity} start with this fragment
53  * to generate a Wi-Fi DPP QR code for other device to initiate as an enrollee.
54  */
55 public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
56     private static final String TAG = "WifiDppQrCodeGeneratorFragment";
57 
58     private ImageView mQrCodeView;
59     private String mQrCode;
60 
61     private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
62     private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
63     private static final String EXTRA_WIFI_CREDENTIALS_BUNDLE =
64             "android.intent.extra.WIFI_CREDENTIALS_BUNDLE";
65     private static final String EXTRA_SSID = "android.intent.extra.SSID";
66     private static final String EXTRA_PASSWORD = "android.intent.extra.PASSWORD";
67     private static final String EXTRA_SECURITY_TYPE = "android.intent.extra.SECURITY_TYPE";
68     private static final String EXTRA_HIDDEN_SSID = "android.intent.extra.HIDDEN_SSID";
69 
70     @Override
getMetricsCategory()71     public int getMetricsCategory() {
72         return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
73     }
74 
75     @Override
onActivityCreated(Bundle savedInstanceState)76     public void onActivityCreated(Bundle savedInstanceState) {
77         super.onActivityCreated(savedInstanceState);
78 
79         // setTitle for TalkBack
80         final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
81         if (getActivity() != null) {
82             if (wifiNetworkConfig.isHotspot()) {
83                 getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
84             } else {
85                 getActivity().setTitle(R.string.wifi_dpp_share_wifi);
86             }
87         }
88     }
89 
90     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)91     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
92         final MenuItem menuItem = menu.findItem(Menu.FIRST);
93         if (menuItem != null) {
94             menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
95         }
96 
97         super.onCreateOptionsMenu(menu, inflater);
98     }
99 
100     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)101     public final View onCreateView(LayoutInflater inflater, ViewGroup container,
102             Bundle savedInstanceState) {
103         return inflater.inflate(R.layout.wifi_dpp_qrcode_generator_fragment, container,
104                 /* attachToRoot */ false);
105     }
106 
107     @Override
onViewCreated(View view, Bundle savedInstanceState)108     public void onViewCreated(View view, Bundle savedInstanceState) {
109         super.onViewCreated(view, savedInstanceState);
110 
111         mQrCodeView = view.findViewById(R.id.qrcode_view);
112 
113         final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
114         if (wifiNetworkConfig.isHotspot()) {
115             setHeaderTitle(R.string.wifi_dpp_share_hotspot);
116         } else {
117             setHeaderTitle(R.string.wifi_dpp_share_wifi);
118         }
119 
120         final String password = wifiNetworkConfig.getPreSharedKey();
121         TextView passwordView = view.findViewById(R.id.password);
122         if (TextUtils.isEmpty(password)) {
123             mSummary.setText(getString(
124                     R.string.wifi_dpp_scan_open_network_qr_code_with_another_device,
125                     wifiNetworkConfig.getSsid()));
126 
127             passwordView.setVisibility(View.GONE);
128         } else {
129             mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
130                     wifiNetworkConfig.getSsid()));
131 
132             if (wifiNetworkConfig.isHotspot()) {
133                 passwordView.setText(getString(R.string.wifi_dpp_hotspot_password, password));
134             } else {
135                 passwordView.setText(getString(R.string.wifi_dpp_wifi_password, password));
136             }
137         }
138 
139         final Intent intent = new Intent().setComponent(getNearbySharingComponent());
140         addActionButton(view.findViewById(R.id.wifi_dpp_layout), createNearbyButton(intent, v -> {
141             intent.setAction(Intent.ACTION_SEND);
142             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
143             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
144 
145             Bundle wifiCredentialBundle = new Bundle();
146 
147             String ssid = WifiDppUtils.removeFirstAndLastDoubleQuotes(wifiNetworkConfig.getSsid());
148 
149             String passwordExtra = wifiNetworkConfig.getPreSharedKey();
150             String securityType = wifiNetworkConfig.getSecurity();
151             boolean hiddenSsid = wifiNetworkConfig.getHiddenSsid();
152 
153             wifiCredentialBundle.putString(EXTRA_SSID, ssid);
154             wifiCredentialBundle.putString(EXTRA_PASSWORD, passwordExtra);
155             wifiCredentialBundle.putString(EXTRA_SECURITY_TYPE, securityType);
156             wifiCredentialBundle.putBoolean(EXTRA_HIDDEN_SSID, hiddenSsid);
157 
158             intent.putExtra(EXTRA_WIFI_CREDENTIALS_BUNDLE, wifiCredentialBundle);
159             startActivity(intent);
160         }));
161 
162         mQrCode = wifiNetworkConfig.getQrCode();
163         setQrCode();
164     }
165 
166     @VisibleForTesting
getNearbySharingComponent()167     ComponentName getNearbySharingComponent() {
168         String nearbyComponent = Settings.Secure.getString(
169                 getContext().getContentResolver(),
170                 Settings.Secure.NEARBY_SHARING_COMPONENT);
171         if (TextUtils.isEmpty(nearbyComponent)) {
172             nearbyComponent = getString(
173                     com.android.internal.R.string.config_defaultNearbySharingComponent);
174         }
175         if (TextUtils.isEmpty(nearbyComponent)) {
176             return null;
177         }
178         return ComponentName.unflattenFromString(nearbyComponent);
179     }
180 
getNearbySharingTarget(Intent originalIntent)181     private TargetInfo getNearbySharingTarget(Intent originalIntent) {
182         final ComponentName cn = getNearbySharingComponent();
183         if (cn == null) return null;
184 
185         final Intent resolveIntent = new Intent(originalIntent);
186         resolveIntent.setComponent(cn);
187         PackageManager pm = getContext().getPackageManager();
188         final ResolveInfo resolveInfo = pm.resolveActivity(
189                 resolveIntent, PackageManager.GET_META_DATA);
190         if (resolveInfo == null || resolveInfo.activityInfo == null) {
191             Log.e(TAG, "Device-specified nearby sharing component (" + cn
192                     + ") not available");
193             return null;
194         }
195 
196         // Allow the nearby sharing component to provide a more appropriate icon and label
197         // for the chip.
198         CharSequence name = null;
199         Drawable icon = null;
200         final Bundle metaData = resolveInfo.activityInfo.metaData;
201         if (metaData != null) {
202             try {
203                 final Resources pkgRes = pm.getResourcesForActivity(cn);
204                 final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
205                 name = pkgRes.getString(nameResId);
206                 final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
207                 icon = pkgRes.getDrawable(resId);
208             } catch (Resources.NotFoundException ex) {
209             } catch (PackageManager.NameNotFoundException ex) {
210             }
211         }
212         if (TextUtils.isEmpty(name)) {
213             name = resolveInfo.loadLabel(pm);
214         }
215         if (icon == null) {
216             icon = resolveInfo.loadIcon(pm);
217         }
218 
219         final DisplayResolveInfo dri = new DisplayResolveInfo(
220                 originalIntent, resolveInfo, name, "", resolveIntent, null);
221         dri.setDisplayIcon(icon);
222         return dri;
223     }
224 
createActionButton(Drawable icon, CharSequence title, View.OnClickListener r)225     private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
226         final Button b = (Button) LayoutInflater.from(getContext()).inflate(
227                 R.layout.action_button, null);
228         if (icon != null) {
229             final int size = getResources().getDimensionPixelSize(R.dimen.action_button_icon_size);
230             icon.setBounds(0, 0, size, size);
231             b.setCompoundDrawablesRelative(icon, null, null, null);
232         }
233         b.setText(title);
234         b.setOnClickListener(r);
235         return b;
236     }
237 
addActionButton(ViewGroup parent, Button b)238     private void addActionButton(ViewGroup parent, Button b) {
239         if (b == null) return;
240         final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
241                 ViewGroup.LayoutParams.WRAP_CONTENT,
242                 ViewGroup.LayoutParams.WRAP_CONTENT
243         );
244         final int gap = getResources().getDimensionPixelSize(
245                 com.android.internal.R.dimen.resolver_icon_margin) / 2;
246         lp.setMarginsRelative(gap, 0, gap, 0);
247         parent.addView(b, lp);
248     }
249 
250     @VisibleForTesting
251     @Nullable
createNearbyButton(Intent originalIntent, View.OnClickListener r)252     Button createNearbyButton(Intent originalIntent, View.OnClickListener r) {
253         final TargetInfo ti = getNearbySharingTarget(originalIntent);
254         if (ti == null) return null;
255         final Button button = createActionButton(ti.getDisplayIcon(getContext()),
256                 ti.getDisplayLabel(), r);
257         button.setAllCaps(false);
258         return button;
259     }
260 
setQrCode()261     private void setQrCode() {
262         try {
263             final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
264                     R.dimen.qrcode_size);
265             final Bitmap bmp = QrCodeGenerator.encodeQrCode(mQrCode, qrcodeSize);
266             mQrCodeView.setImageBitmap(bmp);
267         } catch (WriterException e) {
268             Log.e(TAG, "Error generating QR code bitmap " + e);
269         }
270     }
271 
getWifiNetworkConfigFromHostActivity()272     private WifiNetworkConfig getWifiNetworkConfigFromHostActivity() {
273         final WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
274                 .getWifiNetworkConfig();
275         if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
276             throw new IllegalStateException("Invalid Wi-Fi network for configuring");
277         }
278 
279         return wifiNetworkConfig;
280     }
281 
282     @Override
isFooterAvailable()283     protected boolean isFooterAvailable() {
284         return false;
285     }
286 }
287