Nicer rating dialog (#7011)
This commit is contained in:
parent
27aa5cba96
commit
0a6b7ed699
|
@ -125,9 +125,6 @@ dependencies {
|
||||||
implementation 'com.github.skydoves:balloon:1.5.3'
|
implementation 'com.github.skydoves:balloon:1.5.3'
|
||||||
implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3'
|
implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3'
|
||||||
|
|
||||||
// Non-free dependencies:
|
|
||||||
playImplementation "com.google.android.play:review:2.0.1"
|
|
||||||
|
|
||||||
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||||
androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1'
|
androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1'
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||||
|
|
|
@ -25,7 +25,6 @@ import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
|
||||||
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.awaitility.core.ConditionTimeoutException;
|
import org.awaitility.core.ConditionTimeoutException;
|
||||||
|
@ -166,9 +165,6 @@ public class EspressoTestUtils {
|
||||||
.edit()
|
.edit()
|
||||||
.putString(UserPreferences.PREF_UPDATE_INTERVAL, "0")
|
.putString(UserPreferences.PREF_UPDATE_INTERVAL, "0")
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
RatingDialog.init(InstrumentationRegistry.getInstrumentation().getTargetContext());
|
|
||||||
RatingDialog.saveRated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setLaunchScreen(String tag) {
|
public static void setLaunchScreen(String tag) {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package de.danoeh.antennapod.dialog;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
|
|
||||||
public class RatingDialog {
|
|
||||||
public static void init(Context context) {}
|
|
||||||
|
|
||||||
public static void check() {}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void saveRated() {}
|
|
||||||
}
|
|
|
@ -39,7 +39,7 @@ import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter;
|
||||||
import de.danoeh.antennapod.ui.common.ThemeSwitcher;
|
import de.danoeh.antennapod.ui.common.ThemeSwitcher;
|
||||||
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
|
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
|
||||||
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
|
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
|
||||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
import de.danoeh.antennapod.dialog.rating.RatingDialogManager;
|
||||||
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
|
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
|
||||||
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
|
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
|
||||||
import de.danoeh.antennapod.event.MessageEvent;
|
import de.danoeh.antennapod.event.MessageEvent;
|
||||||
|
@ -496,14 +496,13 @@ public class MainActivity extends CastEnabledActivity {
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
RatingDialog.init(this);
|
new RatingDialogManager(this).showIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
handleNavIntent();
|
handleNavIntent();
|
||||||
RatingDialog.check();
|
|
||||||
|
|
||||||
if (lastTheme != ThemeSwitcher.getNoTitleTheme(this)) {
|
if (lastTheme != ThemeSwitcher.getNoTitleTheme(this)) {
|
||||||
finish();
|
finish();
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package de.danoeh.antennapod.dialog.rating;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.text.HtmlCompat;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
|
import de.danoeh.antennapod.databinding.RatingDialogBinding;
|
||||||
|
import de.danoeh.antennapod.ui.common.DateFormatter;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class RatingDialogFragment extends DialogFragment {
|
||||||
|
private static final String EXTRA_TOTAL_TIME = "totalTime";
|
||||||
|
private static final String EXTRA_OLDEST_DATE = "oldestDate";
|
||||||
|
|
||||||
|
public static RatingDialogFragment newInstance(long totalTime, long oldestDate) {
|
||||||
|
RatingDialogFragment fragment = new RatingDialogFragment();
|
||||||
|
Bundle arguments = new Bundle();
|
||||||
|
arguments.putLong(EXTRA_TOTAL_TIME, totalTime);
|
||||||
|
arguments.putLong(EXTRA_OLDEST_DATE, oldestDate);
|
||||||
|
fragment.setArguments(arguments);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
return new MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setView(onCreateView(getLayoutInflater(), null, savedInstanceState))
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
RatingDialogBinding viewBinding = RatingDialogBinding.inflate(inflater);
|
||||||
|
long totalTime = getArguments().getLong(EXTRA_TOTAL_TIME, 0);
|
||||||
|
long oldestDate = getArguments().getLong(EXTRA_OLDEST_DATE, 0);
|
||||||
|
|
||||||
|
viewBinding.headerLabel.setText(HtmlCompat.fromHtml(getString(R.string.rating_tagline,
|
||||||
|
DateFormatter.formatAbbrev(getContext(), new Date(oldestDate)),
|
||||||
|
"<br/><b><big><big><big><big><big>", totalTime / 3600L,
|
||||||
|
"</big></big></big></big></big></b><br/>"), HtmlCompat.FROM_HTML_MODE_LEGACY));
|
||||||
|
viewBinding.neverAgainButton.setOnClickListener(v -> {
|
||||||
|
new RatingDialogManager(getActivity()).saveRated();
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
viewBinding.showLaterButton.setOnClickListener(v -> {
|
||||||
|
new RatingDialogManager(getActivity()).resetStartDate();
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
viewBinding.rateButton.setOnClickListener(v -> {
|
||||||
|
IntentUtils.openInBrowser(getContext(),
|
||||||
|
"https://play.google.com/store/apps/details?id=de.danoeh.antennapod");
|
||||||
|
new RatingDialogManager(getActivity()).saveRated();
|
||||||
|
});
|
||||||
|
viewBinding.contibuteButton.setOnClickListener(v -> {
|
||||||
|
IntentUtils.openInBrowser(getContext(), IntentUtils.getLocalizedWebsiteLink(getContext()) + "/contribute/");
|
||||||
|
new RatingDialogManager(getActivity()).saveRated();
|
||||||
|
});
|
||||||
|
return viewBinding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel(@NonNull DialogInterface dialog) {
|
||||||
|
super.onCancel(dialog);
|
||||||
|
new RatingDialogManager(getActivity()).resetStartDate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package de.danoeh.antennapod.dialog.rating;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import de.danoeh.antennapod.core.BuildConfig;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.StatisticsItem;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import kotlin.Pair;
|
||||||
|
|
||||||
|
public class RatingDialogManager {
|
||||||
|
private static final int AFTER_DAYS = 20;
|
||||||
|
private static final String TAG = "RatingDialog";
|
||||||
|
private static final String PREFS_NAME = "RatingPrefs";
|
||||||
|
private static final String KEY_RATED = "KEY_WAS_RATED";
|
||||||
|
private static final String KEY_FIRST_START_DATE = "KEY_FIRST_HIT_DATE";
|
||||||
|
|
||||||
|
private final SharedPreferences preferences;
|
||||||
|
private final FragmentActivity fragmentActivity;
|
||||||
|
private Disposable disposable;
|
||||||
|
|
||||||
|
public RatingDialogManager(FragmentActivity activity) {
|
||||||
|
this.fragmentActivity = activity;
|
||||||
|
preferences = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showIfNeeded() {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
if (isRated() || BuildConfig.DEBUG || "free".equals(BuildConfig.FLAVOR)) {
|
||||||
|
return;
|
||||||
|
} else if (!enoughTimeSinceInstall()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
disposable = Observable.fromCallable(
|
||||||
|
() -> {
|
||||||
|
DBReader.StatisticsResult statisticsData = DBReader.getStatistics(false, 0, Long.MAX_VALUE);
|
||||||
|
long totalTime = 0;
|
||||||
|
for (StatisticsItem item : statisticsData.feedTime) {
|
||||||
|
totalTime += item.timePlayed;
|
||||||
|
}
|
||||||
|
return new Pair<>(totalTime, statisticsData.oldestDate);
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(result -> {
|
||||||
|
long totalTime = result.getFirst();
|
||||||
|
long oldestDate = result.getSecond();
|
||||||
|
if (totalTime < TimeUnit.SECONDS.convert(15, TimeUnit.HOURS)) {
|
||||||
|
return;
|
||||||
|
} else if (oldestDate > System.currentTimeMillis()
|
||||||
|
- TimeUnit.MILLISECONDS.convert(AFTER_DAYS, TimeUnit.DAYS)) {
|
||||||
|
return; // In case the app was opened but nothing was played
|
||||||
|
}
|
||||||
|
RatingDialogFragment.newInstance(result.getFirst(), result.getSecond())
|
||||||
|
.show(fragmentActivity.getSupportFragmentManager(), TAG);
|
||||||
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRated() {
|
||||||
|
return preferences.getBoolean(KEY_RATED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveRated() {
|
||||||
|
preferences.edit().putBoolean(KEY_RATED, true).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetStartDate() {
|
||||||
|
preferences.edit().putLong(KEY_FIRST_START_DATE, System.currentTimeMillis()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enoughTimeSinceInstall() {
|
||||||
|
if (preferences.getLong(KEY_FIRST_START_DATE, 0) == 0) {
|
||||||
|
resetStartDate();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long firstDate = preferences.getLong(KEY_FIRST_START_DATE, now);
|
||||||
|
long diff = now - firstDate;
|
||||||
|
long diffDays = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
|
||||||
|
return diffDays >= AFTER_DAYS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,13 +17,6 @@ import de.danoeh.antennapod.activity.BugReportActivity;
|
||||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.fragment.preferences.about.AboutFragment;
|
import de.danoeh.antennapod.fragment.preferences.about.AboutFragment;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
@ -113,7 +106,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
findPreference(PREF_DOCUMENTATION).setOnPreferenceClickListener(preference -> {
|
findPreference(PREF_DOCUMENTATION).setOnPreferenceClickListener(preference -> {
|
||||||
IntentUtils.openInBrowser(getContext(), getLocalizedWebsiteLink() + "/documentation/");
|
IntentUtils.openInBrowser(getContext(),
|
||||||
|
IntentUtils.getLocalizedWebsiteLink(getContext()) + "/documentation/");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_VIEW_FORUM).setOnPreferenceClickListener(preference -> {
|
findPreference(PREF_VIEW_FORUM).setOnPreferenceClickListener(preference -> {
|
||||||
|
@ -121,7 +115,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_CONTRIBUTE).setOnPreferenceClickListener(preference -> {
|
findPreference(PREF_CONTRIBUTE).setOnPreferenceClickListener(preference -> {
|
||||||
IntentUtils.openInBrowser(getContext(), getLocalizedWebsiteLink() + "/contribute/");
|
IntentUtils.openInBrowser(getContext(),
|
||||||
|
IntentUtils.getLocalizedWebsiteLink(getContext()) + "/contribute/");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
|
findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
|
||||||
|
@ -130,20 +125,6 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLocalizedWebsiteLink() {
|
|
||||||
try (InputStream is = getContext().getAssets().open("website-languages.txt")) {
|
|
||||||
String[] languages = IOUtils.toString(is, StandardCharsets.UTF_8.name()).split("\n");
|
|
||||||
String deviceLanguage = Locale.getDefault().getLanguage();
|
|
||||||
if (ArrayUtils.contains(languages, deviceLanguage) && !"en".equals(deviceLanguage)) {
|
|
||||||
return "https://antennapod.org/" + deviceLanguage;
|
|
||||||
} else {
|
|
||||||
return "https://antennapod.org";
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSearch() {
|
private void setupSearch() {
|
||||||
SearchPreference searchPreference = findPreference("searchPreference");
|
SearchPreference searchPreference = findPreference("searchPreference");
|
||||||
SearchConfiguration config = searchPreference.getSearchConfiguration();
|
SearchConfiguration config = searchPreference.getSearchConfiguration();
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="32dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_blue_gradient"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingVertical="32dp"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/logo_monochrome" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/headerLabel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/rating_volunteers_label"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:textColor="?colorOnBackground" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/rateButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:text="@string/rating_rate"
|
||||||
|
android:layout_gravity="right|end"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/rating_contribute_label"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:textColor="?colorOnBackground" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/contibuteButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:text="@string/rating_contribute_button"
|
||||||
|
android:layout_gravity="right|end"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/showLaterButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/rating_later"
|
||||||
|
android:layout_gravity="right|end"
|
||||||
|
android:gravity="right|end"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/neverAgainButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:text="@string/checkbox_do_not_show_again"
|
||||||
|
android:layout_gravity="right|end"
|
||||||
|
android:gravity="right|end"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
|
@ -1,123 +0,0 @@
|
||||||
package de.danoeh.antennapod.dialog;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import com.google.android.gms.tasks.Task;
|
|
||||||
import com.google.android.play.core.review.ReviewInfo;
|
|
||||||
import com.google.android.play.core.review.ReviewManager;
|
|
||||||
import com.google.android.play.core.review.ReviewManagerFactory;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.BuildConfig;
|
|
||||||
|
|
||||||
public class RatingDialog {
|
|
||||||
|
|
||||||
private RatingDialog() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String TAG = RatingDialog.class.getSimpleName();
|
|
||||||
private static final int AFTER_DAYS = 14;
|
|
||||||
|
|
||||||
private static WeakReference<Context> mContext;
|
|
||||||
private static SharedPreferences mPreferences;
|
|
||||||
|
|
||||||
private static final String PREFS_NAME = "RatingPrefs";
|
|
||||||
private static final String KEY_RATED = "KEY_WAS_RATED";
|
|
||||||
private static final String KEY_FIRST_START_DATE = "KEY_FIRST_HIT_DATE";
|
|
||||||
private static final String KEY_NUMBER_OF_REVIEWS = "NUMBER_OF_REVIEW_ATTEMPTS";
|
|
||||||
|
|
||||||
public static void init(Context context) {
|
|
||||||
mContext = new WeakReference<>(context);
|
|
||||||
mPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, 0);
|
|
||||||
if (firstDate == 0) {
|
|
||||||
resetStartDate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void check() {
|
|
||||||
if (shouldShow()) {
|
|
||||||
try {
|
|
||||||
showInAppReview();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showInAppReview() {
|
|
||||||
Context context = mContext.get();
|
|
||||||
if (context == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReviewManager manager = ReviewManagerFactory.create(context);
|
|
||||||
Task<ReviewInfo> request = manager.requestReviewFlow();
|
|
||||||
|
|
||||||
request.addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
ReviewInfo reviewInfo = task.getResult();
|
|
||||||
Task<Void> flow = manager.launchReviewFlow((Activity) context, reviewInfo);
|
|
||||||
flow.addOnCompleteListener(task1 -> {
|
|
||||||
int previousAttempts = mPreferences.getInt(KEY_NUMBER_OF_REVIEWS, 0);
|
|
||||||
if (previousAttempts >= 3) {
|
|
||||||
saveRated();
|
|
||||||
} else {
|
|
||||||
resetStartDate();
|
|
||||||
mPreferences
|
|
||||||
.edit()
|
|
||||||
.putInt(KEY_NUMBER_OF_REVIEWS, previousAttempts + 1)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
Log.i("ReviewDialog", "Successfully finished in-app review");
|
|
||||||
})
|
|
||||||
.addOnFailureListener(error -> {
|
|
||||||
Log.i("ReviewDialog", "failed in reviewing process");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.addOnFailureListener(error -> {
|
|
||||||
Log.i("ReviewDialog", "failed to get in-app review request");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean rated() {
|
|
||||||
return mPreferences.getBoolean(KEY_RATED, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void saveRated() {
|
|
||||||
mPreferences
|
|
||||||
.edit()
|
|
||||||
.putBoolean(KEY_RATED, true)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resetStartDate() {
|
|
||||||
mPreferences
|
|
||||||
.edit()
|
|
||||||
.putLong(KEY_FIRST_START_DATE, System.currentTimeMillis())
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldShow() {
|
|
||||||
if (rated() || BuildConfig.DEBUG) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, now);
|
|
||||||
long diff = now - firstDate;
|
|
||||||
long diffDays = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
|
|
||||||
return diffDays >= AFTER_DAYS;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,8 +9,14 @@ import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import de.danoeh.antennapod.core.R;
|
import de.danoeh.antennapod.core.R;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class IntentUtils {
|
public class IntentUtils {
|
||||||
private static final String TAG = "IntentUtils";
|
private static final String TAG = "IntentUtils";
|
||||||
|
@ -45,4 +51,19 @@ public class IntentUtils {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getLocalizedWebsiteLink(Context context) {
|
||||||
|
try (InputStream is = context.getAssets().open("website-languages.txt")) {
|
||||||
|
String[] languages = IOUtils.toString(is, StandardCharsets.UTF_8.name()).split("\n");
|
||||||
|
String deviceLanguage = Locale.getDefault().getLanguage();
|
||||||
|
if (ArrayUtils.contains(languages, deviceLanguage) && !"en".equals(deviceLanguage)) {
|
||||||
|
return "https://antennapod.org/" + deviceLanguage;
|
||||||
|
} else {
|
||||||
|
return "https://antennapod.org";
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
@ -672,6 +672,14 @@
|
||||||
<string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string>
|
<string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string>
|
||||||
<string name="pref_pausePlaybackForFocusLoss_title">Pause for interruptions</string>
|
<string name="pref_pausePlaybackForFocusLoss_title">Pause for interruptions</string>
|
||||||
|
|
||||||
|
<!-- Rating dialog -->
|
||||||
|
<string name="rating_tagline">Since %1$s, you played %2$s%3$d%4$s hours of podcasts.</string>
|
||||||
|
<string name="rating_contribute_label">Want to join? Whether you want to translate, support, design or code, we would be happy to have you!</string>
|
||||||
|
<string name="rating_contribute_button">Discover ways to contribute</string>
|
||||||
|
<string name="rating_volunteers_label">AntennaPod is developed by volunteers in our free time. We would be happy if you appreciated our work by leaving a nice rating.</string>
|
||||||
|
<string name="rating_rate">Rate AntennaPod</string>
|
||||||
|
<string name="rating_later">Later</string>
|
||||||
|
|
||||||
<!-- Online feed view -->
|
<!-- Online feed view -->
|
||||||
<string name="subscribe_label">Subscribe</string>
|
<string name="subscribe_label">Subscribe</string>
|
||||||
<string name="subscribing_label">Subscribing…</string>
|
<string name="subscribing_label">Subscribing…</string>
|
||||||
|
|
Loading…
Reference in New Issue