diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1b717a3e5..8438fdf83 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -172,6 +172,11 @@
+
+
requestPermissionLauncher;
-
public static Intent newIntent(Context context, int resItem) {
return new Intent(context, MainActivity.class).putExtra(RES_ITEM, resItem).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
@@ -97,14 +88,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
.withExtra(AUTOTEST_DIALOG)
.build().show(getSupportFragmentManager(), null);
} else if (notificationManager.shouldShow()) {
- new ConfirmDialogFragment.Builder()
- .withTitle(getString(R.string.Modal_EnableNotifications_Title))
- .withMessage(getString(R.string.Modal_EnableNotifications_Paragraph))
- .withPositiveButton(getString(R.string.Modal_SoundsGreat))
- .withNegativeButton(getString(R.string.Modal_NotNow))
- .withNeutralButton(getString(R.string.Modal_DontAskAgain))
- .withExtra(NOTIFICATION_DIALOG)
- .build().show(getSupportFragmentManager(), null);
+ registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
+ if (result.getResultCode() == Activity.RESULT_OK) {}
+ }).launch(PromptActivity.newIntent(this, PromptActivity.Prompt.CENSORSHIP_CONSENT));
}
ThirdPartyServices.checkUpdates(this);
}
@@ -118,46 +104,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
}
- requestNotificationPermission();
}
- private void requestNotificationPermission() {
-
- requestPermissionLauncher = registerForActivityResult(
- new ActivityResultContracts.RequestPermission(),
- (result) -> {
- if (!result) {
- Snackbar.make(
- binding.getRoot(),
- "Please grant Notification permission from App Settings",
- Snackbar.LENGTH_LONG
- ).setAction(R.string.Settings_Title, view -> {
- Intent intent = new Intent();
- intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- //for Android 5-7
- intent.putExtra("app_package", getPackageName());
- intent.putExtra("app_uid", getApplicationInfo().uid);
-
- // for Android 8 and above
- intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName());
-
- startActivity(intent);
- }).show();
- }
- }
- );
- NotificationUtility.setChannel(getApplicationContext(), CHANNEL_ID, getString(R.string.Settings_AutomatedTesting_Label), false, false, false);
- if (ContextCompat.checkSelfPermission(
- this,
- Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
- ) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
- }
- }
- }
@Override
protected void onNewIntent(Intent intent) {
@@ -179,17 +127,6 @@ else if (intent.getExtras().containsKey(NOTIFICATION_DIALOG)){
@Override
public void onConfirmation(Serializable extra, int i) {
if (extra == null) return;
- if (extra.equals(NOTIFICATION_DIALOG)) {
- notificationManager.getUpdates(i == DialogInterface.BUTTON_POSITIVE);
-
- //If positive answer reload consents and init notification
- if (i == DialogInterface.BUTTON_POSITIVE){
- ThirdPartyServices.reloadConsents((Application) getApplication());
- }
- else if (i == DialogInterface.BUTTON_NEUTRAL){
- notificationManager.disableAskNotificationDialog();
- }
- }
if (extra.equals(AUTOTEST_DIALOG)) {
preferenceManager.setNotificationsFromDialog(i == DialogInterface.BUTTON_POSITIVE);
if (i == DialogInterface.BUTTON_POSITIVE){
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/PromptActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/PromptActivity.java
new file mode 100644
index 000000000..303ed944b
--- /dev/null
+++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/PromptActivity.java
@@ -0,0 +1,206 @@
+package org.openobservatory.ooniprobe.activity;
+
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.snackbar.Snackbar;
+
+import org.openobservatory.ooniprobe.R;
+import org.openobservatory.ooniprobe.databinding.ActivityPromptBinding;
+import org.openobservatory.ooniprobe.domain.UpdatesNotificationManager;
+
+import javax.inject.Inject;
+
+public class PromptActivity extends AbstractActivity {
+ private static final String PROMPT_ITEM = "promptItem";
+ @Inject
+ UpdatesNotificationManager notificationManager;
+ private ActivityPromptBinding binding;
+ private Prompt prompt;
+
+ private ActivityResultLauncher requestPermissionLauncher;
+
+ public static Intent newIntent(Context context, Prompt prompt) {
+ return new Intent(context, PromptActivity.class).putExtra(PROMPT_ITEM, prompt);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActivityComponent().inject(this);
+ binding = ActivityPromptBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+ prompt = (Prompt) getIntent().getExtras().get(PROMPT_ITEM);
+ binding.title.setText(prompt.title);
+ binding.description.setText(prompt.paragraph);
+
+ registerPermissionRequest();
+ setUpClickListeners();
+ }
+
+ private void registerPermissionRequest() {
+ requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), (result) -> {
+ if (!result) {
+ Snackbar.make(binding.getRoot(), "Please grant Notification permission from App Settings", Snackbar.LENGTH_LONG).setAction(R.string.Settings_Title, view -> {
+ Intent intent = new Intent();
+ intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ //for Android 5-7
+ intent.putExtra("app_package", getPackageName());
+ intent.putExtra("app_uid", getApplicationInfo().uid);
+
+ // for Android 8 and above
+ intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName());
+
+ startActivity(intent);
+ }).show();
+ }
+ setResult(result ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
+ finish();
+ });
+ }
+
+ private void setUpClickListeners() {
+ OnPromptAction actions;
+ switch (prompt) {
+ case CENSORSHIP_CONSENT:
+ actions = new InternetCensorshipConsentActions();
+ break;
+ case TEST_PROGRESS_CONSENT:
+ actions = new TestProgressNotificationConsentActions();
+ break;
+ default:
+ actions = new OnPromptAction() {
+ @Override
+ public void onClickPositive(View view) { /*No Implementation*/ }
+
+ @Override
+ public void onClickNeutral(View view) { /*No Implementation*/ }
+
+ @Override
+ public void onClickNegative(View view) { /*No Implementation*/ }
+ };
+ }
+
+ binding.soundsGreat.setOnClickListener(actions::onClickPositive);
+ binding.notNow.setOnClickListener(actions::onClickNeutral);
+ binding.dontAskAgain.setOnClickListener(actions::onClickNegative);
+ }
+
+ public void requestNotificationPermission() {
+ if (ContextCompat.checkSelfPermission(
+ this, Manifest.permission.POST_NOTIFICATIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+ }
+ }
+ }
+
+ public enum Prompt {
+ CENSORSHIP_CONSENT(R.string.Modal_EnableNotifications_Title, R.string.Modal_EnableNotifications_Paragraph),
+ TEST_PROGRESS_CONSENT(R.string.Prompt_EnableTestProgressNotifications_Title, R.string.Prompt_EnableTestProgressNotifications_Paragraph);
+
+ private final int title;
+ private final int paragraph;
+
+ Prompt(@StringRes int title, @StringRes int paragraph) {
+ this.title = title;
+ this.paragraph = paragraph;
+ }
+ }
+
+ interface OnPromptAction {
+ /**
+ * Callback for View#onClick of `soundsGreat` button.
+ *
+ * @param view The view that was clicked.
+ */
+ void onClickPositive(View view);
+
+ /**
+ * Callback for View#onClick of `notNow` button.
+ *
+ * @param view The view that was clicked.
+ */
+ void onClickNeutral(View view);
+
+ /**
+ * Callback for View#onClick of `dontAskAgain` button.
+ *
+ * @param view The view that was clicked.
+ */
+ void onClickNegative(View view);
+ }
+
+ private class InternetCensorshipConsentActions implements OnPromptAction {
+
+ @Override
+ public void onClickPositive(View view) {
+ notificationManager.getUpdates(true);
+ if (ContextCompat.checkSelfPermission(
+ PromptActivity.this,
+ Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED){
+ PromptActivity.this.requestNotificationPermission();
+ } else {
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ }
+
+ @Override
+ public void onClickNeutral(View view) {
+ PromptActivity.this.setResult(Activity.RESULT_CANCELED);
+ PromptActivity.this.finish();
+ }
+
+ @Override
+ public void onClickNegative(View view) {
+ notificationManager.disableAskNotificationDialog();
+ onClickNeutral(view);
+ }
+ }
+
+ private class TestProgressNotificationConsentActions implements OnPromptAction {
+
+ @Override
+ public void onClickPositive(View view) {
+ notificationManager.setTestProgressNotificationConsent(true);
+ if (ContextCompat.checkSelfPermission(
+ PromptActivity.this,
+ Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED){
+ PromptActivity.this.requestNotificationPermission();
+ } else {
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ }
+
+ @Override
+ public void onClickNeutral(View view) {
+ PromptActivity.this.setResult(Activity.RESULT_CANCELED);
+ PromptActivity.this.finish();
+ }
+
+ @Override
+ public void onClickNegative(View view) {
+ notificationManager.disableAskTestProgressNotificationConsent();
+ onClickNeutral(view);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java
index c65b13df1..465da02dc 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java
@@ -70,6 +70,11 @@ public static void runAsForegroundService(AbstractActivity context,
ArrayList testSuites,
OnTestServiceStartedListener onTestServiceStartedListener,
PreferenceManager iPreferenceManager) {
+
+ if (iPreferenceManager.shouldShowTestProgressConsent()){
+ context.startActivity(PromptActivity.newIntent(context, PromptActivity.Prompt.TEST_PROGRESS_CONSENT));
+ }
+
if (ReachabilityManager.getNetworkType(context).equals(ReachabilityManager.NO_INTERNET)) {
new MessageDialogFragment.Builder()
.withTitle(context.getString(R.string.Modal_Error))
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManager.java b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManager.java
index ba228a80b..c7b38a860 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManager.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManager.java
@@ -27,6 +27,7 @@ public class PreferenceManager {
public static final int NOTIFICATION_DIALOG_COUNT = 7;
public static final int AUTOTEST_DIALOG_COUNT = 5;
static final String NOTIFICATION_DIALOG_DISABLE = "isNotificationDialogDisabled";
+ static final String TEST_PROGRESS_NOTIFICATION_DISABLE = "isTestProgressNotificationDisabled";
private static final String AUTOTEST_DIALOG_DISABLE = "isAutomaticTestDialogDisabled";
private static final String TOKEN = "token";
static final String SHOW_ONBOARDING = "first_run";
@@ -115,6 +116,31 @@ public boolean isNotifications() {
return sp.getBoolean(r.getString(R.string.notifications_enabled), false);
}
+ public boolean isTestProgressNotifications() {
+ return sp.getBoolean(r.getString(R.string.test_progress_notifications_enabled), false);
+ }
+
+ public boolean isAskTestProgressNotificationConsent() {
+ return sp.getBoolean(TEST_PROGRESS_NOTIFICATION_DISABLE, false);
+ }
+
+ public void disableAskTestProgressNotificationConsent() {
+ sp.edit().putBoolean(TEST_PROGRESS_NOTIFICATION_DISABLE, true)
+ .apply();
+ }
+
+ public void setTestProgressNotificationConsent(boolean enabled) {
+ //set notification value and increment app open
+ sp.edit()
+ .putBoolean(r.getString(R.string.test_progress_notifications_enabled), enabled)
+ .apply();
+ }
+
+ public boolean shouldShowTestProgressConsent() {
+ return !isTestProgressNotifications()
+ && !isAskTestProgressNotificationConsent();
+ }
+
public boolean isDarkTheme() {
return sp.getBoolean(r.getString(R.string.theme_enabled), false);
}
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/di/ActivityComponent.java b/app/src/main/java/org/openobservatory/ooniprobe/di/ActivityComponent.java
index f88835ebf..e3a54992b 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/di/ActivityComponent.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/di/ActivityComponent.java
@@ -7,6 +7,7 @@
import org.openobservatory.ooniprobe.activity.MeasurementDetailActivity;
import org.openobservatory.ooniprobe.activity.OoniRunActivity;
import org.openobservatory.ooniprobe.activity.OverviewActivity;
+import org.openobservatory.ooniprobe.activity.PromptActivity;
import org.openobservatory.ooniprobe.activity.ProxyActivity;
import org.openobservatory.ooniprobe.activity.ResultDetailActivity;
import org.openobservatory.ooniprobe.activity.RunningActivity;
@@ -28,4 +29,5 @@ public interface ActivityComponent {
void inject(RunningActivity activity);
void inject(TextActivity activity);
void inject(LogActivity activity);
+ void inject(PromptActivity activity);
}
\ No newline at end of file
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/domain/UpdatesNotificationManager.java b/app/src/main/java/org/openobservatory/ooniprobe/domain/UpdatesNotificationManager.java
index da20fd3f7..3a4790381 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/domain/UpdatesNotificationManager.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/domain/UpdatesNotificationManager.java
@@ -34,4 +34,12 @@ public void getUpdates(boolean notificationUpdates) {
public void disableAskNotificationDialog() {
pm.disableAskNotificationDialog();
}
+
+ public void disableAskTestProgressNotificationConsent() {
+ pm.disableAskTestProgressNotificationConsent();
+ }
+
+ public void setTestProgressNotificationConsent(boolean enabled) {
+ pm.setTestProgressNotificationConsent(enabled);
+ }
}
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/PreferenceFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/PreferenceFragment.java
index a3de2c4b8..e3daa7f45 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/PreferenceFragment.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/PreferenceFragment.java
@@ -2,10 +2,12 @@
import static org.openobservatory.ooniprobe.common.PreferenceManager.COUNT_WEBSITE_CATEGORIES;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -17,10 +19,14 @@
import android.view.MenuItem;
import android.widget.Toast;
+import androidx.activity.result.ActivityResultCallerLauncher;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.XmlRes;
+import androidx.core.content.ContextCompat;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
@@ -48,6 +54,7 @@ public class PreferenceFragment extends ExtendedPreferenceFragment requestPermissionLauncher;
public static PreferenceFragment newInstance(@XmlRes int preferencesResId, @IdRes int preferencesContainerResId, String rootKey) {
PreferenceFragment fragment = new PreferenceFragment();
@@ -59,6 +66,12 @@ public static PreferenceFragment newInstance(@XmlRes int preferencesResId, @IdRe
return fragment;
}
+ @Override
+ public void onAttach(@NonNull Context context) {
+ requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), (result) -> {});
+ super.onAttach(context);
+ }
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
this.rootKey = rootKey;
@@ -171,9 +184,19 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
if (key.equals(getString(R.string.automated_testing_charging)) ||
key.equals(getString(R.string.automated_testing_wifionly))){
//stop and re-enable scheduler in case of wifi charging option changed
+
ServiceUtil.stopJob(getContext());
ServiceUtil.scheduleJob(getContext());
}
+ if (key.equals(getString(R.string.test_progress_notifications_enabled))){
+ if (ContextCompat.checkSelfPermission(
+ getContext(), Manifest.permission.POST_NOTIFICATIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+ }
+ }
+ }
if (key.equals(getString(R.string.send_crash)) ||
key.equals(getString(R.string.notifications_enabled))){
ThirdPartyServices.reloadConsents((Application) getActivity().getApplication());
diff --git a/app/src/main/res/drawable/ooni_phone.xml b/app/src/main/res/drawable/ooni_phone.xml
new file mode 100644
index 000000000..231083216
--- /dev/null
+++ b/app/src/main/res/drawable/ooni_phone.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_prompt.xml b/app/src/main/res/layout/activity_prompt.xml
new file mode 100644
index 000000000..a1115b081
--- /dev/null
+++ b/app/src/main/res/layout/activity_prompt.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index db87e2f35..1068a4560 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -530,4 +530,7 @@
Benign or innocuous content used for control
Intergovernmental organizations including The United Nations
Sites that haven\'t been categorized yet
+ Don’t ask again
+ Enable test progress notifications
+ Would you like to enable notifications on OONI Probe test progress and display running tests in the notifications drawer?
diff --git a/app/src/main/res/values/untraslatable.xml b/app/src/main/res/values/untraslatable.xml
index f3490ad0d..e2394b3f5 100644
--- a/app/src/main/res/values/untraslatable.xml
+++ b/app/src/main/res/values/untraslatable.xml
@@ -30,6 +30,7 @@
5 - 200 MB
notifications_enabled
+ test_progress_notifications_enabled
notifications_completion
notifications_news
automated_testing_enabled
diff --git a/app/src/main/res/xml/preferences_global.xml b/app/src/main/res/xml/preferences_global.xml
index c973e7ed1..8b3fdc0ac 100644
--- a/app/src/main/res/xml/preferences_global.xml
+++ b/app/src/main/res/xml/preferences_global.xml
@@ -8,8 +8,14 @@
android:title="@string/Settings_Notifications_Label">
+ android:title="@string/Modal_EnableNotifications_Title" />
+