/** * Send push token to server * * @param context Context * @param clear Remove token from sever * @param withCallback Send broadcast after token refresh has been completed or failed
*/ publicstaticvoid enqueuePushTokenUpdate(Context context, boolean clear, boolean withCallback) {
Data workerFlags = new Data.Builder()
.putBoolean(EXTRA_CLEAR_TOKEN, clear)
.putBoolean(EXTRA_WITH_CALLBACK, withCallback)
.build();
Constraints workerConstraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
// worker differs between hms and regular builds, see fcm and hms directory for for overwriting push worker versions
WorkRequest pushTokenRegistrationRequest = new OneTimeWorkRequest.Builder(PushRegistrationWorker.class)
.setInputData(workerFlags)
.setConstraints(workerConstraints)
.build();
/** * Send a push token to the server * * @param token String representing the token * @param type int representing the token type (fcm, hms or none in case of a reset)
*/ publicstaticvoid sendTokenToServer(@NonNull String token, int type) throws ThreemaException {
ServiceManager serviceManager = ThreemaApplication.getServiceManager();
if (serviceManager != null) {
serviceManager.getTaskCreator().scheduleSendPushTokenTask(token, type);
logger.info("Sending push token of type 0x{} successfully scheduled", Integer.toHexString(type)); // Note that the last sent date of the push token is set in the task
} else { thrownew ThreemaException("Unable to send / clear push token. ServiceManager not available");
}
}
/** * Signal a push token update through a local broadcast * * @param error String potential error message * @param clearToken boolean whether the token was reset
*/ publicstaticvoid signalRegistrationFinished(@Nullable String error, boolean clearToken) { final Intent intent = new Intent(AppConstants.INTENT_PUSH_REGISTRATION_COMPLETE); if (error != null) {
logger.error("Failed to get push token {}", error);
intent.putExtra(PushUtil.EXTRA_REGISTRATION_ERROR_BROADCAST, true);
} else {
intent.putExtra(PushUtil.EXTRA_CLEAR_TOKEN, clearToken);
}
LocalBroadcastManager.getInstance(ThreemaApplication.getAppContext()).sendBroadcast(intent);
}
/** * Process the Data mapping received from a FCM message * * @param data Map<String, String> key value pairs with webclient session infos
*/ publicstaticvoid processRemoteMessage(Map<String, String> data) {
logger.info("processRemoteMessage");
// Webclient push if (data != null && data.containsKey(WEBCLIENT_SESSION) && data.containsKey(WEBCLIENT_TIMESTAMP)) {
sendWebclientNotification(data);
} else { // New messages push, trigger a reconnect and show new message notification(s)
sendNotification();
}
}
if (ConfigUtils.isBackgroundDataRestricted(appContext)) {
logger.warn("Network blocked (background data disabled?)"); // The same message may arrive twice (due to a network change). so we simply ignore messages that we were unable to fetch due to a blocked network // Simply schedule a poll when the device is back online
JobScheduler js = (JobScheduler) appContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
js.cancel(RECONNECT_JOB);
JobInfo job = new JobInfo.Builder(RECONNECT_JOB, new ComponentName(appContext, ReConnectJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setRequiresCharging(false)
.build();
@Override publicvoid resetLockTimer(boolean restartAfterReset) { // not needed in this context
}
@Override publicvoid addOnLockAppStateListener(OnLockAppStateListener c) { //not needed in this context
}
}, new ConversationCategoryService() {
@Override publicvoid markContactChatAsPrivate(@NonNull ContactModel contactModel) { // Nothing to do
}
@Override publicvoid removePrivateMarkFromContactChat(@NonNull ContactModel contactModel) { // Nothing to do
}
@Override publicvoid removePrivateMarkFromContactChat(@NonNull ch.threema.storage.models.ContactModel contactModel) { // Nothing to do
}
@Override publicvoid markGroupChatAsPrivate(long groupDatabaseId) { // Nothing to do
}
@Override publicvoid removePrivateMarkFromGroupChat(long groupDatabaseId) { // Nothing to do
}
privatestaticvoid sendWebclientNotification(Map<String, String> data) { final String session = data.get(WEBCLIENT_SESSION); final String timestamp = data.get(WEBCLIENT_TIMESTAMP); final String version = data.get(WEBCLIENT_VERSION); final String affiliationId = data.get(WEBCLIENT_AFFILIATION_ID); if (session != null && !session.isEmpty() && timestamp != null && !timestamp.isEmpty()) {
logger.debug("Received webclient wakeup for session {}", session);
finalThread t = newThread(() -> {
logger.info("Trying to wake up webclient session {}", session);
// Parse version number
Integer versionNumber = null; if (version != null) { // Can be null during beta, if an old client doesn't yet send the version field try {
versionNumber = Integer.parseInt(version, 10);
} catch (NumberFormatException e) {
logger.error("Could not parse webclient protocol version number: ", e); return;
}
}
// Try to wake up session
SessionWakeUpServiceImpl.getInstance()
.resume(session, versionNumber == null ? 0 : versionNumber, affiliationId);
});
t.setName("webclient-wakeup");
t.start();
}
}
/** * Clear the "token last updated" setting in shared preferences * * @param context Context
*/ publicstaticvoid clearPushTokenSentDate(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); if (sharedPreferences != null) {
sharedPreferences
.edit()
.putLong(context.getString(R.string.preferences__token_sent_date), 0L)
.apply();
}
}
/** * Check if the token needs to be uploaded to the server i.e. no more than once a day. * * @param context Context * @return true if more than a day has passed since the token has been last sent to the server, false otherwise
*/ publicstaticboolean pushTokenNeedsRefresh(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences != null) { long lastDate = sharedPreferences.getLong(context.getString(R.string.preferences__token_sent_date), 0L); // refresh push token at least once a day return (System.currentTimeMillis() - lastDate) > DateUtils.DAY_IN_MILLIS;
} returntrue;
}
/** * Check if push services are enabled and polling is not used. * * @param context Context * @return true if polling is disabled or shared preferences are not available, false otherwise
*/ publicstaticboolean isPushEnabled(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.2Bemerkung:
(vorverarbeitet am 2026-04-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.