Huawei: fix voip calls notifications support. Basic support.

This commit is contained in:
Me7c7
2025-11-25 20:56:58 +02:00
parent 539f2b416a
commit d4a1eb5ba1
9 changed files with 121 additions and 10 deletions
@@ -840,6 +840,24 @@ public class HuaweiCoordinator {
return false;
}
public boolean supportsVoipType1() {
if (supportsExpandCapability())
return supportsExpandCapability(249);
return false;
}
public boolean supportsVoipType2() {
if (supportsExpandCapability())
return supportsExpandCapability(185);
return false;
}
public boolean supportsVoipType3() {
if (supportsExpandCapability())
return supportsExpandCapability(152);
return false;
}
public boolean supportsCannedReplies() {
if (supportsExpandCapability())
return supportsExpandCapability(82);
@@ -64,6 +64,8 @@ public class Notifications {
public byte subscriptionId = 0;
public String address = "";
public String category = "";
public int voipType = 0;
public long when = 0;
}
// TODO: support other types of notifications
@@ -90,12 +92,6 @@ public class Notifications {
short msgId,
byte notificationType,
ArrayList<TextElement> content,
// int encoding,
// String titleContent,
// String senderContent,
// String bodyContent,
// int numberFormat,
// String numberContent,
String sourceAppId,
AdditionalParams addParams
) {
@@ -132,7 +128,6 @@ public class Notifications {
this.tlv.put(0x11, sourceAppId.length() > 127?sourceAppId.substring(0, 127): sourceAppId);
if(addParams != null) {
if(!TextUtils.isEmpty(addParams.category)) { // type >= 34
this.tlv.put(0x12, addParams.category); // "imcall" also possible value, not standard for android
}
@@ -151,6 +146,13 @@ public class Notifications {
this.tlv.put(0x1a, addParams.notificationId);
this.tlv.put(0x1b, (addParams.channelId != null) ? addParams.channelId : "");
}
if(addParams.voipType != 0) {
this.tlv.put(0x29, (byte)addParams.voipType);
}
if(addParams.when != 0) {
this.tlv.put(0x32, addParams.when);
}
}
this.complete = true;
@@ -761,6 +761,15 @@ public class NotificationListener extends NotificationListenerService {
if (appName != null) {
callSpec.sourceName = appName;
}
callSpec.isVoip = true;
callSpec.key = sbn.getKey();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
callSpec.channelId = noti.getChannelId();
}
callSpec.category = noti.category;
callSpec.command = callStarted ? CallSpec.CALL_START : CallSpec.CALL_INCOMING;
mLastCallCommand = callSpec.command;
GBApplication.deviceService().onSetCallState(callSpec);
@@ -230,6 +230,10 @@ public class GBDeviceService implements DeviceService {
.putExtra(EXTRA_CALL_DISPLAYNAME, callSpec.name)
.putExtra(EXTRA_CALL_SOURCENAME, callSpec.sourceName)
.putExtra(EXTRA_CALL_SOURCEAPPID, callSpec.sourceAppId)
.putExtra(EXTRA_CALL_KEY, callSpec.key)
.putExtra(EXTRA_CALL_CHANNELID, callSpec.channelId)
.putExtra(EXTRA_CALL_CATEGORY, callSpec.category)
.putExtra(EXTRA_CALL_ISVOIP, callSpec.isVoip)
.putExtra(EXTRA_CALL_COMMAND, callSpec.command)
.putExtra(EXTRA_CALL_DNDSUPPRESSED, callSpec.dndSuppressed);
invokeService(intent);
@@ -36,6 +36,12 @@ public class CallSpec {
*/
public String sourceAppId;
public String key;
public String channelId;
public String category;
public boolean isVoip = false;
public int command;
public int dndSuppressed;
}
@@ -109,6 +109,10 @@ public interface DeviceService extends EventHandler {
String EXTRA_CALL_DISPLAYNAME = "call_displayname";
String EXTRA_CALL_SOURCENAME = "call_sourcename";
String EXTRA_CALL_SOURCEAPPID = "call_sourceappid";
String EXTRA_CALL_KEY = "call_key";
String EXTRA_CALL_CHANNELID = "call_channel_id";
String EXTRA_CALL_CATEGORY = "call_category";
String EXTRA_CALL_ISVOIP = "call_is_voip";
String EXTRA_CALL_DNDSUPPRESSED = "call_dndsuppressed";
String EXTRA_CANNEDMESSAGES = "cannedmessages";
String EXTRA_CANNEDMESSAGES_TYPE = "cannedmessages_type";
@@ -1010,6 +1010,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
callSpec.name = intentCopy.getStringExtra(EXTRA_CALL_DISPLAYNAME);
callSpec.sourceName = intentCopy.getStringExtra(EXTRA_CALL_SOURCENAME);
callSpec.sourceAppId = intentCopy.getStringExtra(EXTRA_CALL_SOURCEAPPID);
callSpec.key = intentCopy.getStringExtra(EXTRA_CALL_KEY);
callSpec.channelId = intentCopy.getStringExtra(EXTRA_CALL_CHANNELID);
callSpec.category = intentCopy.getStringExtra(EXTRA_CALL_CATEGORY);
callSpec.isVoip = intentCopy.getBooleanExtra(EXTRA_CALL_ISVOIP, false);
callSpec.dndSuppressed = intentCopy.getIntExtra(EXTRA_CALL_DNDSUPPRESSED, 0);
deviceSupport.onSetCallState(callSpec);
break;
@@ -28,6 +28,7 @@ import java.util.Queue;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotificationRequest;
@@ -66,6 +67,13 @@ public class HuaweiNotificationsManager {
return "0|" + notificationSpec.sourceAppId + "|" + notificationSpec.getId() + "||0";
}
public static String getCallSpecKey(CallSpec callSpec, int id) {
if(!TextUtils.isEmpty(callSpec.key)) {
return callSpec.key;
}
return "0|" + callSpec.sourceAppId + "|" + id + "||0";
}
public void onNotification(NotificationSpec notificationSpec) {
addNotificationToCache(notificationSpec);
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiNotificationsManager.getCallSpecKey;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiNotificationsManager.getNotificationKey;
import org.slf4j.Logger;
@@ -23,6 +24,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications;
@@ -95,6 +97,7 @@ public class SendNotificationRequest extends Request {
params.channelId = notificationSpec.channelId;
params.category = notificationSpec.category;
params.address = notificationSpec.phoneNumber;
params.when = notificationSpec.when;
ArrayList<Notifications.NotificationActionRequest.TextElement> content = new ArrayList<>();
content.add(
@@ -127,6 +130,11 @@ public class SendNotificationRequest extends Request {
}
public void buildNotificationTLVFromCallSpec(CallSpec callSpec) {
byte notificationType = callSpec.command == CallSpec.CALL_OUTGOING?Notifications.NotificationType.outgoingCall:Notifications.NotificationType.call;
Notifications.NotificationActionRequest.AdditionalParams params = null;
String sourceAppId = null;
ArrayList<Notifications.NotificationActionRequest.TextElement> content = new ArrayList<>();
content.add(
new Notifications.NotificationActionRequest.TextElement(
@@ -134,6 +142,54 @@ public class SendNotificationRequest extends Request {
(byte)supportProvider.getHuaweiCoordinator().getContentFormat(),
callSpec.name)
);
if(callSpec.isVoip && callSpec.command == CallSpec.CALL_INCOMING) {
sourceAppId = callSpec.sourceAppId;
params = new Notifications.NotificationActionRequest.AdditionalParams();
params.supportsReply = supportProvider.getHuaweiCoordinator().supportsNotificationsReply();
params.supportsRepeatedNotify = supportProvider.getHuaweiCoordinator().supportsNotificationsRepeatedNotify();
params.supportsRemoveSingle = supportProvider.getHuaweiCoordinator().supportsNotificationsRemoveSingle();
params.supportsReplyActions = supportProvider.getHuaweiCoordinator().supportsNotificationsReplyActions();
params.supportsTimestamp = supportProvider.getHuaweiCoordinator().supportsNotificationsAddIconTimestamp();
params.notificationId = new Random().nextInt(Integer.MAX_VALUE - 1);
params.notificationKey = getCallSpecKey(callSpec, params.notificationId);
params.channelId = callSpec.channelId;
if(supportProvider.getHuaweiCoordinator().supportsVoipType3()) {
params.category = "imcall";
} else {
params.category = callSpec.category;
}
params.address = null;
if(supportProvider.getHuaweiCoordinator().supportsVoipType2()) {
params.voipType = 1;
}
if (supportProvider.getHuaweiCoordinator().supportsVoipType1() || supportProvider.getHuaweiCoordinator().supportsVoipType2()) {
notificationType = Notifications.NotificationType.generic;
content.add(
new Notifications.NotificationActionRequest.TextElement(
(byte) Notifications.TextType.title,
(byte) supportProvider.getHuaweiCoordinator().getContentFormat(),
callSpec.name)
);
content.add(
new Notifications.NotificationActionRequest.TextElement(
(byte) Notifications.TextType.sender,
(byte) supportProvider.getHuaweiCoordinator().getContentFormat(),
callSpec.name)
);
// TODO: Reject action, need to be parsed from the notification and added here. Then the watch send it back in the service id: 0x2 Command id: 0x11
// content.add(
// new Notifications.NotificationActionRequest.TextElement(
// (byte) 8,
// (byte) supportProvider.getHuaweiCoordinator().getContentFormat(),
// "REJECT_CALL")
// );
}
}
if(supportProvider.getHuaweiCoordinator().supportsIncomingNumber()) {
content.add(
new Notifications.NotificationActionRequest.TextElement(
@@ -142,14 +198,14 @@ public class SendNotificationRequest extends Request {
callSpec.number)
);
}
final byte notificationType = callSpec.command == CallSpec.CALL_OUTGOING?Notifications.NotificationType.outgoingCall:Notifications.NotificationType.call;
this.packet = new Notifications.NotificationActionRequest(
paramsProvider,
supportProvider.getNotificationId(),
notificationType,
content,
null,
null
sourceAppId,
params
);
}