Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed starting activities #4878

Merged
merged 52 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2dd2d17
Implemented simple IntentLauncher class
grzesiek2010 Oct 22, 2021
a9a9f22
Use IntentLauncher instead of ActivityAvailability in MediaUtils
grzesiek2010 Oct 22, 2021
e062843
Converted MediaUtils to kotlin
grzesiek2010 Oct 22, 2021
3e84a07
Added launchForResult() to IntentLauncher
grzesiek2010 Oct 22, 2021
677f4eb
Added IntentLauncher interface to allow replacing the implementation …
grzesiek2010 Oct 22, 2021
3ab64e4
Use IntentLauncher instead of ActivityAvailability in OSMWidget
grzesiek2010 Oct 22, 2021
9e78be4
Fixed OSMWidgetTest
grzesiek2010 Oct 22, 2021
7e059f5
Extended IntentLauncher to support ActivityResultLauncher
grzesiek2010 Oct 22, 2021
cc272e4
Use IntentLauncher instead of ActivityAvailability in QrCodeProjectCr…
grzesiek2010 Oct 22, 2021
e1a7459
Use IntentLauncher instead of ActivityAvailability in QrCodeMenuDelegate
grzesiek2010 Oct 22, 2021
c1ed8c0
Fixed QRCodeMenuDelegateTest
grzesiek2010 Oct 22, 2021
67fada9
Use IntentLauncher instead of ActivityAvailability in ManualProjectCr…
grzesiek2010 Oct 22, 2021
d80133b
Fixed ManualProjectCreatorDialogTest
grzesiek2010 Oct 22, 2021
c1400c1
Use IntentLauncher instead of ActivityAvailability in GetContentAudio…
grzesiek2010 Oct 22, 2021
42ea455
Fixed GetContentAudioFileRequesterTest
grzesiek2010 Oct 22, 2021
8706e70
Converted GetContentAudioFileRequester and its test to kotlin
grzesiek2010 Oct 22, 2021
e915d35
Use IntentLauncher instead of ActivityAvailability in ExternalAppReco…
grzesiek2010 Oct 22, 2021
53db64e
Fixed ExternalAppRecordingRequesterTest
grzesiek2010 Oct 22, 2021
017069d
Converted ExternalAppRecordingRequester and its test to kotlin
grzesiek2010 Oct 22, 2021
1895385
Code improvements
grzesiek2010 Oct 22, 2021
7b3c9f1
Added new methods in ExternalAppIntentProvider to avoid using Activit…
grzesiek2010 Oct 25, 2021
3f9d0c7
Fixed ExAudioWidget to use the new methods from ExternalAppIntentProv…
grzesiek2010 Oct 25, 2021
027e57d
Fixed ExImageWidget to use the new methods from ExternalAppIntentProv…
grzesiek2010 Oct 25, 2021
05810bc
Fixed ExVideoWidget to use the new methods from ExternalAppIntentProv…
grzesiek2010 Oct 25, 2021
1a98491
Fixed ExStringWidgets to use the new methods from ExternalAppIntentPr…
grzesiek2010 Oct 25, 2021
2038872
Cleared ExternalAppIntentProviderTest
grzesiek2010 Oct 25, 2021
9a54da7
Removed unused ActivityAvailability class
grzesiek2010 Oct 25, 2021
e7cb298
Fixed imports
grzesiek2010 Oct 25, 2021
a883043
Do not use queryIntentActivities in AboutActivity
grzesiek2010 Oct 25, 2021
2b2d0a7
Added tests for IntentLauncherImpl
grzesiek2010 Oct 26, 2021
60b0efc
Extracted ExWidgetIntentLauncher and used it in ExAudioWidget
grzesiek2010 Oct 26, 2021
bb3b884
Use new ExWidgetIntentLauncher in ExVideoWidget
grzesiek2010 Oct 26, 2021
b8ce5be
Use new ExWidgetIntentLauncher in ExImageWidget
grzesiek2010 Oct 26, 2021
c3c2f8c
Use new ExWidgetIntentLauncher in ExArbitraryFileWidget
grzesiek2010 Oct 26, 2021
f594f97
Use new ExWidgetIntentLauncher in ExStringWidget
grzesiek2010 Oct 26, 2021
d3818c6
Improved ExWidgetIntentLauncherImpl
grzesiek2010 Oct 26, 2021
f2c977c
Simplified ExWidgetIntentLauncherImpl
grzesiek2010 Oct 26, 2021
cfdff76
Added tests for ExWidgetIntentLauncher#launchForFileWidget
grzesiek2010 Oct 26, 2021
66a27e5
Divided ExWidgetIntentLauncher into two classes
grzesiek2010 Oct 26, 2021
baf1274
Added tests for ExStringWidgetIntentLauncherImpl
grzesiek2010 Oct 27, 2021
e509161
Improved ExFileWidgetIntentLauncherImplTest
grzesiek2010 Oct 27, 2021
dbf424b
Converted ExternalAppIntentProviderTest to kotlin
grzesiek2010 Oct 27, 2021
86f0b06
Removed unused DummyIntentLauncher class
grzesiek2010 Oct 27, 2021
7f826b4
Removed redundant dagger setup for androidshared module
grzesiek2010 Oct 27, 2021
28327e1
Test improvements
grzesiek2010 Oct 27, 2021
e078b27
Moved IntentLauncherImpl to system package and renamed its file after…
grzesiek2010 Oct 27, 2021
ae66398
Pass IntentLauncher and ExternalAppIntentProvider to FileRequester an…
grzesiek2010 Oct 27, 2021
3d77e28
Removed unused field from QuestionWidget
grzesiek2010 Oct 29, 2021
2e9d81d
Fixed IntentLauncher - accept nullable intents
grzesiek2010 Oct 29, 2021
9269fc5
Fixed passing answers in external string widgets
grzesiek2010 Oct 29, 2021
bec3693
Added tests
grzesiek2010 Nov 2, 2021
6ddd8eb
Do not populate external params on a null intent
grzesiek2010 Nov 2, 2021
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
1 change: 1 addition & 0 deletions androidshared/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
testImplementation Dependencies.androidx_test_ext_junit
testImplementation Dependencies.androidx_test_espresso_core
testImplementation Dependencies.robolectric
testImplementation Dependencies.mockito_kotlin

debugImplementation project(':fragmentstest')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.odk.collect.androidshared.system

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher

object IntentLauncherImpl : IntentLauncher {
override fun launch(context: Context, intent: Intent?, onError: () -> Unit) {
try {
context.startActivity(intent)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}

override fun launchForResult(
activity: Activity,
intent: Intent?,
requestCode: Int,
onError: () -> Unit
) {
try {
activity.startActivityForResult(intent, requestCode)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}

override fun launchForResult(
resultLauncher: ActivityResultLauncher<Intent>,
intent: Intent?,
onError: () -> Unit
) {
try {
resultLauncher.launch(intent)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}
}

interface IntentLauncher {
fun launch(context: Context, intent: Intent?, onError: () -> Unit)

fun launchForResult(activity: Activity, intent: Intent?, requestCode: Int, onError: () -> Unit)

fun launchForResult(
resultLauncher: ActivityResultLauncher<Intent>,
intent: Intent?,
onError: () -> Unit
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package org.odk.collect.androidshared.utils

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.odk.collect.androidshared.system.IntentLauncherImpl
import java.lang.Exception

class IntentLauncherImplTest {
private val context = mock<Context>()
private val activity = mock<Activity>()
private val activityResultLauncher = mock<ActivityResultLauncher<Intent>>()
private val intent = mock<Intent>()
private val onError = mock<() -> Unit>()
private val intentLauncher = IntentLauncherImpl

@Test
fun `startActivity with given intent should be called on the context when calling IntentLauncher#launch(context, intent, onError)`() {
intentLauncher.launch(context, intent, onError)
verify(context).startActivity(intent)
verifyNoMoreInteractions(onError)
}

@Test
fun `onError should be called if any exception occurs when calling IntentLauncher#launch(context, intent, onError)`() {
whenever(context.startActivity(intent)).then {
throw Exception()
}
intentLauncher.launch(context, intent, onError)
verify(onError).invoke()
}

@Test
fun `onError should be called if any error occurs when calling IntentLauncher#launch(context, intent, onError)`() {
whenever(context.startActivity(intent)).then {
throw Error()
}
intentLauncher.launch(context, intent, onError)
verify(onError).invoke()
}

@Test
fun `startActivityForResult with given intent should be called on the context when calling IntentLauncher#launchForResult(context, intent, requestCode, onError)`() {
intentLauncher.launchForResult(activity, intent, 1, onError)
verify(activity).startActivityForResult(intent, 1)
verifyNoMoreInteractions(onError)
}

@Test
fun `onError should be called if any exception occurs when calling IntentLauncher#launchForResult(context, intent, requestCode, onError)`() {
whenever(activity.startActivityForResult(intent, 1)).then {
throw Exception()
}
intentLauncher.launchForResult(activity, intent, 1, onError)
verify(onError).invoke()
}

@Test
fun `onError should be called if any error occurs when calling IntentLauncher#launchForResult(context, intent, requestCode, onError)`() {
whenever(activity.startActivityForResult(intent, 1)).then {
throw Error()
}
intentLauncher.launchForResult(activity, intent, 1, onError)
verify(onError).invoke()
}

@Test
fun `startActivityForResult with given intent should be called on the context when calling IntentLauncher#launchForResult(resultLauncher, intent, onError)`() {
intentLauncher.launchForResult(activityResultLauncher, intent, onError)
verify(activityResultLauncher).launch(intent)
verifyNoMoreInteractions(onError)
}

@Test
fun `onError should not be called if no exception occurs when calling IntentLauncher#launchForResult(resultLauncher, intent, onError)`() {
intentLauncher.launchForResult(activityResultLauncher, intent, onError)
verifyNoMoreInteractions(onError)
}

@Test
fun `onError should be called if any exception occurs when calling IntentLauncher#launchForResult(resultLauncher, intent, onError)`() {
whenever(activityResultLauncher.launch(intent)).then {
throw Exception()
}
intentLauncher.launchForResult(activityResultLauncher, intent, onError)
verify(onError).invoke()
}

@Test
fun `onError should be called if any error occurs when calling IntentLauncher#launchForResult(resultLauncher, intent, onError)`() {
whenever(activityResultLauncher.launch(intent)).then {
throw Error()
}
intentLauncher.launchForResult(activityResultLauncher, intent, onError)
verify(onError).invoke()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import android.Manifest;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
Expand All @@ -19,10 +18,8 @@
import org.odk.collect.android.RecordedIntentsRule;
import org.odk.collect.android.support.CollectTestRule;
import org.odk.collect.android.support.RunnableRule;
import org.odk.collect.android.support.TestDependencies;
import org.odk.collect.android.support.TestRuleChain;
import org.odk.collect.android.support.pages.MainMenuPage;
import org.odk.collect.android.utilities.ActivityAvailability;

import java.io.File;
import java.io.IOException;
Expand All @@ -34,22 +31,10 @@
@RunWith(AndroidJUnit4.class)
public class ExternalAudioRecordingTest {

public final TestDependencies testDependencies = new TestDependencies() {
@Override
public ActivityAvailability providesActivityAvailability(Context context) {
return new ActivityAvailability(context) {
@Override
public boolean isActivityAvailable(Intent intent) {
return true;
}
};
}
};

public final CollectTestRule rule = new CollectTestRule();

@Rule
public final RuleChain chain = TestRuleChain.chain(testDependencies)
public final RuleChain chain = TestRuleChain.chain()
.around(GrantPermissionRule.grant(Manifest.permission.RECORD_AUDIO))
.around(new RecordedIntentsRule())
.around(new RunnableRule(() -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package org.odk.collect.android.feature.smoke;

import android.content.Context;

import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.odk.collect.android.injection.config.AppDependencyModule;
import org.odk.collect.android.support.CopyFormRule;
import org.odk.collect.android.support.FormActivityTestRule;
import org.odk.collect.android.support.ResetStateRule;
import org.odk.collect.android.utilities.ActivityAvailability;

import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
Expand All @@ -25,7 +20,6 @@
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Mockito.mock;

/**
* Integration test that runs through a form with all question types.
Expand All @@ -36,22 +30,14 @@
*/
public class AllWidgetsFormTest {

private final ActivityAvailability activityAvailability = mock(ActivityAvailability.class);

@ClassRule
public static final LocaleTestRule LOCALE_TEST_RULE = new LocaleTestRule();

public FormActivityTestRule activityTestRule = new FormActivityTestRule("all-widgets.xml");

@Rule
public RuleChain copyFormChain = RuleChain
.outerRule(new ResetStateRule(new AppDependencyModule() {
@Override
public ActivityAvailability providesActivityAvailability(Context context) {
return activityAvailability;
}
}))
.around(new CopyFormRule("all-widgets.xml", true))
.outerRule(new CopyFormRule("all-widgets.xml", true))
.around(activityTestRule);

@BeforeClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@

package org.odk.collect.android.activities;

import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.DefaultItemAnimator;
Expand All @@ -31,13 +27,12 @@

import org.odk.collect.android.R;
import org.odk.collect.android.adapters.AboutListAdapter;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.injection.DaggerUtils;
import org.odk.collect.android.utilities.ExternalWebPageHelper;
import org.odk.collect.android.utilities.MultiClickGuard;
import org.odk.collect.androidshared.system.IntentLauncher;

import java.util.List;

import timber.log.Timber;
import javax.inject.Inject;

public class AboutActivity extends CollectAbstractActivity implements
AboutListAdapter.AboutItemClickListener {
Expand All @@ -50,10 +45,15 @@ public class AboutActivity extends CollectAbstractActivity implements
private Uri websiteUri;
private Uri forumUri;

@Inject
IntentLauncher intentLauncher;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about_layout);
DaggerUtils.getComponent(this).inject(this);

initToolbar();

int[][] items = {
Expand Down Expand Up @@ -103,37 +103,16 @@ public void onClick(int position) {
getString(R.string.tell_your_friends)));
break;
case 3:
boolean intentStarted = false;
try {
// Open the google play store app if present
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + getPackageName()));
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo info : list) {
ActivityInfo activity = info.activityInfo;
if (activity.name.contains("com.google.android")) {
ComponentName name = new ComponentName(
activity.applicationInfo.packageName,
activity.name);
intent.setComponent(name);
startActivity(intent);
intentStarted = true;
}
}
} catch (android.content.ActivityNotFoundException anfe) {
Toast.makeText(Collect.getInstance(),
getString(R.string.activity_not_found, "market view"),
Toast.LENGTH_SHORT).show();
Timber.d(anfe);
}
if (!intentStarted) {
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + getPackageName()));
intentLauncher.launch(this, intent, () -> {
// Show a list of all available browsers if user doesn't have a default browser
startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse(GOOGLE_PLAY_URL + getPackageName())));
}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(GOOGLE_PLAY_URL + getPackageName())));
return null;
});
break;
case 4:
Intent intent = new Intent(this, WebViewActivity.class);
intent = new Intent(this, WebViewActivity.class);
intent.putExtra(ExternalWebPageHelper.OPEN_URL, LICENSES_HTML_PATH);
startActivity(intent);
break;
Expand Down
Loading