/** * 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(); }
/** * 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; }
/** * 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()); }
/** * 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; } }
/** * 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(); }
/** * @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; } } }
/** * <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(); } }