diff --git a/src/main/java/org/medicmobile/webapp/mobile/DomainVerificationActivity.java b/src/main/java/org/medicmobile/webapp/mobile/DomainVerificationActivity.java index 29ad28f1..c4fcfcf5 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/DomainVerificationActivity.java +++ b/src/main/java/org/medicmobile/webapp/mobile/DomainVerificationActivity.java @@ -2,19 +2,15 @@ import static org.medicmobile.webapp.mobile.MedicLog.trace; +import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.verify.domain.DomainVerificationManager; -import android.content.pm.verify.domain.DomainVerificationUserState; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.view.View; +import android.widget.TextView; -import java.util.Map; public class DomainVerificationActivity extends Activity { @Override @@ -22,40 +18,17 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); trace(this, "onCreate()"); - if (!this.checkIfDomainsAreVerified()) { - setContentView(R.layout.request_app_domain_association); - } else { - finish(); - } - } - - private boolean checkIfDomainsAreVerified() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - Context context = getApplicationContext(); - DomainVerificationManager manager = - context.getSystemService(DomainVerificationManager.class); - try { - DomainVerificationUserState userState = - manager.getDomainVerificationUserState(context.getPackageName()); - - Map hostToStateMap = userState.getHostToStateMap(); - - for (String stateValue : hostToStateMap.values()) { + setContentView(R.layout.request_app_domain_association); - if (stateValue != DomainVerificationUserState.DOMAIN_STATE_VERIFIED && stateValue != DomainVerificationUserState.DOMAIN_STATE_SELECTED) { - return false; - } - } - } catch (PackageManager.NameNotFoundException e) { - return true; - } - } - return true; + String appName = getResources().getString(R.string.app_name); + String title = getResources().getString(R.string.domainAppAssociationTitle); + TextView field = findViewById(R.id.domainAppAssociationTitleText); + field.setText(String.format(title, appName)); } public void onClickOk(View view) { trace(this, "DomainVerificationActivity :: User agreed with prominent disclosure message."); - Intent intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, Uri.parse("package:" + this.getPackageName())); + @SuppressLint("InlinedApi") Intent intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, Uri.parse("package:" + this.getPackageName())); this.startActivity(intent); finish(); } diff --git a/src/main/java/org/medicmobile/webapp/mobile/StartupActivity.java b/src/main/java/org/medicmobile/webapp/mobile/StartupActivity.java index 3f998599..e1337a20 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/StartupActivity.java +++ b/src/main/java/org/medicmobile/webapp/mobile/StartupActivity.java @@ -1,9 +1,11 @@ package org.medicmobile.webapp.mobile; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; +import android.util.Log; import static org.medicmobile.webapp.mobile.MedicLog.trace; import static org.medicmobile.webapp.mobile.Utils.createUseragentFrom; @@ -32,7 +34,9 @@ private void configureAndStartNextActivity() { } private void startDomainVerificationActivity() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + Context context = getApplicationContext(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !Utils.checkIfDomainsAreVerified(context)) { Intent intent = new Intent(this, DomainVerificationActivity.class); startActivity(intent); finish(); diff --git a/src/main/java/org/medicmobile/webapp/mobile/Utils.java b/src/main/java/org/medicmobile/webapp/mobile/Utils.java index e4188663..210dd240 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/Utils.java +++ b/src/main/java/org/medicmobile/webapp/mobile/Utils.java @@ -3,16 +3,22 @@ import static org.medicmobile.webapp.mobile.BuildConfig.APPLICATION_ID; import static org.medicmobile.webapp.mobile.BuildConfig.DEBUG; import static org.medicmobile.webapp.mobile.BuildConfig.VERSION_NAME; +import static org.medicmobile.webapp.mobile.MedicLog.warn; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationUserState; import android.net.Uri; +import android.os.Build; import org.json.JSONException; import org.json.JSONObject; import java.io.File; +import java.util.Map; import java.util.Optional; final class Utils { @@ -81,7 +87,7 @@ static String createUseragentFrom(String current) { if(current.contains(APPLICATION_ID)) return current; return String.format("%s %s/%s", - current, APPLICATION_ID, VERSION_NAME); + current, APPLICATION_ID, VERSION_NAME); } static void restartApp(Context context) { @@ -117,4 +123,26 @@ static Optional getUriFromFilePath(String path) { static boolean isDebug() { return DEBUG; } + + static boolean checkIfDomainsAreVerified(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + DomainVerificationManager manager = + context.getSystemService(DomainVerificationManager.class); + try { + DomainVerificationUserState userState = + manager.getDomainVerificationUserState(context.getPackageName()); + + Map hostToStateMap = userState.getHostToStateMap(); + + for (Integer stateValue : hostToStateMap.values()) { + if (stateValue != DomainVerificationUserState.DOMAIN_STATE_VERIFIED && stateValue != DomainVerificationUserState.DOMAIN_STATE_SELECTED) { + return false; + } + } + } catch (PackageManager.NameNotFoundException e) { + warn(e, "Error while getting package name"); + } + } + return true; + } } diff --git a/src/main/res/layout/request_app_domain_association.xml b/src/main/res/layout/request_app_domain_association.xml index a5a7aa91..900e8beb 100644 --- a/src/main/res/layout/request_app_domain_association.xml +++ b/src/main/res/layout/request_app_domain_association.xml @@ -45,7 +45,8 @@ style="@style/borderlessButton" android:onClick="onClickNegative" android:text="@string/domainAppAssociationRequestDenyButton" - tools:ignore="OnClick" /> + tools:ignore="OnClick" + android:layout_marginTop="10dp"/> + diff --git a/src/main/res/values-bm/strings.xml b/src/main/res/values-bm/strings.xml index 7ab6aeb8..a02060c5 100644 --- a/src/main/res/values-bm/strings.xml +++ b/src/main/res/values-bm/strings.xml @@ -5,10 +5,4 @@ Awɔ Ayi man sɔn Icône de mon emplacement - - Asosiyɛti Domɛn ni Ap - Kɔnsɔn bɛ taali ɲuman ka ɛ saayaɲɔgɔn ladu ni domɛnnuw ye. O tɛ bɛ a to lonkuruw bɛ fere kɛ baara ra mɔgɔ musomɔgɔya ra. Ee bɛ fɛ ka ɛ saayaɲɔgɔn ladu ni domɛnnuw hɛɛrɛhɛrɛninw ye wa? - Domɛn Ayikon - Ɔwan - Ꝋ꙲ diff --git a/src/main/res/values-ceb/strings.xml b/src/main/res/values-ceb/strings.xml index b5c990bb..0238d371 100644 --- a/src/main/res/values-ceb/strings.xml +++ b/src/main/res/values-ceb/strings.xml @@ -5,10 +5,4 @@ Ablihan Dili lang, salamat Ang icon sa akong lokasyon - - I-asosiyo ang Domain sa App - Alang sa labi pang maayong kasinatian, girekomemdar namo nga i-asosiyo kining app sa mga relevante nga domain. Kini masiguro nga ang mga link muabli sirectly sa app.\nGusto ba nimong i-asosiyo kining app sa gikinahanglan nga mga domain? - Ikonikong Domain - Oo - Dili diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 5bd9e829..39f92676 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -29,9 +29,9 @@ Por favor espere a que el proceso de migración haya finalizado - Asociar Dominio con la Aplicación - Para una mejor experiencia, recomendamos asociar esta aplicación con los dominios relevantes. Esto garantizará que los enlaces se abran directamente en la aplicación.\n¿Le gustaría asociar esta aplicación con los dominios necesarios? - Icono de Dominio - - No + ¿Vincular dominio con %s? + Esto garantiza que todos los enlaces se abrirán en su aplicación para mejor experiencia + Icono del dominio + Sí, vincule dominio + No vincular diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 567711e8..a09665ee 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -30,9 +30,9 @@ Veuillez attendre que la migration de données termine Utiliser uniquement pendant la formation - Associer le Domaine avec l\'Application - Pour une meilleure expérience, nous vous recommandons d\'associer cette application aux domaines pertinents. Cela garantira que les liens s\'ouvriront directement dans l\'application.\nSouhaitez-vous associer cette application aux domaines nécessaires ? + Associer le domaine à %s ? + Cela garantit que tous les liens s\'ouvriront dans votre application pour une meilleure expérience. Icône de domaine - Oui - Non + Oui, lier le domaine + Non, ne pas lier diff --git a/src/main/res/values-hi/strings.xml b/src/main/res/values-hi/strings.xml index 59430d15..b6bc81b1 100644 --- a/src/main/res/values-hi/strings.xml +++ b/src/main/res/values-hi/strings.xml @@ -6,9 +6,9 @@ जी नहीं, धन्यवाद मेरा स्थान आइकन - डोमेन को ऐप के साथ संबद्ध करें - बेहतर अनुभव के लिए, हम इस ऐप को संबंधित डोमेन के साथ संबद्ध करने की अनुशंसा करते हैं। इससे यह सुनिश्चित होगा कि लिंक सीधे ऐप में खुलेंगे।\nक्या आप इस ऐप को आवश्यक डोमेन के साथ जोड़ना चाहेंगे? - डोमेन चिह्न - हाँ - नहीं + डोमेन को %s से लिंक करें? + यह सुनिश्चित करता है कि आपके ऐप में सभी लिंक खुलेंगे एक बेहतर अनुभव के लिए + डोमेन का चिह्न + हां, डोमेन लिंक करें + लिंक न करें diff --git a/src/main/res/values-hil/strings.xml b/src/main/res/values-hil/strings.xml index afd81ba0..9afd043b 100644 --- a/src/main/res/values-hil/strings.xml +++ b/src/main/res/values-hil/strings.xml @@ -5,10 +5,4 @@ Buksan Indi na, Salamat nalang Ang icon sang lokasyon ko - - Ikapareho ang Domain sa App - Para sa mas maayo nga eksperyensya, ginarekomendar namon nga ikapareho ang app ini sa mga nabaton nga mga dominyo. Ini magasiguro nga ang mga link magabukas diretso sa sulod sang app.\nGusto mo bala nga ikapareho ang app ini sa kinahanglanon nga mga dominyo? - Icon sang Dominyo - Oo - Indi diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml index bdcb492a..fe1755d9 100644 --- a/src/main/res/values-in/strings.xml +++ b/src/main/res/values-in/strings.xml @@ -6,9 +6,9 @@ Tidak, terima kasih Ikon lokasiku - Asosiasikan Domain dengan Aplikasi - Untuk pengalaman yang lebih baik, kami merekomendasikan untuk mengasosiasikan aplikasi ini dengan domain yang relevan. Hal ini akan memastikan bahwa tautan dibuka langsung di dalam aplikasi.\nApakah Anda ingin mengasosiasikan aplikasi ini dengan domain yang relevan? - Icon Domain - Ya - Tidak + Tautkan domain dengan %s? + Ini memastikan semua tautan akan terbuka di aplikasi Anda untuk pengalaman yang lebih baik. + Ikon Domain + Ya, tautkan domain + Jangan tautkan diff --git a/src/main/res/values-ne/strings.xml b/src/main/res/values-ne/strings.xml index d79759c0..e510f2bf 100644 --- a/src/main/res/values-ne/strings.xml +++ b/src/main/res/values-ne/strings.xml @@ -6,9 +6,9 @@ हुँदैन, धन्यबाद मेरो स्थान आइकन - एप्लिकेशन साथ डोमेन संबद्ध गर्नुहोस् - राम्रो अनुभवको लागि, हामी यस एप्लिकेशनलाई सम्बन्धित डोमेनहरूसँग जोड्न अनुरोध गर्छौं। यसले यो पक्षमा तत्त्वाधिकारी अनुसार खोल्नु हुनेछ। के तपाईं यस एप्लिकेशनलाई आवश्यक डोमेनहरूसँग जोड्न चाहनुहुन्छ? - डोमेन आइकन - हुन्छ + %s डोमेन लिंक गर्नुहुन्छ? + यसले राम्रो अनुभवको लागि तपाइँको एपमा सबै लिङ्कहरू खुल्ने सुनिश्चित गर्दछ। + डोमेन चिन्ह + हुन्छ, डोमेन लिङ्क गर्नुहोस् हुदैन diff --git a/src/main/res/values-tl/strings.xml b/src/main/res/values-tl/strings.xml index 286a8a38..2b24b1c2 100644 --- a/src/main/res/values-tl/strings.xml +++ b/src/main/res/values-tl/strings.xml @@ -6,9 +6,9 @@ Hindi na, Salamat na lang Ang icon ng lokasyon ko - Iugnay ang Domain sa App - Para sa mas magandang karanasan, inirerekomenda namin na iugnay ang aplikasyong ito sa mga naaangkop na domain. Ito ay magtitiyak na ang mga link ay magbubukas nang direkta sa loob ng aplikasyon.\nNais mo bang iugnay ang aplikasyong ito sa mga kinakailangang domain? + I-link ang domain sa %s? + Tinitiyak nito na magbubukas ang lahat ng link sa iyong app para sa mas magandang karanasan. Icon ng Domain - Oo - Hindi + Oo, i-link ang domain + Huwag i-link diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7f4b1e51..a11ba0b3 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -58,9 +58,9 @@ Please wait until the migration process is finished Use only during training - Associate Domain with App - For a better experience, we recommend associating this app with the relevant domains. This will ensure that links open directly in the app.\nWould you like to associate this app with the necessary domains? + Link domain with %s? + This ensures all links will open in your app for a better experience. Domain Icon - Yes - No + Yes, link domain + Don\'t link diff --git a/src/test/java/org/medicmobile/webapp/mobile/DomainVerificationActivityTest.java b/src/test/java/org/medicmobile/webapp/mobile/DomainVerificationActivityTest.java new file mode 100644 index 00000000..abd4c395 --- /dev/null +++ b/src/test/java/org/medicmobile/webapp/mobile/DomainVerificationActivityTest.java @@ -0,0 +1,69 @@ +package org.medicmobile.webapp.mobile; + +import static android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.content.Intent; + +import androidx.lifecycle.Lifecycle; +import androidx.test.core.app.ActivityScenario; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowActivity; + +@RunWith(RobolectricTestRunner.class) +public class DomainVerificationActivityTest { + @Test + public void onClickOk_withSdkVersionGreaterThanS_startAppOpenByDefaultSettings() { + try ( + MockedStatic medicLogMock = mockStatic(MedicLog.class); + ActivityScenario scenario = ActivityScenario.launch(DomainVerificationActivity.class) + ) { + scenario.onActivity(domainVerificationActivity -> { + ShadowActivity shadowActivity = shadowOf(domainVerificationActivity); + + domainVerificationActivity.onClickOk(null); + + Intent startedIntent = shadowActivity.getNextStartedActivity(); + assertNotNull(startedIntent); + assertEquals(ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, startedIntent.getAction()); + assertEquals("package:" + domainVerificationActivity.getPackageName(), startedIntent.getData().toString()); + + medicLogMock.verify(() -> MedicLog.trace( + any(DomainVerificationActivity.class), + eq("DomainVerificationActivity :: User agreed with prominent disclosure message.") + )); + }); + + scenario.moveToState(Lifecycle.State.DESTROYED); + } + } + + @Test + public void onClickNegative_finishActivity() { + try ( + MockedStatic medicLogMock = mockStatic(MedicLog.class); + ActivityScenario scenario = ActivityScenario.launchActivityForResult(DomainVerificationActivity.class) + ) { + scenario.onActivity(domainVerificationActivity -> { + domainVerificationActivity.onClickNegative(null); + + assertEquals(Activity.RESULT_CANCELED, scenario.getResult().getResultCode()); + + medicLogMock.verify(() -> MedicLog.trace( + any(DomainVerificationActivity.class), + eq("DomainVerificationActivity :: User disagreed with prominent disclosure message.") + )); + }); + } + } +} diff --git a/src/test/java/org/medicmobile/webapp/mobile/UtilsTest.java b/src/test/java/org/medicmobile/webapp/mobile/UtilsTest.java index d389b1ca..efe3ddf1 100644 --- a/src/test/java/org/medicmobile/webapp/mobile/UtilsTest.java +++ b/src/test/java/org/medicmobile/webapp/mobile/UtilsTest.java @@ -3,17 +3,27 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationUserState; import android.net.Uri; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; @RunWith(RobolectricTestRunner.class) @@ -30,7 +40,7 @@ public void isUrlRelated_goodNormalUrls() { for(String goodUrl : goodUrls) { assertTrue("Expected URL to be accepted, but it wasn't: " + goodUrl, - Utils.isUrlRelated("https://example.com", Uri.parse(goodUrl))); + Utils.isUrlRelated("https://example.com", Uri.parse(goodUrl))); } } @@ -42,7 +52,7 @@ public void isUrlRelated_goodBlobs() { for(String goodBlobUrl : goodBlobUrls) { assertTrue("Expected URL to be accepted, but it wasn't: " + goodBlobUrl, - Utils.isUrlRelated("https://example.com", Uri.parse(goodBlobUrl))); + Utils.isUrlRelated("https://example.com", Uri.parse(goodBlobUrl))); } } @@ -58,7 +68,7 @@ public void isUrlRelated_badUrls() { for(String badUrl : badUrls) { assertFalse("Expected URL to be rejected, but it wasn't: " + badUrl, - Utils.isUrlRelated("https://example.com", Uri.parse(badUrl))); + Utils.isUrlRelated("https://example.com", Uri.parse(badUrl))); } } @@ -113,58 +123,149 @@ public void validNavigationUrls() { for(String goodBlobUrl : goodBlobUrls) { assertTrue("Expected URL to be accepted, but it wasn't: " + goodBlobUrl, - Utils.isValidNavigationUrl("https://gamma-cht.dev.medicmobile.org", goodBlobUrl)); + Utils.isValidNavigationUrl("https://gamma-cht.dev.medicmobile.org", goodBlobUrl)); } } @Test public void nullUrlsNotValid() { final String[][] nullUrls = { - {null, null}, - {"https://gamma-cht.dev.medicmobile.org", null}, - {null, "https://gamma-cht.dev.medicmobile.org"}, - {"", ""}, + {null, null}, + {"https://gamma-cht.dev.medicmobile.org", null}, + {null, "https://gamma-cht.dev.medicmobile.org"}, + {"", ""}, }; for(String[] nullUrlPair : nullUrls) { assertFalse("Not expected URLs to be accepted, but they were: " + nullUrlPair[0] + " , " + nullUrlPair[1], - Utils.isValidNavigationUrl(nullUrlPair[0], nullUrlPair[1])); + Utils.isValidNavigationUrl(nullUrlPair[0], nullUrlPair[1])); } } @Test public void noMismatchNavigationUrl() { assertFalse(Utils.isValidNavigationUrl( - "https://gamma-cht.dev.medicmobile.org", - "https://example.com/path")); + "https://gamma-cht.dev.medicmobile.org", + "https://example.com/path")); } @Test public void notValidMalformedAppUrl() { assertFalse(Utils.isValidNavigationUrl( - "not-valid-url", - "https://not-valid-url.com/res")); + "not-valid-url", + "https://not-valid-url.com/res")); } @Test public void notValidNavigationUri() { assertFalse(Utils.isValidNavigationUrl( - "https://gamma-cht.dev.medicmobile.org", - "/resource/without/base")); + "https://gamma-cht.dev.medicmobile.org", + "/resource/without/base")); } @Test public void notValidNavigationLoginUri() { assertFalse(Utils.isValidNavigationUrl( - "https://gamma-cht.dev.medicmobile.org", - "https://gamma-cht.dev.medicmobile.org/medic/login?")); + "https://gamma-cht.dev.medicmobile.org", + "https://gamma-cht.dev.medicmobile.org/medic/login?")); } @Test public void notValidNavigationRewriteUri() { assertFalse(Utils.isValidNavigationUrl( - "https://gamma-cht.dev.medicmobile.org", - "https://gamma-cht.dev.medicmobile.org/medic/_rewrite")); + "https://gamma-cht.dev.medicmobile.org", + "https://gamma-cht.dev.medicmobile.org/medic/_rewrite")); + } + + @Test + public void testAllDomainsVerified() throws PackageManager.NameNotFoundException { + Context mockContext = Mockito.mock(Context.class); + + DomainVerificationManager mockManager = Mockito.mock(DomainVerificationManager.class); + DomainVerificationUserState mockUserState = Mockito.mock(DomainVerificationUserState.class); + Map mockHostToStateMap = new HashMap<>(); + mockHostToStateMap.put("domain1.com", DomainVerificationUserState.DOMAIN_STATE_VERIFIED); + mockHostToStateMap.put("domain2.com", DomainVerificationUserState.DOMAIN_STATE_VERIFIED); + Mockito.when(mockUserState.getHostToStateMap()).thenReturn(mockHostToStateMap); + Mockito.when(mockManager.getDomainVerificationUserState(Mockito.anyString())).thenReturn(mockUserState); + Mockito.when(mockContext.getSystemService(DomainVerificationManager.class)).thenReturn(mockManager); + Mockito.when(mockContext.getPackageName()).thenReturn(Mockito.anyString()); + + boolean isVerified = Utils.checkIfDomainsAreVerified(mockContext); + + assertTrue(isVerified); + Mockito.verify(mockManager).getDomainVerificationUserState(Mockito.anyString()); + Mockito.verify(mockUserState).getHostToStateMap(); } -} + @Test + public void testSomeDomainNotVerified() throws PackageManager.NameNotFoundException { + Context mockContext = Mockito.mock(Context.class); + + DomainVerificationManager mockManager = Mockito.mock(DomainVerificationManager.class); + DomainVerificationUserState mockUserState = Mockito.mock(DomainVerificationUserState.class); + Map mockHostToStateMap = new HashMap<>(); + mockHostToStateMap.put("domain1.com", DomainVerificationUserState.DOMAIN_STATE_VERIFIED); + mockHostToStateMap.put("domain2.com", DomainVerificationUserState.DOMAIN_STATE_NONE); + Mockito.when(mockUserState.getHostToStateMap()).thenReturn(mockHostToStateMap); + Mockito.when(mockManager.getDomainVerificationUserState(Mockito.anyString())).thenReturn(mockUserState); + Mockito.when(mockContext.getSystemService(DomainVerificationManager.class)).thenReturn(mockManager); + Mockito.when(mockContext.getPackageName()).thenReturn(Mockito.anyString()); + + boolean isVerified = Utils.checkIfDomainsAreVerified(mockContext); + + assertFalse(isVerified); + Mockito.verify(mockManager).getDomainVerificationUserState(Mockito.anyString()); + Mockito.verify(mockUserState).getHostToStateMap(); + } + + @Test + public void testNoDomains() throws PackageManager.NameNotFoundException { + // Mock Context + Context mockContext = Mockito.mock(Context.class); + + // Mock DomainVerificationManager and UserState + DomainVerificationManager mockManager = Mockito.mock(DomainVerificationManager.class); + DomainVerificationUserState mockUserState = Mockito.mock(DomainVerificationUserState.class); + Map mockHostToStateMap = new HashMap<>(); + Mockito.when(mockUserState.getHostToStateMap()).thenReturn(mockHostToStateMap); + Mockito.when(mockManager.getDomainVerificationUserState(Mockito.anyString())).thenReturn(mockUserState); + + // Mock Context methods + Mockito.when(mockContext.getSystemService(DomainVerificationManager.class)).thenReturn(mockManager); + Mockito.when(mockContext.getPackageName()).thenReturn(Mockito.anyString()); + + // Call the method + boolean isVerified = Utils.checkIfDomainsAreVerified(mockContext); + + assertTrue(isVerified); + Mockito.verify(mockManager).getDomainVerificationUserState(Mockito.anyString()); + Mockito.verify(mockUserState).getHostToStateMap(); + } + + @Test + public void testPackageManagerException() throws PackageManager.NameNotFoundException { + try (MockedStatic mockMedicLog = Mockito.mockStatic(MedicLog.class)) { + // Mock Context + Context mockContext = Mockito.mock(Context.class); + + // Mock DomainVerificationManager and UserState + DomainVerificationManager mockManager = Mockito.mock(DomainVerificationManager.class); + DomainVerificationUserState mockUserState = Mockito.mock(DomainVerificationUserState.class); + Mockito.when(mockManager.getDomainVerificationUserState(Mockito.anyString())).thenThrow(new PackageManager.NameNotFoundException()); + + // Mock Context methods + Mockito.when(mockContext.getSystemService(DomainVerificationManager.class)).thenReturn(mockManager); + Mockito.when(mockContext.getPackageName()).thenReturn(Mockito.anyString()); + // Call the method + boolean isVerified = Utils.checkIfDomainsAreVerified(mockContext); + + assertTrue(isVerified); + Mockito.verify(mockManager).getDomainVerificationUserState(Mockito.anyString()); + mockMedicLog.verify(() -> MedicLog.warn( + any(Exception.class), + eq("Error while getting package name") + )); + } + } +}