From 8d5e5f0c8fb61a9a8240d647864e4237a7c6024f Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 13 Sep 2019 15:41:40 +0200 Subject: [PATCH] Improved way to ensure Service is running when UI resumes --- .../java/org/linphone/LinphoneContext.java | 291 ++++++++++++++++++ .../java/org/linphone/LinphoneManager.java | 4 +- .../java/org/linphone/LinphoneService.java | 259 ++-------------- .../linphone/activities/AboutActivity.java | 3 - .../linphone/activities/DialerActivity.java | 3 - .../activities/LinphoneGenericActivity.java | 28 +- .../activities/LinphoneLauncherActivity.java | 54 +--- .../org/linphone/activities/MainActivity.java | 10 +- .../AccountConnectionAssistantActivity.java | 3 - .../linphone/assistant/AssistantActivity.java | 7 +- ...EmailAccountCreationAssistantActivity.java | 3 - ...ailAccountValidationAssistantActivity.java | 3 - .../GenericConnectionAssistantActivity.java | 3 - .../assistant/MenuAssistantActivity.java | 3 - .../OpenH264DownloadAssistantActivity.java | 3 - ...PhoneAccountCreationAssistantActivity.java | 3 - .../PhoneAccountLinkingAssistantActivity.java | 3 - ...oneAccountValidationAssistantActivity.java | 3 - .../QrCodeConfigurationAssistantActivity.java | 3 - .../RemoteConfigurationAssistantActivity.java | 3 - .../java/org/linphone/call/CallActivity.java | 3 - .../linphone/call/CallIncomingActivity.java | 7 +- .../java/org/linphone/call/CallManager.java | 17 +- .../linphone/call/CallOutgoingActivity.java | 7 +- .../linphone/chat/ChatMessagesFragment.java | 6 +- .../org/linphone/chat/GroupInfoAdapter.java | 7 +- .../org/linphone/contacts/AndroidContact.java | 24 +- .../linphone/contacts/ContactsManager.java | 11 +- .../linphone/contacts/LinphoneContact.java | 7 +- .../contacts/SearchContactsAdapter.java | 5 +- .../linphone/fragments/StatusBarFragment.java | 19 +- .../NotificationBroadcastReceiver.java | 12 +- .../notifications/NotificationsManager.java | 42 +-- .../recording/RecordingsActivity.java | 3 - .../settings/AdvancedSettingsFragment.java | 6 +- .../settings/LinphonePreferences.java | 10 +- .../org/linphone/utils/LinphoneUtils.java | 19 +- .../org/linphone/utils/ServiceWaitThread.java | 51 +++ .../utils/ServiceWaitThreadListener.java | 24 ++ .../org/linphone/views/BitmapWorkerTask.java | 6 +- .../main/java/org/linphone/views/Digit.java | 4 +- .../views/LinphoneGL2JNIViewOverlay.java | 4 +- .../views/LinphoneTextureViewOverlay.java | 4 +- 43 files changed, 544 insertions(+), 446 deletions(-) create mode 100644 app/src/main/java/org/linphone/LinphoneContext.java create mode 100644 app/src/main/java/org/linphone/utils/ServiceWaitThread.java create mode 100644 app/src/main/java/org/linphone/utils/ServiceWaitThreadListener.java diff --git a/app/src/main/java/org/linphone/LinphoneContext.java b/app/src/main/java/org/linphone/LinphoneContext.java new file mode 100644 index 000000000..393b4f076 --- /dev/null +++ b/app/src/main/java/org/linphone/LinphoneContext.java @@ -0,0 +1,291 @@ +package org.linphone; + +/* +LinphoneContext.java +Copyright (C) 2019 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Handler; +import android.provider.ContactsContract; +import org.linphone.call.CallActivity; +import org.linphone.call.CallIncomingActivity; +import org.linphone.call.CallOutgoingActivity; +import org.linphone.contacts.ContactsManager; +import org.linphone.core.Call; +import org.linphone.core.Core; +import org.linphone.core.CoreListenerStub; +import org.linphone.core.Factory; +import org.linphone.core.LogLevel; +import org.linphone.core.LoggingService; +import org.linphone.core.LoggingServiceListener; +import org.linphone.core.tools.Log; +import org.linphone.mediastream.Version; +import org.linphone.notifications.NotificationsManager; +import org.linphone.settings.LinphonePreferences; +import org.linphone.utils.LinphoneUtils; + +public class LinphoneContext { + private static final String START_LINPHONE_LOGS = " ==== Phone information dump ===="; + private static LinphoneContext sInstance = null; + + private Context mContext; + public final Handler handler = new Handler(); + + private final LoggingServiceListener mJavaLoggingService = + new LoggingServiceListener() { + @Override + public void onLogMessageWritten( + LoggingService logService, String domain, LogLevel lev, String message) { + switch (lev) { + case Debug: + android.util.Log.d(domain, message); + break; + case Message: + android.util.Log.i(domain, message); + break; + case Warning: + android.util.Log.w(domain, message); + break; + case Error: + android.util.Log.e(domain, message); + break; + case Fatal: + default: + android.util.Log.wtf(domain, message); + break; + } + } + }; + + private CoreListenerStub mListener; + private NotificationsManager mNotificationManager; + private LinphoneManager mLinphoneManager; + private ContactsManager mContactsManager; + + private Class mIncomingReceivedActivity = CallIncomingActivity.class; + + public static boolean isReady() { + return sInstance != null; + } + + public static LinphoneContext instance() { + return sInstance; + } + + public LinphoneContext(Context context) { + mContext = context; + + LinphonePreferences.instance().setContext(context); + Factory.instance().setLogCollectionPath(context.getFilesDir().getAbsolutePath()); + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneUtils.configureLoggingService(isDebugEnabled, context.getString(R.string.app_name)); + + // Dump some debugging information to the logs + Log.i(START_LINPHONE_LOGS); + dumpDeviceInformation(); + dumpInstalledLinphoneInformation(); + + String incomingReceivedActivityName = + LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived(); + try { + mIncomingReceivedActivity = + (Class) Class.forName(incomingReceivedActivityName); + } catch (ClassNotFoundException e) { + Log.e(e); + } + + sInstance = this; + Log.i("[Context] Ready"); + + mListener = + new CoreListenerStub() { + @Override + public void onCallStateChanged( + Core core, Call call, Call.State state, String message) { + if (mContext.getResources().getBoolean(R.bool.enable_call_notification)) { + mNotificationManager.displayCallNotification(call); + } + + if (state == Call.State.IncomingReceived + || state == Call.State.IncomingEarlyMedia) { + // Starting SDK 24 (Android 7.0) we rely on the fullscreen intent of the + // call incoming notification + if (Version.sdkStrictlyBelow(Version.API24_NOUGAT_70)) { + if (!mLinphoneManager.getCallGsmON()) onIncomingReceived(); + } + } else if (state == Call.State.OutgoingInit) { + onOutgoingStarted(); + } else if (state == Call.State.Connected) { + onCallStarted(); + } else if (state == Call.State.End + || state == Call.State.Released + || state == Call.State.Error) { + if (LinphoneService.isReady()) { + LinphoneService.instance().destroyOverlay(); + } + + if (state == Call.State.Released + && call.getCallLog().getStatus() == Call.Status.Missed) { + mNotificationManager.displayMissedCallNotification(call); + } + } + } + }; + + mLinphoneManager = new LinphoneManager(context); + mNotificationManager = new NotificationsManager(context); + } + + public void start(boolean isPush) { + Log.i("[Context] Starting"); + mLinphoneManager.startLibLinphone(isPush); + LinphoneManager.getCore().addListener(mListener); + + mNotificationManager.onCoreReady(); + + mContactsManager = new ContactsManager(mContext, handler); + if (!Version.sdkAboveOrEqual(Version.API26_O_80) + || (mContactsManager.hasReadContactsAccess())) { + mContext.getContentResolver() + .registerContentObserver( + ContactsContract.Contacts.CONTENT_URI, true, mContactsManager); + } + if (mContactsManager.hasReadContactsAccess()) { + mContactsManager.enableContactsAccess(); + } + mContactsManager.initializeContactManager(); + } + + public void destroy() { + Log.i("[Context] Destroying"); + Core core = LinphoneManager.getCore(); + if (core != null) { + core.removeListener(mListener); + core = null; // To allow the gc calls below to free the Core + } + + // Make sure our notification is gone. + if (mNotificationManager != null) { + mNotificationManager.destroy(); + } + + if (mContactsManager != null) { + mContactsManager.destroy(); + } + + // Destroy the LinphoneManager second to last to ensure any getCore() call will work + if (mLinphoneManager != null) { + mLinphoneManager.destroy(); + } + + // Wait for every other object to be destroyed to make LinphoneService.instance() invalid + sInstance = null; + + if (LinphonePreferences.instance().useJavaLogger()) { + Factory.instance().getLoggingService().removeListener(mJavaLoggingService); + } + LinphonePreferences.instance().destroy(); + } + + public void updateContext(Context context) { + mContext = context; + } + + public Context getApplicationContext() { + return mContext; + } + + /* Managers accessors */ + + public LoggingServiceListener getJavaLoggingService() { + return mJavaLoggingService; + } + + public NotificationsManager getNotificationManager() { + return mNotificationManager; + } + + public LinphoneManager getLinphoneManager() { + return mLinphoneManager; + } + + public ContactsManager getContactsManager() { + return mContactsManager; + } + + /* Log device related information */ + + private void dumpDeviceInformation() { + StringBuilder sb = new StringBuilder(); + sb.append("DEVICE=").append(Build.DEVICE).append("\n"); + sb.append("MODEL=").append(Build.MODEL).append("\n"); + sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n"); + sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n"); + sb.append("Supported ABIs="); + for (String abi : Version.getCpuAbis()) { + sb.append(abi).append(", "); + } + sb.append("\n"); + Log.i(sb.toString()); + } + + private void dumpInstalledLinphoneInformation() { + PackageInfo info = null; + try { + info = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException nnfe) { + Log.e(nnfe); + } + + if (info != null) { + Log.i( + "[Context] Linphone version is ", + info.versionName + " (" + info.versionCode + ")"); + } else { + Log.i("[Context] Linphone version is unknown"); + } + } + + /* Call activities */ + + private void onIncomingReceived() { + Intent intent = new Intent().setClass(mContext, mIncomingReceivedActivity); + // This flag is required to start an Activity from a Service context + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + private void onOutgoingStarted() { + Intent intent = new Intent(mContext, CallOutgoingActivity.class); + // This flag is required to start an Activity from a Service context + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + private void onCallStarted() { + Intent intent = new Intent(mContext, CallActivity.class); + // This flag is required to start an Activity from a Service context + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } +} diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index 65944e11c..71d115973 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -299,7 +299,7 @@ public class LinphoneManager implements SensorEventListener { @Override public void onFriendListCreated(Core core, FriendList list) { - if (LinphoneService.isReady()) { + if (LinphoneContext.isReady()) { list.addListener(ContactsManager.getInstance()); } } @@ -345,7 +345,7 @@ public class LinphoneManager implements SensorEventListener { } public static synchronized LinphoneManager getInstance() { - LinphoneManager manager = LinphoneService.instance().getLinphoneManager(); + LinphoneManager manager = LinphoneContext.instance().getLinphoneManager(); if (manager == null) { throw new RuntimeException( "[Manager] Linphone Manager should be created before accessed"); diff --git a/app/src/main/java/org/linphone/LinphoneService.java b/app/src/main/java/org/linphone/LinphoneService.java index 3254fac71..3332ca484 100644 --- a/app/src/main/java/org/linphone/LinphoneService.java +++ b/app/src/main/java/org/linphone/LinphoneService.java @@ -19,90 +19,28 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.app.Activity; import android.app.Application; import android.app.Service; import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; -import android.os.Handler; import android.os.IBinder; -import android.provider.ContactsContract; import android.view.WindowManager; -import org.linphone.call.CallActivity; -import org.linphone.call.CallIncomingActivity; -import org.linphone.call.CallOutgoingActivity; -import org.linphone.contacts.ContactsManager; import org.linphone.core.Call; -import org.linphone.core.Call.State; import org.linphone.core.Core; -import org.linphone.core.CoreListenerStub; -import org.linphone.core.Factory; -import org.linphone.core.LogLevel; -import org.linphone.core.LoggingService; -import org.linphone.core.LoggingServiceListener; import org.linphone.core.tools.Log; import org.linphone.mediastream.Version; -import org.linphone.notifications.NotificationsManager; import org.linphone.settings.LinphonePreferences; import org.linphone.utils.ActivityMonitor; -import org.linphone.utils.LinphoneUtils; import org.linphone.views.LinphoneGL2JNIViewOverlay; import org.linphone.views.LinphoneOverlay; import org.linphone.views.LinphoneTextureViewOverlay; -/** - * Linphone service, reacting to Incoming calls, ...
- * - *

Roles include: - * - *