diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 84e7f59..767e361 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -36,10 +36,11 @@ jobs: run: ./gradlew test - name: Upload test reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: unitTestReports path: | TransifexNativeSDK/clitool/build/reports/tests/test/ TransifexNativeSDK/common/build/reports/tests/test/ TransifexNativeSDK/txsdk/build/reports/tests/ + if-no-files-found: warn diff --git a/README.md b/README.md index 435eaf4..d888df0 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,43 @@ Context wrappedContext = TxNative.wrap(getApplicationContext()); wrappedContext.getString(); ``` +If you want to wrap the application context itself, you need to move the SDK's initialization from the application's `onCreate()` to `attachBaseContext()`: + +```java + @Override + protected void attachBaseContext(Context base) { + // Initialize TxNative + String token = ""; + + LocaleState localeState = new LocaleState( + base, // Use the base context instead of getApplicationContext() + // source locale + "en", + // supported locales + new String[]{"en", "el", "de", "fr", "ar", "sl", "es_ES", "es_MX"}, + null); + + TxNative.init( + // application context + base, // Use the base context instead of getApplicationContext() + // a LocaleState instance + localeState, + // token + token, + // cdsHost URL + null, + // a TxCache implementation + null, + // a MissingPolicy implementation + new AndroidMissingPolicy()); + + // Wrap the base application context + super.attachBaseContext(TxNative.wrap(base)); +} +``` + +Note though that this global wrapper can interfere with third-party libraries that use their own string resources. In that case, use "AndroidMissingPolicy" so that these libraries have their strings translated. + If you want to disable the SDK functionality, don't initialize it and don't call any `TxNative` methods. `TxNative.wrap()` will be a no-op and the context will not be wrapped. Thus, all `getString()` etc methods, won't flow through the SDK. ## Cache diff --git a/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/MyApplication.java b/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/MyApplication.java index 5740a75..37d9730 100644 --- a/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/MyApplication.java +++ b/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/MyApplication.java @@ -1,6 +1,7 @@ package com.transifex.myapplication; import android.app.Application; +import android.content.Context; import android.content.Intent; import com.transifex.txnative.LocaleState; @@ -20,36 +21,50 @@ public void onCreate() { // https://stackoverflow.com/questions/55265834/change-locale-not-work-after-migrate-to-androidx/58004553#58004553 // AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + // Start a service just for testing purposes + Intent serviceIntent = new Intent(this, SimpleIntentService.class); + SimpleIntentService.enqueueWork(this, serviceIntent); + + // Uncomment to use strings as served by Android prefixed with "test: " +// TxNative.setTestMode(true); + + // Uncomment, to disable styling of strings with HTML markup such as + // R.string.styled_text_not_escaped +// TxNative.setSupportSpannable(false); + + // Fetch all translations from CDS + TxNative.fetchTranslations(null, null); + } + + @Override + protected void attachBaseContext(Context base) { // Initialize TxNative String token = null; // The app locales entered here should match the ones in `resConfigs` in gradle, so that // multi locale support works for newer Androids. - LocaleState localeState = new LocaleState(getApplicationContext(), + LocaleState localeState = new LocaleState(base, "en", new String[]{"en", "el", "de", "fr", "ar", "sl"}, null); + TxNative.init( - getApplicationContext(), // application context + base, // application context localeState, // a LocaleState instance token, // token null, // cdsHost URL null, // a TxCache implementation null); // a MissingPolicy implementation - // Uncomment to use strings as served by Android prefixed with "test: " -// TxNative.setTestMode(true); - - // Uncomment, to disable styling of strings with HTML markup such as - // R.string.styled_text_not_escaped -// TxNative.setSupportSpannable(false); + // OPTIONAL: + // Wrap the application's base context to allow TxNative to intercept all string resource + // requests (e.g. from getApplicationContext().getString()). + // Warning: This global wrapper can interfere with third-party libraries that use their + // own string resources. Use "AndroidMissingPolicy" so that these libraries have their + // strings translated. + super.attachBaseContext(TxNative.wrap(base)); - // Fetch all translations from CDS - TxNative.fetchTranslations(null, null); - - // Start a service just for testing purposes - Intent serviceIntent = new Intent(this, SimpleIntentService.class); - SimpleIntentService.enqueueWork(this, serviceIntent); + // SAFER: Do not wrap the application's base context. + // super.attachBaseContext(base); } - } diff --git a/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/SimpleIntentService.java b/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/SimpleIntentService.java index 37f1c8d..cff8670 100644 --- a/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/SimpleIntentService.java +++ b/TransifexNativeSDK/app/src/main/java/com/transifex/myapplication/SimpleIntentService.java @@ -24,8 +24,8 @@ public static void enqueueWork(Context context, Intent work) { @Override protected void onHandleWork(@NonNull Intent intent) { - // Make sure that you use getBaseContext() and not getApplicationContext() - String success = getBaseContext().getResources().getString(R.string.success); + // Make sure that you do not use an app context via getApplicationContext().getString() + String success = getString(R.string.success); Log.d(TAG, "Service status: " + success); } diff --git a/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/NativeCore.java b/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/NativeCore.java index 16e4bc3..9b83516 100644 --- a/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/NativeCore.java +++ b/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/NativeCore.java @@ -65,7 +65,7 @@ public NativeCore(@NonNull Context applicationContext, @Nullable String cdsHost, @Nullable TxCache cache, @Nullable MissingPolicy missingPolicy) { - mContext = applicationContext.getApplicationContext(); + mContext = applicationContext; mMainHandler = new Handler(mContext.getMainLooper()); mLocaleState = localeState; mLocaleState.setCurrentLocaleListener(mCurrentLocaleListener); diff --git a/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/TxNative.java b/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/TxNative.java index 8b657aa..1accb88 100644 --- a/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/TxNative.java +++ b/TransifexNativeSDK/txsdk/src/main/java/com/transifex/txnative/TxNative.java @@ -29,9 +29,10 @@ public class TxNative { private static NativeCore sNativeCore = null; /** - * Initialize the SDK. + * Initialize the SDK. The method should only be called once. *

- * Should be called in {@link Application#onCreate()}. + * Should be called in {@link Application#onCreate()} or + * {@link Application#attachBaseContext(Context)}. *

* * @param applicationContext The application context. @@ -63,6 +64,16 @@ public static void init(@NonNull Context applicationContext, ViewPump.init(new TxInterceptor()); } + /** + * Checks if the SDK has been initialized by a previous call to + * {@link #init(Context, LocaleState, String, String, TxCache, MissingPolicy)}. + * + * @return true if the SDK has been initialized, false otherwise. + */ + public static boolean isInitialized() { + return sNativeCore != null; + } + /** * When test mode is enabled, TransifexNative functionality is disabled: the translations provided * by the SDK are not used. The original strings, as provided by Android's localization system, @@ -158,9 +169,8 @@ public static void fetchTranslations(@Nullable String localeCode) { *

* Warning: You should use getBaseContext(), instead of * getApplicationContext() when using string methods in services. - * - * @param context The activity context to wrap. + * @param context The context to wrap. * * @return The wrapped context. */ @@ -181,15 +191,9 @@ public static Context wrap(@NonNull Context context) { /** * Wraps a context to enable TransifexNative functionality in services and other scopes besides * activities. - *

- * Warning: You should use getBaseContext(), instead of - * getApplicationContext() when using string methods in services. - *

- * Check out the installation guide regarding the usage of this method. - * - * @param context The service context to wrap. * * @return The wrapped context. + * @deprecated Use {@link #wrap(Context)} instead. */ @Deprecated public static Context generalWrap(@NonNull Context context) { @@ -201,6 +205,9 @@ public static Context generalWrap(@NonNull Context context) { * that extends {@link androidx.appcompat.app.AppCompatActivity}. *

* This method should be called in {@link AppCompatActivity#getDelegate()}. + *

+ * If your activity is extending {@link com.transifex.txnative.activity.TxBaseAppCompatActivity TxBaseAppCompatActivity} + * you don't have to call this method. * * @param delegate The activity's AppCompatDelegate. * @param baseContext The activity's base context.