diff --git a/.circleci/config.yml b/.circleci/config.yml index fc1961f59..a1cf5ced5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: - save_cache: *save-npm-cache-linux # Android builds - android-build: + android-play-build: <<: *defaults docker: - image: circleci/android:api-28-node @@ -178,12 +178,12 @@ jobs: echo -e "export default { BUGSNAG_API_KEY: '$BUGSNAG_KEY' };" > ./config.js - run: - name: Build Android App + name: Build Android Play App command: | if [[ $KEYSTORE ]]; then - bundle exec fastlane android release + bundle exec fastlane android playRelease else - bundle exec fastlane android build + bundle exec fastlane android playBuild fi working_directory: android @@ -194,8 +194,8 @@ jobs: yarn generate-source-maps-android upload \ --api-key=$BUGSNAG_KEY \ --app-version=$CIRCLE_BUILD_NUM \ - --minifiedFile=android/app/build/generated/assets/react/release/app.bundle \ - --source-map=android/app/build/generated/sourcemaps/react/release/app.bundle.map \ + --minifiedFile=android/app/build/generated/assets/react/play/release/app.bundle \ + --source-map=android/app/build/generated/sourcemaps/react/play/release/app.bundle.map \ --minified-url=app.bundle \ --upload-sources fi @@ -213,6 +213,67 @@ jobs: - android/fastlane/report.xml - android/app/build/outputs + android-foss-build: + <<: *defaults + docker: + - image: circleci/android:api-28-node + + environment: + JAVA_OPTS: '-Xms512m -Xmx2g' + GRADLE_OPTS: '-Xmx3g -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"' + TERM: dumb + <<: *bash-env + + steps: + - checkout + + - run: *install-node + + - restore_cache: *restore-npm-cache-linux + + - run: *install-npm-modules + + - run: *update-fastlane-android + + - restore_cache: *restore-gradle-cache + + - run: + name: Configure Gradle + command: | + echo -e "" > ./gradle.properties + # echo -e "android.enableAapt2=false" >> ./gradle.properties + echo -e "android.useAndroidX=true" >> ./gradle.properties + echo -e "android.enableJetifier=true" >> ./gradle.properties + echo -e "FLIPPER_VERSION=0.51.0" >> ./gradle.properties + echo -e "VERSIONCODE=$CIRCLE_BUILD_NUM" >> ./gradle.properties + + if [[ $KEYSTORE ]]; then + echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE + echo -e "KEYSTORE=$KEYSTORE" >> ./gradle.properties + echo -e "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties + echo -e "KEY_ALIAS=$KEY_ALIAS" >> ./gradle.properties + echo -e "KEY_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties + fi + working_directory: android + + - run: + name: Build Android Foss App + command: bundle exec fastlane android fossRelease + working_directory: android + + - store_artifacts: + path: android/app/build/outputs + + - save_cache: *save-npm-cache-linux + + - save_cache: *save-gradle-cache + + - persist_to_workspace: + root: . + paths: + - android/fastlane/report.xml + - android/app/build/outputs + android-google-play-beta: <<: *defaults docker: @@ -341,13 +402,20 @@ workflows: requires: - ios-hold-testflight - - android-build: + - android-play-build: requires: - lint-testunit - android-hold-google-play-beta: type: approval requires: - - android-build + - android-play-build - android-google-play-beta: requires: - android-hold-google-play-beta + - android-hold-foss-build: + type: approval + requires: + - lint-testunit + - android-foss-build: + requires: + - android-hold-foss-build diff --git a/__mocks__/react-native-config-reader.js b/__mocks__/react-native-config-reader.js new file mode 100644 index 000000000..2286d3cb6 --- /dev/null +++ b/__mocks__/react-native-config-reader.js @@ -0,0 +1,3 @@ +export default { + BuildConfigs: null +}; diff --git a/android/Gemfile.lock b/android/Gemfile.lock index 0c51b23fc..5488439fc 100644 --- a/android/Gemfile.lock +++ b/android/Gemfile.lock @@ -81,7 +81,6 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-appcenter (1.8.0) gh_inspector (1.1.3) google-api-client (0.36.4) addressable (~> 2.5, >= 2.5.1) @@ -156,6 +155,7 @@ GEM uber (0.1.0) unf (0.1.4) unf_ext + unf_ext (0.0.7.7) unf_ext (0.0.7.7-x64-mingw32) unicode-display_width (1.7.0) word_wrap (1.0.0) @@ -171,11 +171,11 @@ GEM xcpretty (~> 0.2, >= 0.0.7) PLATFORMS + ruby x64-mingw32 DEPENDENCIES fastlane - fastlane-plugin-appcenter BUNDLED WITH 2.0.2 diff --git a/android/app/build.gradle b/android/app/build.gradle index c202892ab..566f17dd4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,8 +1,13 @@ +def taskRequests = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase() +def isPlay = !taskRequests.contains("foss") + apply plugin: "com.android.application" -apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'kotlin-android' -apply plugin: 'com.bugsnag.android.gradle' + +if (isPlay) { + apply plugin: 'com.google.firebase.crashlytics' + apply plugin: 'com.bugsnag.android.gradle' +} import com.android.build.OutputFile @@ -141,7 +146,9 @@ android { versionCode VERSIONCODE as Integer versionName "4.11.0" vectorDrawables.useSupportLibrary = true - manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] + if (isPlay) { + manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] + } missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below! } @@ -168,8 +175,10 @@ android { minifyEnabled enableProguardInReleaseBuilds setProguardFiles([getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro']) signingConfig signingConfigs.release - firebaseCrashlytics { - nativeSymbolUploadEnabled true + if (isPlay) { + firebaseCrashlytics { + nativeSymbolUploadEnabled true + } } } } @@ -182,6 +191,19 @@ android { // } // applicationVariants are e.g. debug, release + + flavorDimensions "type" + productFlavors { + foss { + dimension = "type" + buildConfigField "boolean", "FDROID_BUILD", "true" + } + play { + dimension = "type" + buildConfigField "boolean", "FDROID_BUILD", "false" + } + } + applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: @@ -217,7 +239,7 @@ dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules - implementation "com.google.firebase:firebase-messaging:18.0.0" + playImplementation "com.google.firebase:firebase-messaging:18.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' @@ -250,4 +272,7 @@ task copyDownloadableDepsToLibs(type: Copy) { into 'libs' } -apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) \ No newline at end of file +apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) +if (isPlay) { + apply plugin: 'com.google.gms.google-services' +} diff --git a/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java new file mode 100644 index 000000000..d8c0502eb --- /dev/null +++ b/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java @@ -0,0 +1,20 @@ +package chat.rocket.reactnative; + +import android.content.Context; +import android.os.Bundle; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.wix.reactnativenotifications.core.AppLaunchHelper; +import com.wix.reactnativenotifications.core.AppLifecycleFacade; +import com.wix.reactnativenotifications.core.JsIOHelper; +import com.wix.reactnativenotifications.core.notification.PushNotification; + +public class CustomPushNotification extends PushNotification { + public static ReactApplicationContext reactApplicationContext; + + public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) { + super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper); + reactApplicationContext = new ReactApplicationContext(context); + } + +} diff --git a/android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java b/android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java new file mode 100644 index 000000000..48350be51 --- /dev/null +++ b/android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java @@ -0,0 +1,12 @@ +package chat.rocket.reactnative; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class DismissNotification extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + } +} diff --git a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java b/android/app/src/foss/java/chat/rocket/reactnative/Ejson.java similarity index 100% rename from android/app/src/main/java/chat/rocket/reactnative/Ejson.java rename to android/app/src/foss/java/chat/rocket/reactnative/Ejson.java diff --git a/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java new file mode 100644 index 000000000..5203495b8 --- /dev/null +++ b/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java @@ -0,0 +1,13 @@ +package chat.rocket.reactnative; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class ReplyBroadcast extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + + } +} diff --git a/android/app/src/foss/res/drawable/ic_launcher_background.xml b/android/app/src/foss/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..1369c4c71 --- /dev/null +++ b/android/app/src/foss/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/foss/res/drawable/ic_launcher_foreground.xml b/android/app/src/foss/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..4fb25367c --- /dev/null +++ b/android/app/src/foss/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/android/app/src/foss/res/drawable/launch_screen.xml b/android/app/src/foss/res/drawable/launch_screen.xml new file mode 100644 index 000000000..d4015d97e --- /dev/null +++ b/android/app/src/foss/res/drawable/launch_screen.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..b3ee0dad1 --- /dev/null +++ b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..b3ee0dad1 --- /dev/null +++ b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..f1878e47b Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..8538bed39 Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..bd72cd260 Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..1e3da8a39 Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..a92ccfafb Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..dfb390737 Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..ce6abf0d6 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..3ba37c9e9 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..710fac56a Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..1ba89143e Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..07f6de5a9 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..86822fdd2 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..2830a1249 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..5ae3008fe Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..a17913dc1 Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/foss/res/values-night/colors.xml b/android/app/src/foss/res/values-night/colors.xml new file mode 100644 index 000000000..3c1a14f89 --- /dev/null +++ b/android/app/src/foss/res/values-night/colors.xml @@ -0,0 +1,4 @@ + + + #000000 + diff --git a/android/app/src/foss/res/values/colors.xml b/android/app/src/foss/res/values/colors.xml new file mode 100644 index 000000000..35e0dfb6d --- /dev/null +++ b/android/app/src/foss/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #660B0B0B + #eeeff1 + #CC3333 + diff --git a/android/app/src/foss/res/values/strings.xml b/android/app/src/foss/res/values/strings.xml new file mode 100644 index 000000000..22a0b201d --- /dev/null +++ b/android/app/src/foss/res/values/strings.xml @@ -0,0 +1,4 @@ + + Rocket.Chat + Rocket.Chat + diff --git a/android/app/src/foss/res/values/styles.xml b/android/app/src/foss/res/values/styles.xml new file mode 100644 index 000000000..50861d8e0 --- /dev/null +++ b/android/app/src/foss/res/values/styles.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ebfb8c74d..f95275dfe 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -67,9 +67,6 @@ - diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java index 6bcc62be8..12d7c0184 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java @@ -49,8 +49,10 @@ public class MainApplication extends Application implements ReactApplication, IN protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); + if (!BuildConfig.FDROID_BUILD) { + packages.add(new RNNotificationsPackage(MainApplication.this)); + } packages.add(new KeyboardInputPackage(MainApplication.this)); - packages.add(new RNNotificationsPackage(MainApplication.this)); packages.add(new WatermelonDBPackage()); packages.add(new RNCViewPagerPackage()); // packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider)); diff --git a/android/app/src/play/AndroidManifest.xml b/android/app/src/play/AndroidManifest.xml new file mode 100644 index 000000000..ebfb8c74d --- /dev/null +++ b/android/app/src/play/AndroidManifest.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/google-services.json b/android/app/src/play/google-services.json similarity index 100% rename from android/app/google-services.json rename to android/app/src/play/google-services.json diff --git a/android/app/src/main/java/chat/rocket/reactnative/Callback.java b/android/app/src/play/java/chat/rocket/reactnative/Callback.java similarity index 100% rename from android/app/src/main/java/chat/rocket/reactnative/Callback.java rename to android/app/src/play/java/chat/rocket/reactnative/Callback.java diff --git a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java similarity index 99% rename from android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java rename to android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java index 46c086eae..ebc3fb7ce 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java +++ b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java @@ -114,7 +114,7 @@ public class CustomPushNotification extends PushNotification { Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class); notification - .setContentTitle(title) + .setContentTitle(title) .setContentText(message) .setContentIntent(intent) .setPriority(Notification.PRIORITY_HIGH) @@ -336,7 +336,7 @@ public class CustomPushNotification extends PushNotification { intent.putExtra(NOTIFICATION_ID, notificationId); PendingIntent dismissPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, intent, 0); - + notification.setDeleteIntent(dismissPendingIntent); } diff --git a/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java b/android/app/src/play/java/chat/rocket/reactnative/DismissNotification.java similarity index 100% rename from android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java rename to android/app/src/play/java/chat/rocket/reactnative/DismissNotification.java diff --git a/android/app/src/play/java/chat/rocket/reactnative/Ejson.java b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java new file mode 100644 index 000000000..499ddf6c1 --- /dev/null +++ b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java @@ -0,0 +1,97 @@ +package chat.rocket.reactnative; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.Callback; + +import com.ammarahmed.mmkv.SecureKeystore; +import com.tencent.mmkv.MMKV; + +import java.math.BigInteger; + +class RNCallback implements Callback { + public void invoke(Object... args) { + + } +} + +class Utils { + static public String toHex(String arg) { + try { + return String.format("%x", new BigInteger(1, arg.getBytes("UTF-8"))); + } catch (Exception e) { + return ""; + } + } +} + +public class Ejson { + String host; + String rid; + String type; + Sender sender; + String messageId; + String notificationType; + + private MMKV mmkv; + + private String TOKEN_KEY = "reactnativemeteor_usertoken-"; + + public Ejson() { + ReactApplicationContext reactApplicationContext = CustomPushNotification.reactApplicationContext; + + // Start MMKV container + MMKV.initialize(reactApplicationContext); + SecureKeystore secureKeystore = new SecureKeystore(reactApplicationContext); + + // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31 + String alias = Utils.toHex("com.MMKV.default"); + + // Retrieve container password + secureKeystore.getSecureKey(alias, new RNCallback() { + @Override + public void invoke(Object... args) { + String error = (String) args[0]; + if (error == null) { + String password = (String) args[1]; + mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password); + } + } + }); + } + + public String getAvatarUri() { + if (type == null) { + return null; + } + return serverURL() + "/avatar/" + this.sender.username + "?rc_token=" + token() + "&rc_uid=" + userId(); + } + + public String token() { + String userId = userId(); + if (mmkv != null && userId != null) { + return mmkv.decodeString(TOKEN_KEY.concat(userId)); + } + return ""; + } + + public String userId() { + String serverURL = serverURL(); + if (mmkv != null && serverURL != null) { + return mmkv.decodeString(TOKEN_KEY.concat(serverURL)); + } + return ""; + } + + public String serverURL() { + String url = this.host; + if (url != null && url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + return url; + } + + public class Sender { + String username; + String _id; + } +} diff --git a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java b/android/app/src/play/java/chat/rocket/reactnative/LoadNotification.java similarity index 100% rename from android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java rename to android/app/src/play/java/chat/rocket/reactnative/LoadNotification.java diff --git a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/play/java/chat/rocket/reactnative/ReplyBroadcast.java similarity index 100% rename from android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java rename to android/app/src/play/java/chat/rocket/reactnative/ReplyBroadcast.java diff --git a/android/build.gradle b/android/build.gradle index 52f6ff0f0..13ea8c1ee 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -17,15 +17,18 @@ buildscript { url 'https://maven.fabric.io/public' } } - dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' - classpath 'com.google.gms:google-services:4.2.0' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + def taskRequests = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase() + def isPlay = !taskRequests.contains("foss") + + dependencies { + if (isPlay) { + classpath 'com.google.gms:google-services:4.2.0' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0' + classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+' + } + classpath 'com.android.tools.build:gradle:3.5.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index d574e24d0..023db49f7 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -16,20 +16,25 @@ default_platform(:android) platform :android do - desc "Build App for development" - lane :build do - gradle(task: "assembleDebug") + desc "Play build for development" + lane :playBuild do + gradle(task: "assemblePlayDebug") end - desc "Build App for release" - lane :release do - gradle(task: "bundleRelease") + desc "Foss build for release" + lane :fossRelease do + gradle(task: "assembleFossRelease") + end + + desc "Play build for release" + lane :playRelease do + gradle(task: "bundlePlayRelease") end desc "Upload App to Play Store Internal" lane :beta do upload_to_play_store( - track: 'internal', + track: 'internal', aab: 'android/app/build/outputs/bundle/release/app-release.aab' ) end diff --git a/android/fastlane/README.md b/android/fastlane/README.md index 3031222f9..32a0f12c2 100644 --- a/android/fastlane/README.md +++ b/android/fastlane/README.md @@ -16,19 +16,24 @@ or alternatively using `brew cask install fastlane` # Available Actions ## Android -### android build +### android playBuild ``` -fastlane android build +fastlane android playBuild ``` -Build App for development -### android release +Play build for development +### android fossRelease ``` -fastlane android release +fastlane android fossRelease ``` -Build App for release -### android alpha +Foss build for release +### android playRelease ``` -fastlane android alpha +fastlane android playRelease +``` +Play build for release +### android playAlpha +``` +fastlane android playAlpha ``` Upload App to Play store diff --git a/app/constants/environment.js b/app/constants/environment.js new file mode 100644 index 000000000..099d27d1c --- /dev/null +++ b/app/constants/environment.js @@ -0,0 +1,3 @@ +import RNConfigReader from 'react-native-config-reader'; + +export const isFDroidBuild = RNConfigReader.FDROID_BUILD; diff --git a/app/constants/links.js b/app/constants/links.js index f676ded9a..71b6cc07b 100644 --- a/app/constants/links.js +++ b/app/constants/links.js @@ -3,6 +3,7 @@ import { getBundleId, isIOS } from '../utils/deviceInfo'; const APP_STORE_ID = '1272915472'; export const PLAY_MARKET_LINK = `https://play.google.com/store/apps/details?id=${ getBundleId }`; +export const FDROID_MARKET_LINK = 'https://f-droid.org/en/packages/chat.rocket.android'; export const APP_STORE_LINK = `https://itunes.apple.com/app/id${ APP_STORE_ID }`; export const LICENSE_LINK = 'https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/LICENSE'; export const STORE_REVIEW_LINK = isIOS ? `itms-apps://itunes.apple.com/app/id${ APP_STORE_ID }?action=write-review` : `market://details?id=${ getBundleId }`; diff --git a/app/index.js b/app/index.js index 60fd2c934..4455a2f14 100644 --- a/app/index.js +++ b/app/index.js @@ -36,6 +36,7 @@ import Toast from './containers/Toast'; import InAppNotification from './containers/InAppNotification'; import { ActionSheetProvider } from './containers/ActionSheet'; import debounce from './utils/debounce'; +import { isFDroidBuild } from './constants/environment'; RNScreens.enableScreens(); @@ -64,7 +65,9 @@ export default class Root extends React.Component { constructor(props) { super(props); this.init(); - this.initCrashReport(); + if (!isFDroidBuild) { + this.initCrashReport(); + } const { width, height, scale } = Dimensions.get('window'); this.state = { theme: defaultTheme(), diff --git a/app/notifications/push/index.js b/app/notifications/push/index.js index 40ccbea16..df4ac152d 100644 --- a/app/notifications/push/index.js +++ b/app/notifications/push/index.js @@ -1,8 +1,8 @@ import EJSON from 'ejson'; - import PushNotification from './push'; import store from '../../lib/createStore'; import { deepLinkingOpen } from '../../actions/deepLinking'; +import { isFDroidBuild } from '../../constants/environment'; export const onNotification = (notification) => { if (notification) { @@ -38,8 +38,10 @@ export const onNotification = (notification) => { export const getDeviceToken = () => PushNotification.getDeviceToken(); export const setBadgeCount = count => PushNotification.setBadgeCount(count); export const initializePushNotifications = () => { - setBadgeCount(); - return PushNotification.configure({ - onNotification - }); + if (!isFDroidBuild) { + setBadgeCount(); + return PushNotification.configure({ + onNotification + }); + } }; diff --git a/app/utils/log/index.js b/app/utils/log/index.js index ea00ca325..0b67bf575 100644 --- a/app/utils/log/index.js +++ b/app/utils/log/index.js @@ -1,10 +1,17 @@ -import { Client } from 'bugsnag-react-native'; -import analytics from '@react-native-firebase/analytics'; -import crashlytics from '@react-native-firebase/crashlytics'; +import firebaseAnalytics from '@react-native-firebase/analytics'; +import { isFDroidBuild } from '../../constants/environment'; import config from '../../../config'; import events from './events'; -const bugsnag = new Client(config.BUGSNAG_API_KEY); +const analytics = firebaseAnalytics || ''; +let bugsnag = ''; +let crashlytics; + +if (!isFDroidBuild) { + const { Client } = require('bugsnag-react-native'); + crashlytics = require('@react-native-firebase/crashlytics'); + bugsnag = new Client(config.BUGSNAG_API_KEY); +} export { analytics }; export const loggerConfig = bugsnag.config; @@ -21,20 +28,24 @@ export const logServerVersion = (serverVersion) => { export const logEvent = (eventName, payload) => { try { - analytics().logEvent(eventName, payload); - leaveBreadcrumb(eventName, payload); + if (!isFDroidBuild) { + analytics().logEvent(eventName, payload); + leaveBreadcrumb(eventName, payload); + } } catch { // Do nothing } }; export const setCurrentScreen = (currentScreen) => { - analytics().setCurrentScreen(currentScreen); - leaveBreadcrumb(currentScreen, { type: 'navigation' }); + if (!isFDroidBuild) { + analytics().setCurrentScreen(currentScreen); + leaveBreadcrumb(currentScreen, { type: 'navigation' }); + } }; export default (e) => { - if (e instanceof Error && e.message !== 'Aborted' && !__DEV__) { + if (e instanceof Error && bugsnag && e.message !== 'Aborted' && !__DEV__) { bugsnag.notify(e, (report) => { report.metadata = { details: { @@ -42,7 +53,9 @@ export default (e) => { } }; }); - crashlytics().recordError(e); + if (!isFDroidBuild) { + crashlytics().recordError(e); + } } else { console.log(e); } diff --git a/app/utils/review.js b/app/utils/review.js index 17ba73c90..98d673631 100644 --- a/app/utils/review.js +++ b/app/utils/review.js @@ -5,6 +5,7 @@ import { isIOS } from './deviceInfo'; import I18n from '../i18n'; import { showErrorAlert } from './info'; import { STORE_REVIEW_LINK } from '../constants/links'; +import { isFDroidBuild } from '../constants/environment'; import { logEvent, events } from './log'; const store = isIOS ? 'App Store' : 'Play Store'; @@ -87,12 +88,14 @@ class ReviewApp { positiveEventCount = 0; pushPositiveEvent = () => { - if (this.positiveEventCount >= numberOfPositiveEvent) { - return; - } - this.positiveEventCount += 1; - if (this.positiveEventCount === numberOfPositiveEvent) { - tryReview(); + if (!isFDroidBuild) { + if (this.positiveEventCount >= numberOfPositiveEvent) { + return; + } + this.positiveEventCount += 1; + if (this.positiveEventCount === numberOfPositiveEvent) { + tryReview(); + } } } } diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 83e265461..5f14d09c7 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -29,7 +29,9 @@ import styles from './styles'; import { loggerConfig, analytics, logEvent, events } from '../../utils/log'; -import { PLAY_MARKET_LINK, APP_STORE_LINK, LICENSE_LINK } from '../../constants/links'; +import { + PLAY_MARKET_LINK, FDROID_MARKET_LINK, APP_STORE_LINK, LICENSE_LINK +} from '../../constants/links'; import { withTheme } from '../../theme'; import SidebarView from '../SidebarView'; import { LISTENER } from '../../containers/Toast'; @@ -37,6 +39,8 @@ import EventEmitter from '../../utils/events'; import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app'; import { onReviewPress } from '../../utils/review'; import SafeAreaView from '../../containers/SafeAreaView'; +import { isFDroidBuild } from '../../constants/environment'; + const SectionSeparator = React.memo(({ theme }) => ( false); + if (!isFDroidBuild) { + loggerConfig.autoNotify = value; + analytics().setAnalyticsCollectionEnabled(value); + if (value) { + loggerConfig.clearBeforeSendCallbacks(); + } else { + loggerConfig.registerBeforeSendCallback(() => false); + } } } @@ -142,8 +148,16 @@ class SettingsView extends React.Component { } shareApp = () => { - logEvent(events.SE_SHARE_THIS_APP); - Share.share({ message: isAndroid ? PLAY_MARKET_LINK : APP_STORE_LINK }); + let message; + if (isAndroid) { + message = PLAY_MARKET_LINK; + if (isFDroidBuild) { + message = FDROID_MARKET_LINK; + } + } else { + message = APP_STORE_LINK; + } + Share.share({ message }); } copyServerVersion = () => { @@ -230,14 +244,18 @@ class SettingsView extends React.Component { theme={theme} /> - + {!isFDroidBuild ? ( + <> + + + ) : null} - this.renderCrashReportSwitch()} - theme={theme} - /> - - + {this.showLivechat ? ( + <> + this.renderLivechatSwitch()} + theme={theme} + /> + + + ) : null} + + {!isFDroidBuild ? ( + <> + this.renderCrashReportSwitch()} + theme={theme} + /> + + + + ) : null}