Java tutorial
/* * WiFiKeyShare. Share Wi-Fi passwords with QR codes or NFC tags. * Copyright (C) 2016 Bruno Parmentier <dev@brunoparmentier.be> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package be.brunoparmentier.wifikeyshare.ui.activities; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.design.widget.TabLayout; import android.support.design.widget.TextInputLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.InputFilter; import android.text.InputType; import android.text.Spanned; import android.text.TextWatcher; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.google.zxing.WriterException; import be.brunoparmentier.wifikeyshare.R; import be.brunoparmentier.wifikeyshare.db.WifiKeysDataSource; import be.brunoparmentier.wifikeyshare.model.WifiAuthType; import be.brunoparmentier.wifikeyshare.model.WifiException; import be.brunoparmentier.wifikeyshare.model.WifiNetwork; import be.brunoparmentier.wifikeyshare.ui.AboutDialog; import be.brunoparmentier.wifikeyshare.utils.NfcUtils; import be.brunoparmentier.wifikeyshare.utils.QrCodeUtils; public class WifiNetworkActivity extends AppCompatActivity { private static final String TAG = WifiNetworkActivity.class.getSimpleName(); private static final String KEY_WIFI_NETWORK = "wifi_network"; private static final String KEY_NETWORK_ID = "network_id"; private WifiNetwork wifiNetwork; private int wifiNetworkId; private boolean isInWriteMode; private NfcAdapter nfcAdapter; private BroadcastReceiver nfcStateChangeBroadcastReceiver; private AlertDialog writeTagDialog; private int screenWidth; private PendingIntent nfcPendingIntent; private IntentFilter[] nfcIntentFilters; private String[][] nfcTechLists; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wifi_network); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); // Set up the ViewPager with the sections adapter. ViewPager mViewPager = (ViewPager) findViewById(R.id.container); mViewPager.setAdapter(mSectionsPagerAdapter); TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(mViewPager); wifiNetwork = (WifiNetwork) getIntent().getSerializableExtra(KEY_WIFI_NETWORK); if (wifiNetwork.needsPassword()) { wifiNetworkId = getIntent().getIntExtra(KEY_NETWORK_ID, -1); showWifiPasswordDialog(); } writeTagDialog = new AlertDialog.Builder(this).setTitle(getString(R.string.write_to_tag)) .setMessage(getString(R.string.write_to_tag_msg)) .setNegativeButton(getString(R.string.action_cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { disableTagWriteMode(); dialogInterface.dismiss(); } }).setCancelable(false).create(); isInWriteMode = false; getSupportActionBar().setTitle(wifiNetwork.getSsid()); nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (isNfcAvailable()) { initializeNfcStateChangeListener(); setupForegroundDispatch(); nfcAdapter.setNdefPushMessage(NfcUtils.generateNdefMessage(wifiNetwork), this); } } void showWifiPasswordDialog() { final LayoutInflater inflater = getLayoutInflater(); final View wifiPasswordDialogLayout = inflater.inflate(R.layout.dialog_wifi_password, null); final TextInputLayout wifiPasswordWrapper = (TextInputLayout) wifiPasswordDialogLayout .findViewById(R.id.wifi_key_wrapper); final EditText passwordEditText = (EditText) wifiPasswordDialogLayout.findViewById(R.id.wifi_key); //setPasswordRestrictions(passwordEditText); final CheckBox showPasswordCheckBox = (CheckBox) wifiPasswordDialogLayout .findViewById(R.id.show_password_checkbox); showPasswordCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { int selectionIndex = passwordEditText.getSelectionStart(); if (isChecked) { passwordEditText.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); } else { passwordEditText .setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } passwordEditText.setSelection(selectionIndex); } }); final AlertDialog wifiPasswordDialog = new AlertDialog.Builder(this) .setTitle(getString(R.string.wifi_dialog_password_title)) .setMessage(String.format(getString(R.string.wifi_dialog_password_msg), wifiNetwork.getSsid())) .setView(wifiPasswordDialogLayout) .setPositiveButton(getString(R.string.action_ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { // this method gets overriden after we show the dialog } }).setNegativeButton(getString(R.string.action_cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { finish(); } }).setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { finish(); } }).create(); passwordEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { wifiPasswordDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(editable.length() >= 5); if (wifiPasswordWrapper.getError() != null) { try { if (WifiNetwork.isValidKeyLength(wifiNetwork.getAuthType(), editable.toString())) { wifiPasswordWrapper.setError(null); } } catch (final WifiException e) { switch (e.getErrorCode()) { case WifiException.WEP_KEY_LENGTH_ERROR: wifiPasswordWrapper.setError(getString(R.string.error_wep_password_length)); break; case WifiException.WPA_KEY_LENGTH_ERROR: wifiPasswordWrapper.setError(getString(R.string.error_wpa_password_length)); break; default: wifiPasswordWrapper.setError(e.getMessage()); break; } } } } }); wifiPasswordDialog.show(); wifiPasswordDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false); // disabled by default wifiPasswordDialog.getButton(DialogInterface.BUTTON_POSITIVE) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { if (WifiNetwork.isValidKeyLength(wifiNetwork.getAuthType(), passwordEditText.getText().toString())) { wifiPasswordWrapper.setError(null); wifiNetwork.setKey(passwordEditText.getText().toString()); // Update QR code image FragmentManager fm = getSupportFragmentManager(); QrCodeFragment qrCodeFragment = (QrCodeFragment) fm.getFragments().get(0); qrCodeFragment.updateQrCode(wifiNetwork); WifiKeysDataSource.getInstance().insertWifiKey(wifiNetwork); Intent passwordResultIntent = new Intent(); passwordResultIntent.putExtra(KEY_NETWORK_ID, wifiNetworkId); setResult(RESULT_OK, passwordResultIntent); wifiPasswordDialog.dismiss(); } } catch (WifiException e) { switch (e.getErrorCode()) { case WifiException.WEP_KEY_LENGTH_ERROR: wifiPasswordWrapper.setError(getString(R.string.error_wep_password_length)); break; case WifiException.WPA_KEY_LENGTH_ERROR: wifiPasswordWrapper.setError(getString(R.string.error_wpa_password_length)); break; default: wifiPasswordWrapper.setError(null); break; } } } }); } private static void setPasswordRestrictions(EditText editText) { // Source: http://stackoverflow.com/a/4401227 InputFilter filter = new InputFilter() { @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { // TODO: check that the filter follows WEP/WPA recommendations for (int i = start; i < end; i++) { if (!Character.isLetterOrDigit(source.charAt(i))) { return ""; } } return null; } }; editText.setFilters(new InputFilter[] { filter }); } void initializeNfcStateChangeListener() { nfcStateChangeBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) { final int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_OFF); switch (state) { case NfcAdapter.STATE_OFF: case NfcAdapter.STATE_TURNING_OFF: onNfcDisabled(); break; case NfcAdapter.STATE_TURNING_ON: break; case NfcAdapter.STATE_ON: onNfcEnabled(); break; } } } }; } @Override protected void onPause() { super.onPause(); if (isNfcAvailable()) { stopForegroundDispatch(); unregisterReceiver(nfcStateChangeBroadcastReceiver); } } @Override protected void onResume() { super.onResume(); if (isNfcAvailable()) { IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); registerReceiver(nfcStateChangeBroadcastReceiver, filter); startForegroundDispatch(); } } private void enableTagWriteMode() { isInWriteMode = true; writeTagDialog.show(); } private void disableTagWriteMode() { isInWriteMode = false; writeTagDialog.dismiss(); } protected boolean isNfcAvailable() { return (nfcAdapter != null); } protected boolean isNfcEnabled() { return (isNfcAvailable() && nfcAdapter.isEnabled()); } protected void onNfcEnabled() { if (wifiNetwork.getAuthType() != WifiAuthType.WEP) { // writing WEP config is not supported // Update NFC write button and status text FragmentManager fm = getSupportFragmentManager(); NfcFragment nfcFragment = (NfcFragment) fm.getFragments().get(1); nfcFragment.setNfcStateEnabled(true); } } protected void onNfcDisabled() { if (wifiNetwork.getAuthType() != WifiAuthType.WEP) { // writing WEP config is not supported // Update NFC write button and status text FragmentManager fm = getSupportFragmentManager(); NfcFragment nfcFragment = (NfcFragment) fm.getFragments().get(1); nfcFragment.setNfcStateEnabled(false); } } private void setupForegroundDispatch() { /* initialize the PendingIntent to start for the dispatch */ nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); /* initialize the IntentFilters to override dispatching for */ nfcIntentFilters = new IntentFilter[3]; nfcIntentFilters[0] = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); nfcIntentFilters[0].addCategory(Intent.CATEGORY_DEFAULT); nfcIntentFilters[1] = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); nfcIntentFilters[1].addCategory(Intent.CATEGORY_DEFAULT); nfcIntentFilters[2] = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); nfcIntentFilters[2].addCategory(Intent.CATEGORY_DEFAULT); try { nfcIntentFilters[0].addDataType("*/*"); // Handle all MIME based dispatches. } catch (IntentFilter.MalformedMimeTypeException e) { Log.e(TAG, "setupForegroundDispatch: " + e.getMessage()); } /* Initialize the tech lists used to perform matching for dispatching of the * ACTION_TECH_DISCOVERED intent */ nfcTechLists = new String[][] {}; } private void startForegroundDispatch() { nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, nfcIntentFilters, nfcTechLists); } private void stopForegroundDispatch() { nfcAdapter.disableForegroundDispatch(this); } @Override protected void onNewIntent(Intent intent) { handleIntent(intent); } private void handleIntent(Intent intent) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String action = intent.getAction(); Log.d(TAG, "handleIntent: action=" + action); if (isInWriteMode) { /* Write tag */ Log.d(TAG, "Writing tag"); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { if (NfcUtils.writeTag(wifiNetwork, tag)) { Toast.makeText(this, R.string.nfc_tag_written, Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, R.string.error_nfc_tag_write, Toast.LENGTH_LONG).show(); } disableTagWriteMode(); } } else { /* Read tag */ Log.d(TAG, "Reading tag"); if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { if (NfcUtils.NFC_TOKEN_MIME_TYPE.equals(intent.getType())) { Intent configureNetworkIntent = new Intent(intent) .setClass(this, ConfirmConnectToWifiNetworkActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(configureNetworkIntent); } else { Log.d(TAG, "Not a Wi-Fi configuration tag"); } } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_wifi_network, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_view_password: // FIXME: redundant with @WifiListActivity#onContextItemSelected final AlertDialog viewPasswordDialog = new AlertDialog.Builder(this) .setTitle(getString(R.string.wifi_dialog_view_password)).setView(R.layout.dialog_view_password) .setPositiveButton(R.string.action_close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }).create(); viewPasswordDialog.show(); /* Set SSID, security and password values */ TextView ssidTextView = (TextView) viewPasswordDialog.findViewById(R.id.ssid_value); TextView authTypeTextView = (TextView) viewPasswordDialog.findViewById(R.id.auth_type_value); TextView passwordTextView = (TextView) viewPasswordDialog.findViewById(R.id.password_value); ssidTextView.setText(wifiNetwork.getSsid()); authTypeTextView.setText(wifiNetwork.getAuthType().toString()); passwordTextView.setText(wifiNetwork.getKey()); passwordTextView.setTextIsSelectable(true); return true; case R.id.action_settings: startActivity(new Intent(this, SettingsActivity.class)); return true; case R.id.action_about: final AlertDialog aboutDialog = new AboutDialog(this); aboutDialog.show(); return true; default: return super.onOptionsItemSelected(item); } } public static class QrCodeFragment extends Fragment { private ImageView qrCodeImageView; public QrCodeFragment() { } public static QrCodeFragment newInstance(WifiNetwork wifiNetwork) { QrCodeFragment fragment = new QrCodeFragment(); Bundle args = new Bundle(); args.putSerializable(KEY_WIFI_NETWORK, wifiNetwork); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_qrcode, container, false); WifiNetwork wifiNetwork = (WifiNetwork) getArguments().getSerializable(KEY_WIFI_NETWORK); qrCodeImageView = (ImageView) rootView.findViewById(R.id.qr_code); qrCodeImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // TODO: show fullscreen QR code } }); updateQrCode(wifiNetwork); return rootView; } public void updateQrCode(WifiNetwork wifiNetwork) { DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); float widthInDp = 200f; int widthInPixels = (int) (metrics.density * widthInDp + 0.5f); try { Bitmap qrCodeBitmap = QrCodeUtils.generateWifiQrCode(widthInPixels, wifiNetwork); qrCodeImageView.setImageBitmap(qrCodeBitmap); } catch (final WriterException e) { Log.d(TAG, e.getMessage()); } } } public static class NfcFragment extends Fragment { private WifiNetwork wifiNetwork; private Button writeTagButton; private TextView nfcStatusTextView; private Button nfcSettingsButton; public NfcFragment() { } public static NfcFragment newInstance(WifiNetwork wifiNetwork) { NfcFragment fragment = new NfcFragment(); Bundle args = new Bundle(); args.putSerializable(KEY_WIFI_NETWORK, wifiNetwork); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_nfc, container, false); wifiNetwork = (WifiNetwork) getArguments().getSerializable(KEY_WIFI_NETWORK); writeTagButton = (Button) rootView.findViewById(R.id.nfc_write_button); writeTagButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ((WifiNetworkActivity) getActivity()).enableTagWriteMode(); } }); nfcStatusTextView = (TextView) rootView.findViewById(R.id.nfc_status); nfcSettingsButton = (Button) rootView.findViewById(R.id.open_nfc_settings); nfcSettingsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { intent = new Intent(Settings.ACTION_NFC_SETTINGS); } else { intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); } startActivity(intent); } }); return rootView; } @Override public void onResume() { super.onResume(); if (wifiNetwork.getAuthType() == WifiAuthType.WEP) { writeTagButton.setEnabled(false); nfcStatusTextView.setText(R.string.error_wep_to_nfc_not_supported); nfcStatusTextView.setVisibility(View.VISIBLE); } else { boolean isNfcAvailable = ((WifiNetworkActivity) getActivity()).isNfcAvailable(); boolean isNfcEnabled = (((WifiNetworkActivity) getActivity()).isNfcEnabled()); if (!isNfcAvailable) { setNfcStateAvailable(false); } else if (!isNfcEnabled) { setNfcStateEnabled(false); } else { setNfcStateAvailable(true); setNfcStateEnabled(true); } } } public void setNfcStateEnabled(boolean enabled) { writeTagButton.setEnabled(enabled); if (enabled) { nfcSettingsButton.setVisibility(View.GONE); nfcStatusTextView.setVisibility(View.GONE); nfcStatusTextView.setText(null); } else { nfcStatusTextView.setText(R.string.error_turn_nfc_on); nfcStatusTextView.setVisibility(View.VISIBLE); nfcSettingsButton.setVisibility(View.VISIBLE); } } public void setNfcStateAvailable(boolean available) { writeTagButton.setEnabled(available); if (available) { nfcStatusTextView.setVisibility(View.GONE); nfcStatusTextView.setText(null); } else { nfcStatusTextView.setText(R.string.error_nfc_not_available); nfcStatusTextView.setVisibility(View.VISIBLE); } } } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. */ public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { if (position == 1) { return NfcFragment.newInstance(wifiNetwork); } else { return QrCodeFragment.newInstance(wifiNetwork); } } @Override public int getCount() { return 2; } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return getString(R.string.qrcode_fragment_tab_title); case 1: return getString(R.string.nfc_fragment_tab_title); } return null; } } }