Fix call ringtone not playing on some custom ROMs and Samsung Android 11 devices.

fork-5.53.8
Cody Henthorne 2021-03-26 12:58:17 -04:00 zatwierdzone przez Alex Hart
rodzic 243b4b9414
commit 2144dc3b67
5 zmienionych plików z 176 dodań i 25 usunięć

Wyświetl plik

@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.RingtoneUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import static android.app.Activity.RESULT_OK;
@ -165,10 +166,12 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
if (value == null || TextUtils.isEmpty(value.toString())) {
preference.setSummary(R.string.preferences__silent);
} else {
Ringtone tone = RingtoneManager.getRingtone(getActivity(), value);
Ringtone tone = RingtoneUtil.getRingtone(requireContext(), value);
if (tone != null) {
preference.setSummary(tone.getTitle(getActivity()));
} else {
preference.setSummary(R.string.preferences__default);
}
}

Wyświetl plik

@ -24,16 +24,20 @@ import androidx.lifecycle.ViewModelProviders;
import com.annimon.stream.function.Consumer;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.RingtoneUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.Objects;
public class CustomNotificationsDialogFragment extends DialogFragment {
private static final String TAG = Log.tag(CustomNotificationsDialogFragment.class);
private static final short MESSAGE_RINGTONE_PICKER_REQUEST_CODE = 13562;
private static final short CALL_RINGTONE_PICKER_REQUEST_CODE = 23621;
@ -224,8 +228,7 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
} else if (ringtone.toString().isEmpty()) {
return context.getString(R.string.preferences__silent);
} else {
Ringtone tone = RingtoneManager.getRingtone(getActivity(), ringtone);
Ringtone tone = RingtoneUtil.getRingtone(requireContext(), ringtone);
if (tone != null) {
return tone.getTitle(context);
}

Wyświetl plik

@ -0,0 +1,121 @@
package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Some custom ROMs and some Samsung Android 11 devices have quirks around accessing the default ringtone. This attempts to deal
* with them with progressively worse approaches.
*/
public final class RingtoneUtil {
private static final String TAG = Log.tag(RingtoneUtil.class);
private RingtoneUtil() {}
public static @Nullable Ringtone getRingtone(@NonNull Context context, @NonNull Uri uri) {
Ringtone tone;
try {
tone = RingtoneManager.getRingtone(context, uri);
} catch (SecurityException e) {
Log.w(TAG, "Unable to get default ringtone due to permission", e);
tone = RingtoneManager.getRingtone(context, RingtoneUtil.getActualDefaultRingtoneUri(context));
}
return tone;
}
public static @Nullable Uri getActualDefaultRingtoneUri(@NonNull Context context) {
Log.i(TAG, "Attempting to get default ringtone directly via normal way");
try {
return RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE);
} catch (SecurityException e) {
Log.w(TAG, "Failed to set ringtone with first fallback approach", e);
}
Log.i(TAG, "Attempting to get default ringtone directly via reflection");
String uriString = getStringForUser(context.getContentResolver(), getUserId(context));
Uri ringtoneUri = uriString != null ? Uri.parse(uriString) : null;
if (ringtoneUri != null && getUserIdFromAuthority(ringtoneUri.getAuthority(), getUserId(context)) == getUserId(context)) {
ringtoneUri = getUriWithoutUserId(ringtoneUri);
}
return ringtoneUri;
}
@SuppressWarnings("JavaReflectionMemberAccess")
@SuppressLint("DiscouragedPrivateApi")
private static @Nullable String getStringForUser(@NonNull ContentResolver resolver, int userHandle) {
try {
Method getStringForUser = Settings.System.class.getMethod("getStringForUser", ContentResolver.class, String.class, int.class);
return (String) getStringForUser.invoke(Settings.System.class, resolver, Settings.System.RINGTONE, userHandle);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, "Unable to getStringForUser via reflection", e);
}
return null;
}
@SuppressWarnings("JavaReflectionMemberAccess")
@SuppressLint("DiscouragedPrivateApi")
private static int getUserId(@NonNull Context context) {
try {
Object userId = Context.class.getMethod("getUserId").invoke(context);
if (userId instanceof Integer) {
return (Integer) userId;
} else {
Log.w(TAG, "getUserId did not return an integer");
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
Log.w(TAG, "Unable to getUserId via reflection", e);
}
return 0;
}
private static @Nullable Uri getUriWithoutUserId(@Nullable Uri uri) {
if (uri == null) {
return null;
}
Uri.Builder builder = uri.buildUpon();
builder.authority(getAuthorityWithoutUserId(uri.getAuthority()));
return builder.build();
}
private static @Nullable String getAuthorityWithoutUserId(@Nullable String auth) {
if (auth == null) {
return null;
}
int end = auth.lastIndexOf('@');
return auth.substring(end + 1);
}
private static int getUserIdFromAuthority(@Nullable String authority, int defaultUserId) {
if (authority == null) {
return defaultUserId;
}
int end = authority.lastIndexOf('@');
if (end == -1) {
return defaultUserId;
}
String userIdString = authority.substring(0, end);
try {
return Integer.parseInt(userIdString);
} catch (NumberFormatException e) {
return defaultUserId;
}
}
}

Wyświetl plik

@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.webrtc.audio;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
@ -9,11 +8,13 @@ import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Vibrator;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.RingtoneUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import java.io.IOException;
@ -24,7 +25,7 @@ public class IncomingRinger {
private static final long[] VIBRATE_PATTERN = {0, 1000, 1000};
private final Context context;
private final Context context;
private final Vibrator vibrator;
private MediaPlayer player;
@ -37,8 +38,13 @@ public class IncomingRinger {
public void start(@Nullable Uri uri, boolean vibrate) {
AudioManager audioManager = ServiceUtil.getAudioManager(context);
if (player != null) player.release();
if (uri != null) player = createPlayer(uri);
if (player != null) {
player.release();
}
if (uri != null) {
player = createPlayer(uri);
}
int ringerMode = audioManager.getRingerMode();
@ -61,7 +67,7 @@ public class IncomingRinger {
player = null;
}
} else {
Log.w(TAG, "Not ringing, mode: " + ringerMode);
Log.w(TAG, "Not ringing, player: " + (player != null ? "available" : "null") + " mode: " + ringerMode);
}
}
@ -81,11 +87,6 @@ public class IncomingRinger {
return true;
}
return shouldVibrateNew(context, ringerMode, vibrate);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private boolean shouldVibrateNew(Context context, int ringerMode, boolean vibrate) {
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator == null || !vibrator.hasVibrator()) {
@ -99,26 +100,24 @@ public class IncomingRinger {
}
}
private boolean shouldVibrateOld(Context context, boolean vibrate) {
AudioManager audioManager = ServiceUtil.getAudioManager(context);
return vibrate && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER);
}
private MediaPlayer createPlayer(@NonNull Uri ringtoneUri) {
private @Nullable MediaPlayer createPlayer(@NonNull Uri ringtoneUri) {
try {
MediaPlayer mediaPlayer = new MediaPlayer();
MediaPlayer mediaPlayer = safeCreatePlayer(ringtoneUri);
if (mediaPlayer == null) {
Log.w(TAG, "Failed to create player for incoming call ringer due to custom rom most likely");
return null;
}
mediaPlayer.setOnErrorListener(new MediaPlayerErrorListener());
mediaPlayer.setDataSource(context, ringtoneUri);
mediaPlayer.setLooping(true);
if (Build.VERSION.SDK_INT <= 21) {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
} else {
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
.build());
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
.build());
}
return mediaPlayer;
@ -128,6 +127,30 @@ public class IncomingRinger {
}
}
private @Nullable MediaPlayer safeCreatePlayer(@NonNull Uri ringtoneUri) throws IOException {
try {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(context, ringtoneUri);
return mediaPlayer;
} catch (SecurityException e) {
Log.w(TAG, "Failed to create player with ringtone the normal way", e);
}
if (ringtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) {
try {
Uri defaultRingtoneUri = RingtoneUtil.getActualDefaultRingtoneUri(context);
if (defaultRingtoneUri != null) {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(context, defaultRingtoneUri);
return mediaPlayer;
}
} catch (SecurityException e) {
Log.w(TAG, "Failed to set default ringtone with fallback approach", e);
}
}
return null;
}
private class MediaPlayerErrorListener implements MediaPlayer.OnErrorListener {
@Override

Wyświetl plik

@ -2211,6 +2211,7 @@
<string name="preferences__pref_led_blink_title">LED blink pattern</string>
<string name="preferences__sound">Sound</string>
<string name="preferences__silent">Silent</string>
<string name="preferences__default">Default</string>
<string name="preferences__repeat_alerts">Repeat alerts</string>
<string name="preferences__never">Never</string>
<string name="preferences__one_time">One time</string>