/** * <tt>ViewDependentProvider</tt> is used to implement classes that provide objects dependent on * <tt>View</tt> visibility state. It means that they can provide it only when <tt>View</tt> is * visible and they have to release such object before <tt>View</tt> is hidden. * * @author Pawel Domas */ public abstract class ViewDependentProvider<T> { /** The logger */ private static final Logger logger = Logger.getLogger(ViewDependentProvider.class); /** Timeout for dispose surface operation */ private static final long REMOVAL_TIMEOUT = 10000L; /** Timeout for create surface operation */ private static final long CREATE_TIMEOUT = 10000L; /** <tt>Activity</tt> context. */ protected final Activity activity; /** The container that will hold maintained view. */ private final ViewGroup container; /** The view maintained by this instance. */ protected View view; /** Provided object created when <tt>View</tt> is visible. */ protected T providedObject; /** * Creates new instance of <tt>ViewDependentProvider</tt>. * * @param activity parent <tt>Activity</tt> that manages the <tt>container</tt>. * @param container the container that will hold maintained <tt>View</tt>. */ public ViewDependentProvider(Activity activity, ViewGroup container) { this.activity = activity; this.container = container; } /** * Checks if the view is currently created. If not creates new <tt>View</tt> and adds it to the * <tt>container</tt>. */ protected void ensureViewCreated() { if (view == null) { activity.runOnUiThread( new Runnable() { @Override public void run() { view = createViewInstance(); ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); container.addView(view, params); container.setVisibility(View.VISIBLE); } }); } } /** * Factory method that creates new <tt>View</tt> instance. * * @return new <tt>View</tt> instance. */ protected abstract View createViewInstance(); /** Checks if maintained view exists and removes if from the <tt>container</tt>. */ protected void ensureViewDestroyed() { if (view != null) { final View viewToRemove = view; view = null; activity.runOnUiThread( new Runnable() { @Override public void run() { container.removeView(viewToRemove); container.setVisibility(View.GONE); } }); } } /** * Must be called by subclasses when provided object is created. * * @param obj provided object instance. */ protected synchronized void onObjectCreated(T obj) { this.providedObject = obj; this.notifyAll(); } /** * Should be called by consumer to obtain the object. It is causing hidden <tt>View</tt> to be * displayed and eventually {@link #onObjectCreated(Object)} method to be called which results in * object creation. * * @return provided object. */ public synchronized T obtainObject() { ensureViewCreated(); if (this.providedObject == null) { try { logger.info("Waiting for object..." + hashCode()); this.wait(CREATE_TIMEOUT); if (providedObject == null) { throw new RuntimeException("Timeout waiting for surface"); } logger.info("Returning object! " + hashCode()); } catch (InterruptedException e) { throw new RuntimeException(e); } } return providedObject; } /** * Checks if provider has already the object and returns it immediately. If there is no object and * we would have to wait for it, then the <tt>null</tt> is returned. * * @return the object if it is currently held by this provider or <tt>null</tt> otherwise. */ public synchronized T tryObtainObject() { return providedObject; } /** Should be called by subclasses when object is destroyed. */ protected synchronized void onObjectDestroyed() { releaseObject(); } /** Should be called by the consumer to release the object. */ public void onObjectReleased() { releaseObject(); // Remove the view once it's released ensureViewDestroyed(); } /** Releases the subject object and notifies all threads waiting on the lock. */ protected synchronized void releaseObject() { if (providedObject == null) return; this.providedObject = null; this.notifyAll(); } /** * Blocks current thread until subject object is released. It should be used to block UI thread * before the <tt>View</tt> is hidden. */ public synchronized void waitForObjectRelease() { if (providedObject != null) { try { logger.info("Waiting for object release... " + hashCode()); this.wait(REMOVAL_TIMEOUT); if (providedObject != null) { throw new RuntimeException("Timeout waiting for preview surface removal"); } logger.info("Object released! " + hashCode()); } catch (InterruptedException e) { throw new RuntimeException(e); } } ensureViewDestroyed(); } }
/** * @author Yana Stamcheva * @author Pawel Domas */ public class AndroidCallUtil { /** The logger for this class. */ private static final Logger logger = Logger.getLogger(AndroidCallUtil.class); /** Field used to track the thread used to create outgoing calls. */ private static Thread createCallThread; /** * Creates an android call. * * @param context the android context * @param callButtonView the button view that generated the call * @param contact the contact address to call */ public static void createAndroidCall(Context context, View callButtonView, String contact) { if (AccountUtils.getRegisteredProviders().size() > 1) showCallViaMenu(context, callButtonView, contact); else createCall(context, contact); } /** * Creates new call to target <tt>destination</tt>. * * @param context the android context * @param destination the target callee name that will be used. */ private static void createCall(Context context, String destination) { Iterator<ProtocolProviderService> allProviders = AccountUtils.getRegisteredProviders().iterator(); if (!allProviders.hasNext()) { logger.error("No registered providers found"); return; } createCall(context, destination, allProviders.next()); } /** * Creates new call to given <tt>destination</tt> using selected <tt>provider</tt>. * * @param context the android context * @param destination target callee name. * @param provider the provider that will be used to make a call. */ public static void createCall( final Context context, final String destination, final ProtocolProviderService provider) { if (createCallThread != null) { logger.warn("Another call is already being created"); return; } else if (CallManager.getActiveCallsCount() > 0) { logger.warn("Another call is in progress"); return; } final long dialogId = ProgressDialogFragment.showProgressDialog( JitsiApplication.getResString(R.string.service_gui_OUTGOING_CALL), JitsiApplication.getResString(R.string.service_gui_OUTGOING_CALL_MSG, destination)); createCallThread = new Thread("Create call thread") { public void run() { try { CallManager.createCall(provider, destination); } catch (Throwable t) { logger.error("Error creating the call: " + t.getMessage(), t); AndroidUtils.showAlertDialog( context, context.getString(R.string.service_gui_ERROR), t.getMessage()); } finally { if (DialogActivity.waitForDialogOpened(dialogId)) { DialogActivity.closeDialog(JitsiApplication.getGlobalContext(), dialogId); } else { logger.error("Failed to wait for the dialog: " + dialogId); } createCallThread = null; } } }; createCallThread.start(); } /** * Shows "call via" menu allowing user to selected from multiple providers. * * @param context the android context * @param v the View that will contain the popup menu. * @param destination target callee name. */ private static void showCallViaMenu(final Context context, View v, final String destination) { PopupMenu popup = new PopupMenu(context, v); Menu menu = popup.getMenu(); Iterator<ProtocolProviderService> registeredProviders = AccountUtils.getRegisteredProviders().iterator(); while (registeredProviders.hasNext()) { final ProtocolProviderService provider = registeredProviders.next(); String accountAddress = provider.getAccountID().getAccountAddress(); MenuItem menuItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, accountAddress); menuItem.setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { createCall(context, destination, provider); return false; } }); } popup.show(); } /** * Checks if there is a call in progress. If true then shows a warning toast and finishes the * activity. * * @param activity activity doing a check. * @return <tt>true</tt> if there is call in progress and <tt>Activity</tt> was finished. */ public static boolean checkCallInProgress(Activity activity) { if (CallManager.getActiveCallsCount() > 0) { logger.warn("Call is in progress"); Toast t = Toast.makeText(activity, R.string.service_gui_WARN_CALL_IN_PROGRESS, Toast.LENGTH_SHORT); t.show(); activity.finish(); return true; } else { return false; } } }
/** * This activity allows user to add new contacts. * * @author Pawel Domas */ public class AddContactActivity extends OSGiActivity { /** The logger. */ private static final Logger logger = Logger.getLogger(AddContactActivity.class); /** {@inheritDoc} */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_contact); setTitle(R.string.service_gui_ADD_CONTACT); initAccountSpinner(); initContactGroupSpinner(); } /** Initializes "select account" spinner with existing accounts. */ private void initAccountSpinner() { Spinner accountsSpiner = (Spinner) findViewById(R.id.selectAccountSpinner); Iterator<ProtocolProviderService> providers = AccountUtils.getRegisteredProviders().iterator(); List<AccountID> accounts = new ArrayList<AccountID>(); int selectedIdx = -1; int idx = 0; while (providers.hasNext()) { ProtocolProviderService provider = providers.next(); OperationSet opSet = provider.getOperationSet(OperationSetPresence.class); if (opSet == null) continue; AccountID account = provider.getAccountID(); accounts.add(account); idx++; if (account.isPreferredProvider()) { selectedIdx = idx; } } AccountsListAdapter accountsAdapter = new AccountsListAdapter( this, R.layout.select_account_row, R.layout.select_account_dropdown, accounts, true); accountsSpiner.setAdapter(accountsAdapter); // if we have only select account option and only one account // select the available account if (accounts.size() == 1) accountsSpiner.setSelection(0); else accountsSpiner.setSelection(selectedIdx); } /** Initializes select contact group spinner with contact groups. */ private void initContactGroupSpinner() { Spinner groupSpinner = (Spinner) findViewById(R.id.selectGroupSpinner); MetaContactGroupAdapter contactGroupAdapter = new MetaContactGroupAdapter(this, R.id.selectGroupSpinner, true, true); contactGroupAdapter.setItemLayout(R.layout.simple_spinner_item); contactGroupAdapter.setDropDownLayout(R.layout.dropdown_spinner_item); groupSpinner.setAdapter(contactGroupAdapter); } /** * Method fired when "add" button is clicked. * * @param v add button's <tt>View</tt> */ public void onAddClicked(View v) { Spinner accountsSpiner = (Spinner) findViewById(R.id.selectAccountSpinner); Account selectedAcc = (Account) accountsSpiner.getSelectedItem(); if (selectedAcc == null) { logger.error("No account selected"); return; } ProtocolProviderService pps = selectedAcc.getProtocolProvider(); if (pps == null) { logger.error("No provider registered for account " + selectedAcc.getAccountName()); return; } View content = findViewById(android.R.id.content); String contactAddress = ViewUtil.getTextViewValue(content, R.id.editContactName); String displayName = ViewUtil.getTextViewValue(content, R.id.editDisplayName); if (displayName != null && displayName.length() > 0) { addRenameListener(pps, null, contactAddress, displayName); } Spinner groupSpinner = (Spinner) findViewById(R.id.selectGroupSpinner); ContactListUtils.addContact( pps, (MetaContactGroup) groupSpinner.getSelectedItem(), contactAddress); finish(); } /** * Adds a rename listener. * * @param protocolProvider the protocol provider to which the contact was added * @param metaContact the <tt>MetaContact</tt> if the new contact was added to an existing meta * contact * @param contactAddress the address of the newly added contact * @param displayName the new display name */ private void addRenameListener( final ProtocolProviderService protocolProvider, final MetaContact metaContact, final String contactAddress, final String displayName) { AndroidGUIActivator.getContactListService() .addMetaContactListListener( new MetaContactListAdapter() { @Override public void metaContactAdded(MetaContactEvent evt) { if (evt.getSourceMetaContact().getContact(contactAddress, protocolProvider) != null) { renameContact(evt.getSourceMetaContact(), displayName); } } @Override public void protoContactAdded(ProtoContactEvent evt) { if (metaContact != null && evt.getNewParent().equals(metaContact)) { renameContact(metaContact, displayName); } } }); } /** * Renames the given meta contact. * * @param metaContact the <tt>MetaContact</tt> to rename * @param displayName the new display name */ private void renameContact(final MetaContact metaContact, final String displayName) { new Thread() { @Override public void run() { AndroidGUIActivator.getContactListService().renameMetaContact(metaContact, displayName); } }.start(); } }