package io.evercam.connect; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import net.sbbi.upnp.messages.UPNPResponseException; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Random; import io.evercam.connect.db.Camera; import io.evercam.connect.db.CameraOperation; import io.evercam.connect.helper.Constants; import io.evercam.connect.net.NetInfo; import io.evercam.network.discovery.GatewayDevice; import io.evercam.network.discovery.NatMapEntry; public class UpnpForwardingTab extends Fragment { private final String TAG = "UpnpForwardingTab"; private Handler handler = new Handler(); private CheckBox useUpnpCheckbox; private TextView isAvaliableTxt; private ImageView faceImg; private TextView httpTxt; private TextView rtspTxt; private TextView bottomLabel; private TextView httpLabel; private TextView rtspLabel; private TextView natList; private LinearLayout useUpnpLayout; private LinearLayout manualUpnpLayout; private Button saveBtn; private Button addBtn; private Button removeBtn; private TextView helpMsgTxt; private RadioGroup autoOrManuRadioGroup; private GatewayDevice gatewayDevice = null; private String cameraIP; private String ssid; private Camera camera; private CameraOperation cameraOperation; private NetInfo netInfo; private boolean httpMapped; private boolean rtspMapped; private ArrayList<NatMapEntry> forwardedList; private ArrayList<String> spinnerList = new ArrayList<String>(); private Spinner spinnerRemovePort; private ArrayAdapter<String> removePortAdapter; private TextView cameraip; private EditText internalPortEdit; private EditText externalPortEdit; private EditText descriptionEdit; private RadioButton udpRadioButton; private UPNPTask upnpTask; private ProgressBar processAnimate; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.tab_one, container, false); cameraIP = getActivity().getIntent().getExtras().get("IP").toString(); ssid = getActivity().getIntent().getExtras().get("SSID").toString(); cameraOperation = new CameraOperation(getActivity().getApplicationContext()); camera = cameraOperation.getCamera(cameraIP, ssid); // set up page isAvaliableTxt = (TextView) view.findViewById(R.id.isAvailiable_txt); faceImg = (ImageView) view.findViewById(R.id.face_img); isAvaliableTxt.setText(R.string.searchingRouter); helpMsgTxt = (TextView) view.findViewById(R.id.helpMsg); useUpnpCheckbox = (CheckBox) view.findViewById(R.id.use_upnp_checkbox); useUpnpLayout = (LinearLayout) view.findViewById(R.id.use_upnp_layout); autoOrManuRadioGroup = (RadioGroup) view.findViewById(R.id.radioGroup_autoOrManu); final RadioButton autoRadioBtn = (RadioButton) view.findViewById(R.id.radio_auto); final RadioButton manuallyRadioBtn = (RadioButton) view.findViewById(R.id.radio_manually); httpTxt = (TextView) view.findViewById(R.id.externalHTTP_value); rtspTxt = (TextView) view.findViewById(R.id.externalRTSP_value); bottomLabel = (TextView) view.findViewById(R.id.buttomLabel); saveBtn = (Button) view.findViewById(R.id.saveForwarding_button); httpLabel = (TextView) view.findViewById(R.id.externalHTTP_Label); rtspLabel = (TextView) view.findViewById(R.id.externalRTSP_Label); processAnimate = (ProgressBar) view.findViewById(R.id.processBarUPNP); // manually page elements manualUpnpLayout = (LinearLayout) view.findViewById(R.id.manual_upnp_layout); natList = (TextView) view.findViewById(R.id.nat_table_txt); addBtn = (Button) view.findViewById(R.id.add_button); removeBtn = (Button) view.findViewById(R.id.remove_button); // listener for use upnp or not useUpnpCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) { if(autoRadioBtn.isChecked()) { autoOrManuRadioGroup.setVisibility(View.VISIBLE); useUpnpLayout.setVisibility(View.VISIBLE); } else if(manuallyRadioBtn.isChecked()) { autoOrManuRadioGroup.setVisibility(View.VISIBLE); manualUpnpLayout.setVisibility(View.VISIBLE); } } else { useUpnpLayout.setVisibility(View.GONE); autoOrManuRadioGroup.setVisibility(View.GONE); manualUpnpLayout.setVisibility(View.GONE); } } }); // listener for manually or automatically autoOrManuRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if(checkedId == autoRadioBtn.getId()) { manualUpnpLayout.setVisibility(View.GONE); useUpnpLayout.setVisibility(View.VISIBLE); httpTxt.setText(""); rtspTxt.setText(""); bottomLabel.setText(""); httpLabel.setText(""); rtspLabel.setText(""); launchUpdateForward(); } else { manualUpnpLayout.setVisibility(View.VISIBLE); useUpnpLayout.setVisibility(View.GONE); natList.setText(R.string.loadingMsg); handler.postDelayed(new Runnable() { @Override public void run() { updateNATListString(); } }, 1000); } } }); // clicking forward button saveBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { saveBtn.setVisibility(View.GONE); bottomLabel.setText(R.string.pleaseWait); bottomLabel.setVisibility(View.VISIBLE); try { if(camera.getHttp() != 0) { httpMapped = gatewayDevice.getIGD().addPortMapping(Constants .UPNP_HTTP_DESCRIPTION, null, camera.getHttp(), Integer.parseInt (httpTxt.getText().toString()), cameraIP, 0, Constants .PROTOCOL_TCP); } if(camera.getRtsp() != 0) { rtspMapped = gatewayDevice.getIGD().addPortMapping(Constants .UPNP_RTSP_DESCRIPTION, null, camera.getRtsp(), Integer.parseInt (rtspTxt.getText().toString()), cameraIP, 0, Constants .PROTOCOL_TCP); } // what if only one of them is successful? if(httpMapped || rtspMapped) { handler.postDelayed(new Runnable() { @Override public void run() { try { gatewayDevice = new GatewayDevice(netInfo.getGatewayIp()); } catch(Exception e) { Log.e(TAG, "Save Auto Forward" + e.toString()); } launchUpdateForward(); } }, 1000); } else { return; } } catch(NumberFormatException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } catch(UPNPResponseException e) { e.printStackTrace(); } } }); // clicking add button addBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showAddPortDialog(); } }); // clicking remove button removeBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showRemovePortDialog(); } }); return view; } @Override public void onStart() { super.onStart(); netInfo = new NetInfo(getActivity().getApplicationContext()); upnpTask = new UPNPTask(); // Check has internal ports or not. if(camera.hasInternalPorts()) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { upnpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { upnpTask.execute(); } } else { isAvaliableTxt.setText(R.string.noInternalPort); faceImg.setVisibility(View.VISIBLE); faceImg.setImageResource(R.drawable.not_smile); helpMsgTxt.setVisibility(View.VISIBLE); helpMsgTxt.setText(R.string.helpNoInternalPort); } } @Override public void onStop() { super.onStop(); upnpTask.cancel(true); } // get a random number in [8000, 65535] private int getRandomPortNumber() { Random random = new Random(); int randomPort = random.nextInt(65535) % (65535 - 8000 + 1) + 8000; return randomPort; } private void launchUpdateForward() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { forwardedList = gatewayDevice.getMatchedEntries(cameraIP); return null; } @Override protected void onPostExecute(Void result) { updateForwardPage(forwardedList); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void updateForwardPage(ArrayList<NatMapEntry> forwardedList) { // get corresponding forwarded ports if(gatewayDevice != null) { if(forwardedList.iterator().hasNext()) { for(int i = 0; i < forwardedList.size(); i++) { String internalPort = String.valueOf(forwardedList.get(i).getInternalPort()); String externalPort = String.valueOf(forwardedList.get(i).getExternalPort()); if(camera.getHttp() != 0 && internalPort.equals(String.valueOf(camera.getHttp ()))) { // HTTP already forwarded, show forwarded port httpTxt.setText(externalPort); httpLabel.setVisibility(View.VISIBLE); httpLabel.setText(R.string.forwardedBracket); cameraOperation.updateAttributeInt(cameraIP, ssid, "exthttp", Integer .parseInt(externalPort)); } else if(camera.getHttp() == 0) { httpTxt.setText(this.getResources().getString(R.string.notAvaliable)); httpLabel.setVisibility(View.GONE); } if(camera.getRtsp() != 0 && internalPort.equals(String.valueOf(camera.getRtsp ()))) { rtspTxt.setText(externalPort); rtspLabel.setVisibility(View.VISIBLE); rtspLabel.setText(R.string.forwardedBracket); cameraOperation.updateAttributeInt(cameraIP, ssid, "extrtsp", Integer .parseInt(externalPort)); } else if(camera.getRtsp() == 0) { rtspTxt.setText(this.getResources().getString(R.string.notAvaliable)); rtspLabel.setVisibility(View.GONE); } } } else { // nothing matches, update database to set to 0. cameraOperation.updateAttributeInt(cameraIP, ssid, "exthttp", 0); cameraOperation.updateAttributeInt(cameraIP, ssid, "extrtsp", 0); } // show msg if(httpTxt.getText().length() != 0 && rtspTxt.getText().length() != 0) { bottomLabel.setVisibility(View.VISIBLE); bottomLabel.setText(R.string.msg_isForwarded); } // get random http if(httpTxt.getText().length() == 0) { httpTxt.setText(String.valueOf(getRandomPortNumber())); saveBtn.setVisibility(View.VISIBLE); httpLabel.setVisibility(View.VISIBLE); httpLabel.setText(R.string.readyBracket); } // get random rtsp if(rtspTxt.getText().length() == 0) { rtspTxt.setText(String.valueOf(getRandomPortNumber())); saveBtn.setVisibility(View.VISIBLE); rtspLabel.setVisibility(View.VISIBLE); rtspLabel.setText(R.string.readyBracket); } } } private void updateNATListString() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { if(gatewayDevice != null) { forwardedList = gatewayDevice.getMatchedEntries(cameraIP); } return null; } @Override protected void onPostExecute(Void result) { String cameraNATListStr = "Camera: " + cameraIP + " forwarded list:" + "\n"; if(gatewayDevice != null) { if(forwardedList.iterator().hasNext()) { removeBtn.setVisibility(View.VISIBLE); for(int i = 0; i < forwardedList.size(); i++) { String number = (i + 1) + ""; String description = forwardedList.get(i).getDescription(); String internalPort = String.valueOf(forwardedList.get(i) .getInternalPort()); String externalPort = String.valueOf(forwardedList.get(i) .getExternalPort()); String protocol = forwardedList.get(i).getProtocal(); String thisEntry = number + "." + "\n" + "Description: " + description + "\n" + "Internal Port: " + internalPort + "\n" + "External Port: " + externalPort + "\n" + "Protocol: " + protocol + "\n"; cameraNATListStr += thisEntry; } } else { cameraNATListStr = "Camera: " + cameraIP + " has no ports mapped."; removeBtn.setVisibility(View.GONE); } } // if igd is null else { cameraNATListStr = "Camera: " + cameraIP + " has no ports mapped."; } natList.setText(cameraNATListStr); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void showAddPortDialog() { LayoutInflater mInflater = LayoutInflater.from(getActivity().getApplicationContext()); final View forwardView = mInflater.inflate(R.layout.forward_dialog, null); final AlertDialog.Builder forwardBuilder = new AlertDialog.Builder(this.getActivity()); forwardBuilder.setView(forwardView); // show camera IP cameraip = (TextView) forwardView.findViewById(R.id.cameraip_value); cameraip.setText(cameraIP); // get user's input internalPortEdit = (EditText) forwardView.findViewById(R.id.internal_edit); externalPortEdit = (EditText) forwardView.findViewById(R.id.external_edit); descriptionEdit = (EditText) forwardView.findViewById(R.id.portDiscription_edit); udpRadioButton = (RadioButton) forwardView.findViewById(R.id.radio_udp); forwardBuilder.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final String internalPortStr = internalPortEdit.getText().toString(); final String externalPortStr = externalPortEdit.getText().toString(); final String descriptionStr = descriptionEdit.getText().toString(); final String protocolStr; if(udpRadioButton.isChecked()) { protocolStr = Constants.PROTOCOL_UDP; } else { protocolStr = Constants.PROTOCOL_TCP; } if(!(internalPortStr.length() == 0) && !(externalPortStr.length() == 0) && ! (descriptionStr.length() == 0)) { try { Field field = dialog.getClass().getSuperclass().getDeclaredField ("mShowing"); field.setAccessible(true); field.set(dialog, true); } catch(Exception e) { e.printStackTrace(); } final int internalPortInt; final int externalPortInt; try { internalPortInt = Integer.parseInt(internalPortStr); externalPortInt = Integer.parseInt(externalPortStr); } catch(NumberFormatException e) { Toast toast = Toast.makeText(getActivity().getApplicationContext(), R .string.portRangeMsg, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return; } new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { boolean portAdded = false; try { portAdded = gatewayDevice.getIGD().addPortMapping(descriptionStr, null, internalPortInt, externalPortInt, cameraIP, 0, protocolStr); } catch(IOException e) { e.printStackTrace(); } catch(UPNPResponseException e) { e.printStackTrace(); } catch(Exception e) { Log.e(TAG, "Add port forward" + e.toString()); } if(portAdded) { publishProgress(); try { gatewayDevice = new GatewayDevice(netInfo.getGatewayIp()); return true; } catch(Exception e) { Log.e(TAG, "After add port forward" + e.toString()); } } return false; } @Override protected void onPostExecute(Boolean success) { if(success) { updateNATListString(); } else { Toast toast = Toast.makeText(getActivity().getApplicationContext (), R.string.portForwardFailed, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } } @Override protected void onProgressUpdate(Void... values) { natList.setText(R.string.manualForwardSuccessMsg); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } // If form not been complete else { Toast toast = Toast.makeText(getActivity().getApplicationContext(), R.string.fillInAllMsg, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); try { Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing"); field.setAccessible(true); field.set(dialog, false); } catch(Exception e) { e.printStackTrace(); } } } }); forwardBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { try { Field fieldAdd = dialog.getClass().getSuperclass().getDeclaredField("mShowing"); fieldAdd.setAccessible(true); fieldAdd.set(dialog, true); } catch(Exception e) { e.printStackTrace(); } } }); forwardBuilder.setTitle(R.string.addPortForward); forwardBuilder.setCancelable(false); forwardBuilder.show(); } private void showRemovePortDialog() { LayoutInflater mInflater = LayoutInflater.from(getActivity().getApplicationContext()); final View removeView = mInflater.inflate(R.layout.remove_layout, null); final AlertDialog.Builder removeBuilder = new AlertDialog.Builder(this.getActivity()); removeBuilder.setView(removeView); // spinner spinnerList = getSpinnerList(); spinnerRemovePort = (Spinner) removeView.findViewById(R.id.removePort_spinner); removePortAdapter = new ArrayAdapter<String>(getActivity().getApplicationContext(), R.layout.spinner_item, spinnerList); spinnerRemovePort.setAdapter(removePortAdapter); removeBuilder.setPositiveButton(R.string.remove, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { String[] selectedValues = spinnerRemovePort.getSelectedItem().toString().split(" - "); String extPort = selectedValues[0]; String protocol = selectedValues[1]; boolean unmapped = false; try { unmapped = gatewayDevice.getIGD().deletePortMapping(null, Integer.parseInt(extPort), protocol); } catch(NumberFormatException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } catch(UPNPResponseException e) { e.printStackTrace(); } if(unmapped) { this.publishProgress(); try { gatewayDevice = new GatewayDevice(netInfo.getGatewayIp()); return true; } catch(Exception e) { Log.e(TAG, "After remove port forward" + e.toString()); } } return false; } @Override protected void onPostExecute(Boolean success) { if(success) { updateNATListString(); } else { Toast toast = Toast.makeText(getActivity().getApplicationContext(), R.string.deleteForwardFailed, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } } @Override protected void onProgressUpdate(Void... values) { natList.setText(R.string.manualDeleteSuccessMsg); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } }); removeBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { return; } }); removeBuilder.setTitle(R.string.removePortForward); removeBuilder.setCancelable(false); removeBuilder.show(); } // get list display in spinner for removing private ArrayList<String> getSpinnerList() { spinnerList.clear(); for(int i = 0; i < forwardedList.size(); i++) { spinnerList.add(forwardedList.get(i).getExternalPort() + " - " + forwardedList.get(i).getProtocal() + " - " + forwardedList.get(i).getDescription()); } return spinnerList; } private class UPNPTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { processAnimate.setVisibility(View.VISIBLE); } @Override protected Void doInBackground(Void... params) { gatewayDevice = null; try { gatewayDevice = new GatewayDevice(netInfo.getGatewayIp()); } catch(Exception e) { Log.e(TAG, "UPnPTask" + e.toString()); } return null; } @Override protected void onPostExecute(Void result) { processAnimate.setVisibility(View.GONE); if(gatewayDevice.isUPnPAvaliable()) { isAvaliableTxt.setText(R.string.routerAvaliable); faceImg.setVisibility(View.VISIBLE); faceImg.setImageResource(R.drawable.smile); useUpnpCheckbox.setVisibility(View.VISIBLE); } else { isAvaliableTxt.setText(R.string.routerNotAvaliable); faceImg.setVisibility(View.VISIBLE); faceImg.setImageResource(R.drawable.not_smile); helpMsgTxt.setVisibility(View.VISIBLE); helpMsgTxt.setText(R.string.helpNotAvailiable); } launchUpdateForward(); } } }