1 /*
2  * Copyright (C) 2021 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.systemui.qs.tiles.dialog;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.graphics.drawable.Drawable;
22 import android.text.Html;
23 import android.text.TextUtils;
24 import android.util.Log;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.WindowManager;
29 import android.widget.ImageView;
30 import android.widget.LinearLayout;
31 import android.widget.TextView;
32 
33 import androidx.annotation.NonNull;
34 import androidx.annotation.Nullable;
35 import androidx.recyclerview.widget.RecyclerView;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.settingslib.Utils;
39 import com.android.settingslib.wifi.WifiUtils;
40 import com.android.systemui.res.R;
41 import com.android.wifi.flags.Flags;
42 import com.android.wifitrackerlib.WifiEntry;
43 
44 import kotlinx.coroutines.CoroutineScope;
45 import kotlinx.coroutines.Job;
46 
47 import java.util.List;
48 import java.util.concurrent.atomic.AtomicReference;
49 
50 /**
51  * Adapter for showing Wi-Fi networks.
52  */
53 public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
54 
55     private static final String TAG = "InternetAdapter";
56 
57     private final InternetDialogController mInternetDialogController;
58     private final CoroutineScope mCoroutineScope;
59     @Nullable
60     private List<WifiEntry> mWifiEntries;
61     @VisibleForTesting
62     protected int mWifiEntriesCount;
63     @VisibleForTesting
64     protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
65 
66     protected View mHolderView;
67     protected Context mContext;
68 
InternetAdapter(InternetDialogController controller, CoroutineScope coroutineScope)69     public InternetAdapter(InternetDialogController controller, CoroutineScope coroutineScope) {
70         mInternetDialogController = controller;
71         mCoroutineScope = coroutineScope;
72     }
73 
74     @Override
onCreateViewHolder(@onNull ViewGroup viewGroup, int viewType)75     public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
76             int viewType) {
77         mContext = viewGroup.getContext();
78         mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
79                 viewGroup, false);
80         return new InternetViewHolder(mHolderView, mInternetDialogController, mCoroutineScope);
81     }
82 
83     @Override
onBindViewHolder(@onNull InternetViewHolder viewHolder, int position)84     public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
85         if (mWifiEntries == null || position >= mWifiEntriesCount) {
86             return;
87         }
88         viewHolder.onBind(mWifiEntries.get(position));
89     }
90 
91     /**
92      * Updates the Wi-Fi networks.
93      *
94      * @param wifiEntries the updated Wi-Fi entries.
95      * @param wifiEntriesCount the total number of Wi-Fi entries.
96      */
setWifiEntries(@ullable List<WifiEntry> wifiEntries, int wifiEntriesCount)97     public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
98         mWifiEntries = wifiEntries;
99         mWifiEntriesCount =
100                 (wifiEntriesCount < mMaxEntriesCount) ? wifiEntriesCount : mMaxEntriesCount;
101     }
102 
103     /**
104      * Gets the total number of Wi-Fi networks.
105      *
106      * @return The total number of Wi-Fi entries.
107      */
108     @Override
getItemCount()109     public int getItemCount() {
110         return mWifiEntriesCount;
111     }
112 
113     /**
114      * Sets the maximum number of Wi-Fi networks.
115      */
setMaxEntriesCount(int count)116     public void setMaxEntriesCount(int count) {
117         if (count < 0 || mMaxEntriesCount == count) {
118             return;
119         }
120         mMaxEntriesCount = count;
121         if (mWifiEntriesCount > count) {
122             mWifiEntriesCount = count;
123             notifyDataSetChanged();
124         }
125     }
126 
127     /**
128      * ViewHolder for binding Wi-Fi view.
129      */
130     static class InternetViewHolder extends RecyclerView.ViewHolder {
131 
132         final LinearLayout mContainerLayout;
133         final LinearLayout mWifiListLayout;
134         final LinearLayout mWifiNetworkLayout;
135         final ImageView mWifiIcon;
136         final TextView mWifiTitleText;
137         final TextView mWifiSummaryText;
138         final ImageView mWifiEndIcon;
139         final Context mContext;
140         final InternetDialogController mInternetDialogController;
141         final CoroutineScope mCoroutineScope;
142         @Nullable
143         private Job mJob;
144 
InternetViewHolder(View view, InternetDialogController internetDialogController, CoroutineScope coroutineScope)145         InternetViewHolder(View view, InternetDialogController internetDialogController,
146                 CoroutineScope coroutineScope) {
147             super(view);
148             mContext = view.getContext();
149             mInternetDialogController = internetDialogController;
150             mCoroutineScope = coroutineScope;
151             mContainerLayout = view.requireViewById(R.id.internet_container);
152             mWifiListLayout = view.requireViewById(R.id.wifi_list);
153             mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
154             mWifiIcon = view.requireViewById(R.id.wifi_icon);
155             mWifiTitleText = view.requireViewById(R.id.wifi_title);
156             mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
157             mWifiEndIcon = view.requireViewById(R.id.wifi_end_icon);
158         }
159 
onBind(@onNull WifiEntry wifiEntry)160         void onBind(@NonNull WifiEntry wifiEntry) {
161             mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
162             setWifiNetworkLayout(wifiEntry.getTitle(),
163                     Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
164 
165             final int connectedState = wifiEntry.getConnectedState();
166             final int security = wifiEntry.getSecurity();
167             updateEndIcon(connectedState, security);
168 
169             mWifiListLayout.setEnabled(shouldEnabled(wifiEntry));
170             if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
171                 mWifiListLayout.setOnClickListener(
172                         v -> mInternetDialogController.launchWifiDetailsSetting(
173                                 wifiEntry.getKey(), v));
174                 return;
175             }
176             mWifiListLayout.setOnClickListener(v -> onWifiClick(wifiEntry, v));
177         }
178 
shouldEnabled(@onNull WifiEntry wifiEntry)179         boolean shouldEnabled(@NonNull WifiEntry wifiEntry) {
180             if (wifiEntry.canConnect()) {
181                 return true;
182             }
183             // If Wi-Fi is connected or saved network, leave it enabled to disconnect or configure.
184             if (wifiEntry.canDisconnect() || wifiEntry.isSaved()) {
185                 return true;
186             }
187             return false;
188         }
189 
onWifiClick(@onNull WifiEntry wifiEntry, @NonNull View view)190         void onWifiClick(@NonNull WifiEntry wifiEntry, @NonNull View view) {
191             if (Flags.androidVWifiApi() && wifiEntry.getSecurityTypes().contains(
192                     WifiEntry.SECURITY_WEP)) {
193                 if (mJob == null) {
194                     mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
195                             WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
196                                 mInternetDialogController.startActivity(intent, view);
197                                 return null;
198                             }, () -> {
199                                 wifiConnect(wifiEntry, view);
200                                 return null;
201                             });
202                 }
203                 return;
204             }
205             wifiConnect(wifiEntry, view);
206         }
207 
wifiConnect(@onNull WifiEntry wifiEntry, @NonNull View view)208         void wifiConnect(@NonNull WifiEntry wifiEntry, @NonNull View view) {
209             if (wifiEntry.shouldEditBeforeConnect()) {
210                 final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(),
211                         true /* connectForCaller */);
212                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
213                 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
214                 mContext.startActivity(intent);
215                 return;
216             }
217 
218             if (wifiEntry.canConnect()) {
219                 mInternetDialogController.connect(wifiEntry);
220                 return;
221             }
222 
223             if (wifiEntry.isSaved()) {
224                 Log.w(TAG, "The saved Wi-Fi network does not allow to connect. SSID:"
225                         + wifiEntry.getSsid());
226                 mInternetDialogController.launchWifiDetailsSetting(wifiEntry.getKey(), view);
227             }
228         }
229 
setWifiNetworkLayout(CharSequence title, CharSequence summary)230         void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
231             mWifiTitleText.setText(title);
232             if (TextUtils.isEmpty(summary)) {
233                 mWifiSummaryText.setVisibility(View.GONE);
234                 return;
235             }
236             mWifiSummaryText.setVisibility(View.VISIBLE);
237             mWifiSummaryText.setText(summary);
238         }
239 
240         @Nullable
getWifiDrawable(@onNull WifiEntry wifiEntry)241         Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
242             Drawable drawable = mInternetDialogController.getWifiDrawable(wifiEntry);
243             if (drawable == null) {
244                 return null;
245             }
246             drawable.setTint(
247                     Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorTertiary));
248             final AtomicReference<Drawable> shared = new AtomicReference<>();
249             shared.set(drawable);
250             return shared.get();
251         }
252 
updateEndIcon(int connectedState, int security)253         void updateEndIcon(int connectedState, int security) {
254             Drawable drawable = null;
255             if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
256                 drawable = mContext.getDrawable(R.drawable.ic_settings_24dp);
257             } else if (security != WifiEntry.SECURITY_NONE && security != WifiEntry.SECURITY_OWE) {
258                 drawable = mContext.getDrawable(R.drawable.ic_friction_lock_closed);
259             }
260             if (drawable == null) {
261                 mWifiEndIcon.setVisibility(View.GONE);
262                 return;
263             }
264             mWifiEndIcon.setVisibility(View.VISIBLE);
265             mWifiEndIcon.setImageDrawable(drawable);
266         }
267     }
268 }
269