/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2015, Telestax Inc and individual contributors * by the @authors tag. * * This program is free software: you can redistribute it and/or modify * under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> * * For questions related to commercial use licensing, please contact sales@telestax.com. * */ package org.restcomm.android.olympus; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.IBinder; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.ImageButton; import android.widget.Toast; import org.restcomm.android.sdk.RCClient; import org.restcomm.android.sdk.RCConnection; import org.restcomm.android.sdk.RCDevice; import org.restcomm.android.sdk.RCDeviceListener; import org.restcomm.android.sdk.RCPresenceEvent; import org.restcomm.android.sdk.util.RCException; import java.util.HashMap; public class MessageActivity extends AppCompatActivity implements MessageFragment.Callbacks, RCDeviceListener, View.OnClickListener, ServiceConnection { private RCDevice device; boolean serviceBound = false; HashMap<String, Object> params = new HashMap<String, Object>(); // keep around for each jobId that creates a message the index it gets inside the ListView //HashMap<String, Integer> indexes = new HashMap<String, Integer>(); private static final String TAG = "MessageActivity"; private AlertDialog alertDialog; private String currentPeer; private String fullPeer; ImageButton btnSend; EditText txtMessage; public static String ACTION_OPEN_MESSAGE_SCREEN = "org.restcomm.android.olympus.ACTION_OPEN_MESSAGE_SCREEN"; public static String EXTRA_CONTACT_NAME = "org.restcomm.android.olympus.EXTRA_CONTACT_NAME"; private MessageFragment listFragment; /** * Whether or not the activity is in two-pane mode, i.e. running on a tablet * device. */ private boolean mTwoPane; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_message); Toolbar toolbar = (Toolbar) findViewById(R.id.message_toolbar); setSupportActionBar(toolbar); toolbar.setTitle(getTitle()); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { // Show the Up button in the action bar. actionBar.setDisplayHomeAsUpEnabled(true); } listFragment = (MessageFragment) getSupportFragmentManager().findFragmentById(R.id.message_list); alertDialog = new AlertDialog.Builder(MessageActivity.this, R.style.SimpleAlertStyle).create(); btnSend = (ImageButton) findViewById(R.id.button_send); btnSend.setOnClickListener(this); txtMessage = (EditText) findViewById(R.id.text_message); txtMessage.setOnClickListener(this); fullPeer = getIntent().getStringExtra(RCDevice.EXTRA_DID); // keep on note of the current peer we are texting with currentPeer = getIntent().getStringExtra(RCDevice.EXTRA_DID).replaceAll("^sip:", "").replaceAll("@.*$", ""); setTitle(currentPeer); } @Override protected void onStart() { super.onStart(); // The activity is about to become visible. Log.i(TAG, "%% onStart"); //handleCall(getIntent()); bindService(new Intent(this, RCDevice.class), this, Context.BIND_AUTO_CREATE); } @Override protected void onResume() { super.onResume(); Log.i(TAG, "%% onResume"); if (device != null) { // needed if we are returning from Message screen that becomes the Device listener device.setDeviceListener(this); } } @Override protected void onPause() { super.onPause(); // Another activity is taking focus (this activity is about to be "paused"). Log.i(TAG, "%% onPause"); Intent intent = getIntent(); // #129: clear the action so that on subsequent indirect activity open (i.e. via lock & unlock) we don't get the old action firing again intent.setAction("CLEAR_ACTION"); setIntent(intent); } @Override protected void onStop() { super.onStop(); // The activity is no longer visible (it is now "stopped") Log.i(TAG, "%% onStop"); // Unbind from the service if (serviceBound) { //device.detach(); unbindService(this); serviceBound = false; } } @Override protected void onDestroy() { super.onDestroy(); // The activity is about to be destroyed. Log.i(TAG, "%% onDestroy"); } // We 've set MessageActivity to be 'singleTop' on the manifest to be able to receive messages while already open, without instantiating // a new activity. When that happens we receive onNewIntent() // An activity will always be paused before receiving a new intent, so you can count on onResume() being called after this method @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); // Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent. setIntent(intent); // if a mesage arrives after we have created activity it will land here handleMessage(intent); } // Callbacks for service binding, passed to bindService() @Override public void onServiceConnected(ComponentName className, IBinder service) { Log.i(TAG, "%% onServiceConnected"); // We've bound to LocalService, cast the IBinder and get LocalService instance RCDevice.RCDeviceBinder binder = (RCDevice.RCDeviceBinder) service; device = binder.getService(); // needed if we are returning from Message screen that becomes the Device listener device.setDeviceListener(this); // We have the device reference, let's handle the call handleMessage(getIntent()); serviceBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { Log.i(TAG, "%% onServiceDisconnected"); serviceBound = false; } private void handleMessage(Intent intent) { if (device.getState() == RCDevice.DeviceState.OFFLINE) { getSupportActionBar().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.colorTextSecondary))); } else { getSupportActionBar().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.colorPrimary))); } // Get Intent parameters. if (intent.getAction().equals(ACTION_OPEN_MESSAGE_SCREEN)) { params.put(RCConnection.ParameterKeys.CONNECTION_PEER, intent.getStringExtra(RCDevice.EXTRA_DID)); String shortname = intent.getStringExtra(RCDevice.EXTRA_DID).replaceAll("^sip:", "").replaceAll("@.*$", ""); setTitle(shortname); } if (intent.getAction().equals(RCDevice.ACTION_INCOMING_MESSAGE)) { String message = intent.getStringExtra(RCDevice.EXTRA_MESSAGE_TEXT); //HashMap<String, String> intentParams = (HashMap<String, String>) finalIntent.getSerializableExtra(RCDevice.INCOMING_MESSAGE_PARAMS); //String username = intentParams.get(RCConnection.ParameterKeys.CONNECTION_PEER); String username = intent.getStringExtra(RCDevice.EXTRA_DID); String shortname = username.replaceAll("^sip:", "").replaceAll("@.*$", ""); if (!shortname.equals(currentPeer)) { // message originating from another peer, not the one we are currently texting with, just update DB and show a Toast Toast.makeText(getApplicationContext(), "New text from \'" + shortname + "\': " + message, Toast.LENGTH_LONG).show(); if (DatabaseManager.getInstance().addContactIfNeded(username)) { Toast.makeText(getApplicationContext(), "Adding '" + shortname + "\' to contacts as it doesn't exist", Toast.LENGTH_LONG).show(); } //DatabaseManager.getInstance().addMessage(shortname, message, false); listFragment.addRemoteMessage(message, shortname); return; } params.put(RCConnection.ParameterKeys.CONNECTION_PEER, username); //setTitle(shortname); if (DatabaseManager.getInstance().addContactIfNeded(username)) { Toast.makeText(getApplicationContext(), "Text message sender not found; updating Contacts with \'" + shortname + "\'", Toast.LENGTH_LONG).show(); } listFragment.addRemoteMessage(message, shortname); } } /** * Main Activity onClick */ public void onClick(View view) { if (view.getId() == R.id.button_send) { HashMap<String, String> sendParams = new HashMap<String, String>(); String connectionPeer = (String) params.get(RCConnection.ParameterKeys.CONNECTION_PEER); sendParams.put(RCConnection.ParameterKeys.CONNECTION_PEER, connectionPeer); try { String jobId = device.sendMessage(txtMessage.getText().toString(), sendParams); // also output the message in the wall listFragment.addLocalMessage(txtMessage.getText().toString(), connectionPeer.replaceAll("^sip:", "").replaceAll("@.*$", ""), jobId); //indexes.put(messageStatus.jobId, index); txtMessage.setText(""); //txtWall.append("Me: " + txtMessage.getText().toString() + "\n\n"); } catch (RCException e) { showOkAlert("RCDevice Error", "No Wifi connectivity"); } } } /** * RCDeviceListener callbacks */ public void onInitialized(RCDevice device, RCDeviceListener.RCConnectivityStatus connectivityStatus, int statusCode, String statusText) { Log.i(TAG, "%% onInitialized"); } public void onInitializationError(int errorCode, String errorText) { Log.i(TAG, "%% onInitializationError"); } public void onStartListening(RCDevice device, RCConnectivityStatus connectivityStatus) { Log.i(TAG, "%% onStartListening"); } public void onStopListening(RCDevice device) { Log.i(TAG, "%% onStopListening"); } public void onStopListening(RCDevice device, int errorCode, String errorText) { if (errorCode == RCClient.ErrorCodes.SUCCESS.ordinal()) { handleConnectivityUpdate(RCConnectivityStatus.RCConnectivityStatusNone, "RCDevice: " + errorText); } else { handleConnectivityUpdate(RCConnectivityStatus.RCConnectivityStatusNone, "RCDevice Error: " + errorText); } } public void onConnectivityUpdate(RCDevice device, RCConnectivityStatus connectivityStatus) { Log.i(TAG, "%% onConnectivityUpdate"); handleConnectivityUpdate(connectivityStatus, null); } public void onMessageSent(RCDevice device, int statusCode, String statusText, String jobId) { Log.i(TAG, "onMessageSent(): statusCode: " + statusCode + ", statusText: " + statusText); //Integer index = indexes.get(jobId); listFragment.updateMessageDeliveryStatus(jobId, statusCode, currentPeer); /* if (statusCode != RCClient.ErrorCodes.SUCCESS.ordinal()) { //listView.getAdapter().getItem(index); //messageTextView.setTextColor(ContextCompat.getColor(this, R.color.colorError)); } else { //messageTextView.setTextColor(ContextCompat.getColor(this, R.color.colorTextSecondary)); } */ //indexes.remove(jobId); } public void onReleased(RCDevice device, int statusCode, String statusText) { if (statusCode != RCClient.ErrorCodes.SUCCESS.ordinal()) { showOkAlert("RCDevice Error", statusText); } } public boolean receivePresenceEvents(RCDevice device) { return false; } public void onPresenceChanged(RCDevice device, RCPresenceEvent presenceEvent) { } /** * Helpers */ private void showOkAlert(final String title, final String detail) { Log.d(TAG, "Showing alert: " + title + ", detail: " + detail); if (alertDialog.isShowing()) { Log.w(TAG, "Alert already showing, hiding to show new alert"); alertDialog.hide(); } alertDialog.setTitle(title); alertDialog.setMessage(detail); alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); alertDialog.show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_message, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_video_call) { Intent intent = new Intent(this, CallActivity.class); intent.setAction(RCDevice.ACTION_OUTGOING_CALL); intent.putExtra(RCDevice.EXTRA_DID, fullPeer); intent.putExtra(RCDevice.EXTRA_VIDEO_ENABLED, true); startActivity(intent); } if (id == R.id.action_audio_call) { Intent intent = new Intent(this, CallActivity.class); intent.setAction(RCDevice.ACTION_OUTGOING_CALL); intent.putExtra(RCDevice.EXTRA_DID, fullPeer); intent.putExtra(RCDevice.EXTRA_VIDEO_ENABLED, false); startActivity(intent); } return super.onOptionsItemSelected(item); } public void handleConnectivityUpdate(RCConnectivityStatus connectivityStatus, String text) { if (text == null) { if (connectivityStatus == RCConnectivityStatus.RCConnectivityStatusNone) { text = "RCDevice connectivity change: Lost connectivity"; } if (connectivityStatus == RCConnectivityStatus.RCConnectivityStatusWiFi) { text = "RCDevice connectivity change: Reestablished connectivity (Wifi)"; } if (connectivityStatus == RCConnectivityStatus.RCConnectivityStatusCellular) { text = "RCDevice connectivity change: Reestablished connectivity (Cellular)"; } if (connectivityStatus == RCConnectivityStatus.RCConnectivityStatusEthernet) { text = "RCDevice connectivity change: Reestablished connectivity (Ethernet)"; } } if (connectivityStatus == RCConnectivityStatus.RCConnectivityStatusNone) { getSupportActionBar().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.colorTextSecondary))); } else { getSupportActionBar().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.colorPrimary))); } Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show(); } }