diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java
index 17b2e83554..4569c5ec00 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java
@@ -67,6 +67,7 @@ public interface BindableConversationItem extends Unbindable {
void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient);
void onJoinGroupCallClicked();
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
+ void onEnableCallNotificationsClicked();
/** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
index c33bdaa871..a87b1acbf9 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
@@ -88,6 +88,7 @@ import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.StickyHeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
+import org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSettingsDialog;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -1558,6 +1559,23 @@ public class ConversationFragment extends LoggingFragment {
public void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId) {
GroupLinkInviteFriendsBottomSheetDialogFragment.show(requireActivity().getSupportFragmentManager(), groupId);
}
+
+ @Override
+ public void onEnableCallNotificationsClicked() {
+ EnableCallNotificationSettingsDialog.fixAutomatically(requireContext());
+ if (EnableCallNotificationSettingsDialog.shouldShow(requireContext())) {
+ EnableCallNotificationSettingsDialog.show(getChildFragmentManager());
+ } else {
+ refreshList();
+ }
+ }
+ }
+
+ public void refreshList() {
+ ConversationAdapter listAdapter = getListAdapter();
+ if (listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
}
@Override
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
index e118b18aeb..d301b34b70 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
@@ -23,6 +23,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.VerifyIdentityActivity;
+import org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSettingsDialog;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
@@ -302,6 +303,14 @@ public final class ConversationUpdateItem extends FrameLayout
eventListener.onInviteFriendsToGroupClicked(conversationRecipient.requireGroupId().requireV2());
}
});
+ } else if ((conversationMessage.getMessageRecord().isMissedAudioCall() || conversationMessage.getMessageRecord().isMissedVideoCall()) && EnableCallNotificationSettingsDialog.shouldShow(getContext())) {
+ actionButton.setVisibility(VISIBLE);
+ actionButton.setText(R.string.ConversationUpdateItem_enable_call_notifications);
+ actionButton.setOnClickListener(v -> {
+ if (eventListener != null) {
+ eventListener.onEnableCallNotificationsClicked();
+ }
+ });
} else {
actionButton.setVisibility(GONE);
actionButton.setOnClickListener(null);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/EnableCallNotificationSettingsDialog.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/EnableCallNotificationSettingsDialog.java
new file mode 100644
index 0000000000..21c2c78862
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/EnableCallNotificationSettingsDialog.java
@@ -0,0 +1,233 @@
+package org.thoughtcrime.securesms.conversation.ui.error;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
+import org.signal.core.util.logging.Log;
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.conversation.ConversationFragment;
+import org.thoughtcrime.securesms.notifications.NotificationChannels;
+import org.thoughtcrime.securesms.util.DeviceProperties;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+/**
+ * Provide basic steps to fix potential call notification issues based on what we can detect on the system
+ * and app settings.
+ */
+@TargetApi(26)
+public final class EnableCallNotificationSettingsDialog extends DialogFragment {
+
+ private static final String TAG = Log.tag(EnableCallNotificationSettingsDialog.class);
+ private static final String FRAGMENT_TAG = "MissedCallCheckSettingsDialog";
+
+ private static final int NOTIFICATIONS_DISABLED = 1 << 1;
+ private static final int CALL_NOTIFICATIONS_DISABLED = 1 << 2;
+ private static final int CALL_CHANNEL_INVALID = 1 << 4;
+ private static final int BACKGROUND_RESTRICTED = 1 << 8;
+
+ private View view;
+
+ public static boolean shouldShow(@NonNull Context context) {
+ return getCallNotificationSettingsBitmask(context) != 0;
+ }
+
+ public static void fixAutomatically(@NonNull Context context) {
+ if (areCallNotificationsDisabled(context)) {
+ TextSecurePreferences.setCallNotificationsEnabled(context, true);
+ Toast.makeText(context, R.string.EnableCallNotificationSettingsDialog__call_notifications_enabled, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public static void show(@NonNull FragmentManager fragmentManager) {
+ if (fragmentManager.findFragmentByTag(FRAGMENT_TAG) != null) {
+ Log.i(TAG, "Dialog already being shown");
+ return;
+ }
+
+ new EnableCallNotificationSettingsDialog().show(fragmentManager, FRAGMENT_TAG);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(requireContext(), R.style.Signal_ThemeOverlay_Dialog_Rounded);
+
+ Runnable action = null;
+ switch (getCallNotificationSettingsBitmask(requireContext())) {
+ case NOTIFICATIONS_DISABLED:
+ dialogBuilder.setTitle(R.string.EnableCallNotificationSettingsDialog__enable_call_notifications)
+ .setMessage(R.string.EnableCallNotificationSettingsDialog__to_receive_call_notifications_tap_settings_and_turn_on_show_notifications)
+ .setPositiveButton(R.string.EnableCallNotificationSettingsDialog__settings, null);
+ action = this::showNotificationSettings;
+ break;
+ case CALL_CHANNEL_INVALID:
+ dialogBuilder.setTitle(R.string.EnableCallNotificationSettingsDialog__enable_call_notifications)
+ .setMessage(R.string.EnableCallNotificationSettingsDialog__to_receive_call_notifications_tap_settings_and_turn_on_notifications)
+ .setPositiveButton(R.string.EnableCallNotificationSettingsDialog__settings, null);
+ action = this::showNotificationChannelSettings;
+ break;
+ case BACKGROUND_RESTRICTED:
+ dialogBuilder.setTitle(R.string.EnableCallNotificationSettingsDialog__enable_background_activity)
+ .setMessage(R.string.EnableCallNotificationSettingsDialog__to_receive_call_notifications_tap_settings_and_enable_background_activity_in_battery_settings)
+ .setPositiveButton(R.string.EnableCallNotificationSettingsDialog__settings, null);
+ action = this::showAppSettings;
+ break;
+ default:
+ dialogBuilder.setTitle(R.string.EnableCallNotificationSettingsDialog__enable_call_notifications)
+ .setView(createView())
+ .setPositiveButton(android.R.string.ok, null);
+ break;
+ }
+
+ dialogBuilder.setNegativeButton(android.R.string.cancel, null);
+
+ AlertDialog dialog = dialogBuilder.create();
+
+ if (action != null) {
+ final Runnable localAction = action;
+ dialog.setOnShowListener(d -> dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> localAction.run()));
+ }
+
+ return dialog;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (getCallNotificationSettingsBitmask(requireContext()) == 0) {
+ dismissAllowingStateLoss();
+ } else if (view != null) {
+ bind(view);
+ }
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (getParentFragment() instanceof ConversationFragment) {
+ ((ConversationFragment) getParentFragment()).refreshList();
+ }
+ }
+
+ @SuppressLint("InflateParams")
+ private @NonNull View createView() {
+ view = LayoutInflater.from(getContext()).inflate(R.layout.enable_call_notification_settings_dialog_fragment, null, false);
+ bind(view);
+ return view;
+ }
+
+ private void bind(@NonNull View view) {
+ TextView allConfigured = view.findViewById(R.id.enable_call_notification_settings_dialog_system_all_configured);
+ AppCompatImageView systemSettingIndicator = view.findViewById(R.id.enable_call_notification_settings_dialog_system_setting_indicator);
+ TextView systemSettingText = view.findViewById(R.id.enable_call_notification_settings_dialog_system_setting_text);
+ AppCompatImageView channelSettingIndicator = view.findViewById(R.id.enable_call_notification_settings_dialog_channel_setting_indicator);
+ TextView channelSettingText = view.findViewById(R.id.enable_call_notification_settings_dialog_channel_setting_text);
+ AppCompatImageView backgroundRestrictedIndicator = view.findViewById(R.id.enable_call_notification_settings_dialog_background_restricted_indicator);
+ TextView backgroundRestrictedText = view.findViewById(R.id.enable_call_notification_settings_dialog_background_restricted_text);
+
+ if (areNotificationsDisabled(requireContext())) {
+ systemSettingIndicator.setVisibility(View.VISIBLE);
+ systemSettingText.setVisibility(View.VISIBLE);
+ systemSettingText.setOnClickListener(v -> showNotificationSettings());
+ } else {
+ systemSettingIndicator.setVisibility(View.GONE);
+ systemSettingText.setVisibility(View.GONE);
+ systemSettingText.setOnClickListener(null);
+ }
+
+ if (isCallChannelInvalid(requireContext())) {
+ channelSettingIndicator.setVisibility(View.VISIBLE);
+ channelSettingText.setVisibility(View.VISIBLE);
+ channelSettingText.setOnClickListener(v -> showNotificationChannelSettings());
+ } else {
+ channelSettingIndicator.setVisibility(View.GONE);
+ channelSettingText.setVisibility(View.GONE);
+ channelSettingText.setOnClickListener(null);
+ }
+
+ if (isBackgroundRestricted(requireContext())) {
+ backgroundRestrictedIndicator.setVisibility(View.VISIBLE);
+ backgroundRestrictedText.setVisibility(View.VISIBLE);
+ backgroundRestrictedText.setOnClickListener(v -> showAppSettings());
+ } else {
+ backgroundRestrictedIndicator.setVisibility(View.GONE);
+ backgroundRestrictedText.setVisibility(View.GONE);
+ backgroundRestrictedText.setOnClickListener(null);
+ }
+
+ allConfigured.setVisibility(shouldShow(requireContext()) ? View.GONE : View.VISIBLE);
+ }
+
+ private void showNotificationSettings() {
+ Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().getPackageName());
+ startActivity(intent);
+ }
+
+ private void showNotificationChannelSettings() {
+ NotificationChannels.openChannelSettings(requireContext(), NotificationChannels.CALLS);
+ }
+
+ private void showAppSettings() {
+ Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", requireContext().getPackageName(), null));
+ startActivity(intent);
+ }
+
+ private static boolean areNotificationsDisabled(@NonNull Context context) {
+ return !NotificationChannels.areNotificationsEnabled(context);
+ }
+
+ private static boolean areCallNotificationsDisabled(Context context) {
+ return !TextSecurePreferences.isCallNotificationsEnabled(context);
+ }
+
+ private static boolean isCallChannelInvalid(Context context) {
+ return !NotificationChannels.isCallsChannelValid(context);
+ }
+
+ private static boolean isBackgroundRestricted(Context context) {
+ return Build.VERSION.SDK_INT >= 28 && DeviceProperties.isBackgroundRestricted(context);
+ }
+
+ private static int getCallNotificationSettingsBitmask(@NonNull Context context) {
+ int bitmask = 0;
+
+ if (areNotificationsDisabled(context)) {
+ bitmask |= NOTIFICATIONS_DISABLED;
+ }
+
+ if (areCallNotificationsDisabled(context)) {
+ bitmask |= CALL_NOTIFICATIONS_DISABLED;
+ }
+
+ if (isCallChannelInvalid(context)) {
+ bitmask |= CALL_CHANNEL_INVALID;
+ }
+
+ if (isBackgroundRestricted(context)) {
+ bitmask |= BACKGROUND_RESTRICTED;
+ }
+
+ return bitmask;
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java
index 1d2cb3597a..47e0480032 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java
@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@@ -13,6 +14,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -219,10 +221,15 @@ public class NotificationChannels {
return;
}
- Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
- intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
- context.startActivity(intent);
+ try {
+ Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Channel settings activity not found", e);
+ Toast.makeText(context, R.string.NotificationChannels__no_activity_available_to_open_notification_channel_settings, Toast.LENGTH_SHORT).show();
+ }
}
/**
@@ -413,6 +420,16 @@ public class NotificationChannels {
return group != null && !group.isBlocked();
}
+ public static boolean isCallsChannelValid(@NonNull Context context) {
+ if (!supported()) {
+ return true;
+ }
+
+ NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
+ NotificationChannel channel = notificationManager.getNotificationChannel(CALLS);
+
+ return channel != null && channel.getImportance() == NotificationManager.IMPORTANCE_HIGH;
+ }
/**
* Whether or not notifications for the entire app are enabled.
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java
index fba10f50c3..f709ad932a 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java
@@ -31,10 +31,8 @@ import org.signal.ringrtc.HttpHeader;
import org.signal.ringrtc.Remote;
import org.signal.storageservice.protos.groups.GroupExternalCredential;
import org.signal.zkgroup.VerificationFailedException;
-import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.WebRtcCallActivity;
-import org.thoughtcrime.securesms.components.sensors.DeviceOrientationMonitor;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -57,6 +55,7 @@ import org.thoughtcrime.securesms.service.webrtc.IdleActionProcessor;
import org.thoughtcrime.securesms.service.webrtc.WebRtcInteractor;
import org.thoughtcrime.securesms.service.webrtc.WebRtcUtil;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
+import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.BubbleUtil;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
@@ -90,10 +89,14 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import static org.thoughtcrime.securesms.events.WebRtcViewModel.GroupCallState.IDLE;
+import static org.thoughtcrime.securesms.events.WebRtcViewModel.State.CALL_INCOMING;
+
public class WebRtcCallService extends Service implements CallManager.Observer,
- BluetoothStateManager.BluetoothStateListener,
- CameraEventListener,
- GroupCall.Observer
+ BluetoothStateManager.BluetoothStateListener,
+ CameraEventListener,
+ GroupCall.Observer,
+ AppForegroundObserver.Listener
{
private static final String TAG = Log.tag(WebRtcCallService.class);
@@ -276,6 +279,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
new SignalAudioManager(this),
bluetoothStateManager,
this,
+ this,
this);
return true;
}
@@ -479,15 +483,16 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
return listenableFutureTask;
}
- public void startCallCardActivityIfPossible() {
+ public boolean startCallCardActivityIfPossible() {
if (Build.VERSION.SDK_INT >= 29 && !ApplicationDependencies.getAppForegroundObserver().isForegrounded()) {
- return;
+ return false;
}
Intent activityIntent = new Intent();
activityIntent.setClass(this, WebRtcCallActivity.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
+ return true;
}
private static @NonNull OfferMessage.Type getOfferTypeFromCallMediaType(@NonNull CallManager.CallMediaType mediaType) {
@@ -516,6 +521,18 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
return null;
}
+ @Override
+ public void onForeground() {
+ WebRtcViewModel.State callState = serviceState.getCallInfoState().getCallState();
+ if (callState == CALL_INCOMING && serviceState.getCallInfoState().getGroupCallState() == IDLE) {
+ startCallCardActivityIfPossible();
+ }
+ ApplicationDependencies.getAppForegroundObserver().removeListener(this);
+ }
+
+ @Override
+ public void onBackground() { }
+
private static class WiredHeadsetStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallSetupActionProcessorDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallSetupActionProcessorDelegate.java
index 61e00d20de..cb0a148e98 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallSetupActionProcessorDelegate.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallSetupActionProcessorDelegate.java
@@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.signal.ringrtc.CallException;
import org.signal.ringrtc.CallManager;
+import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.ringrtc.CallState;
import org.thoughtcrime.securesms.ringrtc.Camera;
@@ -37,6 +38,7 @@ public class CallSetupActionProcessorDelegate extends WebRtcActionProcessor {
RemotePeer activePeer = currentState.getCallInfoState().requireActivePeer();
+ ApplicationDependencies.getAppForegroundObserver().removeListener(webRtcInteractor.getForegroundListener());
webRtcInteractor.startAudioCommunication(activePeer.getState() == CallState.REMOTE_RINGING);
webRtcInteractor.setWantsBluetoothConnection(true);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java
index 7071469ea5..59ff052d49 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java
@@ -12,6 +12,7 @@ import org.signal.ringrtc.CallId;
import org.thoughtcrime.securesms.components.webrtc.OrientationAwareVideoSink;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
+import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.notifications.DoNotDisturbUtil;
@@ -157,7 +158,11 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
boolean shouldDisturbUserWithCall = DoNotDisturbUtil.shouldDisturbUserWithCall(context.getApplicationContext(), recipient);
if (shouldDisturbUserWithCall) {
- webRtcInteractor.startWebRtcCallActivityIfPossible();
+ boolean started = webRtcInteractor.startWebRtcCallActivityIfPossible();
+ if (!started) {
+ Log.i(TAG, "Unable to start call activity due to OS version or not being in the foreground");
+ ApplicationDependencies.getAppForegroundObserver().addListener(webRtcInteractor.getForegroundListener());
+ }
}
webRtcInteractor.initializeAudioForCall();
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java
index 92f7ec93e8..9456a42863 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java
@@ -13,6 +13,7 @@ import org.signal.ringrtc.CallId;
import org.signal.ringrtc.GroupCall;
import org.thoughtcrime.securesms.components.sensors.Orientation;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
+import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -723,6 +724,8 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
+ ApplicationDependencies.getAppForegroundObserver().removeListener(webRtcInteractor.getForegroundListener());
+
webRtcInteractor.updatePhoneState(LockManager.PhoneState.PROCESSING);
webRtcInteractor.stopForegroundService();
boolean playDisconnectSound = (activePeer.getState() == CallState.DIALING) ||
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java
index 465177b98f..8dd7693a72 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java
@@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.ringrtc.CameraEventListener;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
+import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.webrtc.audio.BluetoothStateManager;
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger;
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
@@ -29,13 +30,14 @@ import java.util.UUID;
*/
public class WebRtcInteractor {
- @NonNull private final WebRtcCallService webRtcCallService;
- @NonNull private final CallManager callManager;
- @NonNull private final LockManager lockManager;
- @NonNull private final SignalAudioManager audioManager;
- @NonNull private final BluetoothStateManager bluetoothStateManager;
- @NonNull private final CameraEventListener cameraEventListener;
- @NonNull private final GroupCall.Observer groupCallObserver;
+ @NonNull private final WebRtcCallService webRtcCallService;
+ @NonNull private final CallManager callManager;
+ @NonNull private final LockManager lockManager;
+ @NonNull private final SignalAudioManager audioManager;
+ @NonNull private final BluetoothStateManager bluetoothStateManager;
+ @NonNull private final CameraEventListener cameraEventListener;
+ @NonNull private final GroupCall.Observer groupCallObserver;
+ @NonNull private final AppForegroundObserver.Listener foregroundListener;
public WebRtcInteractor(@NonNull WebRtcCallService webRtcCallService,
@NonNull CallManager callManager,
@@ -43,7 +45,8 @@ public class WebRtcInteractor {
@NonNull SignalAudioManager audioManager,
@NonNull BluetoothStateManager bluetoothStateManager,
@NonNull CameraEventListener cameraEventListener,
- @NonNull GroupCall.Observer groupCallObserver)
+ @NonNull GroupCall.Observer groupCallObserver,
+ @NonNull AppForegroundObserver.Listener foregroundListener)
{
this.webRtcCallService = webRtcCallService;
this.callManager = callManager;
@@ -52,6 +55,7 @@ public class WebRtcInteractor {
this.bluetoothStateManager = bluetoothStateManager;
this.cameraEventListener = cameraEventListener;
this.groupCallObserver = groupCallObserver;
+ this.foregroundListener = foregroundListener;
}
@NonNull CameraEventListener getCameraEventListener() {
@@ -70,6 +74,10 @@ public class WebRtcInteractor {
return groupCallObserver;
}
+ @NonNull AppForegroundObserver.Listener getForegroundListener() {
+ return foregroundListener;
+ }
+
void setWantsBluetoothConnection(boolean enabled) {
bluetoothStateManager.setWantsConnection(enabled);
}
@@ -118,8 +126,8 @@ public class WebRtcInteractor {
webRtcCallService.insertMissedCall(remotePeer, signal, timestamp, isVideoOffer);
}
- void startWebRtcCallActivityIfPossible() {
- webRtcCallService.startCallCardActivityIfPossible();
+ boolean startWebRtcCallActivityIfPossible() {
+ return webRtcCallService.startCallCardActivityIfPossible();
}
void registerPowerButtonReceiver() {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java
index 4f27cd446d..168c718ea1 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java
@@ -987,6 +987,10 @@ public class TextSecurePreferences {
return getBooleanPreference(context, NOTIFICATION_PREF, true);
}
+ public static void setCallNotificationsEnabled(Context context, boolean enabled) {
+ setBooleanPreference(context, CALL_NOTIFICATIONS_PREF, enabled);
+ }
+
public static boolean isCallNotificationsEnabled(Context context) {
return getBooleanPreference(context, CALL_NOTIFICATIONS_PREF, true);
}
diff --git a/app/src/main/res/drawable/ic_check_24.xml b/app/src/main/res/drawable/ic_check_24.xml
index 3f70fa3fe8..6554551de0 100644
--- a/app/src/main/res/drawable/ic_check_24.xml
+++ b/app/src/main/res/drawable/ic_check_24.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/app/src/main/res/drawable/ic_info_white_24.xml b/app/src/main/res/drawable/ic_info_white_24.xml
index e0e1e9d88a..27437af495 100644
--- a/app/src/main/res/drawable/ic_info_white_24.xml
+++ b/app/src/main/res/drawable/ic_info_white_24.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/app/src/main/res/layout/enable_call_notification_settings_dialog_fragment.xml b/app/src/main/res/layout/enable_call_notification_settings_dialog_fragment.xml
new file mode 100644
index 0000000000..075132a598
--- /dev/null
+++ b/app/src/main/res/layout/enable_call_notification_settings_dialog_fragment.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9020c8e5ca..8386f7c7de 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -640,7 +640,7 @@
- Error revoking invite
- Error revoking invites
-
+
Pending member requests
No member requests to show.
@@ -799,7 +799,7 @@
Enabled
Disabled
Default
-
+
Shareable group link
Manage & share
@@ -1722,7 +1722,7 @@
Messages
Unknown
Voice Notes
-
+ No activity available to open notification channel settings.
@@ -1871,6 +1871,7 @@
Return to call
Call is full
Invite friends
+ Enable Call Notifications
Play … Pause
@@ -1899,6 +1900,19 @@
View
Previous verified
+
+ Call notifications enabled.
+ Enable call notifications
+ Enable background activity
+ Everything looks good now!
+ To receive call notifications, tap here and turn on \"Show notifications.\"
+ To receive call notifications, tap here and turn on notifications and make sure Sound and Pop-up are enabled.
+ To receive call notifications, tap here and enable background activity in \"Battery\" settings.
+ Settings
+ To receive call notifications, tap Settings and turn on \"Show notifications.\"
+ To receive call notifications, tap Settings and turn on notifications and make sure Sound and Pop-up are enabled.
+ To receive call notifications, tap Settings and enable background activity in \"Battery\" settings.
+
Loading countries…
Search