Skip to content

Commit

Permalink
Merge pull request #18 from transifex/petrakeas/improve-dependencies
Browse files Browse the repository at this point in the history
Make Appcompat dependency optional
  • Loading branch information
Petrakeas committed Apr 19, 2021
2 parents 0b009e5 + 66b0865 commit 069ce92
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 38 deletions.
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ implementation 'com.transifex.txnative:txsdk:0.x.y'
Please replace `x` and `y` with the latest version numbers: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.transifex.txnative/txsdk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.transifex.txnative/txsdk)


The library's minimum supported SDK is 18 (Android 4.3) and uses [appcompat](https://developer.android.com/jetpack/androidx/releases/appcompat) 1.2.0.
The library's minimum supported SDK is 18 (Android 4.3) and is compatible with [Appcompat](https://developer.android.com/jetpack/androidx/releases/appcompat) 1.2.0.

The SDK does not add Appcompat as a dependency. It can work in apps that don't use Appcompat and in apps that use Appcompat 1.2.0.

### SDK configuration

Expand Down Expand Up @@ -64,7 +66,7 @@ The language codes supported by Transifex can be found [here](https://www.transi

In this example, the SDK uses its default cache, `TxStandardCache`, and missing policy, `SourceStringPolicy`. However, you can choose between different cache and missing policy implementations or even provide your own. You can read more on that later.

If you want to enable [multilingual support](https://developer.android.com/guide/topics/resources/multilingual-support.html) starting from Android N, place the supported app languages in your app's gradle file:
Starting from Android N, Android has [multilingual support](https://developer.android.com/guide/topics/resources/multilingual-support.html): users can select more that one locale in Android's settings and the OS will try to pick the topmost locale that is supported by the app. If your app makes use of `Appcompat`, it suffices to place the supported app languages in your apps gradle file:

```gradle
android {
Expand All @@ -76,6 +78,17 @@ android {
}
```

If your app doesn't use `Appcompat`, you should define a dummy string in your default, unlocalized `strings.xml` file and place a `strings.xml` file for each supported locale and define the same string there. For example:

```
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="dummy">dummy</string>
</resources>
```

This will let Android know which locales your app supports and help it choose the correct one in case of a multilingual user. If you don't do that, Android will always pick the first locale selected by the user.

### Context Wrapping

The SDK's functionality is enabled by wrapping the context, so that all string resource related methods, such a [`getString()`](https://developer.android.com/reference/android/content/res/Resources#getString(int,%20java.lang.Object...)), [`getText()`](https://developer.android.com/reference/android/content/res/Resources#getText(int)), flow through the SDK.
Expand Down Expand Up @@ -194,7 +207,7 @@ If you want to have your memory cache updated with the new translations when `fe

### Sample app

You can see the SDK used and configured in more advanced ways in the provided sample app.
You can see the SDK used and configured in more advanced ways in the provided sample app of this repo. You can also check out a simpler app at the [Transifex Native repo](https://github.com/transifex/transifex-native-sandbox).

## Transifex Command Line Tool

Expand Down Expand Up @@ -270,15 +283,15 @@ If you have a different setup, you can enter the path to your app's `assets` dir

### Disable TxNative for specific strings

There are cases where you don't want TxNative to interfere with string loading. For example, many apps have API keys or some configuration saved in non-translatable strings in their `strings.xml` file. A method like `getString()` is used to retreive the strings. If you are using the SDK's default missing policy, `SourceStringPolicy`, the expected string will be returned. If, however, you are using some other policy, the string may be altered and your app will not behave as expected. In such a case, make sure that you are using a non-wrapped context when loading such a string:
There are cases where you don't want TxNative to interfere with string loading. For example, many apps have API keys or some configuration saved in non-translatable strings in their `strings.xml` file. A method like `getString()` is used to retrieve the strings. If you are using the SDK's default missing policy, `SourceStringPolicy`, the expected string will be returned. If, however, you are using some other policy, the string may be altered and your app will not behave as expected. In such a case, make sure that you are using a non-wrapped context when loading such a string:

```java
getApplicationContext().getString(<string_ID>);
```

### TxNative and 3rd party libraries

Some libs may containt their own localized strings, views or activities. In such as case, you don't want TxNative to interfere with string loading. To accomplish that, make sure that you pass a non-wrapped context to the library's initialization method:
Some libs may contain their own localized strings, views or activities. In such as case, you don't want TxNative to interfere with string loading. To accomplish that, make sure that you pass a non-wrapped context to the library's initialization method:

```java
SomeSDK.init(getApplicationContext());
Expand Down
2 changes: 1 addition & 1 deletion TransifexNativeSDK/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'

testImplementation 'junit:junit:4.13.2'
Expand Down
22 changes: 11 additions & 11 deletions TransifexNativeSDK/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.versions = [
'kotlin' : '1.4.32',
'androidXAnnotation' : '1.2.0'
]
ext {
kotlin_version = '1.4.10'
sdkVersionCode = 1 // version code for txsdk
sdkVersion = '0.1.0' // version for txsdk and common
pomGroupID = "com.transifex.txnative" // pom group id for txsdk and common

cliVersion = '0.1.0' // clitool version
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand All @@ -20,14 +28,6 @@ plugins {
id "io.github.gradle-nexus.publish-plugin" version "1.0.0"
}

ext {
sdkVersionCode = 1 // version code for txsdk
sdkVersion = '0.1.0' // version for txsdk and common
pomGroupID = "com.transifex.txnative" // pom group id for txsdk and common

cliVersion = '0.1.0' // clitool version
}

allprojects {
repositories {
google()
Expand Down Expand Up @@ -101,7 +101,7 @@ gradle.projectsEvaluated {
subproject.name == 'txsdk' || subproject.name == 'common'
}

// Merge properties of androidJavadoc tasks (which is supported by both subprojects)
// Merge the properties of the androidJavadoc tasks of the subprojects
source = projectSet.androidJavadoc.source
classpath = project.files(projectSet.androidJavadoc.classpath)
excludes = projectSet.androidJavadoc.excludes.flatten().unique()
Expand Down
3 changes: 2 additions & 1 deletion TransifexNativeSDK/clitool/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ tasks.withType(Test) {
}

dependencies {
implementation 'androidx.annotation:annotation:1.2.0'
compileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
implementation 'org.jdom:jdom:2.0.2'
implementation 'info.picocli:picocli:4.6.1'
implementation project(':common')

testCompileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.google.truth:truth:1.1'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
Expand Down
3 changes: 2 additions & 1 deletion TransifexNativeSDK/common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ tasks.withType(Test) {
}

dependencies {
implementation 'androidx.annotation:annotation:1.2.0'
compileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'net.moznion:uribuilder-tiny:2.7.1'

testCompileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.google.truth:truth:1.1'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
Expand Down
1 change: 1 addition & 0 deletions TransifexNativeSDK/publish-helper.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ task androidJavadoc(type: Javadoc) {
else {
source = sourceSets.main.allJava
classpath += configurations.runtimeClasspath
classpath += configurations.compileClasspath
}
exclude '**/R.html', '**/R.*.html', '**/index.html', '**/*.kt'
options.encoding 'utf-8'
Expand Down
15 changes: 12 additions & 3 deletions TransifexNativeSDK/txsdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,29 @@ tasks.withType(Test) {
systemProperty "file.encoding", "UTF-8"
}

configurations.all {
resolutionStrategy {
force "androidx.annotation:annotation:$versions.androidXAnnotation"
}
}

dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
compileOnly 'androidx.appcompat:appcompat:1.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
implementation 'io.github.inflationx:viewpump:2.0.3'
api project(':common')

testImplementation 'junit:junit:4.13.1'
testCompileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.google.truth:truth:1.1'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'org.robolectric:robolectric:4.4'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
testImplementation 'org.mockito:mockito-core:3.8.0'

androidTestCompileOnly "androidx.annotation:annotation:$versions.androidXAnnotation"
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'com.google.truth:truth:1.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
import androidx.annotation.StringRes;
import androidx.core.text.HtmlCompat;

import static android.text.Html.FROM_HTML_MODE_LEGACY;

/**
* The main class of the framework, responsible for orchestrating all functionality.
Expand Down Expand Up @@ -353,7 +354,7 @@ CharSequence translateQuantityString(TxResources txResources, @PluralsRes int id
@NonNull CharSequence getSpannedString(@NonNull String string) {
if (mSupportSpannableEnabled) {
// If a span was found, return a "Spanned" object. Otherwise, return "String".
Spanned spanned = HtmlCompat.fromHtml(string, HtmlCompat.FROM_HTML_MODE_LEGACY);
Spanned spanned = Utils.fromHtml(string, FROM_HTML_MODE_LEGACY);
if (spanned.getSpans(0, spanned.length(), Object.class).length != 0) {
return new SpannedString(spanned);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public InflateResult intercept(@NonNull Chain chain) {
if (view instanceof TextView) {
mTextViewTransformer.transform(context, view, attrs);
}
else if (view instanceof androidx.appcompat.widget.Toolbar) {
else if (Utils.isAppcompatPresent() && view instanceof androidx.appcompat.widget.Toolbar) {
mSupportToolbarTransformer.transform(context, view, attrs);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view instanceof Toolbar) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.text.Html;
import android.text.Spanned;
import android.util.AttributeSet;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;

import androidx.annotation.AttrRes;
Expand All @@ -22,6 +20,12 @@ public class Utils {

private static final String TAG = Utils.class.getSimpleName();

private static final boolean sIsAppcompatPresent;

static {
sIsAppcompatPresent = isClassPresent("androidx.appcompat.widget.Toolbar");
}

/**
* Returns the string resource id that this attributeId is resolving to under the provided
* attribute set and the current theme.
Expand Down Expand Up @@ -126,4 +130,38 @@ public static Resources getDefaultLocaleResources(@NonNull Context context) {
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}

/**
* Invokes {@link Html#fromHtml(String, int)} on API 24 and newer, otherwise {@code flags} are
* ignored and {@link Html#fromHtml(String)} is used.
*/
@SuppressWarnings("deprecation")
@NonNull
public static Spanned fromHtml(@NonNull String source, int flags) {
// taken from androidx.core.text
if (Build.VERSION.SDK_INT >= 24) {
return Html.fromHtml(source, flags);
}
return Html.fromHtml(source);
}

/**
* Checks, using reflection, if the provided class is available at runtime.
*/
public static boolean isClassPresent(String className) {
try {
Class.forName(className);
return true;
} catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}

/**
* Checks if <code>Androidx.appcompat</code> classes are available at runtime.
*/
public static boolean isAppcompatPresent() {
return sIsAppcompatPresent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ public void transform(@NonNull Context context, @NonNull View view, @NonNull Att
CharSequence xa = toolbar.getTitle();

int titleResourceId = Utils.getStringResourceId(context, attrs, android.R.attr.title);
int titleCompatResourceId = Utils.getStringResourceId(context, attrs, androidx.appcompat.R.attr.title);
int titleCompatResourceId = 0;
if (Utils.isAppcompatPresent()) {
titleCompatResourceId = Utils.getStringResourceId(context, attrs, androidx.appcompat.R.attr.title);
}
if (titleResourceId != 0) {
toolbar.setTitle(titleResourceId);
}
Expand All @@ -34,7 +37,10 @@ else if (titleCompatResourceId != 0) {


int subtitleResourceId = Utils.getStringResourceId(context, attrs, android.R.attr.subtitle);
int subtitleCompatResourceId = Utils.getStringResourceId(context, attrs, androidx.appcompat.R.attr.subtitle);
int subtitleCompatResourceId = 0;
if (Utils.isAppcompatPresent()) {
subtitleCompatResourceId = Utils.getStringResourceId(context, attrs, androidx.appcompat.R.attr.subtitle);
}
if (subtitleResourceId != 0) {
toolbar.setSubtitle(subtitleResourceId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

import java.util.HashMap;

import androidx.core.text.HtmlCompat;
import androidx.test.core.app.ApplicationProvider;

import static android.text.Html.FROM_HTML_MODE_LEGACY;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

Expand Down Expand Up @@ -246,7 +246,7 @@ public void testGetSpannedString_spanSupportEnabledWithHTMLEscapedString_returnS

assertThat(string).isInstanceOf(String.class);

string = HtmlCompat.fromHtml(string.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY);
string = Utils.fromHtml(string.toString(), FROM_HTML_MODE_LEGACY);

CharSequence styledPart = null;

Expand Down Expand Up @@ -298,7 +298,7 @@ public void testGetSpannedString_spanSupportDisabled_returnStringWithTags() {

assertThat(string).isInstanceOf(String.class);

string = HtmlCompat.fromHtml(string.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY);
string = Utils.fromHtml(string.toString(), FROM_HTML_MODE_LEGACY);

CharSequence styledPart = null;

Expand Down Expand Up @@ -332,7 +332,7 @@ public void testGetSpannedString_spanSupportDisabledWithHTMLEscapedString_return

assertThat(string).isInstanceOf(String.class);

string = HtmlCompat.fromHtml(string.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY);
string = Utils.fromHtml(string.toString(), FROM_HTML_MODE_LEGACY);

CharSequence styledPart = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import android.text.SpannedString;
import android.text.style.StyleSpan;

import com.transifex.txnative.Utils;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import androidx.core.text.HtmlCompat;

import static android.text.Html.FROM_HTML_MODE_LEGACY;
import static com.google.common.truth.Truth.assertThat;

@RunWith(RobolectricTestRunner.class)
Expand Down Expand Up @@ -72,7 +73,7 @@ public void testWrapString_spannedString() {
// Test that a spanned source string keeps its spans after being processed

WrappedStringPolicy policy = new WrappedStringPolicy("start! ", " !end");
CharSequence sourceString = HtmlCompat.fromHtml("The quick <b>brown</b> fox", HtmlCompat.FROM_HTML_MODE_LEGACY);
CharSequence sourceString = Utils.fromHtml("The quick <b>brown</b> fox", FROM_HTML_MODE_LEGACY);
CharSequence translated = policy.wrapString(sourceString);

assertThat(translated).isInstanceOf(Spanned.class);
Expand Down

0 comments on commit 069ce92

Please sign in to comment.