kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix call ringtone not playing on some custom ROMs and Samsung Android 11 devices.
rodzic
243b4b9414
commit
2144dc3b67
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Ładowanie…
Reference in New Issue