/* Copyright (c) 2013 The MITRE Corporation, All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.mitre.svmp.activities; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.*; import org.mitre.svmp.auth.module.PasswordModule; import org.mitre.svmp.common.ConnectionInfo; import org.mitre.svmp.common.Utility; import org.mitre.svmp.services.SessionService; import org.mitre.svmp.client.R; import org.mitre.svmp.widgets.ConnectionInfoArrayAdapter; import java.util.List; /** * @author Joe Portner & David Schoenheit */ public class ConnectionList extends SvmpActivity { private static String TAG = ConnectionList.class.getName(); private static final int REQUEST_CONNECTIONDETAILS = 100; private static final int REQUEST_CONNECTIONAPPLIST = 101; private static final int REQUEST_STARTVIDEO = 102; // opens AppRTCVideoActivity private static final int REQUEST_CHANGEPASSWORD = 103; // opens AppRTCChangePasswordActivity private List<ConnectionInfo> connectionInfoList; private ListView listView; private BroadcastReceiver receiver; private int sendRequestCode = REQUEST_STARTVIDEO; // used in the "afterStartAppRTC" method to determine what activity gets started public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.connection_list); // title has to be set here instead of in Manifest, for compatibility with shortcuts setTitle(R.string.connectionList_title); // enable long-click on the ListView registerForContextMenu(listView); // register a BroadcastReceiver that will allow for triggered layout refreshes // when a running SessionService stops, it sends this broadcast to notify the ConnectionList to update its // entries (i.e. revert "running" entry green text back to gray text) IntentFilter filter = new IntentFilter(ACTION_REFRESH); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { populateLayout(); } }; registerReceiver(receiver, filter, PERMISSION_REFRESH, null); // if we received an intent indicating which connection to open, act upon it Intent intent = getIntent(); if (intent.hasExtra("connectionID")) { int id = intent.getIntExtra("connectionID", 0); ConnectionInfo connectionInfo = dbHandler.getConnectionInfo(id); if (connectionInfo != null) { // if we allow use of desktop mode, start the connection; otherwise take us to this connection's app list if (Utility.getPrefBool(this, R.string.preferenceKey_connection_useDesktopMode, R.string.preferenceValue_connection_useDesktopMode)) { this.sendRequestCode = REQUEST_STARTVIDEO; authPrompt(connectionInfo); } else { startConnectionAppList(connectionInfo); } } } } @Override public void onDestroy() { super.onDestroy(); // unregister BroadcastReceiver unregisterReceiver(receiver); } // called onResume so preference changes take effect in the layout @Override protected void refreshPreferences() { } // queries the database for the list of connections and populates the ListView in the layout @Override protected void populateLayout() { listView = (ListView)findViewById(R.id.connectionList_listView_connections); // get the list of ConnectionInfo objects connectionInfoList = dbHandler.getConnectionInfoList(); // populate the items in the ListView //listView.setAdapter(new ArrayAdapter<ConnectionInfo>(getApplicationContext(), android.R.layout.simple_list_item_1, connectionInfoList)); listView.setAdapter(new ConnectionInfoArrayAdapter(this, connectionInfoList.toArray(new ConnectionInfo[connectionInfoList.size()]))); // use two-line list items } public void onClick_Item(View view) { try { int position = listView.getPositionForView(view); ConnectionInfo connectionInfo = (ConnectionInfo)listView.getItemAtPosition(position); // if we allow use of desktop mode, start the connection; otherwise take us to this connection's app list if (Utility.getPrefBool(this, R.string.preferenceKey_connection_useDesktopMode, R.string.preferenceValue_connection_useDesktopMode)) { this.sendRequestCode = REQUEST_STARTVIDEO; authPrompt(connectionInfo); } else { startConnectionAppList(connectionInfo); } } catch( Exception e ) { // don't care } } public void onClick_Apps(View view) { try { View item = (View)view.getParent(); int position = listView.getPositionForView(item); ConnectionInfo connectionInfo = (ConnectionInfo)listView.getItemAtPosition(position); startConnectionAppList(connectionInfo); } catch( Exception e ) { // don't care } } // new button is clicked public void onClick_New(View v) { // start a ConnectionDetails activity for creating a new connection startConnectionDetails(0); } // exit button is clicked public void onClick_Exit(View v) { finish(); } // Context Menu handles long-pressing (prompt allows user to edit or remove connections) @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { if(v.getId() == R.id.connectionList_listView_connections){ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; ConnectionInfo connectionInfo = connectionInfoList.get(info.position); menu.setHeaderTitle(connectionInfo.getDescription()); // if our preferences are set to allow use of "Desktop mode", enable that context button if (Utility.getPrefBool(this, R.string.preferenceKey_connection_useDesktopMode, R.string.preferenceValue_connection_useDesktopMode)) menu.add(Menu.NONE, 0, 0, R.string.connectionList_context_connectToDesktop_text); menu.add(Menu.NONE, 1, 1, R.string.connectionList_context_editConnection_text); menu.add(Menu.NONE, 2, 2, R.string.connectionList_context_removeConnection_text); // if this uses password authentication, add an option to change the password if ((connectionInfo.getAuthType() & PasswordModule.AUTH_MODULE_ID) == PasswordModule.AUTH_MODULE_ID) menu.add(Menu.NONE, 99, 99, R.string.connectionList_context_changePassword_text); // if this connection is running, display the context option to close it if (SessionService.isRunningForConn(connectionInfo.getConnectionID())) menu.add(Menu.NONE, 100, 100, R.string.connectionList_context_stop_text); } } @Override public boolean onContextItemSelected(MenuItem item){ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); ConnectionInfo connectionInfo = connectionInfoList.get(info.position); switch(item.getItemId()){ case 0: // Connect this.sendRequestCode = REQUEST_STARTVIDEO; authPrompt(connectionInfo); break; case 1: // Edit connection // start a ConnectionDetails activity for editing an existing connection startConnectionDetails( connectionInfo.getConnectionID() ); break; case 2: // Remove connection // find the connectionID; if that connection is running, stop the session service int connectionID = connectionInfo.getConnectionID(); if (SessionService.isRunningForConn(connectionID)) stopService(new Intent(ConnectionList.this, SessionService.class)); // delete the connection info and repopulate the layout to reflect changes dbHandler.deleteConnectionInfo(connectionID); populateLayout(); toastLong(R.string.connectionList_toast_removed); break; case 99: // Change password this.sendRequestCode = REQUEST_CHANGEPASSWORD; passwordChangePrompt(connectionInfo); break; case 100: // Stop stopService(new Intent(ConnectionList.this, SessionService.class)); // stop the service that's running break; } return true; } // starts a ConnectionDetails activity for editing an existing connection or creating a new connection private void startConnectionDetails(int id) { // create explicit intent Intent intent = new Intent(ConnectionList.this, ConnectionDetails.class); // if the given ID is valid (i.e. greater than zero), we are editing an existing connection if( id > 0 ) intent.putExtra("id", id); // start the activity and expect a result intent when it is finished startActivityForResult(intent, REQUEST_CONNECTIONDETAILS); } private void startConnectionAppList(ConnectionInfo connectionInfo) { // create explicit intent Intent intent = new Intent(ConnectionList.this, AppList.class); intent.putExtra("connectionID", connectionInfo.getConnectionID()); intent.putExtra("description", connectionInfo.getDescription()); // start the activity and expect a result intent when it is finished startActivityForResult(intent, REQUEST_CONNECTIONAPPLIST); } @Override protected void afterStartAppRTC(ConnectionInfo connectionInfo) { // after we have handled the auth prompt and made sure the service is started... // create explicit intent Intent intent = new Intent(); if (sendRequestCode == REQUEST_STARTVIDEO) { intent.setClass(ConnectionList.this, AppRTCVideoActivity.class); } else if (sendRequestCode == REQUEST_CHANGEPASSWORD) { intent.setClass(ConnectionList.this, AppRTCChangePasswordActivity.class); } intent.putExtra("connectionID", connectionInfo.getConnectionID()); // start the AppRTCActivity startActivityForResult(intent, sendRequestCode); } // activity returns @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { busy = false; if (requestCode == REQUEST_CHANGEPASSWORD && resultCode == RESULT_OK) { toastShort(R.string.connectionList_toast_passwordChange_success); } else // fall back to superclass method super.onActivityResult(requestCode, resultCode, data); } }