Make Localization.relativeTime testable
Problem is global state in static variable prettyTime. But for performance reasons on Android that is preferred. Now allow injecting prettyTime dependency by making init function public.
This commit is contained in:
parent
68175c1cf0
commit
50a026183d
|
@ -6,16 +6,26 @@ import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.multidex.MultiDexApplication;
|
import androidx.multidex.MultiDexApplication;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||||
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
|
import io.reactivex.rxjava3.exceptions.CompositeException;
|
||||||
|
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
||||||
|
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
||||||
|
import io.reactivex.rxjava3.exceptions.UndeliverableException;
|
||||||
|
import io.reactivex.rxjava3.functions.Consumer;
|
||||||
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import org.acra.ACRA;
|
import org.acra.ACRA;
|
||||||
import org.acra.config.ACRAConfigurationException;
|
import org.acra.config.ACRAConfigurationException;
|
||||||
import org.acra.config.CoreConfiguration;
|
import org.acra.config.CoreConfiguration;
|
||||||
|
@ -31,21 +41,6 @@ import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
|
||||||
import io.reactivex.rxjava3.exceptions.CompositeException;
|
|
||||||
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
|
||||||
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
|
||||||
import io.reactivex.rxjava3.exceptions.UndeliverableException;
|
|
||||||
import io.reactivex.rxjava3.functions.Consumer;
|
|
||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||||
* App.java is part of NewPipe.
|
* App.java is part of NewPipe.
|
||||||
|
@ -93,9 +88,9 @@ public class App extends MultiDexApplication {
|
||||||
SettingsActivity.initSettings(this);
|
SettingsActivity.initSettings(this);
|
||||||
|
|
||||||
NewPipe.init(getDownloader(),
|
NewPipe.init(getDownloader(),
|
||||||
Localization.getPreferredLocalization(this),
|
Localization.getPreferredLocalization(this),
|
||||||
Localization.getPreferredContentCountry(this));
|
Localization.getPreferredContentCountry(this));
|
||||||
Localization.init(getApplicationContext());
|
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
|
||||||
|
|
||||||
StateSaver.init(this);
|
StateSaver.init(this);
|
||||||
initNotificationChannels();
|
initNotificationChannels();
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -41,7 +43,6 @@ import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
|
@ -52,9 +53,10 @@ import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import org.schabi.newpipe.databinding.ActivityMainBinding;
|
import org.schabi.newpipe.databinding.ActivityMainBinding;
|
||||||
import org.schabi.newpipe.databinding.DrawerHeaderBinding;
|
import org.schabi.newpipe.databinding.DrawerHeaderBinding;
|
||||||
import org.schabi.newpipe.databinding.DrawerLayoutBinding;
|
import org.schabi.newpipe.databinding.DrawerLayoutBinding;
|
||||||
|
@ -87,12 +89,6 @@ import org.schabi.newpipe.util.TLSSocketFactoryCompat;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
import org.schabi.newpipe.views.FocusOverlayView;
|
import org.schabi.newpipe.views.FocusOverlayView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||||
|
@ -468,7 +464,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
assureCorrectAppLanguage(this);
|
assureCorrectAppLanguage(this);
|
||||||
// Change the date format to match the selected language on resume
|
// Change the date format to match the selected language on resume
|
||||||
Localization.init(getApplicationContext());
|
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
// Close drawer on return, and don't show animation,
|
// Close drawer on return, and don't show animation,
|
||||||
|
|
|
@ -9,19 +9,10 @@ import android.icu.text.CompactDecimalFormat;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.PluralsRes;
|
import androidx.annotation.PluralsRes;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.ocpsoft.prettytime.PrettyTime;
|
|
||||||
import org.ocpsoft.prettytime.units.Decade;
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
|
||||||
import org.schabi.newpipe.ktx.OffsetDateTimeKt;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
@ -33,6 +24,12 @@ import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import org.ocpsoft.prettytime.PrettyTime;
|
||||||
|
import org.ocpsoft.prettytime.units.Decade;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||||
|
import org.schabi.newpipe.ktx.OffsetDateTimeKt;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -62,10 +59,6 @@ public final class Localization {
|
||||||
|
|
||||||
private Localization() { }
|
private Localization() { }
|
||||||
|
|
||||||
public static void init(final Context context) {
|
|
||||||
initPrettyTime(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static String concatenateStrings(final String... strings) {
|
public static String concatenateStrings(final String... strings) {
|
||||||
return concatenateStrings(Arrays.asList(strings));
|
return concatenateStrings(Arrays.asList(strings));
|
||||||
|
@ -307,12 +300,16 @@ public final class Localization {
|
||||||
// Pretty Time
|
// Pretty Time
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private static void initPrettyTime(final Context context) {
|
public static void initPrettyTime(final PrettyTime time) {
|
||||||
prettyTime = new PrettyTime(getAppLocale(context));
|
prettyTime = time;
|
||||||
// Do not use decades as YouTube doesn't either.
|
// Do not use decades as YouTube doesn't either.
|
||||||
prettyTime.removeUnit(Decade.class);
|
prettyTime.removeUnit(Decade.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PrettyTime resolvePrettyTime(final Context context) {
|
||||||
|
return new PrettyTime(getAppLocale(context));
|
||||||
|
}
|
||||||
|
|
||||||
public static String relativeTime(final OffsetDateTime offsetDateTime) {
|
public static String relativeTime(final OffsetDateTime offsetDateTime) {
|
||||||
return relativeTime(OffsetDateTimeKt.toCalendar(offsetDateTime));
|
return relativeTime(OffsetDateTimeKt.toCalendar(offsetDateTime));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ocpsoft.prettytime.PrettyTime
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class LocalizationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `After initializing pretty time relativeTime() with a Calendar must work`() {
|
||||||
|
val reference = SimpleDateFormat("yyyy/MM/dd").parse("2021/1/1")
|
||||||
|
Localization.initPrettyTime(PrettyTime(reference, Locale.ENGLISH))
|
||||||
|
|
||||||
|
val actual = Localization.relativeTime(GregorianCalendar(2021, 1, 6))
|
||||||
|
|
||||||
|
assertEquals("1 month from now", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException::class)
|
||||||
|
fun `relativeTime() must fail without initializing pretty time`() {
|
||||||
|
Localization.relativeTime(GregorianCalendar(2021, 1, 6))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `relativeTime() with a OffsetDateTime must work`() {
|
||||||
|
val reference = SimpleDateFormat("yyyy/MM/dd").parse("2021/1/1")
|
||||||
|
Localization.initPrettyTime(PrettyTime(reference, Locale.ENGLISH))
|
||||||
|
|
||||||
|
val offset = OffsetDateTime.of(2021, 1, 6, 0, 0, 0, 0, ZoneOffset.UTC)
|
||||||
|
val actual = Localization.relativeTime(offset)
|
||||||
|
|
||||||
|
assertEquals("5 days from now", actual)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue