/*
ConfirmationFragment
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.theta.fragment;
import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.deviceconnect.android.activity.PermissionUtility;
import org.deviceconnect.android.deviceplugin.theta.R;
import org.deviceconnect.android.deviceplugin.theta.ThetaDeviceApplication;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDevice;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDeviceEventListener;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDeviceManager;
import org.deviceconnect.android.deviceplugin.theta.utils.UserSettings;
import org.deviceconnect.android.deviceplugin.theta.utils.WiFiUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
/**
* The page for confirmation of the connection between THETA and Android device.
*
* @author NTT DOCOMO, INC.
*/
public class ConfirmationFragment extends SettingsFragment implements ThetaDeviceEventListener {
/** Interval. */
private static final int INTERVAL = 1000;
/** View to display the service ID. */
private TextView mServiceIdView;
/** Wifi management class. */
private WifiManager mWifiMgr;
/** Class that holds the configuration. */
private UserSettings mSettings;
/** Search in dialog. */
private ThetaDialogFragment mDialog;
/** Logger. */
private final Logger mLogger = Logger.getLogger("theta.dplugin");
/** Wi-Fi State Receiver. */
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
String action = intent.getAction();
mLogger.info("ConfirmationFragment: action = " + action
+ ", isWaitingWiFiEnabled = " + mIsWaitingWifiEnabled);
if (!mIsWaitingWifiEnabled) {
return;
}
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
switch (state) {
case WifiManager.WIFI_STATE_ENABLED:
mIsWaitingWifiEnabled = false;
connectTheta();
break;
default:
break;
}
}
}
};
private boolean mIsWaitingWifiEnabled;
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_confirmation, null);
mServiceIdView = (TextView) rootView.findViewById(R.id.camera_search_message);
mSettings = new UserSettings(getActivity());
mWifiMgr = getWifiManager();
Button btnCameraSearch = (Button) rootView.findViewById(R.id.btn_camera_search);
btnCameraSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
String ssId = mWifiMgr.getConnectionInfo().getSSID();
mLogger.info("Current Wi-Fi SSID: " + ssId);
if (WiFiUtil.checkSSID(ssId)) {
mServiceIdView.setText(getThetaName());
} else {
connectTheta();
}
}
});
return rootView;
}
private WifiManager getWifiManager() {
return (WifiManager) getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
}
/* Get connected Theta device's info. */
private ThetaDevice getConnectedDevice() {
Activity activity = getActivity();
if (activity != null) {
ThetaDeviceApplication app = (ThetaDeviceApplication) activity.getApplication();
ThetaDeviceManager deviceManager = app.getDeviceManager();
return deviceManager.getConnectedDevice();
} else {
return null;
}
}
/* Get Theta Device's Name. */
private String getThetaName() {
ThetaDevice device = getConnectedDevice();
String message;
if (device != null) {
message = getString(R.string.camera_search_message_found);
message = message.replace("$NAME$", device.getName());
} else {
message = getString(R.string.camera_search_message_not_found);
}
return message;
}
@Override
public void onPause() {
super.onPause();
Activity activity = getActivity();
if (activity != null) {
ThetaDeviceApplication app = (ThetaDeviceApplication) activity.getApplication();
ThetaDeviceManager deviceManager = app.getDeviceManager();
deviceManager.unregisterDeviceEventListener(this);
activity.unregisterReceiver(mReceiver);
}
}
@Override
public void onResume() {
super.onResume();
Activity activity = getActivity();
if (activity != null) {
ThetaDeviceApplication app = (ThetaDeviceApplication) activity.getApplication();
ThetaDeviceManager deviceManager = app.getDeviceManager();
deviceManager.registerDeviceEventListener(this);
activity.registerReceiver(mReceiver,
new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
}
}
/**
* Explore the WiFi that exist around.
* <p>
* Search results, we want to display to WiFiDeviceListFragment.
* </p>
*/
private void searchTheta() {
WifiInfo info = mWifiMgr.getConnectionInfo();
if (info != null && WiFiUtil.checkSSID(info.getSSID())) {
mServiceIdView.setText(getThetaName());
} else {
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
}
/**
* Connection to the Theta device.
*/
private void connectTheta() {
if (!mWifiMgr.isWifiEnabled()) {
ThetaDialogFragment.showConfirmAlert(getActivity(),
getString(R.string.theta_confirm_wifi),
getString(R.string.theta_confirm_wifi_enable),
getString(R.string.ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
turnOnWifi();
}
});
} else {
WifiInfo wifiInfo = mWifiMgr.getConnectionInfo();
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
mServiceIdView.setText(R.string.theta_connecting);
}
});
}
if (WiFiUtil.checkSSID(wifiInfo.getSSID())) {
searchTheta();
} else {
searchThetaWifi();
}
}
}
/**
* Search to connect the Wifi of Theta device.
*/
private void searchThetaWifi() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
getThetaAPList();
} else {
checkLocationServiceEnabled();
}
}
/** Check Location Service Permission. */
private void checkLocationServiceEnabled() {
checkPermission();
}
/** Check Permission. */
private void checkPermission() {
// WiFi scan requires location permissions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (getContext().checkSelfPermission(
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& getContext().checkSelfPermission(
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
getThetaAPList();
} else {
PermissionUtility.requestPermissions(getContext(), new Handler(Looper.getMainLooper()),
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION},
new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
getThetaAPList();
}
@Override
public void onFail(@NonNull String deniedPermission) {
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_confirm_wifi),
getString(R.string.theta_error_request_permission), null);
}
});
}
}
}
/** Get Theta AP List. */
private void getThetaAPList() {
final List<ScanResult> scanList = new ArrayList<ScanResult>();
mWifiMgr.startScan();
final AtomicBoolean unregistered = new AtomicBoolean(false);
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> results = mWifiMgr.getScanResults();
for (ScanResult result : results) {
if (WiFiUtil.checkSSID(result.SSID)) {
scanList.add(result);
}
}
if (scanList.size() > 0) {
String[] wifiList = new String[scanList.size()];
for (int i = 0; i < scanList.size(); i++) {
wifiList[i] = scanList.get(i).SSID;
wifiList[i] = wifiList[i].replace("\"", "");
}
final int[] pos = new int[1];
ThetaDialogFragment.showSelectWifiDialog(getActivity(), getString(R.string.theta_confirm_wifi),
wifiList,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
pos[0] = whichButton;
}
},
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
ScanResult result = scanList.get(pos[0]);
List<WifiConfiguration> list = mWifiMgr.getConfiguredNetworks();
mLogger.info("Selected Wi-Fi: SSID = " + result.SSID);
mLogger.info("Configured Networks: size = " + list.size());
boolean isEnabled = false;
for (WifiConfiguration i : list) {
mLogger.info("Found Wi-Fi SSID = " + i.SSID);
if (i.SSID != null && i.SSID.indexOf(result.SSID) > 0) {
isEnabled = mWifiMgr.enableNetwork(i.networkId, true);
break;
}
}
if (!isEnabled) {
mLogger.info("Need to create new Wi-Fi configuration: SSID = " + result.SSID);
connectWifi(result);
} else {
mLogger.info("Enable other network: isEnabled = " + isEnabled + ", SSID = " + result.SSID);
showConnectionProgress();
}
}
},
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
mServiceIdView.setText(R.string.theta_no_device);
}
});
} else {
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_confirm_wifi),
getString(R.string.camera_search_message_not_found), null);
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
synchronized (unregistered) {
if (!unregistered.get()) {
getContext().unregisterReceiver(this);
unregistered.set(true);
}
}
}
};
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
@Override
public void run() {
synchronized (unregistered) {
if (!unregistered.get()) {
getContext().unregisterReceiver(receiver);
unregistered.set(true);
}
}
}
}, 30, TimeUnit.SECONDS);
getContext().registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
/**
* Dialog to enter a password.
*
* @param password password
* @param listener listener
*/
private void showPasswordDialog(final String password, final PasswordListener listener) {
final EditText editView = new EditText(getActivity());
if (password != null) {
String ps = password.replace("\"", "");
editView.setText(ps);
}
ThetaDialogFragment.showPasswordDialog(getActivity(), editView,
new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int whichButton) {
String password = "\"" + editView.getText() + "\"";
if (listener != null) {
listener.onInputPassword(password);
}
}
},
new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int whichButton) {
if (listener != null) {
listener.onCancel();
}
}
}
);
}
/**
* Connect to WiFi the specified SSID.
*
* @param result Destination SSID
*/
private void connectWifi(final ScanResult result) {
final WifiConfiguration wc = new WifiConfiguration();
String capabilities = result.capabilities;
String ssid = "\"" + result.SSID + "\"";
if (capabilities.contains("WPA")) {
String password = mSettings.getSSIDPassword(ssid);
showPasswordDialog(password, new PasswordListener() {
@Override
public void onInputPassword(final String password) {
wc.SSID = "\"" + result.SSID + "\"";
wc.preSharedKey = password;
wc.hiddenSSID = true;
wc.status = WifiConfiguration.Status.ENABLED;
wc.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
testConnectWifi(wc, password);
}
@Override
public void onCancel() {
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
});
} else if (capabilities.contains("WEP")) {
String password = mSettings.getSSIDPassword(ssid);
showPasswordDialog(password, new PasswordListener() {
@Override
public void onInputPassword(final String password) {
wc.SSID = "\"" + result.SSID + "\"";
wc.wepKeys[0] = password;
wc.wepTxKeyIndex = 0;
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
testConnectWifi(wc, password);
}
@Override
public void onCancel() {
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
});
} else {
wc.SSID = "\"" + result.SSID + "\"";
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
testConnectWifi(wc, null);
}
}
/**
* Check whether the specified network are reflected, to make a connection.
*
* @param networkId Network ID
* @param targetSSID Connect SSID
* @return True if the connection is successful, otherwise false
*/
private boolean connectWifi(final int networkId, final String targetSSID) {
String ssid = targetSSID.replace("\"", "");
mWifiMgr.startScan();
for (ScanResult result : mWifiMgr.getScanResults()) {
if (result.SSID.replace("\"", "").equals(ssid)) {
WifiInfo info = mWifiMgr.getConnectionInfo();
if (info != null) {
mWifiMgr.disableNetwork(info.getNetworkId());
}
return mWifiMgr.enableNetwork(networkId, true);
}
}
return false;
}
/**
* Connection confirmation to the specified Wifi.
*
* @param wifiConfig wifi settings
* @param password pasword
*/
private void testConnectWifi(final WifiConfiguration wifiConfig, final String password) {
showConnectionProgress();
final int networkId = mWifiMgr.addNetwork(wifiConfig);
mLogger.info("addNetwork: networkId = " + networkId);
if (networkId != -1) {
mWifiMgr.saveConfiguration();
mWifiMgr.updateNetwork(wifiConfig);
new Thread(new Runnable() {
@Override
public void run() {
// Since the addNetwork and does not wait a little from the set is not reflected, to wait a little
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
return;
}
if (!connectWifi(networkId, wifiConfig.SSID)) {
connectTheta();
} else {
if (password != null) {
mSettings.setSSIDPassword(wifiConfig.SSID, password);
}
}
}
}).start();
} else {
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_confirm_wifi),
getString(R.string.camera_search_message_not_found), null);
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
}
private void showConnectionProgress() {
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mDialog == null) {
mDialog = ThetaDialogFragment.newInstance(getString(R.string.theta_ssid_prefix), getString(R.string.connecting));
mDialog.show(getActivity().getFragmentManager(),
"fragment_dialog");
}
new Handler().postDelayed(new Runnable() { //Timeout Handler
@Override
public void run() {
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_confirm_wifi),
getString(R.string.theta_error_wrong_password), null);
mServiceIdView.setText(R.string.camera_search_message_not_found);
}
}
}, 30000);
}
});
}
}
/**
* Enable the Wifi function.
*/
private void turnOnWifi() {
mIsWaitingWifiEnabled = true;
mWifiMgr.setWifiEnabled(true);
}
@Override
public void onConnected(final ThetaDevice device) {
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
mServiceIdView.setText(getThetaName());
}
});
}
}
@Override
public void onDisconnected(final ThetaDevice device) {
this.onConnected(device);
}
/**
* Enter Password completion listener.
*/
private interface PasswordListener {
/**
* Notify the entered password.
*
* @param password Entered password
*/
void onInputPassword(final String password);
/**
* Notify the input has been canceled.
*/
void onCancel();
}
}