Skip to content

Commit 63b27be

Browse files
committed
UPDATE: Improve the implementation of OngoingCallTweaker (previously VoiceCall).
1 parent 1b938e8 commit 63b27be

File tree

4 files changed

+113
-56
lines changed

4 files changed

+113
-56
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.oasisfeng.nevo.decorators.wechat;
2+
3+
import android.app.Notification;
4+
import android.content.Context;
5+
import android.media.AudioManager;
6+
import android.media.AudioRecordingConfiguration;
7+
import android.media.MediaRecorder;
8+
import android.os.Bundle;
9+
import android.util.Log;
10+
11+
import com.oasisfeng.nevo.sdk.MutableNotification;
12+
import com.oasisfeng.nevo.sdk.NevoDecoratorService;
13+
14+
import java.util.List;
15+
import java.util.Objects;
16+
17+
import androidx.annotation.RequiresApi;
18+
19+
import static android.os.Build.VERSION.SDK_INT;
20+
import static android.os.Build.VERSION_CODES.N;
21+
import static android.os.Build.VERSION_CODES.O;
22+
import static com.oasisfeng.nevo.decorators.wechat.WeChatDecorator.TAG;
23+
24+
/**
25+
* Tweaks to the voice call on-going notification.
26+
*
27+
* Created by Oasis on 2019-3-17.
28+
*/
29+
class OngoingCallTweaker {
30+
31+
private static final String EXTRA_CALL_START_TIME = "call.start";
32+
33+
boolean apply(final NevoDecoratorService service, final String key, final MutableNotification n) {
34+
if ((n.flags & Notification.FLAG_ONGOING_EVENT) == 0) return false;
35+
final CharSequence text_cs = n.extras.getCharSequence(Notification.EXTRA_TEXT);
36+
if (text_cs == null) return false;
37+
final String text = text_cs.toString();
38+
final String[] keywords = service.getResources().getStringArray(R.array.text_keywords_for_ongoing_call);
39+
for (final String keyword : keywords) {
40+
if (! text.contains(keyword)) continue;
41+
mOngoingKey = key;
42+
Log.i(TAG, "Tweak notification of ongoing call: " + key);
43+
return tweakOngoingCall(n);
44+
}
45+
return false;
46+
}
47+
48+
private boolean tweakOngoingCall(final MutableNotification n) {
49+
n.category = Notification.CATEGORY_CALL;
50+
n.flags |= Notification.FLAG_FOREGROUND_SERVICE; // For EXTRA_COLORIZED to work. (Foreground service is already used by newer version of WeChat)
51+
if (SDK_INT >= O) n.extras.putBoolean(Notification.EXTRA_COLORIZED, true);
52+
if (SDK_INT >= N) getAudioManager().registerAudioRecordingCallback(mAudioRecordingCallback, null);
53+
54+
final long start_time = n.extras.getLong(EXTRA_CALL_START_TIME, 0);
55+
n.when = start_time > 0 ? start_time : System.currentTimeMillis();
56+
n.extras.putBoolean(Notification.EXTRA_SHOW_CHRONOMETER, true);
57+
return true;
58+
}
59+
60+
void onNotificationRemoved(final String key) {
61+
if (! key.equals(mOngoingKey)) return;
62+
Log.i(TAG, "Notification of ongoing call is removed: " + key);
63+
mOngoingKey = null;
64+
unregisterAudioRecordingCallback();
65+
}
66+
67+
void close() {
68+
unregisterAudioRecordingCallback();
69+
}
70+
71+
private void unregisterAudioRecordingCallback() { if (SDK_INT >= N) getAudioManager().unregisterAudioRecordingCallback(mAudioRecordingCallback); }
72+
private AudioManager getAudioManager() { return Objects.requireNonNull((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)); }
73+
74+
interface NotificationUpdater {
75+
void update(String key, Bundle addition);
76+
}
77+
78+
OngoingCallTweaker(final NevoDecoratorService service, final NotificationUpdater updater) {
79+
mContext = service;
80+
mUpdater = updater;
81+
}
82+
83+
@RequiresApi(N) private final AudioManager.AudioRecordingCallback mAudioRecordingCallback = new AudioManager.AudioRecordingCallback() {
84+
85+
@Override public void onRecordingConfigChanged(final List<AudioRecordingConfiguration> configs) {
86+
for (final AudioRecordingConfiguration config : configs) {
87+
final int audio_source = config.getClientAudioSource();
88+
Log.d(TAG, "Detected recording audio source: " + audio_source);
89+
if (audio_source != MediaRecorder.AudioSource.VOICE_COMMUNICATION) continue;
90+
final Bundle updates = new Bundle(); updates.putLong(EXTRA_CALL_START_TIME, System.currentTimeMillis());
91+
mUpdater.update(mOngoingKey, updates);
92+
break;
93+
}
94+
}
95+
};
96+
97+
private final Context mContext;
98+
private final NotificationUpdater mUpdater;
99+
private String mOngoingKey;
100+
}

src/main/java/com/oasisfeng/nevo/decorators/wechat/VoiceCall.java

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/main/java/com/oasisfeng/nevo/decorators/wechat/WeChatDecorator.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public class WeChatDecorator extends NevoDecoratorService {
8080
private static final String OLD_CHANNEL_MISC = "misc"; // old name for migration
8181
private static final String CHANNEL_DND = "message_dnd_mode_channel_id"; // Channel ID used by WeChat for its own DND mode
8282
private static final String CHANNEL_GROUP_CONVERSATION = "group"; // WeChat has no separate group for group conversation
83+
private static final String GROUP_MISC = "misc";
8384

8485
private static final @ColorInt int PRIMARY_COLOR = 0xFF33B332;
8586
private static final @ColorInt int LIGHT_COLOR = 0xFF00FF00;
@@ -99,14 +100,11 @@ public class WeChatDecorator extends NevoDecoratorService {
99100
n.color = PRIMARY_COLOR; // Tint the small icon
100101

101102
final String channel_id = SDK_INT >= O ? n.getChannelId() : null;
102-
if (CHANNEL_MISC.equals(channel_id)) { // Misc. notifications on Android 8+.
103-
if (SDK_INT >= O && (n.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
104-
VoiceCall.tweakIfNeeded(this, n);
105-
} else Log.d(TAG, "Skip further process for non-conversation notification: " + title); // E.g. web login confirmation notification.
106-
return;
107-
} else if (n.tickerText == null) { // Legacy misc. notifications.
103+
if (n.tickerText == null/* Legacy misc. notifications */|| CHANNEL_MISC.equals(channel_id)) {
108104
if (SDK_INT >= O && channel_id == null) n.setChannelId(CHANNEL_MISC);
109-
Log.d(TAG, "Skip further process for non-conversation notification: " + title); // E.g. web login confirmation notification.
105+
n.setGroup(GROUP_MISC); // Avoid being auto-grouped
106+
if (! mOngoingCallTweaker.apply(this, evolving.getOriginalKey(), n))
107+
Log.d(TAG, "Skip further process for non-conversation notification: " + title); // E.g. web login confirmation notification.
110108
return;
111109
}
112110
final CharSequence content_text = extras.getCharSequence(EXTRA_TEXT);
@@ -173,7 +171,7 @@ private boolean isDistinctId(final Notification n, final String pkg) {
173171
@Override protected void onNotificationRemoved(final String key, final int reason) {
174172
if (reason == REASON_APP_CANCEL) { // Only if "Removal-Aware" of Nevolution is activated
175173
Log.d(TAG, "Cancel notification: " + key);
176-
cancelNotification(key); // Will cancel all notifications evolved from this original key, thus trigger the "else" branch below
174+
mOngoingCallTweaker.onNotificationRemoved(key);
177175
} else if (reason == REASON_CHANNEL_BANNED) { // In case WeChat deleted our notification channel for group conversation in Insider delivery mode
178176
mHandler.post(() -> reviveNotification(key));
179177
} else if (SDK_INT < O || reason == REASON_CANCEL) { // Exclude the removal request by us in above case. (Removal-Aware is only supported on Android 8+)
@@ -241,6 +239,7 @@ private boolean isWeChatTargeting26OrAbove() {
241239
mPrefKeyWear = getString(R.string.pref_wear);
242240

243241
mMessagingBuilder = new MessagingBuilder(this, mPreferences, this::recastNotification); // Must be called after loadPreferences().
242+
mOngoingCallTweaker = new OngoingCallTweaker(this, this::recastNotification);
244243
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package");
245244
registerReceiver(mPackageEventReceiver, filter);
246245
registerReceiver(mSettingsChangedReceiver, new IntentFilter(ACTION_SETTINGS_CHANGED));
@@ -249,6 +248,7 @@ private boolean isWeChatTargeting26OrAbove() {
249248
@Override public void onDestroy() {
250249
unregisterReceiver(mSettingsChangedReceiver);
251250
unregisterReceiver(mPackageEventReceiver);
251+
mOngoingCallTweaker.close();
252252
mMessagingBuilder.close();
253253
super.onDestroy();
254254
}
@@ -303,6 +303,7 @@ private static String getDefaultSharedPreferencesName(final Context context) {
303303

304304
private final ConversationManager mConversationManager = new ConversationManager();
305305
private MessagingBuilder mMessagingBuilder;
306+
private OngoingCallTweaker mOngoingCallTweaker;
306307
private boolean mWeChatTargetingO;
307308
private SharedPreferences mPreferences;
308309
private String mPrefKeyWear;

src/main/res/values/values.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
3-
<string-array name="text_prefix_for_voice_call">
4-
<item>语音通话中</item> <!-- 简体 -->
5-
<item>語音通話中</item> <!-- 繁體 -->
6-
<item>Tap to continue as voice call in progress</item> <!-- English -->
3+
<string-array name="text_keywords_for_ongoing_call">
4+
<item>通话</item> <!-- 简体 -->
5+
<item>通話</item> <!-- 繁體 -->
6+
<item>call</item> <!-- English -->
77
</string-array>
88
<string name="pref_activate" translatable="false">activate</string>
99
<string name="pref_extension" translatable="false">extension</string>

0 commit comments

Comments
 (0)