Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
name: Publish Release

on:
push:
branches:
- main
workflow_dispatch:
inputs:
ref:
description: "Branch or commit SHA to run on"
required: false
default: "main"
release_on_maven:
description: "Release on Maven Central"
required: true
default: true
type: boolean

permissions:
contents: write
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
publish:
runs-on: ubuntu-latest

# Auto-run only if this is a rel/* merge commit into main, OR manual dispatch
if: github.event_name == 'workflow_dispatch' ||
(startsWith(github.event.head_commit.message, 'Merge pull request') &&
contains(github.event.head_commit.message, 'rel/'))

env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
Expand Down Expand Up @@ -82,7 +83,16 @@ jobs:
-Psigning.password="$SDK_SIGNING_PASSWORD" \
-Psigning.secretKeyRingFile="$GPG_FILE_PATH"

- name: Publish to Maven Central
- name: Determine Publishing Method
run: |
if [[ "${{ github.event.inputs.release_on_maven }}" == "true" ]]; then
echo "✅ Publishing to Maven Central And Release"
else
echo "🏠 Publishing to Maven Central - Not Releasing"
fi

- name: Publish and Release to Maven Central
if: github.event.inputs.release_on_maven == 'true'
working-directory: OneSignalSDK
run: |
./gradlew publishAndReleaseToMavenCentral --no-configuration-cache \
Expand All @@ -92,6 +102,17 @@ jobs:
-Psigning.password="$SDK_SIGNING_PASSWORD" \
-Psigning.secretKeyRingFile="$GPG_FILE_PATH"

- name: Publish Only to Maven Central
if: github.event.inputs.release_on_maven != 'true'
working-directory: OneSignalSDK
run: |
./gradlew publishToMavenCentral --no-configuration-cache \
-PmavenCentralUsername="$MAVEN_USERNAME" \
-PmavenCentralPassword="$MAVEN_PASSWORD" \
-Psigning.keyId="$SDK_SIGNING_KEY_ID" \
-Psigning.password="$SDK_SIGNING_PASSWORD" \
-Psigning.secretKeyRingFile="$GPG_FILE_PATH"

- name: Get latest merged rel/* PR into main
id: fetch_pr
run: |
Expand Down
14 changes: 14 additions & 0 deletions Examples/OneSignalDemo/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
Expand Down Expand Up @@ -48,6 +49,18 @@ android {
// signingConfig null
// productFlavors.huawei.signingConfig signingConfigs.huawei
debuggable true
// Note: profileable is automatically enabled when debuggable=true
// Enable method tracing for detailed performance analysis
testCoverageEnabled false
}
// Profileable release build for performance testing
profileable {
initWith release
debuggable false
profileable true
minifyEnabled false
signingConfig signingConfigs.debug
matchingFallbacks = ['release']
}
}

Expand All @@ -74,6 +87,7 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
Expand Down
2 changes: 1 addition & 1 deletion Examples/OneSignalDemo/app/src/huawei/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package="com.onesignal.sdktest">

<application
android:name=".application.MainApplication">
android:name=".application.MainApplicationKT">

<service
android:name="com.onesignal.sdktest.notification.HmsMessageServiceAppLevel"
Expand Down
2 changes: 1 addition & 1 deletion Examples/OneSignalDemo/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name=".application.MainApplication"
android:name=".application.MainApplicationKT"
android:allowBackup="true"
android:icon="@mipmap/ic_onesignal_launcher"
android:label="@string/app_name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,26 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* This Java implementation is not used any more. Use {@link MainApplicationKT} instead.
* The Kotlin version provides better async handling and modern coroutines support.
*
*/
public class MainApplication extends MultiDexApplication {
private static final int SLEEP_TIME_TO_MIMIC_ASYNC_OPERATION = 2000;

public MainApplication() {
// run strict mode to surface any potential issues easier
StrictMode.enableDefaults();
Log.w(Tag.LOG_TAG, "MainApplication (Java) is deprecated. Please use MainApplicationKT (Kotlin) instead.");
}

@SuppressLint("NewApi")
@Override
public void onCreate() {
super.onCreate();
Log.w(Tag.LOG_TAG, "DEPRECATED: Using MainApplication (Java). Please migrate to MainApplicationKT (Kotlin) for better async support.");

OneSignal.getDebug().setLogLevel(LogLevel.DEBUG);

// OneSignal Initialization
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.onesignal.sdktest.application

/**
* Modern Kotlin implementation of MainApplication.
*
* This replaces the deprecated MainApplication.java with:
* - Better async handling using Kotlin Coroutines
* - Modern OneSignal API usage
* - Cleaner code structure
* - Proper ANR prevention
*
* @see MainApplication.java (deprecated Java version)
*/
import android.annotation.SuppressLint
import android.os.StrictMode
import android.util.Log
import androidx.annotation.NonNull
import androidx.multidex.MultiDexApplication
import com.onesignal.OneSignal
import com.onesignal.debug.LogLevel
import com.onesignal.inAppMessages.IInAppMessageClickEvent
import com.onesignal.inAppMessages.IInAppMessageClickListener
import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent
import com.onesignal.inAppMessages.IInAppMessageDidDisplayEvent
import com.onesignal.inAppMessages.IInAppMessageLifecycleListener
import com.onesignal.inAppMessages.IInAppMessageWillDismissEvent
import com.onesignal.inAppMessages.IInAppMessageWillDisplayEvent
import com.onesignal.notifications.IDisplayableNotification
import com.onesignal.notifications.INotificationClickEvent
import com.onesignal.notifications.INotificationClickListener
import com.onesignal.notifications.INotificationLifecycleListener
import com.onesignal.notifications.INotificationWillDisplayEvent
import com.onesignal.sdktest.R
import com.onesignal.sdktest.constant.Tag
import com.onesignal.sdktest.constant.Text
import com.onesignal.sdktest.notification.OneSignalNotificationSender
import com.onesignal.sdktest.util.SharedPreferenceUtil
import com.onesignal.user.state.IUserStateObserver
import com.onesignal.user.state.UserChangedState
import com.onesignal.user.state.UserState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

class MainApplicationKT : MultiDexApplication() {

private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)

init {
// run strict mode to surface any potential issues easier
StrictMode.enableDefaults()
}

@SuppressLint("NewApi")
override fun onCreate() {
super.onCreate()
OneSignal.Debug.logLevel = LogLevel.DEBUG

// OneSignal Initialization
var appId = SharedPreferenceUtil.getOneSignalAppId(this)
// If cached app id is null use the default, otherwise use cached.
if (appId == null) {
appId = getString(R.string.onesignal_app_id)
SharedPreferenceUtil.cacheOneSignalAppId(this, appId)
}

OneSignalNotificationSender.setAppId(appId)

// Initialize OneSignal asynchronously on background thread to avoid ANR
applicationScope.launch {
OneSignal.initWithContextSuspend(this@MainApplicationKT, appId)
Log.d(Tag.LOG_TAG, "OneSignal async init completed")

// Set up all OneSignal listeners after successful async initialization
setupOneSignalListeners()

// Request permission - this will internally switch to Main thread for UI operations
// Even though the MainActivity comes on top of this, we can still request permission by tapping the prompt push button.
OneSignal.Notifications.requestPermission(true)

Log.d(Tag.LOG_TAG, Text.ONESIGNAL_SDK_INIT)
}
}

private fun setupOneSignalListeners() {
OneSignal.InAppMessages.addLifecycleListener(object : IInAppMessageLifecycleListener {
override fun onWillDisplay(@NonNull event: IInAppMessageWillDisplayEvent) {
Log.v(Tag.LOG_TAG, "onWillDisplayInAppMessage")
}

override fun onDidDisplay(@NonNull event: IInAppMessageDidDisplayEvent) {
Log.v(Tag.LOG_TAG, "onDidDisplayInAppMessage")
}

override fun onWillDismiss(@NonNull event: IInAppMessageWillDismissEvent) {
Log.v(Tag.LOG_TAG, "onWillDismissInAppMessage")
}

override fun onDidDismiss(@NonNull event: IInAppMessageDidDismissEvent) {
Log.v(Tag.LOG_TAG, "onDidDismissInAppMessage")
}
})

OneSignal.InAppMessages.addClickListener(object : IInAppMessageClickListener {
override fun onClick(event: IInAppMessageClickEvent) {
Log.v(Tag.LOG_TAG, "INotificationClickListener.inAppMessageClicked")
}
})

OneSignal.Notifications.addClickListener(object : INotificationClickListener {
override fun onClick(event: INotificationClickEvent) {
Log.v(Tag.LOG_TAG, "INotificationClickListener.onClick fired" +
" with event: " + event)
}
})

OneSignal.Notifications.addForegroundLifecycleListener(object : INotificationLifecycleListener {
override fun onWillDisplay(@NonNull event: INotificationWillDisplayEvent) {
Log.v(Tag.LOG_TAG, "INotificationLifecycleListener.onWillDisplay fired" +
" with event: " + event)

val notification: IDisplayableNotification = event.notification

//Prevent OneSignal from displaying the notification immediately on return. Spin
//up a new thread to mimic some asynchronous behavior, when the async behavior (which
//takes 2 seconds) completes, then the notification can be displayed.
event.preventDefault()
val r = Runnable {
try {
Thread.sleep(SLEEP_TIME_TO_MIMIC_ASYNC_OPERATION.toLong())
} catch (ignored: InterruptedException) {
}

notification.display()
}

val t = Thread(r)
t.start()
}
})

OneSignal.User.addObserver(object : IUserStateObserver {
override fun onUserStateChange(@NonNull state: UserChangedState) {
val currentUserState: UserState = state.current
Log.v(Tag.LOG_TAG, "onUserStateChange fired " + currentUserState.toJSONObject())
}
})

OneSignal.InAppMessages.paused = true
OneSignal.Location.isShared = false
}

companion object {
private const val SLEEP_TIME_TO_MIMIC_ASYNC_OPERATION = 2000
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.onesignal.sdktest.model;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import com.google.android.material.appbar.AppBarLayout;
Expand Down Expand Up @@ -54,6 +55,9 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RequiresApi(api = Build.VERSION_CODES.N)
public class MainActivityViewModel implements ActivityViewModel, IPushSubscriptionObserver {
Expand Down Expand Up @@ -791,12 +795,6 @@ private void setupPushNotificationLayout() {

private void setupSubscriptionSwitch() {
refreshSubscriptionState();

pushSubscriptionEnabledRelativeLayout.setOnClickListener(v -> {
boolean isSubscriptionEnabled = !pushSubscriptionEnabledSwitch.isChecked();
pushSubscriptionEnabledSwitch.setChecked(isSubscriptionEnabled);
});

// Add a listener to toggle the push notification enablement for the push subscription.
pushSubscriptionEnabledSwitch.setOnClickListener(v -> {
IPushSubscription subscription = OneSignal.getUser().getPushSubscription();
Expand All @@ -811,7 +809,12 @@ private void setupSubscriptionSwitch() {

private void setupPromptPushButton() {
promptPushButton.setOnClickListener(v -> {
OneSignal.getUser().getPushSubscription().optIn();
ExecutorService executor = Executors.newSingleThreadExecutor();
@SuppressLint({"NewApi", "LocalSuppress"}) CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
OneSignal.getNotifications().requestPermission(true, Continue.none());
}, executor);
future.join(); // Waits for the task to complete
executor.shutdown();
});
}

Expand Down
1 change: 1 addition & 0 deletions Examples/OneSignalDemo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:8.8.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.9.1.304'

Expand Down
2 changes: 1 addition & 1 deletion Examples/OneSignalDemo/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ android.enableJetifier=false

# This is the name of the SDK to use when building your project.
# This will be fed from the GitHub Actions workflow.
SDK_VERSION=5.1.37
SDK_VERSION=5.4.0-alpha-02
Loading