Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
8347a64
sonar clean up 5
jonathanbataire May 23, 2025
cabedcc
clean up
jonathanbataire May 23, 2025
3178ecf
optimize worker manager
jonathanbataire May 24, 2025
bf3c426
optimize worker manager
jonathanbataire May 24, 2025
214aa96
clean up
jonathanbataire May 25, 2025
304a479
clean up indent
jonathanbataire May 25, 2025
ef318ff
more
jonathanbataire May 25, 2025
0012b37
clean up
jonathanbataire May 26, 2025
4a3be48
refine
jonathanbataire May 26, 2025
76ef981
add notification default icon
jonathanbataire May 26, 2025
fb794f0
notification id clean up
jonathanbataire May 26, 2025
685822c
notification id clean up
jonathanbataire May 26, 2025
eb1a5b6
align with core changes
jonathanbataire May 27, 2025
07c5ffc
notification intent
jonathanbataire May 27, 2025
ee55f39
:bug: :(
jonathanbataire May 28, 2025
6876138
sonar error
jonathanbataire May 29, 2025
04e1d7c
clean up
jonathanbataire Jun 2, 2025
8222482
clean up 2
jonathanbataire Jun 2, 2025
a184ed7
clean up lint
jonathanbataire Jun 2, 2025
23cbf22
clean up unused import
jonathanbataire Jun 2, 2025
d4c42d8
update deps
jonathanbataire Jun 4, 2025
32a6782
update deps 2
jonathanbataire Jun 4, 2025
eda830d
update deps 3
jonathanbataire Jun 4, 2025
6af9ce2
update deps 4
jonathanbataire Jun 4, 2025
33f1bee
click notification permission dialog
jonathanbataire Jun 4, 2025
4ab9ccb
click notification permission dialog 2
jonathanbataire Jun 4, 2025
87a932d
refactor notification permission in tests
jonathanbataire Jun 4, 2025
8550452
refactor code with api changes
jonathanbataire Jun 12, 2025
f9961cd
Merge branch 'master' into notifications
jonathanbataire Aug 4, 2025
5d6b8c8
clean up manifest
jonathanbataire Aug 4, 2025
cc5f3f0
manifest
jonathanbataire Aug 5, 2025
d9a9317
manifest 2
jonathanbataire Aug 5, 2025
3fab995
clean up layouts
jonathanbataire Aug 5, 2025
e541868
clean up tests
jonathanbataire Aug 5, 2025
13889d8
java 20
jonathanbataire Aug 5, 2025
971111b
revert java version
jonathanbataire Aug 5, 2025
3090c25
notification intent improvements
jonathanbataire Aug 6, 2025
1f45c73
comments
jonathanbataire Aug 6, 2025
f2d592d
clean up code
jonathanbataire Aug 6, 2025
d5f7478
clean up code :tada:
jonathanbataire Aug 6, 2025
596ee5c
clean up sonar error :tada:
jonathanbataire Aug 6, 2025
21eb4e1
improve notification workflow
jonathanbataire Aug 6, 2025
b45c52a
sonar errors
jonathanbataire Aug 6, 2025
0b5373a
clean up 1
jonathanbataire Aug 7, 2025
c5096cc
fix bug
jonathanbataire Aug 7, 2025
121f5cb
clean workflow
jonathanbataire Aug 7, 2025
be2bbd8
clean up
jonathanbataire Aug 7, 2025
fde7541
Merge branch 'master' into notifications
jonathanbataire Aug 8, 2025
171b2ee
Add test
jonathanbataire Aug 8, 2025
ca218f8
address feedback
jonathanbataire Aug 18, 2025
124da15
clean up
jonathanbataire Aug 19, 2025
386d28c
clean up test
jonathanbataire Aug 19, 2025
95bf2ba
sonar errors
jonathanbataire Aug 19, 2025
6584fb8
update lint annotations
jonathanbataire Aug 19, 2025
b8717a7
clean up
jonathanbataire Aug 19, 2025
8fc8286
clean up 2
jonathanbataire Aug 19, 2025
db98fda
improve foreground vs background webview handling
jonathanbataire Aug 22, 2025
0c63562
clean up
jonathanbataire Aug 22, 2025
089a1b1
clean up
jonathanbataire Aug 25, 2025
9140d6c
clean up appnotificationmanager
jonathanbataire Aug 25, 2025
7eac60b
clean up appnotificationmanager 2
jonathanbataire Aug 25, 2025
8b752b3
clean up and feedback
jonathanbataire Aug 27, 2025
cd73a00
clean up notification-manager
jonathanbataire Aug 27, 2025
6ccf399
add delayed handler for foreground notification
jonathanbataire Sep 9, 2025
6b9f6d3
clean up
jonathanbataire Sep 9, 2025
a442e79
feedback update
jonathanbataire Sep 10, 2025
686ad7c
tests :tada:
jonathanbataire Sep 15, 2025
3c5d9f2
tests update
jonathanbataire Sep 15, 2025
f45d9f4
commit 1
jonathanbataire Sep 22, 2025
5f7e21f
feedback updates
jonathanbataire Sep 22, 2025
a4d54e6
clean up
jonathanbataire Sep 22, 2025
9aad6b9
:tada: 01
jonathanbataire Sep 23, 2025
853f674
:tada:
jonathanbataire Sep 23, 2025
c0770f1
clean up
jonathanbataire Sep 23, 2025
e1dd734
clean up handler
jonathanbataire Sep 24, 2025
46fe25d
improve code
jonathanbataire Sep 25, 2025
23ddaba
improve worker code
jonathanbataire Sep 26, 2025
0424cc8
improve handler code
jonathanbataire Sep 27, 2025
9d3bef4
clean up
jonathanbataire Sep 27, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
submodules: true

- name: Set up Java 17
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: '17'
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
submodules: true

- name: Set up Java 17
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: '17'
Expand Down
33 changes: 18 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.4.0'
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.8'
classpath 'com.android.tools.build:gradle:8.10.1'
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.1.13'
}
}

Expand Down Expand Up @@ -477,22 +477,25 @@ dependencies {
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.9.24')
implementation 'androidx.browser:browser:1.8.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.core:core:1.13.1'
implementation 'androidx.activity:activity:1.9.0'
implementation 'androidx.fragment:fragment:1.7.1'
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.8.5'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
implementation 'androidx.work:work-runtime:2.10.3'
implementation 'androidx.concurrent:concurrent-futures:1.3.0'
implementation 'androidx.core:core:1.16.0'
implementation 'androidx.activity:activity:1.10.1'
implementation 'androidx.fragment:fragment:1.8.8'
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.9.3'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:5.2.0'
testImplementation 'com.google.android:android-test:4.1.1.4'
testImplementation 'org.robolectric:robolectric:4.15.1'
testImplementation 'androidx.test.espresso:espresso-core:3.5.1'
testImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
testImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-web:3.5.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
androidTestImplementation 'androidx.test:core:1.5.0'
testImplementation 'androidx.test.espresso:espresso-core:3.7.0'
testImplementation 'androidx.test.espresso:espresso-intents:3.7.0'
testImplementation 'androidx.test.ext:junit:1.3.0'
testImplementation 'androidx.work:work-testing:2.10.3'
androidTestImplementation 'androidx.test.espresso:espresso-web:3.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test:runner:1.7.0'
androidTestImplementation 'androidx.test:rules:1.7.0'
androidTestImplementation 'androidx.test:core:1.7.0'
androidTestImplementation 'org.hamcrest:hamcrest-library:2.2'
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.Manifest;

import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.web.webdriver.DriverAtoms;
import androidx.test.espresso.web.webdriver.Locator;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.GrantPermissionRule;
import androidx.test.filters.LargeTest;

import org.hamcrest.Description;
Expand All @@ -52,16 +54,21 @@ public class LoginTests {
@Rule
public ActivityScenarioRule<SettingsDialogActivity> mActivityTestRule =
new ActivityScenarioRule<>(SettingsDialogActivity.class);
@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(
Manifest.permission.POST_NOTIFICATIONS
);

@Test
public void testLoginScreen() throws Exception {
onData(anything())
.inAdapterView(withId(R.id.lstServers))
.atPosition(0)
.perform(click());
.inAdapterView(withId(R.id.lstServers))
.atPosition(0)
.perform(click());
onView(withText("Continue"))
.inRoot(isDialog())
.perform(click());
.inRoot(isDialog())
.perform(click());
Thread.sleep(7000);//TODO: use better ways to handle delays

ViewInteraction webView = onView(
Expand Down Expand Up @@ -124,4 +131,4 @@ public boolean matchesSafely(View view) {
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.medicmobile.webapp.mobile;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.espresso.web.sugar.Web.onWebView;
import static androidx.test.espresso.web.webdriver.DriverAtoms.clearElement;
import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
import static androidx.test.espresso.web.webdriver.DriverAtoms.webClick;
import static junit.framework.TestCase.assertEquals;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;

import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.web.webdriver.DriverAtoms;
import androidx.test.espresso.web.webdriver.Locator;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.GrantPermissionRule;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

import org.junit.After;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.List;
import java.util.concurrent.ExecutionException;

@RunWith(AndroidJUnit4.class)
public class NotificationTest {
private Context context;
private static final String TEST_SERVER = "";
private static final String TEST_USERNAME = "";
private static final String TEST_PASSWORD = "";

@Rule
public ActivityScenarioRule<SettingsDialogActivity> mActivityTestRule =
new ActivityScenarioRule<>(SettingsDialogActivity.class);
@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(
Manifest.permission.POST_NOTIFICATIONS
);

@After
public void cleanServerRepo() {
context.getSharedPreferences("ServerRepo", Context.MODE_PRIVATE)
.edit().clear().apply();
}

@Ignore("TODO: add TEST_SERVER")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring this test until a test server with these changes medic/cht-core#9987 is available cc @lorerod @jkuester

@SuppressLint("CheckResult")
@Test
public void startsNotificationWorkers() throws InterruptedException, ExecutionException {
context = ApplicationProvider.getApplicationContext();
onView(withText("Custom")).perform(click());
ViewInteraction textAppUrl = onView(withId(R.id.txtAppUrl));
textAppUrl.perform(replaceText(TEST_SERVER), closeSoftKeyboard());
onView(withId(R.id.btnSaveSettings)).perform(click());

Thread.sleep(4000);

assertEquals("expect no worker running at login page", 0, getRunningWorkers());

onWebView()
.withNoTimeout()
.withElement(findElement(Locator.ID, "user"))
.perform(clearElement())
.perform(DriverAtoms.webKeys(TEST_USERNAME)) //to be created first
.withElement(findElement(Locator.ID, "password"))
.perform(clearElement())
.perform(DriverAtoms.webKeys(TEST_PASSWORD))
.withElement(findElement(Locator.ID, "login"))
.perform(webClick());

Thread.sleep(10 * 1000);

ActivityScenario<EmbeddedBrowserActivity> embeddedScenario = ActivityScenario.launch(EmbeddedBrowserActivity.class);
assertEquals("expect no work manager while app is in foreground", 0, getRunningWorkers());

//close app
embeddedScenario.onActivity(EmbeddedBrowserActivity::onBackPressed);
Thread.sleep(1000);
assertEquals("expect work manager enqueued while app is in background", 1, getRunningWorkers());

//reopen app
embeddedScenario = ActivityScenario.launch(EmbeddedBrowserActivity.class);
assertEquals("no work manager on app restart", 0, getRunningWorkers());
}

private long getRunningWorkers() throws ExecutionException, InterruptedException {
List<WorkInfo> workInfos = WorkManager.getInstance(context)
.getWorkInfosByTag(NotificationWorker.NOTIFICATION_WORK_REQUEST_TAG)
.get();
return workInfos.stream()
.filter(info -> info.getState() == WorkInfo.State.ENQUEUED || info.getState() == WorkInfo.State.RUNNING)
.count();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.containsString;

import android.Manifest;

import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.web.webdriver.DriverAtoms;
import androidx.test.espresso.web.webdriver.Locator;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.GrantPermissionRule;

import org.junit.FixMethodOrder;
import org.junit.Rule;
Expand All @@ -57,6 +60,11 @@ public class SettingsDialogActivityTest {
@Rule
public ActivityScenarioRule<SettingsDialogActivity> mActivityTestRule =
new ActivityScenarioRule<>(SettingsDialogActivity.class);
@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(
Manifest.permission.POST_NOTIFICATIONS
);

@Test
public void serverSelectionScreenIsDisplayed() {
Expand All @@ -83,20 +91,20 @@ public void serverSelectionScreenIsDisplayed() {
@Test
public void testLoginScreen() throws Exception {
onData(anything())
.inAdapterView(withId(R.id.lstServers))
.atPosition(1)
.perform(click());
.inAdapterView(withId(R.id.lstServers))
.atPosition(1)
.perform(click());

onView(withText("Login to Gamma Dev?"))
.inRoot(isDialog())
.check(matches(isDisplayed()));
.inRoot(isDialog())
.check(matches(isDisplayed()));
onView(withText("Cancel"))
.inRoot(isDialog())
.check(matches(isDisplayed()));
.inRoot(isDialog())
.check(matches(isDisplayed()));

onView(withText("Continue"))
.inRoot(isDialog())
.perform(click());
.inRoot(isDialog())
.perform(click());

Thread.sleep(7000); //TODO: use better ways to handle delays

Expand Down Expand Up @@ -146,13 +154,13 @@ public void testLoginScreen() throws Exception {
@Test
public void testCancelSelectedServer() {
onData(anything())
.inAdapterView(withId(R.id.lstServers))
.atPosition(2)
.perform(click());
.inAdapterView(withId(R.id.lstServers))
.atPosition(2)
.perform(click());

onView(withText(R.string.btnCancel))
.inRoot(isDialog())
.perform(click());
.inRoot(isDialog())
.perform(click());

onView(withText("CHT Android")).check(matches(isDisplayed()));
onView(withText("Custom")).check(matches(isDisplayed()));
Expand Down Expand Up @@ -197,17 +205,17 @@ public void testFilterServers() throws InterruptedException {
onView(withText(SERVER_THREE)).check(matches(isDisplayed()));

onData(anything())
.inAdapterView(withId(R.id.lstServers))
.atPosition(0)
.perform(click());
.inAdapterView(withId(R.id.lstServers))
.atPosition(0)
.perform(click());

onView(withText("Login to Gamma Dev?"))
.inRoot(isDialog())
.check(matches(isDisplayed()));
.inRoot(isDialog())
.check(matches(isDisplayed()));
}

private String getLanguage(String code) {
Locale aLocale = new Locale(code);
return aLocale.getDisplayName();
}
}
}
8 changes: 7 additions & 1 deletion src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<uses-feature android:name="android.hardware.location.network" android:required="false"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
Expand Down Expand Up @@ -50,7 +51,8 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/scheme" android:host="@string/app_host" android:pathPattern=".*"/>
<data android:scheme="@string/scheme" android:host="@string/app_host" android:pathPattern=".*"
tools:ignore="AppLinkUrlError" />
</intent-filter>
</activity>
<activity android:name="ConnectionErrorActivity"
Expand Down Expand Up @@ -92,6 +94,10 @@
android:resource="@xml/file_paths">
</meta-data>
</provider>
<service android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
tools:targetApi="23" />
Comment on lines +97 to +100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of defining this service here? I tried removing it and it seems like my notifications still get generated as expected... 🤔

Copy link
Contributor Author

@jonathanbataire jonathanbataire Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its just for linting
yeah we can remove it if no errors pop up

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, well I think I was just re-building/running the app when I tried removing it, so maybe there is a deeper linting that will fail if I tried doing the full apk assemble....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbataire Did you ever figure out if this was required?

Copy link
Contributor Author

@jonathanbataire jonathanbataire Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah im able to build APKs but there are linting errors in IDE because of
<androidx.work.impl.background.systemjob.SystemJobService> requires API level 23 (current min is 21)

image

</application>

<queries>
Expand Down
Loading