/*
SonyCameraSettingFragment
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.sonycamera.activity;
import android.Manifest;
import android.app.AlertDialog;
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.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
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.os.ResultReceiver;
import android.provider.Settings;
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.IntentHandlerActivity;
import org.deviceconnect.android.activity.PermissionUtility;
import org.deviceconnect.android.deviceplugin.sonycamera.R;
import org.deviceconnect.android.deviceplugin.sonycamera.utils.SonyCameraUtil;
import org.deviceconnect.android.deviceplugin.sonycamera.utils.UserSettings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
/**
* Sony Camera 接続処理用フラグメント.
* @author NTT DOCOMO, INC.
*/
public class SonyCameraConnectingFragment extends SonyCameraBaseFragment {
/** ロガー. */
private final Logger mLogger = Logger.getLogger("sonycamera.dplugin");
/** インターバル. */
private static final int INTERVAL = 1000;
/** サービスIDを表示するためのView. */
private TextView mServiceIdView;
/** Wifi管理クラス. */
private WifiManager mWifiMgr;
/** 設定を保持するクラス. */
private UserSettings mSettings;
/** スレッド管理クラス. */
private ScheduledExecutorService mExecutorService = Executors.newSingleThreadScheduledExecutor();
/**
* WiFiスキャン実行中フラグ.
* <p>
* スキャン中の場合はtrue、それ以外はfalse
* </p>
*/
private boolean mScanFlag;
/** Wifiの状態通知を受け取るReceiver. */
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent == null) {
return;
}
String action = intent.getAction();
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (ni != null) {
NetworkInfo.State state = ni.getState();
int type = ni.getType();
if (state == NetworkInfo.State.CONNECTED
&& type == ConnectivityManager.TYPE_WIFI) {
WifiInfo wifiInfo = mWifiMgr.getConnectionInfo();
if (SonyCameraUtil.checkSSID(wifiInfo.getSSID())) {
mServiceIdView.setText(R.string.sonycamera_connect);
}
}
}
}
}
};
@Override
public View onCreateView(final LayoutInflater inflater,
final ViewGroup container, final Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_connecting_camera, container, false);
mSettings = new UserSettings(getActivity());
mWifiMgr = getWifiManager();
mServiceIdView = (TextView) view.findViewById(R.id.camera_id);
final Button searchBtn = (Button) view.findViewById(R.id.search_and_connect_button);
searchBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (SonyCameraUtil.checkSSID(mWifiMgr.getConnectionInfo().getSSID())) {
showErrorDialog(getString(R.string.sonycamera_already_connect));
mServiceIdView.setText(R.string.sonycamera_already_connect);
} else {
connectSonyCamera();
}
}
});
saveWiFiSSID();
return view;
}
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mReceiver);
}
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
getActivity().registerReceiver(mReceiver, filter);
}
private WifiManager getWifiManager() {
return (WifiManager) getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
}
/**
* 元々接続してあるWiFiのSSIDを保存します.
*/
private void saveWiFiSSID() {
String ssid = SonyCameraUtil.getSSID(getActivity());
if (ssid != null && !SonyCameraUtil.checkSSID(ssid)) {
mSettings.setSSID(ssid);
}
}
/**
* SonyCameraデバイスに接続を行います.
*/
private void connectSonyCamera() {
if (!mWifiMgr.isWifiEnabled()) {
confirmEnableWifi();
} else {
WifiInfo wifiInfo = mWifiMgr.getConnectionInfo();
mServiceIdView.setText(R.string.sonycamera_connecting);
if (SonyCameraUtil.checkSSID(wifiInfo.getSSID())) {
mServiceIdView.setText(R.string.sonycamera_already_connect);
} else {
searchSonyCameraWifi();
}
}
}
/**
* SonyCameraデバイスのWifiを探索してに接続を行います.
*/
private void searchSonyCameraWifi() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
getSonyCameraAPList();
} else {
checkLocationServiceEnabled();
}
}
/**
* WiFiスキャンを行うには位置情報のパーミッション許可が必要なので、確認を行う.
*/
private void checkLocationServiceEnabled() {
// WiFi scan in SDK 23 requires location service to be enabled.
final LocationManager manager = getContext().getSystemService(LocationManager.class);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
IntentHandlerActivity.startActivityForResult(getContext(),
new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
new ResultReceiver(new Handler(Looper.getMainLooper())) {
@Override
protected void onReceiveResult(int resultCode, final Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
checkLocationPermission();
} else {
showErrorDialog(getString(R.string.sonycamera_request_permission_error));
}
}
});
} else {
checkLocationPermission();
}
}
/**
* WiFiスキャンを行うには位置情報のパーミッション許可が必要なので、確認を行う.
*/
private void checkLocationPermission() {
// 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) {
getSonyCameraAPList();
} 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() {
getSonyCameraAPList();
}
@Override
public void onFail(@NonNull String deniedPermission) {
showErrorDialog(getString(R.string.sonycamera_request_permission_error));
}
});
}
}
}
/**
* SonyCameraリストを表示します.
*/
private synchronized void getSonyCameraAPList() {
if (mScanFlag) {
return;
}
mScanFlag = true;
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 (SonyCameraUtil.checkSSID(result.SSID)) {
scanList.add(result);
mLogger.fine("Found SonyCamera Wifi. SSID=" + result.SSID);
}
}
if (scanList.size() > 0) {
confirmConnectSonyCameraWifi(scanList);
} else {
showErrorDialog(getString(R.string.sonycamera_not_found_wifi));
}
synchronized (unregistered) {
if (!unregistered.get()) {
getContext().unregisterReceiver(this);
unregistered.set(true);
}
}
mScanFlag = false;
}
};
mExecutorService.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));
}
/**
* SonyCameraデバイスのWifiへの接続確認を行う.
*
* @param configs Wifiの設定一覧
*/
private void confirmConnectSonyCameraWifi(final List<ScanResult> configs) {
String[] wifiList = new String[configs.size()];
for (int i = 0; i < configs.size(); i++) {
wifiList[i] = configs.get(i).SSID;
wifiList[i] = wifiList[i].replace("\"", "");
}
final AtomicInteger pos = new AtomicInteger(0);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(R.string.sonycamera_confirm_wifi);
builder.setSingleChoiceItems(wifiList, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
pos.set(whichButton);
}
});
builder.setPositiveButton(R.string.sonycamera_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
connectWifi(configs.get(pos.get()));
}
});
builder.setNegativeButton(R.string.sonycamera_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
mServiceIdView.setText(R.string.sonycamera_no_device);
}
});
builder.setCancelable(true);
builder.show();
}
/**
* Wifi機能を入れる確認を行う.
*/
private void confirmEnableWifi() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(R.string.sonycamera_confirm_wifi);
builder.setMessage(R.string.sonycamera_confirm_wifi_enable);
builder.setPositiveButton(R.string.sonycamera_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
turnOnWifi();
}
});
builder.setNegativeButton(R.string.sonycamera_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
}
});
builder.setCancelable(true);
builder.show();
}
/**
* エラーダイアログを表示する.
*
* @param message エラーメッセージ
*/
private void showErrorDialog(final String message) {
showErrorDialog(getString(R.string.sonycamera_confirm_wifi), message);
}
/**
* エラーダイアログを表示する.
*
* @param message エラーメッセージ
*/
private void showErrorDialog(final String title, final String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(title);
builder.setMessage(message);
builder.setPositiveButton(R.string.sonycamera_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
}
});
builder.setCancelable(true);
builder.show();
mServiceIdView.setText(R.string.sonycamera_no_device);
}
/**
* パスワードを入力するダイアログを表示する.
*
* @param password パスワード
* @param 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);
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle(R.string.sonycamera_password_dialog);
builder.setView(editView).setPositiveButton(R.string.sonycamera_ok, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int whichButton) {
String password = "\"" + editView.getText() + "\"";
if (listener != null) {
listener.onInputPassword(password);
}
}
});
builder.setNegativeButton(R.string.sonycamera_cancel, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int whichButton) {
if (listener != null) {
listener.onCancel();
}
}
});
builder.show();
}
/**
* 指定されたSSIDのWiFiに接続を行う.
*
* @param result 接続先のSSID
*/
private void connectWifi(final ScanResult result) {
if (connectWifi2(result)) {
return;
}
final WifiConfiguration wc = new WifiConfiguration();
final String capabilities = result.capabilities;
final 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 = 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.sonycamera_no_device);
}
});
} else if (capabilities.contains("WEP")) {
String password = mSettings.getSSIDPassword(ssid);
showPasswordDialog(password, new PasswordListener() {
@Override
public void onInputPassword(final String password) {
wc.SSID = ssid;
int length = password.length();
if ((length == 10 || length == 26 || length == 58) && password.matches("[0-9A-Fa-f]*")) {
wc.wepKeys[0] = password;
} else {
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.sonycamera_no_device);
}
});
} else {
wc.SSID = ssid;
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
testConnectWifi(wc, null);
}
}
/**
* 指定されたネットワークが反映されているかをチェックして、接続を行う.
*
* @param networkId ネットワークID
* @param targetSSID 接続するSSID
* @return 接続に成功した場合はtrue、それ以外はfalse
*/
private boolean connectWifi(final int networkId, final String targetSSID) {
saveWiFiSSID();
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;
}
/**
* 指定されたWifiに接続確認を行う.
*
* @param wifiConfig wifi設定
* @param password パスワード
*/
private void testConnectWifi(final WifiConfiguration wifiConfig, final String password) {
final int networkId = mWifiMgr.addNetwork(wifiConfig);
if (networkId != -1) {
mWifiMgr.saveConfiguration();
mWifiMgr.updateNetwork(wifiConfig);
mExecutorService.schedule(new Runnable() {
@Override
public void run() {
// addNetworkしてから少し待たないと設定が反映されないので、少し待つ
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
return;
}
if (!connectWifi(networkId, wifiConfig.SSID)) {
connectSonyCamera();
} else {
if (password != null) {
mSettings.setSSIDPassword(wifiConfig.SSID, password);
}
}
}
}, INTERVAL, TimeUnit.MILLISECONDS);
} else {
showErrorDialog(getString(R.string.sonycamera_not_connected));
}
}
/**
* 既にWifiConfigurationにネットワーク情報が存在する場合には、既存の情報で接続する.
* @param result WiFiスキャン結果
* @return 接続処理が行われた場合にはtrue、それ以外の場合にはfalse
*/
private boolean connectWifi2(final ScanResult result) {
String ssid = '"' + result.SSID + '"';
List<WifiConfiguration> wifiConfigurations = mWifiMgr.getConfiguredNetworks();
for (WifiConfiguration configuration : wifiConfigurations) {
if (configuration.SSID.contains(ssid)) {
return connectWifi(configuration.networkId, configuration.SSID);
}
}
return false;
}
/**
* Wifiの機能を有効にする.
*/
private void turnOnWifi() {
mWifiMgr.setWifiEnabled(true);
}
/**
* パスワードの入力完了リスナー.
*/
private interface PasswordListener {
/**
* 入力されたパスワードを通知する.
*
* @param password 入力されたパスワード
*/
void onInputPassword(String password);
/**
* 入力がキャンセルされたことを通知する.
*/
void onCancel();
}
}