1 /* 2 * Copyright (C) 2017 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.tv.settings.connectivity.setup; 18 19 import android.arch.lifecycle.ViewModelProviders; 20 import android.content.Context; 21 import android.net.wifi.WifiConfiguration; 22 import android.os.Bundle; 23 import android.support.annotation.NonNull; 24 import android.support.v17.leanback.widget.GuidanceStylist; 25 import android.support.v17.leanback.widget.GuidedAction; 26 import android.support.v17.leanback.widget.GuidedActionsStylist; 27 import android.support.v4.app.Fragment; 28 import android.support.v4.app.FragmentActivity; 29 import android.text.InputType; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.CheckBox; 34 import android.widget.EditText; 35 36 import com.android.settingslib.wifi.AccessPoint; 37 import com.android.tv.settings.R; 38 import com.android.tv.settings.connectivity.util.GuidedActionsAlignUtil; 39 import com.android.tv.settings.connectivity.util.State; 40 import com.android.tv.settings.connectivity.util.StateMachine; 41 42 import java.util.List; 43 44 /** 45 * State responsible for entering the password. 46 */ 47 public class EnterPasswordState implements State { 48 private final FragmentActivity mActivity; 49 private Fragment mFragment; 50 51 public EnterPasswordState(FragmentActivity activity) { 52 mActivity = activity; 53 } 54 55 @Override 56 public void processForward() { 57 mFragment = new EnterPasswordFragment(); 58 FragmentChangeListener listener = (FragmentChangeListener) mActivity; 59 listener.onFragmentChange(mFragment, true); 60 } 61 62 @Override 63 public void processBackward() { 64 mFragment = new EnterPasswordFragment(); 65 FragmentChangeListener listener = (FragmentChangeListener) mActivity; 66 listener.onFragmentChange(mFragment, false); 67 } 68 69 @Override 70 public Fragment getFragment() { 71 return mFragment; 72 } 73 74 /** 75 * Fragment that enters password of the Wi-Fi. 76 */ 77 public static class EnterPasswordFragment extends WifiConnectivityGuidedStepFragment { 78 private static final int PSK_MIN_LENGTH = 8; 79 private static final int WEP_MIN_LENGTH = 5; 80 private UserChoiceInfo mUserChoiceInfo; 81 private StateMachine mStateMachine; 82 private EditText mTextInput; 83 private CheckBox mCheckBox; 84 85 @NonNull 86 @Override 87 public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { 88 String title = getString( 89 R.string.wifi_setup_input_password, 90 mUserChoiceInfo.getWifiConfiguration().getPrintableSsid()); 91 return new GuidanceStylist.Guidance( 92 title, 93 null, 94 null, 95 null); 96 } 97 98 @Override 99 public GuidedActionsStylist onCreateActionsStylist() { 100 return new GuidedActionsStylist() { 101 @Override 102 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 103 LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 104 View v = inflater.inflate(onProvideItemLayoutId(viewType), parent, false); 105 return new PasswordViewHolder(v); 106 } 107 108 @Override 109 public void onBindViewHolder(ViewHolder vh, GuidedAction action) { 110 super.onBindViewHolder(vh, action); 111 if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) { 112 PasswordViewHolder viewHolder = (PasswordViewHolder) vh; 113 mTextInput = (EditText) viewHolder.getTitleView(); 114 mCheckBox = viewHolder.mCheckbox; 115 mCheckBox.setOnClickListener(view -> { 116 updatePasswordInputObfuscation(); 117 EnterPasswordFragment.this.openInEditMode(action); 118 }); 119 mCheckBox.setChecked(mUserChoiceInfo.isPasswordHidden()); 120 updatePasswordInputObfuscation(); 121 openInEditMode(action); 122 } 123 } 124 125 @Override 126 public int onProvideItemLayoutId() { 127 return R.layout.setup_password_item; 128 } 129 }; 130 } 131 132 @Override 133 public void onCreate(Bundle savedInstanceState) { 134 mUserChoiceInfo = ViewModelProviders 135 .of(getActivity()) 136 .get(UserChoiceInfo.class); 137 mStateMachine = ViewModelProviders 138 .of(getActivity()) 139 .get(StateMachine.class); 140 super.onCreate(savedInstanceState); 141 } 142 143 @Override 144 public void onCreateActions(@NonNull List<GuidedAction> actions, 145 Bundle savedInstanceState) { 146 Context context = getActivity(); 147 CharSequence prevPassword = mUserChoiceInfo.getPageSummary(UserChoiceInfo.PASSWORD); 148 boolean isPasswordHidden = mUserChoiceInfo.isPasswordHidden(); 149 actions.add(new GuidedAction.Builder(context) 150 .title(prevPassword == null ? "" : prevPassword) 151 .editInputType(InputType.TYPE_CLASS_TEXT 152 | (isPasswordHidden 153 ? InputType.TYPE_TEXT_VARIATION_PASSWORD 154 : InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) 155 .id(GuidedAction.ACTION_ID_CONTINUE) 156 .editable(true) 157 .build()); 158 } 159 160 @Override 161 public long onGuidedActionEditedAndProceed(GuidedAction action) { 162 if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) { 163 String password = action.getTitle().toString(); 164 if (password.length() >= WEP_MIN_LENGTH) { 165 mUserChoiceInfo.put(UserChoiceInfo.PASSWORD, action.getTitle().toString()); 166 mUserChoiceInfo.setPasswordHidden(mCheckBox.isChecked()); 167 setWifiConfigurationPassword(password); 168 mStateMachine.getListener().onComplete(StateMachine.OPTIONS_OR_CONNECT); 169 } 170 } 171 return action.getId(); 172 } 173 174 private void updatePasswordInputObfuscation() { 175 mTextInput.setInputType(InputType.TYPE_CLASS_TEXT 176 | (mCheckBox.isChecked() 177 ? InputType.TYPE_TEXT_VARIATION_PASSWORD 178 : InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)); 179 } 180 181 private void setWifiConfigurationPassword(String password) { 182 int wifiSecurity = mUserChoiceInfo.getWifiSecurity(); 183 WifiConfiguration wifiConfiguration = mUserChoiceInfo.getWifiConfiguration(); 184 if (wifiSecurity == AccessPoint.SECURITY_WEP) { 185 int length = password.length(); 186 // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) 187 if ((length == 10 || length == 26 || length == 32 || length == 58) 188 && password.matches("[0-9A-Fa-f]*")) { 189 wifiConfiguration.wepKeys[0] = password; 190 } else if (length == 5 || length == 13 || length == 16 || length == 29) { 191 wifiConfiguration.wepKeys[0] = '"' + password + '"'; 192 } 193 } else { 194 if (wifiSecurity == AccessPoint.SECURITY_PSK 195 && password.length() < PSK_MIN_LENGTH) { 196 return; 197 } 198 if (password.matches("[0-9A-Fa-f]{64}")) { 199 wifiConfiguration.preSharedKey = password; 200 } else { 201 wifiConfiguration.preSharedKey = '"' + password + '"'; 202 } 203 } 204 } 205 206 private static class PasswordViewHolder extends GuidedActionsAlignUtil.SetupViewHolder { 207 CheckBox mCheckbox; 208 209 PasswordViewHolder(View v) { 210 super(v); 211 mCheckbox = v.findViewById(R.id.password_checkbox); 212 } 213 } 214 } 215 } 216