├── plugins
└── .keep
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── assets
│ │ │ ├── custom-icons.json
│ │ │ ├── fonts
│ │ │ │ └── custom-icons.ttf
│ │ │ ├── BlobDownloader.js
│ │ │ └── offline.html
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ ├── ic_actionbar.png
│ │ │ │ ├── drawer_shadow.9.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_chevron_up_light.png
│ │ │ │ ├── ic_search_black_24dp.png
│ │ │ │ ├── ic_search_white_24dp.png
│ │ │ │ ├── ic_share_black_24dp.png
│ │ │ │ ├── ic_share_white_24dp.png
│ │ │ │ ├── ic_chevron_down_light.png
│ │ │ │ ├── ic_refresh_black_24dp.png
│ │ │ │ └── ic_refresh_white_24dp.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ ├── ic_actionbar.png
│ │ │ │ ├── drawer_shadow.9.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_chevron_up_light.png
│ │ │ │ ├── ic_search_black_24dp.png
│ │ │ │ ├── ic_search_white_24dp.png
│ │ │ │ ├── ic_share_black_24dp.png
│ │ │ │ ├── ic_share_white_24dp.png
│ │ │ │ ├── ic_chevron_down_light.png
│ │ │ │ ├── ic_refresh_black_24dp.png
│ │ │ │ └── ic_refresh_white_24dp.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ ├── ic_actionbar.png
│ │ │ │ ├── drawer_shadow.9.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_chevron_up_light.png
│ │ │ │ ├── ic_share_black_24dp.png
│ │ │ │ ├── ic_share_white_24dp.png
│ │ │ │ ├── ic_chevron_down_light.png
│ │ │ │ ├── ic_refresh_black_24dp.png
│ │ │ │ ├── ic_refresh_white_24dp.png
│ │ │ │ ├── ic_search_black_24dp.png
│ │ │ │ └── ic_search_white_24dp.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ ├── ic_actionbar.png
│ │ │ │ ├── drawer_shadow.9.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_chevron_up_light.png
│ │ │ │ ├── ic_search_black_24dp.png
│ │ │ │ ├── ic_search_white_24dp.png
│ │ │ │ ├── ic_share_black_24dp.png
│ │ │ │ ├── ic_share_white_24dp.png
│ │ │ │ ├── ic_chevron_down_light.png
│ │ │ │ ├── ic_refresh_black_24dp.png
│ │ │ │ └── ic_refresh_white_24dp.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_sidebar_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_sidebar_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_sidebar_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── values-sw600dp
│ │ │ │ ├── attr.xml
│ │ │ │ └── dimens.xml
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ ├── ic_actionbar.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_share_black_24dp.png
│ │ │ │ ├── ic_share_white_24dp.png
│ │ │ │ ├── ic_refresh_black_24dp.png
│ │ │ │ ├── ic_refresh_white_24dp.png
│ │ │ │ ├── ic_search_black_24dp.png
│ │ │ │ └── ic_search_white_24dp.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_sidebar_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_sidebar_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-night-hdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_actionbar.png
│ │ │ ├── drawable-night-mdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_actionbar.png
│ │ │ ├── drawable-night-xhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_actionbar.png
│ │ │ ├── drawable-night-xxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_actionbar.png
│ │ │ ├── drawable-night-xxxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_actionbar.png
│ │ │ ├── mipmap-night-hdpi
│ │ │ │ └── ic_sidebar_logo.png
│ │ │ ├── mipmap-night-mdpi
│ │ │ │ └── ic_sidebar_logo.png
│ │ │ ├── mipmap-night-xhdpi
│ │ │ │ └── ic_sidebar_logo.png
│ │ │ ├── menu
│ │ │ │ └── topmenu.xml
│ │ │ ├── mipmap-night-xxhdpi
│ │ │ │ └── ic_sidebar_logo.png
│ │ │ ├── mipmap-night-xxxhdpi
│ │ │ │ └── ic_sidebar_logo.png
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── integers.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── layout
│ │ │ │ ├── empty.xml
│ │ │ │ ├── tab.xml
│ │ │ │ ├── profile_picker_dropdown.xml
│ │ │ │ ├── button_menu.xml
│ │ │ │ ├── splash_screen.xml
│ │ │ │ ├── menu_child_noicon.xml
│ │ │ │ ├── actionbar_title.xml
│ │ │ │ ├── activity_subscriptions.xml
│ │ │ │ ├── menu_group_noicon.xml
│ │ │ │ ├── view_handle.xml
│ │ │ │ ├── menu_child_icon.xml
│ │ │ │ └── menu_group_icon.xml
│ │ │ ├── anim
│ │ │ │ └── fast_fade_out.xml
│ │ │ ├── xml
│ │ │ │ ├── network_security_config.xml
│ │ │ │ └── filepaths.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_stat_onesignal_default.xml
│ │ │ │ ├── bg_nav_icon.xml
│ │ │ │ ├── ic_go_back.xml
│ │ │ │ ├── ic_go_forward.xml
│ │ │ │ ├── ic_baseline_arrow_forward_24.xml
│ │ │ │ ├── ic_baseline_arrow_back_24.xml
│ │ │ │ └── shape_rounded.xml
│ │ │ ├── values-sw720dp-land
│ │ │ │ └── dimens.xml
│ │ │ ├── values-large
│ │ │ │ └── styles.xml
│ │ │ ├── values-ko
│ │ │ │ └── strings.xml
│ │ │ ├── values-v21
│ │ │ │ └── styles.xml
│ │ │ ├── values-v29
│ │ │ │ └── styles.xml
│ │ │ ├── values-night-v29
│ │ │ │ └── styles.xml
│ │ │ └── values-night
│ │ │ │ ├── styles.xml
│ │ │ │ └── colors.xml
│ │ ├── java
│ │ │ └── io
│ │ │ │ └── gonative
│ │ │ │ └── android
│ │ │ │ ├── JsResultBridge.java
│ │ │ │ ├── WebViewPoolDisownPolicy.java
│ │ │ │ ├── AppLinksActivity.java
│ │ │ │ ├── IOUtils.java
│ │ │ │ ├── MySwipeRefreshLayout.java
│ │ │ │ ├── ConfigPreferences.java
│ │ │ │ ├── widget
│ │ │ │ ├── GoNativeDrawerLayout.java
│ │ │ │ ├── WebViewContainerView.java
│ │ │ │ ├── CircleImageView.java
│ │ │ │ └── HandleView.kt
│ │ │ │ ├── ShakeDialogFragment.java
│ │ │ │ ├── UrlInspector.java
│ │ │ │ ├── files
│ │ │ │ └── CapturedImageSaver.kt
│ │ │ │ ├── JsCustomCodeExecutor.java
│ │ │ │ ├── CustomHeaders.java
│ │ │ │ ├── ConfigUpdater.java
│ │ │ │ ├── KeyboardManager.kt
│ │ │ │ ├── SplashActivity.java
│ │ │ │ ├── GoNativeApplication.java
│ │ │ │ ├── SegmentedController.java
│ │ │ │ ├── ProfilePicker.java
│ │ │ │ ├── LoginManager.java
│ │ │ │ ├── GoNativeWindowManager.java
│ │ │ │ ├── Installation.java
│ │ │ │ └── RegistrationManager.java
│ │ └── AndroidManifest.xml
│ ├── androidTest
│ │ ├── assets
│ │ │ ├── HelperClass.java
│ │ │ └── appConfig.json
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── gonative
│ │ │ └── testFiles
│ │ │ └── FirstTestClass.java
│ └── normal
│ │ └── java
│ │ └── io
│ │ └── gonative
│ │ └── android
│ │ ├── PoolWebViewClient.java
│ │ ├── GoNativeWebviewClient.java
│ │ ├── WebkitCookieManagerProxy.java
│ │ └── WebViewSetup.java
├── proguard-project.txt
└── build.gradle
├── AppIcon
├── HeaderImage
├── NotificationIcon
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── settings.gradle
├── README.md
├── CHANGELOG.md
├── generate-tinted-icons.sh
├── generate-header-images.sh
├── gradlew.bat
├── generate-plugin-icons.sh
├── .github
└── workflows
│ └── firebase-testing.yml
├── plugins.gradle
└── gradlew
/plugins/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/AppIcon:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/AppIcon
--------------------------------------------------------------------------------
/app/src/main/assets/custom-icons.json:
--------------------------------------------------------------------------------
1 | {
2 | "gonative-icon": 59392
3 | }
4 |
--------------------------------------------------------------------------------
/HeaderImage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/HeaderImage
--------------------------------------------------------------------------------
/NotificationIcon:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/NotificationIcon
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/assets/fonts/custom-icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/assets/fonts/custom-icons.ttf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | .idea/
3 | .gradle/
4 | *.iml
5 | local.properties
6 | app/build/
7 | build/
8 | captures/
9 | .DS_Store
10 | plugins/
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m
2 | android.enableJetifier=true
3 | android.useAndroidX=true
4 | enableLogsInRelease=false
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-hdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-hdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-mdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-mdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-hdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-mdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/androidTest/assets/HelperClass.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | public class HelperClass {
4 | public volatile static int newLoad = 0;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xxhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xxxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xxxhdpi/splash.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_chevron_up_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_chevron_up_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_search_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_search_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_chevron_up_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_chevron_up_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_search_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_search_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-hdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-hdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-mdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-mdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xxhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xxhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_chevron_up_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_chevron_up_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-night-hdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-night-hdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-night-mdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-night-mdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-night-xhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-night-xhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_chevron_down_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_chevron_down_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_refresh_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_refresh_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_chevron_down_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_chevron_down_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_refresh_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_refresh_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-xxxhdpi/ic_actionbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-night-xxxhdpi/ic_actionbar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_chevron_down_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_chevron_down_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_refresh_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_refresh_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_search_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_search_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_chevron_up_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_chevron_up_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_search_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_search_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/menu/topmenu.xml:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-night-xxhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-night-xxhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-night-xxxhdpi/ic_sidebar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-night-xxxhdpi/ic_sidebar_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/JsResultBridge.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | public class JsResultBridge {
4 | public static String jsResult = "";
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_chevron_down_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_chevron_down_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_refresh_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_refresh_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_refresh_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_refresh_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_search_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_search_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gonativeio/gonative-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'GoNative'
2 | System.setProperty("user.dir", rootProject.projectDir.toString())
3 | apply from: file("./plugins.gradle"); applyModulesSettingsGradle(settings)
4 |
5 | include ':app'
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/app/src/androidTest/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fast_fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Feb 18 14:21:02 EST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stat_onesignal_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/WebViewPoolDisownPolicy.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | /**
4 | * Created by weiyin on 9/3/14.
5 | */
6 | public enum WebViewPoolDisownPolicy {
7 | Always, Reload, Never;
8 |
9 | public static WebViewPoolDisownPolicy defaultPolicy = WebViewPoolDisownPolicy.Reload;
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_nav_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-large/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_go_back.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_go_forward.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/filepaths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 15
4 | 20
5 | 2
6 | 22
7 | 22
8 | 48
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/profile_picker_dropdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Archive Notice
2 |
3 | This repository is archived and is no longer being maintained. You can now build an app using our online App Studio at https://median.co/app and download custom iOS source code for free.
4 |
5 |
6 | gonative-android
7 | ================
8 |
9 | This is the native Android code previously used by [GoNative](https://median.co).
10 |
11 | It allows the creation of apps from existing mobile-optimized websites.
12 |
13 | How to use
14 | ------------
15 | Import into Android Studio. Edit appConfig.json as appropriate.
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/button_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/splash_screen.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 기기를 흔들면 캐시를 삭제할 수 있음
4 |
5 | - 캐시 삭제
6 | - 취소
7 |
8 | 캐시 삭제중
9 | 작업 선택
10 | 다운로드 시작됨
11 | 파일 다운로드 완료
12 | 다운로드한 파일: %1$s
13 | 다운로드 취소됨
14 | 다운로드 오류
15 | 갤러리에 저장된 이미지
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/AppLinksActivity.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 |
6 | import androidx.annotation.Nullable;
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | public class AppLinksActivity extends AppCompatActivity {
10 |
11 | @Override
12 | protected void onCreate(@Nullable Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | launchApp();
15 | }
16 |
17 | private void launchApp() {
18 | Intent intent = new Intent(this, MainActivity.class);
19 | if (getIntent().getData() != null) {
20 | intent.setData(getIntent().getData());
21 | intent.setAction(Intent.ACTION_VIEW);
22 | }
23 | startActivity(intent);
24 | finish();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/IOUtils.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 |
4 | import java.io.Closeable;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 |
9 | import io.gonative.gonative_core.GNLog;
10 |
11 | public class IOUtils {
12 | private static final String TAG = IOUtils.class.getName();
13 |
14 | public static void copy(InputStream in, OutputStream out) throws IOException{
15 | byte[] buf = new byte[1024];
16 | int len;
17 | while ((len = in.read(buf)) > 0) {
18 | out.write(buf, 0, len);
19 | }
20 | }
21 |
22 | public static void close(Closeable c) {
23 | if (c == null) return;
24 | try {
25 | c.close();
26 | } catch (IOException e){
27 | GNLog.getInstance().logError(TAG, e.toString(), e);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_child_noicon.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/actionbar_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_subscriptions.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 22dp
7 | 22dp
8 | 25dp
9 | 7dp
10 | 7dp
11 | 7dp
12 |
13 | 32dp
14 | 38dp
15 | 8dp
16 | 2dp
17 | 12dp
18 | 1dp
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v29/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night-v29/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 |
17 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffffff
4 | #757575
5 | #000000
6 |
7 | #1E496E
8 | #ffffff
9 | #000000
10 |
11 | #1e496e
12 | #ffffff
13 | #30808080
14 | #1e496e
15 |
16 | #ffffff
17 | #949494
18 | #1e496e
19 |
20 | #fafafa
21 | #888888
22 | #1e88e5
23 |
24 | #1e496e
25 |
26 | #FFFFFF
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #212121
4 | #030303
5 | #ffffff
6 |
7 | #80cbc4
8 | #444444
9 | #ffffff
10 |
11 | #ffffff
12 | #333333
13 | #30ffffff
14 | #ffffff
15 |
16 | #333333
17 | #ffffff
18 | #666666
19 |
20 | #666666
21 | #888888
22 | #ffffff
23 |
24 | #ffffff
25 |
26 | #1e496e
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 2014-01-04
4 |
5 | - Fix a crash on reload with no page loaded.
6 |
7 | ## 2015-01-02
8 |
9 | - Update to latest gradle and build tools versions, making the project compatible with Android Studio 1.0.
10 | - Fix bugs related to syncing of tabs with sidebar menu.
11 |
12 | ## 2014-12-23
13 |
14 | - Allow setting of viewport while preserving ability to zoom.
15 | - Allow dynamic config of navigation title image URLs.
16 | - Various bug fixes involving javascript after page load, and tab coloring, tab animations, and a crash on application resume.
17 |
18 | ## 2014-12-22
19 |
20 | - Fix various threading bugs where UI methods were called from non-UI threads.
21 |
22 | ## 2014-12-05
23 |
24 | - Support showing the navigation title image on specific URLs.
25 |
26 | ## 2014-12-03
27 |
28 | - Support customizing user agent per URL.
29 | - Add color styling options for tabs.
30 |
31 | ## 2014-11-30
32 |
33 | - New tabs with better material design and animations.
34 | - Fix some automatic icon generation scripts.
35 |
36 | ## 2014-11-26
37 |
38 | - Fix a crash involving webview pools.
39 |
40 | ## 2014-11-25
41 |
42 | - Add support for custom actions in action bar.
43 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/MySwipeRefreshLayout.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 |
6 | import io.gonative.android.widget.GoNativeSwipeRefreshLayout;
7 |
8 | /**
9 | * Created by weiyin on 9/13/15.
10 | * Copyright 2014 GoNative.io LLC
11 | */
12 | public class MySwipeRefreshLayout extends GoNativeSwipeRefreshLayout {
13 | private CanChildScrollUpCallback canChildScrollUpCallback;
14 |
15 | public interface CanChildScrollUpCallback {
16 | boolean canSwipeRefreshChildScrollUp();
17 | }
18 |
19 | public MySwipeRefreshLayout(Context context) {
20 | super(context);
21 | }
22 |
23 | public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public void setCanChildScrollUpCallback(CanChildScrollUpCallback canChildScrollUpCallback) {
28 | this.canChildScrollUpCallback = canChildScrollUpCallback;
29 | }
30 |
31 | @Override
32 | public boolean canChildScrollUp() {
33 | if (canChildScrollUpCallback != null) {
34 | return canChildScrollUpCallback.canSwipeRefreshChildScrollUp();
35 | } else {
36 | return super.canChildScrollUp();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_group_noicon.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
21 |
22 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/ConfigPreferences.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.preference.PreferenceManager;
6 | import android.text.TextUtils;
7 |
8 | public class ConfigPreferences {
9 | private static final String APP_THEME_KEY = "io.gonative.android.appTheme";
10 |
11 | private Context context;
12 | private SharedPreferences sharedPreferences;
13 |
14 | public ConfigPreferences(Context context) {
15 | this.context = context;
16 | }
17 |
18 | private SharedPreferences getSharedPreferences() {
19 | if (this.sharedPreferences == null) {
20 | this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.context);
21 | }
22 | return this.sharedPreferences;
23 | }
24 |
25 | public String getAppTheme() {
26 | SharedPreferences preferences = getSharedPreferences();
27 | return preferences.getString(APP_THEME_KEY, null);
28 | }
29 |
30 | public void setAppTheme(String appTheme) {
31 | SharedPreferences preferences = getSharedPreferences();
32 | if (TextUtils.isEmpty(appTheme)) {
33 | preferences.edit().remove(APP_THEME_KEY).commit();
34 | } else {
35 | preferences.edit().putString(APP_THEME_KEY, appTheme).commit();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_handle.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
20 |
21 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_child_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/widget/GoNativeDrawerLayout.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 | import androidx.drawerlayout.widget.DrawerLayout;
11 |
12 | public class GoNativeDrawerLayout extends DrawerLayout {
13 |
14 | private boolean disableTouch = false;
15 |
16 | public GoNativeDrawerLayout(@NonNull Context context) {
17 | this(context, null);
18 | }
19 |
20 | public GoNativeDrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
21 | this(context, attrs, 0);
22 | }
23 |
24 | public GoNativeDrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
25 | super(context, attrs, defStyle);
26 | }
27 |
28 | @Override
29 | public boolean onInterceptTouchEvent(MotionEvent ev) {
30 | if (disableTouch) {
31 | Log.d("SWIPE", "GNDrawerLayout disabled touch");
32 | return false;
33 | }
34 | return super.onInterceptTouchEvent(ev);
35 | }
36 |
37 |
38 | @Override
39 | public boolean onTouchEvent(MotionEvent ev) {
40 | if (disableTouch) {
41 | Log.d("SWIPE", "GNDrawerLayout disabled touch");
42 | return false;
43 | }
44 | return super.onTouchEvent(ev);
45 | }
46 |
47 | public void setDisableTouch(boolean disableTouch){
48 | this.disableTouch = disableTouch;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | false
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/ShakeDialogFragment.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 | import androidx.fragment.app.DialogFragment;
11 |
12 | public class ShakeDialogFragment extends DialogFragment {
13 | public interface ShakeDialogListener {
14 | public void onClearCache(DialogFragment dialog);
15 | }
16 |
17 | ShakeDialogListener listener;
18 |
19 | @NonNull
20 | @Override
21 | public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
22 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
23 | builder.setTitle(R.string.shake_to_clear_cache)
24 | .setItems(R.array.device_shaken_options, new DialogInterface.OnClickListener() {
25 | @Override
26 | public void onClick(DialogInterface dialogInterface, int i) {
27 | if (i == 0) {
28 | listener.onClearCache(ShakeDialogFragment.this);
29 | }
30 | }
31 | });
32 | return builder.create();
33 | }
34 |
35 | @Override
36 | public void onAttach(Context context) {
37 | super.onAttach(context);
38 | try {
39 | listener = (ShakeDialogListener) context;
40 | } catch (ClassCastException e) {
41 | throw new ClassCastException(context.toString() + " must implement ShakeDialogListener");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/UrlInspector.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 |
5 | import java.util.regex.Matcher;
6 | import java.util.regex.Pattern;
7 | import java.util.regex.PatternSyntaxException;
8 |
9 | import io.gonative.gonative_core.AppConfig;
10 | import io.gonative.gonative_core.GNLog;
11 |
12 | /**
13 | * Created by weiyin on 4/28/14.
14 | */
15 | public class UrlInspector {
16 | private static final String TAG = UrlInspector.class.getName();
17 | // singleton
18 | private static UrlInspector instance = null;
19 |
20 | private Pattern userIdRegex = null;
21 |
22 | private String userId = null;
23 |
24 |
25 | public static UrlInspector getInstance(){
26 | if (instance == null) {
27 | instance = new UrlInspector();
28 | }
29 | return instance;
30 | }
31 |
32 | public void init(Context context) {
33 | String regexString = AppConfig.getInstance(context).userIdRegex;
34 | if (regexString != null && !regexString.isEmpty()) {
35 | try {
36 | userIdRegex = Pattern.compile(regexString);
37 | } catch (PatternSyntaxException e) {
38 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
39 | }
40 | }
41 | }
42 |
43 | private UrlInspector() {
44 | // prevent direct instantiation
45 | }
46 |
47 | public void inspectUrl(String url) {
48 | if (userIdRegex != null) {
49 | Matcher matcher = userIdRegex.matcher(url);
50 | if (matcher.groupCount() > 0 && matcher.find()) {
51 | setUserId(matcher.group(1));
52 | }
53 | }
54 | }
55 |
56 | public String getUserId() {
57 | return userId;
58 | }
59 |
60 | private void setUserId(String userId) {
61 | this.userId = userId;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/files/CapturedImageSaver.kt:
--------------------------------------------------------------------------------
1 | package io.gonative.android.files
2 |
3 | import android.content.ContentResolver
4 | import android.content.ContentValues
5 | import android.content.Context
6 | import android.net.Uri
7 | import android.os.Build
8 | import android.os.Environment
9 | import android.provider.MediaStore
10 | import androidx.core.content.FileProvider
11 | import java.io.File
12 | import java.text.SimpleDateFormat
13 | import java.util.Date
14 | import java.util.Locale
15 |
16 |
17 | class CapturedImageSaver {
18 | fun saveCapturedBitmap(context: Context, bitmapUri: Uri): Uri? {
19 | val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
20 | val imageFileName = "IMG_$timeStamp.jpg"
21 |
22 | val resolver: ContentResolver = context.contentResolver
23 | val currentUri = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
24 | val contentValues = ContentValues()
25 | contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, imageFileName)
26 | contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/*")
27 | contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
28 | resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
29 | } else {
30 | val storageDir = Environment.getExternalStoragePublicDirectory(
31 | Environment.DIRECTORY_PICTURES)
32 | val captureFile = File(storageDir, imageFileName)
33 | FileProvider.getUriForFile(context, context.applicationContext.packageName + ".fileprovider", captureFile);
34 | }
35 |
36 | currentUri?.let {
37 | context.contentResolver.openOutputStream(it).use { output ->
38 | context.contentResolver.openInputStream(bitmapUri).use { input ->
39 | output?.write(input?.readBytes())
40 | }
41 | }
42 | }
43 |
44 | return currentUri
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_group_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
21 |
22 |
37 |
38 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/normal/java/io/gonative/android/PoolWebViewClient.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Build;
5 | import android.os.Handler;
6 | import android.webkit.WebResourceRequest;
7 | import android.webkit.WebResourceResponse;
8 | import android.webkit.WebView;
9 | import android.webkit.WebViewClient;
10 |
11 | import io.gonative.gonative_core.GoNativeWebviewInterface;
12 |
13 | /**
14 | * Created by weiyin on 9/9/15.
15 | */
16 | public class PoolWebViewClient extends WebViewClient {
17 | private WebViewPool.WebViewPoolCallback webViewPoolCallback;
18 |
19 | public PoolWebViewClient(WebViewPool.WebViewPoolCallback webViewPoolCallback, LeanWebView view) {
20 | this.webViewPoolCallback = webViewPoolCallback;
21 | view.setWebViewClient(this);
22 | }
23 |
24 | @Override
25 | public void onPageFinished(final WebView view, String url) {
26 | super.onPageFinished(view, url);
27 |
28 | // remove self as webviewclient
29 | new Handler(view.getContext().getMainLooper()).post(new Runnable() {
30 | @Override
31 | public void run() {
32 | view.setWebViewClient(null);
33 | }
34 | });
35 |
36 | webViewPoolCallback.onPageFinished((GoNativeWebviewInterface) view, url);
37 | }
38 |
39 | @Override
40 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
41 | return webViewPoolCallback.interceptHtml((GoNativeWebviewInterface)view, url);
42 | }
43 |
44 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
45 | @Override
46 | public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
47 | String method = request.getMethod();
48 | if (method == null || !method.equalsIgnoreCase("GET")) return null;
49 |
50 | android.net.Uri uri = request.getUrl();
51 | if (uri == null || !uri.getScheme().startsWith("http")) return null;
52 |
53 | return shouldInterceptRequest(view, uri.toString());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/generate-tinted-icons.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | DARK_ICONS=(
5 | ic_refresh_white_24dp.png
6 | ic_search_white_24dp.png
7 | ic_share_white_24dp.png
8 | )
9 |
10 | LIGHT_ICONS=(
11 | ic_refresh_black_24dp.png
12 | ic_search_black_24dp.png
13 | ic_share_black_24dp.png
14 | )
15 |
16 | DARK_DEFAULT_COLOR=ffffff
17 | LIGHT_DEFAULT_COLOR=000000
18 |
19 | BASEDIR=$(dirname $0)
20 |
21 | function showHelp {
22 | echo "Usage: $0 (dark|light) tintColor tintColorDark"
23 | echo "Example: $0 light 0000ff ffffff"
24 | exit 1
25 |
26 | }
27 |
28 | if [[ $# -lt 2 ]]; then
29 | showHelp
30 | fi
31 |
32 | theme=`echo $1 | tr '[:upper:]' '[:lower:]'`
33 | tintColor=`echo $2 | tr '[:upper:]' '[:lower:]'`
34 | tintColorDark=`echo $3 | tr '[:upper:]' '[:lower:]'`
35 |
36 | if [[ $theme = light ]]; then
37 | icons=("${LIGHT_ICONS[@]}")
38 | defaultColor=$LIGHT_DEFAULT_COLOR
39 | elif [[ $theme = dark ]]; then
40 | icons=("${DARK_ICONS[@]}")
41 | defaultColor=$DARK_DEFAULT_COLOR
42 | else
43 | showHelp
44 | fi
45 |
46 | if [[ ${#tintColor} -ne 6 ]]; then
47 | showHelp
48 | fi
49 |
50 | if [[ $tintColor = $defaultColor ]]; then
51 | echo "Requested color $tintColor is same as default for theme. Exiting."
52 | exit
53 | fi
54 |
55 | for drawable in `ls -d $BASEDIR/app/src/main/res/drawable*`; do
56 | for file in "${icons[@]}"; do
57 | filePath=$drawable/$file
58 | if [[ -s "$filePath" ]]; then
59 | echo Tinting $filePath
60 | convert $filePath -fill "#$tintColor" -colorize 100% $filePath
61 | optipng $filePath
62 | fi
63 | done
64 | done
65 |
66 |
67 | if [[ ${#tintColorDark} -ne 6 ]]; then
68 | exit
69 | fi
70 |
71 | for drawable in `ls -d $BASEDIR/app/src/main/res/drawable-night*`; do
72 | for file in "${icons[@]}"; do
73 | filePath=$drawable/$file
74 | if [[ -s "$filePath" ]]; then
75 | echo Tinting $filePath
76 | convert $filePath -fill "#$tintColorDark" -colorize 100% $filePath
77 | optipng $filePath
78 | fi
79 | done
80 | done
--------------------------------------------------------------------------------
/app/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
22 | # webview interfaces
23 |
24 | -keepclassmembers class io.gonative.android.ProfilePicker$ProfileJsBridge {
25 | ;
26 | }
27 | -keepclassmembers class io.gonative.android.MainActivity$StatusCheckerBridge {
28 | ;
29 | }
30 | -keepattributes JavascriptInterface
31 |
32 | # stuff for google play services
33 | -keep class * extends java.util.ListResourceBundle {
34 | protected Object[][] getContents();
35 | }
36 |
37 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
38 | public static final *** NULL;
39 | }
40 |
41 | -keepnames @com.google.android.gms.common.annotation.KeepName class *
42 | -keepclassmembernames class * {
43 | @com.google.android.gms.common.annotation.KeepName *;
44 | }
45 |
46 | -keepnames class * implements android.os.Parcelable {
47 | public static final ** CREATOR;
48 | }
49 |
50 | # Google Cloud Messaging
51 | -keep class com.google.android.gms.** { *; }
52 | -keep interface com.google.android.gms.** { *; }
53 |
54 | # appcompat library
55 | -dontwarn android.support.v4.**
56 | -keep class android.support.v4.** { *; }
57 | -keep interface android.support.v4.** { *; }
58 | -dontwarn android.support.v7.**
59 | -keep class android.support.v7.** { *; }
60 | -keep interface android.support.v7.** { *; }
61 |
--------------------------------------------------------------------------------
/generate-header-images.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BASEDIR=$(dirname $0)
4 |
5 | sips --resampleHeight 36 -s format png --out $BASEDIR/app/src/main/res/drawable-mdpi/ic_actionbar.png $BASEDIR/HeaderImage 2>&1
6 | sips --resampleHeight 54 -s format png --out $BASEDIR/app/src/main/res/drawable-hdpi/ic_actionbar.png $BASEDIR/HeaderImage 2>&1
7 | sips --resampleHeight 72 -s format png --out $BASEDIR/app/src/main/res/drawable-xhdpi/ic_actionbar.png $BASEDIR/HeaderImage 2>&1
8 | sips --resampleHeight 108 -s format png --out $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_actionbar.png $BASEDIR/HeaderImage 2>&1
9 | sips --resampleHeight 144 -s format png --out $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_actionbar.png $BASEDIR/HeaderImage 2>&1
10 |
11 | optipng $BASEDIR/app/src/main/res/drawable-mdpi/ic_actionbar.png 2>&1
12 | optipng $BASEDIR/app/src/main/res/drawable-hdpi/ic_actionbar.png 2>&1
13 | optipng $BASEDIR/app/src/main/res/drawable-xhdpi/ic_actionbar.png 2>&1
14 | optipng $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_actionbar.png 2>&1
15 | optipng $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_actionbar.png 2>&1
16 |
17 | # dark theme icons
18 | DARK_ICON=$BASEDIR/HeaderImageDark
19 | if [[ -f "$DARK_ICON" ]]; then
20 | sips --resampleHeight 36 -s format png --out $BASEDIR/app/src/main/res/drawable-night-mdpi/ic_actionbar.png $BASEDIR/HeaderImageDark 2>&1
21 | sips --resampleHeight 54 -s format png --out $BASEDIR/app/src/main/res/drawable-night-hdpi/ic_actionbar.png $BASEDIR/HeaderImageDark 2>&1
22 | sips --resampleHeight 72 -s format png --out $BASEDIR/app/src/main/res/drawable-night-xhdpi/ic_actionbar.png $BASEDIR/HeaderImageDark 2>&1
23 | sips --resampleHeight 108 -s format png --out $BASEDIR/app/src/main/res/drawable-night-xxhdpi/ic_actionbar.png $BASEDIR/HeaderImageDark 2>&1
24 | sips --resampleHeight 144 -s format png --out $BASEDIR/app/src/main/res/drawable-night-xxxhdpi/ic_actionbar.png $BASEDIR/HeaderImageDark 2>&1
25 |
26 | optipng $BASEDIR/app/src/main/res/drawable-night-mdpi/ic_actionbar.png 2>&1
27 | optipng $BASEDIR/app/src/main/res/drawable-night-hdpi/ic_actionbar.png 2>&1
28 | optipng $BASEDIR/app/src/main/res/drawable-night-xhdpi/ic_actionbar.png 2>&1
29 | optipng $BASEDIR/app/src/main/res/drawable-night-xxhdpi/ic_actionbar.png 2>&1
30 | optipng $BASEDIR/app/src/main/res/drawable-night-xxxhdpi/ic_actionbar.png 2>&1
31 | fi
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/JsCustomCodeExecutor.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import java.util.Map;
7 |
8 | import io.gonative.gonative_core.GNLog;
9 |
10 | public class JsCustomCodeExecutor {
11 | private static final String TAG = JsCustomCodeExecutor.class.getName();
12 |
13 | public static interface CustomCodeHandler {
14 | JSONObject execute(Map params);
15 | }
16 |
17 | // The default CustomCodeHandler "Echo"
18 | // Simply maps all the key/values of the given params into a JSONObject
19 | private static CustomCodeHandler handler = new CustomCodeHandler() {
20 | @Override
21 | public JSONObject execute(Map params) {
22 | if(params != null) {
23 | JSONObject json = new JSONObject();
24 | try {
25 | for(Map.Entry entry : params.entrySet()) {
26 | json.put(entry.getKey(), entry.getValue());
27 | }
28 | }
29 | catch(JSONException e) {
30 | GNLog.getInstance().logError(TAG, "Error building custom Json Data", e);
31 | }
32 | return json;
33 | }
34 | return null;
35 | }
36 | };
37 |
38 | /**
39 | * Set new CustomCodeHandler to override the default "Echo" handler
40 | * @param customHandler
41 | */
42 | public static void setHandler(CustomCodeHandler customHandler) {
43 | if(customHandler == null)
44 | return;
45 | handler = customHandler;
46 | }
47 |
48 | /**
49 | * Code Handler gets triggered by the UrlNavigation class
50 | *
51 | * @param params A map consisting of all URI parameters and their values
52 | * @return A JSONObject as defined by the Code Handler
53 | *
54 | * @see UrlNavigation#shouldOverrideUrlLoading
55 | */
56 | public static JSONObject execute(Map params) {
57 | try {
58 | return handler.execute(params);
59 | } catch(Exception e) {
60 | GNLog.getInstance().logError(TAG, "Error executing custom code", e);
61 | return null;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/CustomHeaders.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.provider.Settings;
7 | import android.util.Base64;
8 |
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | import io.gonative.gonative_core.AppConfig;
14 |
15 | /**
16 | * Created by weiyin on 5/1/17.
17 | */
18 |
19 | public class CustomHeaders {
20 | public static Map getCustomHeaders(Context context) {
21 | AppConfig appConfig = AppConfig.getInstance(context);
22 | if (appConfig.customHeaders == null) return null;
23 |
24 | HashMap result = new HashMap<>();
25 | for (Map.Entry entry : appConfig.customHeaders.entrySet()) {
26 | String key = entry.getKey();
27 | String val;
28 | try {
29 | val = interpolateValues(context, entry.getValue());
30 | } catch (UnsupportedEncodingException e) {
31 | val = null;
32 | }
33 |
34 | if (key != null & val != null) {
35 | result.put(key, val);
36 | }
37 | }
38 |
39 | return result;
40 | }
41 |
42 | private static String interpolateValues(Context context, String value) throws UnsupportedEncodingException {
43 | if (value == null) return null;
44 |
45 | if (value.contains("%DEVICEID%")) {
46 | @SuppressLint("HardwareIds")
47 | String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
48 | if (androidId == null) androidId = "";
49 | value = value.replace("%DEVICEID%", androidId);
50 | }
51 |
52 | if (value.contains("%DEVICENAME64%")) {
53 | // base 64 encoded name
54 | String manufacturer = Build.MANUFACTURER;
55 | String model = Build.MODEL;
56 | String name;
57 | if (model.startsWith(manufacturer)) {
58 | name = model;
59 | } else {
60 | name = manufacturer + " " + model;
61 | }
62 |
63 | String name64 = Base64.encodeToString(name.getBytes("UTF-8"), Base64.NO_WRAP);
64 | value = value.replace("%DEVICENAME64%", name64);
65 | }
66 |
67 | return value;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/assets/BlobDownloader.js:
--------------------------------------------------------------------------------
1 | // This is used because download from native side won't have session changes.
2 |
3 | function gonativeDownloadBlobUrl(url) {
4 | var req = new XMLHttpRequest();
5 | req.open('GET', url, true);
6 | req.responseType = 'blob';
7 |
8 | req.onload = function(event) {
9 | var blob = req.response;
10 | saveBlob(blob);
11 | };
12 | req.send();
13 |
14 | function sendMessage(message) {
15 | if (window.webkit && window.webkit.messageHandlers &&
16 | window.webkit.messageHandlers.fileWriterSharer) {
17 | window.webkit.messageHandlers.fileWriterSharer.postMessage(message);
18 | }
19 | if (window.gonative_file_writer_sharer && window.gonative_file_writer_sharer.postMessage) {
20 | window.gonative_file_writer_sharer.postMessage(JSON.stringify(message));
21 | }
22 | }
23 |
24 | function saveBlob(blob, filename) {
25 | var chunkSize = 1024 * 1024; // 1mb
26 | var index = 0;
27 | // random string to identify this file transfer
28 | var id = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
29 |
30 | function sendHeader() {
31 | sendMessage({
32 | event: 'fileStart',
33 | id: id,
34 | size: blob.size,
35 | type: blob.type,
36 | name: filename
37 | });
38 | }
39 |
40 | function sendChunk() {
41 | if (index >= blob.size) {
42 | return sendEnd();
43 | }
44 |
45 | var chunkToSend = blob.slice(index, index + chunkSize);
46 | var reader = new FileReader();
47 | reader.readAsDataURL(chunkToSend);
48 | reader.onloadend = function() {
49 | sendMessage({
50 | event: 'fileChunk',
51 | id: id,
52 | data: reader.result
53 | });
54 | index += chunkSize;
55 | setTimeout(sendChunk);
56 | };
57 | }
58 |
59 | function sendEnd() {
60 | sendMessage({
61 | event: 'fileEnd',
62 | id:id
63 | });
64 | }
65 |
66 | sendHeader();
67 | gonative_run_after_storage_permissions.push(sendChunk);
68 | }
69 | }
70 |
71 | gonative_run_after_storage_permissions = [];
72 | function gonativeGotStoragePermissions() {
73 | while (gonative_run_after_storage_permissions.length > 0) {
74 | var run = gonative_run_after_storage_permissions.shift();
75 | run();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/ConfigUpdater.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import java.io.OutputStreamWriter;
10 | import java.lang.ref.WeakReference;
11 | import java.net.HttpURLConnection;
12 | import java.net.URL;
13 |
14 | import io.gonative.gonative_core.AppConfig;
15 | import io.gonative.gonative_core.GNLog;
16 |
17 | /**
18 | * Created by weiyin on 8/8/14.
19 | */
20 | public class ConfigUpdater {
21 | private static final String TAG = ConfigUpdater.class.getName();
22 |
23 | private Context context;
24 |
25 | ConfigUpdater(Context context) {
26 | this.context = context;
27 | }
28 |
29 | public void registerEvent() {
30 | if (AppConfig.getInstance(context).disableEventRecorder) return;
31 |
32 | new EventTask(context).execute();
33 | }
34 |
35 | private static class EventTask extends AsyncTask {
36 | WeakReference contextReference;
37 |
38 | EventTask(Context context) {
39 | this.contextReference = new WeakReference<>(context);
40 | }
41 |
42 | @Override
43 | protected Void doInBackground(Void... params) {
44 | Context context = contextReference.get();
45 | if (context == null) return null;
46 |
47 | JSONObject json = new JSONObject(Installation.getInfo(context));
48 |
49 | try {
50 | json.put("event", "launch");
51 | } catch (JSONException e) {
52 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
53 | return null;
54 | }
55 |
56 | try {
57 | URL url = new URL("https://events.gonative.io/api/events/new");
58 | HttpURLConnection connection = (HttpURLConnection) url.openConnection();
59 | connection.setRequestMethod("POST");
60 | connection.setRequestProperty("Content-Type", "application/json");
61 | connection.setDoOutput(true);
62 | connection.setDoInput(false); // we do not care about response
63 | OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
64 | writer.write(json.toString());
65 | writer.close();
66 | connection.connect();
67 | connection.getResponseCode();
68 | connection.disconnect();
69 | } catch (Exception e) {
70 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
71 | }
72 |
73 | return null;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/KeyboardManager.kt:
--------------------------------------------------------------------------------
1 | package io.gonative.android
2 |
3 | import android.graphics.Rect
4 | import android.text.TextUtils
5 | import android.view.ViewGroup
6 | import io.gonative.gonative_core.LeanUtils
7 | import org.json.JSONObject
8 |
9 |
10 | class KeyboardManager(val activity: MainActivity, private val rootLayout: ViewGroup) {
11 |
12 | var callback: String? = ""
13 | private var keyboardWidth = 0
14 | private var keyboardHeight = 0
15 | private var screenWidth = 0
16 | private var screenHeight = 0
17 | private var isKeyboardVisible = false
18 | private var screenHeightOffset = 0
19 |
20 | init {
21 | rootLayout.viewTreeObserver
22 | .addOnGlobalLayoutListener {
23 | val r = Rect()
24 | rootLayout.getWindowVisibleDisplayFrame(r)
25 |
26 | if (screenHeightOffset == 0) {
27 | screenHeightOffset = rootLayout.rootView.height - r.bottom
28 | }
29 |
30 | screenWidth = rootLayout.rootView.width
31 | screenHeight = r.bottom + screenHeightOffset
32 |
33 | keyboardHeight = rootLayout.rootView.height - screenHeight
34 |
35 | if (keyboardHeight == screenHeightOffset) {
36 | keyboardHeight = 0
37 | }
38 |
39 | val visible = keyboardHeight != 0
40 |
41 | if (visible) {
42 | keyboardWidth = screenWidth
43 | if (!isKeyboardVisible) {
44 | isKeyboardVisible = true
45 | notifyCallback();
46 | }
47 | } else {
48 | keyboardWidth = 0
49 | if (isKeyboardVisible) {
50 | isKeyboardVisible = false
51 | notifyCallback();
52 | }
53 | }
54 | }
55 | }
56 |
57 | private fun notifyCallback() {
58 | if (TextUtils.isEmpty(callback)) return
59 | activity.runJavascript(LeanUtils.createJsForCallback(callback, getKeyboardData()))
60 | }
61 |
62 | fun getKeyboardData() : JSONObject {
63 | val keyboardWindowSize = JSONObject()
64 | keyboardWindowSize.put("visible", isKeyboardVisible)
65 | keyboardWindowSize.put("width", keyboardWidth)
66 | keyboardWindowSize.put("height", keyboardHeight)
67 |
68 | val visibleWindowSize = JSONObject()
69 | visibleWindowSize.put("width", screenWidth)
70 | visibleWindowSize.put("height", screenHeight)
71 |
72 | val data = JSONObject()
73 | data.put("keyboardWindowSize", keyboardWindowSize)
74 | data.put("visibleWindowSize", visibleWindowSize)
75 |
76 | return data
77 | }
78 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GoNative
5 | Search
6 | Refresh
7 | Share
8 | Shake Device to Delete Cache
9 |
10 | - Delete Cache
11 | - Cancel
12 |
13 | Open drawer
14 | Close drawer
15 | Search
16 | Problem with submission. Check inputs and try again.
17 | Download
18 | Download canceled
19 | File downloads
20 | Notifications for file downloads
21 | Download in progress
22 | Download complete
23 | Download connection failed.
24 | Camera permission denied
25 | Could not find an app to open this file.
26 | Could not open file chooser to handle this file type.
27 | Allow photo access to upload file.
28 | Error establishing SSL Connection
29 | SSL certificate has expired
30 | Invalid SSL certificate
31 | To continue, turn on device location, which uses Google\'s location service
32 | OK
33 | No Thanks
34 | Please allow location permission
35 | Please allow storage permission for file download
36 | Starting download
37 | Subscriptions
38 | Cleared cache
39 | App Logo
40 | Google Service JSON was not properly set up.
41 | Powered by GoNative
42 | Choose an action
43 | App is not installed
44 | Download started
45 | File downloaded
46 | %1$s downloaded
47 | Download Error
48 | Image saved to Gallery
49 |
50 |
--------------------------------------------------------------------------------
/generate-plugin-icons.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BASEDIR=$(dirname $0)
4 |
5 | # intercom notification icon
6 | if [[ $1 == "intercom" ]]
7 | then
8 | cp $BASEDIR/app/src/main/res/drawable-mdpi/ic_notification.png $BASEDIR/app/src/main/res/drawable-mdpi/intercom_push_icon.png
9 | cp $BASEDIR/app/src/main/res/drawable-hdpi/ic_notification.png $BASEDIR/app/src/main/res/drawable-hdpi/intercom_push_icon.png
10 | cp $BASEDIR/app/src/main/res/drawable-xhdpi/ic_notification.png $BASEDIR/app/src/main/res/drawable-xhdpi/intercom_push_icon.png
11 | cp $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_notification.png $BASEDIR/app/src/main/res/drawable-xxhdpi/intercom_push_icon.png
12 | cp $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_notification.png $BASEDIR/app/src/main/res/drawable-xxxhdpi/intercom_push_icon.png
13 | fi
14 |
15 | # cordial notification icon
16 | if [[ $1 == "cordial" ]]
17 | then
18 | cp $BASEDIR/app/src/main/res/drawable-mdpi/ic_notification.png $BASEDIR/plugins/cordial/src/main/res/drawable-mdpi/ic_notification.png
19 | cp $BASEDIR/app/src/main/res/drawable-hdpi/ic_notification.png $BASEDIR/plugins/cordial/src/main/res/drawable-hdpi/ic_notification.png
20 | cp $BASEDIR/app/src/main/res/drawable-xhdpi/ic_notification.png $BASEDIR/plugins/cordial/src/main/res/drawable-xhdpi/ic_notification.png
21 | cp $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_notification.png $BASEDIR/plugins/cordial/src/main/res/drawable-xxhdpi/ic_notification.png
22 | cp $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_notification.png $BASEDIR/plugins/cordial/src/main/res/drawable-xxxhdpi/ic_notification.png
23 | fi
24 |
25 | # braze notification icon
26 | if [[ $1 == "braze" ]]
27 | then
28 | cp $BASEDIR/app/src/main/res/drawable-mdpi/ic_notification.png $BASEDIR/plugins/braze/src/main/res/drawable-mdpi/ic_notification.png
29 | cp $BASEDIR/app/src/main/res/drawable-hdpi/ic_notification.png $BASEDIR/plugins/braze/src/main/res/drawable-hdpi/ic_notification.png
30 | cp $BASEDIR/app/src/main/res/drawable-xhdpi/ic_notification.png $BASEDIR/plugins/braze/src/main/res/drawable-xhdpi/ic_notification.png
31 | cp $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_notification.png $BASEDIR/plugins/braze/src/main/res/drawable-xxhdpi/ic_notification.png
32 | cp $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_notification.png $BASEDIR/plugins/braze/src/main/res/drawable-xxxhdpi/ic_notification.png
33 | fi
34 |
35 | # moengage notification icon
36 | if [[ $1 == "moengage" ]]
37 | then
38 | cp $BASEDIR/app/src/main/res/drawable-mdpi/ic_notification.png $BASEDIR/plugins/moengage/src/main/res/drawable-mdpi/ic_notification.png
39 | cp $BASEDIR/app/src/main/res/drawable-hdpi/ic_notification.png $BASEDIR/plugins/moengage/src/main/res/drawable-hdpi/ic_notification.png
40 | cp $BASEDIR/app/src/main/res/drawable-xhdpi/ic_notification.png $BASEDIR/plugins/moengage/src/main/res/drawable-xhdpi/ic_notification.png
41 | cp $BASEDIR/app/src/main/res/drawable-xxhdpi/ic_notification.png $BASEDIR/plugins/moengage/src/main/res/drawable-xxhdpi/ic_notification.png
42 | cp $BASEDIR/app/src/main/res/drawable-xxxhdpi/ic_notification.png $BASEDIR/plugins/moengage/src/main/res/drawable-xxxhdpi/ic_notification.png
43 | fi
44 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/widget/WebViewContainerView.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android.widget;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.ViewGroup;
7 | import android.widget.FrameLayout;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.annotation.Nullable;
11 |
12 | import java.lang.reflect.Constructor;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.lang.reflect.Method;
15 |
16 | import io.gonative.android.GoNativeApplication;
17 | import io.gonative.android.LeanWebView;
18 | import io.gonative.android.MainActivity;
19 | import io.gonative.android.WebViewSetup;
20 | import io.gonative.gonative_core.AppConfig;
21 | import io.gonative.gonative_core.Bridge;
22 | import io.gonative.gonative_core.GoNativeWebviewInterface;
23 |
24 | public class WebViewContainerView extends FrameLayout {
25 |
26 | private ViewGroup webview;
27 | private boolean isGecko = false;
28 |
29 | public WebViewContainerView(@NonNull Context context) {
30 | super(context);
31 | }
32 |
33 | public WebViewContainerView(@NonNull Context context, @Nullable AttributeSet attrs) {
34 | super(context, attrs);
35 | initializeWebview(context);
36 | }
37 |
38 | public WebViewContainerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
39 | super(context, attrs, defStyleAttr);
40 | initializeWebview(context);
41 | }
42 |
43 | private void initializeWebview(Context context) {
44 | AppConfig appConfig = AppConfig.getInstance(context);
45 |
46 | if (appConfig.geckoViewEnabled) {
47 | try {
48 | Class> classGecko = Class.forName("io.gonative.plugins.android.gecko.GNGeckoView");
49 | Constructor> consGecko = classGecko.getConstructor(Context.class);
50 | webview = (ViewGroup) consGecko.newInstance(context);
51 | this.isGecko = true;
52 | } catch (Exception e) {
53 | e.printStackTrace();
54 | }
55 | } else {
56 | webview = new LeanWebView(context);
57 | }
58 | ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
59 | webview.setLayoutParams(layoutParams);
60 | this.addView(webview);
61 | }
62 |
63 | public void setupWebview(MainActivity activity, boolean isRoot) {
64 | if (isGecko) {
65 | try {
66 | Class> geckoSetupClass = Class.forName("io.gonative.plugins.android.gecko.WebViewSetup");
67 | Method setupWebview = geckoSetupClass.getMethod("setupWebviewForActivity", Activity.class, GoNativeWebviewInterface.class, Bridge.class, boolean.class);
68 | setupWebview.invoke(geckoSetupClass, activity, (GoNativeWebviewInterface) webview, ((GoNativeApplication) activity.getApplication()).mBridge, isRoot);
69 | } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
70 | e.printStackTrace();
71 | }
72 | } else {
73 | WebViewSetup.setupWebviewForActivity(getWebview(), activity);
74 | }
75 | }
76 |
77 | public GoNativeWebviewInterface getWebview() {
78 | return (GoNativeWebviewInterface) webview;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/assets/offline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Device Offline
5 |
15 |
16 |
17 |
18 |
21 |
22 | No internet connection
Check your connection and try again
23 |
24 |
25 |
26 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.Manifest;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.Color;
7 | import android.os.Bundle;
8 |
9 | import android.os.Handler;
10 | import android.os.Looper;
11 | import android.view.View;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.core.app.ActivityCompat;
15 | import androidx.appcompat.app.AppCompatActivity;
16 |
17 | import java.util.HashSet;
18 |
19 | import androidx.core.content.ContextCompat;
20 | import androidx.core.splashscreen.SplashScreen;
21 |
22 | import io.gonative.gonative_core.AppConfig;
23 |
24 | public class SplashActivity extends AppCompatActivity {
25 | private static final int REQUEST_STARTUP_PERMISSIONS = 100;
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | SplashScreen.installSplashScreen(this);
30 | super.onCreate(savedInstanceState);
31 | AppConfig config = AppConfig.getInstance(this);
32 |
33 | getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
34 | getWindow().setStatusBarColor(Color.TRANSPARENT);
35 | getWindow().setNavigationBarColor(Color.TRANSPARENT);
36 | setContentView(R.layout.splash_screen);
37 |
38 | HashSet permissionsToRequest = new HashSet<>();
39 |
40 | if (LeanWebView.isCrosswalk()) {
41 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
42 | permissionsToRequest.add(Manifest.permission.READ_CONTACTS);
43 | }
44 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED){
45 | permissionsToRequest.add(Manifest.permission.WRITE_CONTACTS);
46 | }
47 | }
48 |
49 | if (permissionsToRequest.isEmpty()) {
50 | handleSplash(config);
51 | } else {
52 | ActivityCompat.requestPermissions(this,
53 | permissionsToRequest.toArray(new String[]{}),
54 | REQUEST_STARTUP_PERMISSIONS);
55 | }
56 | }
57 |
58 | @Override
59 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
60 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
61 | startMainActivity();
62 | }
63 |
64 | private void handleSplash(AppConfig config){
65 | // Check app if unlicensed and show banner
66 | if (!config.isLicensed()) {
67 | findViewById(R.id.banner_text).setVisibility(View.VISIBLE);
68 | }
69 |
70 | Handler handler = new Handler(Looper.getMainLooper());
71 |
72 | // handle splash
73 | double delay = 1.5; // in seconds
74 | double forceTime = config.showSplashForceTime;
75 | double maxTime = config.showSplashMaxTime;
76 | if (forceTime > 0) {
77 | delay = forceTime;
78 | } else if (maxTime > 0) {
79 | delay = maxTime;
80 | }
81 | handler.postDelayed(this::startMainActivity, (long)delay * 1000);
82 | }
83 |
84 | private void startMainActivity() {
85 | Intent intent = new Intent(this, MainActivity.class);
86 | // Make MainActivity think it was started from launcher
87 | intent.addCategory(Intent.CATEGORY_LAUNCHER);
88 | intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
89 | startActivity(intent);
90 | finish();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/GoNativeApplication.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.os.Message;
4 | import android.webkit.ValueCallback;
5 | import android.widget.Toast;
6 |
7 | import androidx.appcompat.app.AppCompatDelegate;
8 | import androidx.multidex.MultiDexApplication;
9 |
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | import io.gonative.gonative_core.AppConfig;
14 | import io.gonative.gonative_core.Bridge;
15 | import io.gonative.gonative_core.BridgeModule;
16 | import io.gonative.gonative_core.GNLog;
17 |
18 | /**
19 | * Created by weiyin on 9/2/15.
20 | * Copyright 2014 GoNative.io LLC
21 | */
22 | public class GoNativeApplication extends MultiDexApplication {
23 |
24 | private LoginManager loginManager;
25 | private RegistrationManager registrationManager;
26 | private WebViewPool webViewPool;
27 | private Message webviewMessage;
28 | private ValueCallback webviewValueCallback;
29 | private GoNativeWindowManager goNativeWindowManager;
30 | private List plugins;
31 | private final static String TAG = GoNativeApplication.class.getSimpleName();
32 | public final Bridge mBridge = new Bridge(this) {
33 | @Override
34 | protected List getPlugins() {
35 | if (GoNativeApplication.this.plugins == null) {
36 | GoNativeApplication.this.plugins = new PackageList(GoNativeApplication.this).getPackages();
37 | }
38 |
39 | return GoNativeApplication.this.plugins;
40 | }
41 | };
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
47 |
48 | mBridge.onApplicationCreate(this);
49 |
50 | AppConfig appConfig = AppConfig.getInstance(this);
51 | if (appConfig.configError != null) {
52 | Toast.makeText(this, "Invalid appConfig json", Toast.LENGTH_LONG).show();
53 | GNLog.getInstance().logError(TAG, "AppConfig error", appConfig.configError);
54 | }
55 |
56 | this.loginManager = new LoginManager(this);
57 |
58 | if (appConfig.registrationEndpoints != null) {
59 | this.registrationManager = new RegistrationManager(this);
60 | registrationManager.processConfig(appConfig.registrationEndpoints);
61 | }
62 |
63 | // some global webview setup
64 | WebViewSetup.setupWebviewGlobals(this);
65 |
66 | webViewPool = new WebViewPool();
67 |
68 | goNativeWindowManager = new GoNativeWindowManager();
69 | }
70 |
71 | public LoginManager getLoginManager() {
72 | return loginManager;
73 | }
74 |
75 | public RegistrationManager getRegistrationManager() {
76 | return registrationManager;
77 | }
78 |
79 | public WebViewPool getWebViewPool() {
80 | return webViewPool;
81 | }
82 |
83 | public Message getWebviewMessage() {
84 | return webviewMessage;
85 | }
86 |
87 | public void setWebviewMessage(Message webviewMessage) {
88 | this.webviewMessage = webviewMessage;
89 | }
90 |
91 | public Map getAnalyticsProviderInfo() {
92 | return mBridge.getAnalyticsProviderInfo();
93 | }
94 |
95 | // Needed for Crosswalk
96 | @SuppressWarnings("unused")
97 | public ValueCallback getWebviewValueCallback() {
98 | return webviewValueCallback;
99 | }
100 |
101 | public void setWebviewValueCallback(ValueCallback webviewValueCallback) {
102 | this.webviewValueCallback = webviewValueCallback;
103 | }
104 |
105 | public GoNativeWindowManager getWindowManager() {
106 | return goNativeWindowManager;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
40 |
41 |
46 |
47 |
49 |
50 |
53 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/gonative/testFiles/FirstTestClass.java:
--------------------------------------------------------------------------------
1 | package com.gonative.testFiles;
2 |
3 | import android.webkit.WebView;
4 | import androidx.test.ext.junit.rules.ActivityScenarioRule;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 | import androidx.test.filters.LargeTest;
7 | import androidx.test.filters.SdkSuppress;
8 | import androidx.test.uiautomator.UiDevice;
9 | import org.json.JSONException;
10 | import org.junit.Before;
11 | import org.junit.Rule;
12 | import org.junit.Test;
13 | import org.junit.runner.RunWith;
14 | import io.gonative.android.MainActivity;
15 | import io.gonative.android.R;
16 | import io.gonative.gonative_core.AppConfig;
17 | import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
18 |
19 | @RunWith(AndroidJUnit4.class)
20 | @SdkSuppress(minSdkVersion = 18)
21 | @LargeTest
22 | public class FirstTestClass{
23 | TestMethods testMethods;
24 | AppConfig appConfig;
25 | WebView webView;
26 | private UiDevice uiDevice;
27 |
28 | @Rule
29 | public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
30 |
31 | @Before
32 | public void initMethod() throws InterruptedException {
33 | for(int i = 0; i < 10; i++){
34 | try{
35 | uiDevice = UiDevice.getInstance(getInstrumentation());
36 | }catch (RuntimeException runtimeException){
37 | Thread.sleep(2000);
38 | continue;
39 | }
40 | Thread.sleep(1000);
41 | break;
42 | }
43 | activityScenarioRule.getScenario().onActivity(activity -> {
44 | appConfig = AppConfig.getInstance(activity);
45 | webView = activity.findViewById(R.id.webview);
46 | testMethods = new TestMethods(activity, webView);
47 | });
48 | }
49 |
50 | //Sidebar Navigation Test
51 | @Test
52 | public void testSidebarNavigation() throws InterruptedException, JSONException {
53 | if(appConfig.showNavigationMenu && (appConfig.menus.get("default") != null)){
54 | if(appConfig.menus.get("default") == null) throw new RuntimeException("Navigation drawer list not found.");
55 | else {
56 | testMethods.waitForPageLoaded();
57 | testMethods.testNavigation(appConfig.menus.get("default"));
58 | }
59 | }
60 | }
61 |
62 | //Tab Menu Navigation Test
63 | @Test
64 | public void testTabMenuNavigation() throws JSONException, InterruptedException {
65 | if(appConfig.tabMenuRegexes.size() == 0) throw new RuntimeException("No Tab Menus found.");
66 | else{
67 | testMethods.waitForPageLoaded();
68 | testMethods.m_testTabNavigation(appConfig.tabMenus, appConfig.tabMenuRegexes);
69 | }
70 | }
71 |
72 | //Internal vs External Links Test
73 | @Test
74 | public void testIvE() throws InterruptedException {
75 | testMethods.waitForPageLoaded();
76 | testMethods.testInternalvExternalLinks(uiDevice);
77 | }
78 |
79 | //Pull to Refresh Test
80 | @Test
81 | public void pullToRefresh() throws InterruptedException {
82 | if(appConfig.pullToRefresh){
83 | testMethods.waitForPageLoaded();
84 | testMethods.testPullToRefresh();
85 | }
86 | }
87 |
88 | //Search Button Test
89 | @Test
90 | public void testSearch() throws InterruptedException {
91 | if(appConfig.searchTemplateUrl != null && !appConfig.searchTemplateUrl.isEmpty()){
92 | testMethods.waitForPageLoaded();
93 | testMethods.testSearchButton();
94 | }
95 | }
96 |
97 | //Refresh Button Test
98 | @Test
99 | public void testRefreshButton() throws InterruptedException {
100 | if(appConfig.showRefreshButton){
101 | testMethods.waitForPageLoaded();
102 | testMethods.testRefreshButton();
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/.github/workflows/firebase-testing.yml:
--------------------------------------------------------------------------------
1 | name: firebase-testing
2 | on: [push, workflow_dispatch]
3 | jobs:
4 | test-app:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - name: Set up gcloud Cloud SDK environment
9 | # You may pin to the exact commit or the version.
10 | # uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7
11 | uses: google-github-actions/setup-gcloud@v0.2.0
12 | with:
13 | # Version of the gcloud SDK to install. If unspecified or set to "latest", the latest available gcloud SDK version for the target platform will be installed. Example: "290.0.1".
14 | version: latest
15 | # Service account email address to use for authentication. This is required for legacy .p12 keys but can be omitted for .json keys. This is usually of the format @.iam.gserviceaccount.com.
16 |
17 | # Service account key to use for authentication. This should be the JSON formatted private key which can be exported from the Cloud Console. The value can be raw or base64-encoded.
18 | service_account_key: ${{ secrets.GCLOUD_KEY }}
19 | # ID of the Google Cloud project. If provided, this will configure gcloud to use this project ID by default for commands. Individual commands can still override the project using the --project flag which takes precedence.
20 | project_id: gn-test-firebase-test-lab
21 | # Export the provided credentials as Google Default Application Credentials. This will make the credentials available to later steps via the GOOGLE_APPLICATION_CREDENTIALS environment variable. Future steps that consume Default Application Credentials will automatically detect and use these credentials.
22 | export_default_credentials: true
23 |
24 | - name: Make gradlew executable
25 | run: chmod +x ./gradlew
26 |
27 | - name: Copying the appConfig file from androidTest to main
28 | run: cp -f ./app/src/androidTest/assets/appConfig.json ./app/src/main/assets/appConfig.json
29 |
30 | - name: Add dependencies in app/build.gradle
31 | run: sed -i "s/dependencies *\n*{/dependencies {\nandroidTestImplementation 'androidx.test.ext:junit:1.1.2'\nandroidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'\nandroidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'\nandroidTestImplementation 'androidx.test.espresso:espresso-web:3.3.0'\nandroidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'\n/" ./app/build.gradle
32 |
33 | - name: Add test runner in defaultConfig in app/build.gradle
34 | run: sed -i "s/defaultConfig *\n*{/defaultConfig {\ntestInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'/" ./app/build.gradle
35 |
36 | - name: Adding Helper method in GoNativeWebviewClient.java
37 | run: sed -i "s/super.onPageFinished *( *view *, *url *) *;/super.onPageFinished(view, url);\nHelperClass.newLoad++;/" ./app/src/normal/java/io/gonative/android/GoNativeWebviewClient.java
38 |
39 | - name: Copying HelperClass.java from androidTest/assets to normal/java/io/gonative/android/
40 | run: cp -f ./app/src/androidTest/assets/HelperClass.java ./app/src/normal/java/io/gonative/android/HelperClass.java
41 |
42 | - name: Build the App
43 | run: ./gradlew assembleDebug assembleAndroidTest
44 |
45 | - name: Testing the App
46 | run: gcloud firebase test android run --type instrumentation --app ./app/build/outputs/apk/normal/debug/app-normal-debug.apk --test ./app/build/outputs/apk/androidTest/normal/debug/app-normal-debug-androidTest.apk --device model=flo,version=21,locale=en,orientation=portrait --device model=hammerhead,version=23,locale=en,orientation=portrait --device model=griffin,version=24,locale=en,orientation=portrait --device model=G8142,version=25,locale=en,orientation=portrait --device model=star2qlteue,version=26,locale=en,orientation=portrait --device model=walleye,version=27,locale=en,orientation=portrait --device model=OnePlus5T,version=28,locale=en,orientation=portrait --device model=x1q,version=29,locale=en,orientation=portrait --device model=flame,version=30,locale=en,orientation=portrait --results-bucket cloud-test-gn-test-firebase-test-lab --timeout 300s
47 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/SegmentedController.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import androidx.localbroadcastmanager.content.LocalBroadcastManager;
8 | import android.view.View;
9 | import android.widget.AdapterView;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.Spinner;
12 |
13 | import org.json.JSONObject;
14 |
15 | import java.util.ArrayList;
16 |
17 | import io.gonative.gonative_core.AppConfig;
18 |
19 | /**
20 | * Created by weiyin on 12/20/15.
21 | * Copyright 2014 GoNative.io LLC
22 | */
23 | public class SegmentedController implements AdapterView.OnItemSelectedListener {
24 | private MainActivity mainActivity;
25 | private ArrayList labels;
26 | private ArrayList urls;
27 | private int selectedIndex;
28 |
29 | private ArrayAdapter adapter;
30 | private Spinner spinner;
31 |
32 | SegmentedController(MainActivity mainActivity, Spinner spinner) {
33 | this.mainActivity = mainActivity;
34 | this.spinner = spinner;
35 |
36 | this.labels = new ArrayList<>();
37 | this.urls = new ArrayList<>();
38 |
39 | this.spinner.setAdapter(getAdapter());
40 | this.spinner.setOnItemSelectedListener(this);
41 |
42 | BroadcastReceiver messageReceiver = new BroadcastReceiver() {
43 | @Override
44 | public void onReceive(Context context, Intent intent) {
45 | if (intent == null || intent.getAction() == null) return;
46 |
47 | if (intent.getAction().equals(AppConfig.PROCESSED_SEGMENTED_CONTROL)) {
48 | updateSegmentedControl();
49 | }
50 | }
51 | };
52 | LocalBroadcastManager.getInstance(this.mainActivity).registerReceiver(
53 | messageReceiver, new IntentFilter(AppConfig.PROCESSED_SEGMENTED_CONTROL));
54 |
55 | updateSegmentedControl();
56 | }
57 |
58 | private void updateSegmentedControl() {
59 | this.labels.clear();
60 | this.urls.clear();
61 | this.selectedIndex = -1;
62 |
63 | AppConfig appConfig = AppConfig.getInstance(mainActivity);
64 | if (appConfig.segmentedControl == null) return;
65 |
66 | for (int i = 0; i < appConfig.segmentedControl.size(); i++) {
67 | JSONObject item = appConfig.segmentedControl.get(i);
68 |
69 | String label = item.optString("label", "Invalid");
70 | String url = item.optString("url", "");
71 | Boolean selected = item.optBoolean("selected");
72 |
73 | this.labels.add(i, label);
74 | this.urls.add(i, url);
75 | if (selected) this.selectedIndex = i;
76 | }
77 |
78 | mainActivity.runOnUiThread(new Runnable() {
79 | @Override
80 | public void run() {
81 | if (selectedIndex > -1) {
82 | spinner.setSelection(selectedIndex);
83 | }
84 |
85 | if (labels.size() > 0) {
86 | spinner.setVisibility(View.VISIBLE);
87 | } else {
88 | spinner.setVisibility(View.GONE);
89 | }
90 |
91 | adapter.notifyDataSetChanged();
92 | }
93 | });
94 |
95 | }
96 |
97 | private ArrayAdapter getAdapter() {
98 | if (this.adapter != null) {
99 | return this.adapter;
100 | }
101 |
102 | ArrayAdapter adapter = new ArrayAdapter<>(mainActivity,
103 | android.R.layout.simple_spinner_item, labels);
104 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
105 | this.adapter = adapter;
106 | return adapter;
107 | }
108 |
109 | @Override
110 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
111 | // only load if selection has changed
112 | if (position != selectedIndex) {
113 | String url = urls.get(position);
114 |
115 | if (url != null && url.length() > 0) {
116 | mainActivity.loadUrl(url);
117 | }
118 |
119 | mainActivity.closeDrawers();
120 | selectedIndex = position;
121 | }
122 | }
123 |
124 | @Override
125 | public void onNothingSelected(AdapterView> parent) {
126 | // do nothing
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/ProfilePicker.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import androidx.annotation.NonNull;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.webkit.JavascriptInterface;
7 | import android.widget.AdapterView;
8 | import android.widget.ArrayAdapter;
9 | import android.widget.Spinner;
10 | import android.widget.TextView;
11 |
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 | import org.json.JSONObject;
15 |
16 | import java.util.ArrayList;
17 |
18 | import io.gonative.gonative_core.GNLog;
19 |
20 | /**
21 | * Created by weiyin on 5/9/14.
22 | */
23 | public class ProfilePicker implements AdapterView.OnItemSelectedListener {
24 | private static final String TAG = ProfilePicker.class.getName();
25 |
26 | private MainActivity mainActivity;
27 | private JSONArray json;
28 | private ArrayList names;
29 | private ArrayList links;
30 | private int selectedIndex;
31 |
32 | private ArrayAdapter adapter;
33 | private Spinner spinner;
34 | private ProfileJsBridge profileJsBridge;
35 |
36 | public ProfilePicker(MainActivity mainActivity, Spinner spinner) {
37 | this.mainActivity = mainActivity;
38 | this.spinner = spinner;
39 | this.names = new ArrayList<>();
40 | this.links = new ArrayList<>();
41 | this.spinner.setAdapter(getAdapter());
42 | this.spinner.setOnItemSelectedListener(this);
43 | this.profileJsBridge = new ProfileJsBridge();
44 | }
45 |
46 | private void parseJson(String s){
47 | try {
48 | json = new JSONArray(s);
49 | this.names.clear();
50 | this.links.clear();
51 |
52 | for (int i = 0; i < json.length(); i++) {
53 | JSONObject item = json.getJSONObject(i);
54 |
55 | this.names.add(item.optString("name", ""));
56 | this.links.add(item.optString("link", ""));
57 |
58 | if (item.optBoolean("selected", false)){
59 | selectedIndex = i;
60 | }
61 | }
62 |
63 | mainActivity.runOnUiThread(new Runnable() {
64 | public void run() {
65 | if (selectedIndex < ProfilePicker.this.names.size()) {
66 | ProfilePicker.this.spinner.setSelection(selectedIndex);
67 | }
68 | if (ProfilePicker.this.json != null &&
69 | ProfilePicker.this.json.length() > 0)
70 | ProfilePicker.this.spinner.setVisibility(View.VISIBLE);
71 | else
72 | ProfilePicker.this.spinner.setVisibility(View.GONE);
73 | getAdapter().notifyDataSetChanged();
74 | }
75 | });
76 |
77 | } catch (JSONException e) {
78 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
79 | }
80 | }
81 |
82 | private ArrayAdapter getAdapter(){
83 | if (adapter == null) {
84 |
85 | adapter = new ArrayAdapter(mainActivity, R.layout.profile_picker_dropdown, names) {
86 | @NonNull
87 | @Override
88 | public View getView(int position, View convertView, @NonNull ViewGroup parent) {
89 | TextView view = (TextView) super.getView(position, convertView, parent);
90 | view.setTextColor(mainActivity.getResources().getColor(R.color.sidebarForeground));
91 | return view;
92 | }
93 |
94 | @Override
95 | public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
96 | TextView view = (TextView) super.getDropDownView(position, convertView, parent);
97 | view.setTextColor(mainActivity.getResources().getColor(R.color.sidebarForeground));
98 | return view;
99 | }
100 | };
101 | }
102 |
103 | return adapter;
104 | }
105 |
106 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
107 | // only load if selection has changed
108 | if (position != selectedIndex) {
109 | mainActivity.loadUrl(links.get(position));
110 | mainActivity.closeDrawers();
111 | selectedIndex = position;
112 | }
113 | }
114 |
115 | public void onNothingSelected(AdapterView> parent) {
116 | // do nothing
117 | }
118 |
119 | public ProfileJsBridge getProfileJsBridge() {
120 | return profileJsBridge;
121 | }
122 |
123 | public class ProfileJsBridge {
124 | @JavascriptInterface
125 | public void parseJson(String s) {
126 | ProfilePicker.this.parseJson(s);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/androidTest/assets/appConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "general": {
3 | "userAgentAdd": "gonative",
4 | "initialUrl": "https://gonative.io",
5 | "appName": "GoNative.io"
6 | },
7 | "navigation": {
8 | "androidPullToRefresh": true,
9 | "sidebarNavigation": {
10 | "sidebarEnabledRegex": null,
11 | "menus": [{
12 | "name": "default",
13 | "items": [{
14 | "url": "https://gonative.io",
15 | "label": "Home",
16 | "subLinks": []
17 | }, {
18 | "url": "https://gonative.io/about",
19 | "label": "About",
20 | "subLinks": []
21 | }, {
22 | "url": "https://gonative.io/examples",
23 | "label": "Examples",
24 | "subLinks": []
25 | }],
26 | "active": true
27 | }]
28 | },
29 | "tabNavigation": {
30 | "tabSelectionConfig": [{
31 | "id": "1",
32 | "regex": ".*about.*"
33 | }],
34 | "tabMenus": [{
35 | "id": "1",
36 | "items": [{
37 | "icon": "fa-cloud",
38 | "label": "Tab 1",
39 | "url": "https://www.gonative.io/pricing"
40 | }, {
41 | "icon": "fa-globe",
42 | "label": "Tab 2",
43 | "url": "https://www.gonative.io/examples"
44 | }, {
45 | "icon": "fa-users",
46 | "label": "Tab 3",
47 | "url": "javascript:alert('You selected tab 3. These tabs are only shown on the about page')"
48 | }]
49 | }],
50 | "active": true
51 | },
52 | "actionConfig": {
53 | "active": true,
54 | "actions": [{
55 | "id": "exampleActions",
56 | "items": [{
57 | "label": "Globe",
58 | "icon": "fa-globe",
59 | "url": "javascript:alert('You tapped the globe! It only appears on the Examples page')"
60 | }]
61 | }],
62 | "actionSelection": [{
63 | "regex": ".*/examples.*",
64 | "id": "exampleActions"
65 | }]
66 | },
67 | "regexInternalExternal": {
68 | "rules": [{
69 | "regex": "https?://([-\\w]+\\.)*facebook\\.com/login.php.*",
70 | "internal": true
71 | }, {
72 | "regex": "https?://([-\\w]+\\.)*facebook\\.com/pages/.*",
73 | "internal": false
74 | }, {
75 | "regex": "https?://([-\\w]+\\.)*facebook\\.com/sharer\\.php.*",
76 | "internal": false
77 | }, {
78 | "regex": "https?://([-\\w]+\\.)*plus\\.google\\.com/share.*",
79 | "internal": false
80 | }, {
81 | "regex": "https?://([-\\w]+\\.)*twitter\\.com/intent/.*",
82 | "internal": false
83 | }, {
84 | "regex": "https?://([-\\w]+\\.)*gonative\\.io/?.*",
85 | "internal": true
86 | }, {
87 | "regex": "https?://([-\\w]+\\.)*google\\.com/?.*",
88 | "internal": true
89 | }, {
90 | "regex": "https://gonative-test-web.web.app/.*",
91 | "internal": true
92 | },
93 | {
94 | "regex": "https://us-central1-gn-test-firebase-test-lab.cloudfunctions.net/.*",
95 | "internal": true
96 | }],
97 | "active": true
98 | },
99 | "redirects": [{
100 | "from": "https://example.com/from/",
101 | "to": "https://example.com/to/"
102 | }]
103 | },
104 | "forms": {
105 | "search": {
106 | "active": true,
107 | "searchTemplateURL": "https://us-central1-gn-test-firebase-test-lab.cloudfunctions.net/gnTestSearch?q="
108 | }
109 | },
110 | "styling": {
111 | "showActionBar": true,
112 | "showNavigationBar": true,
113 | "iosTitleColor": "#333333",
114 | "iosTintColor": "#0091fe",
115 | "androidTheme": "Light.DarkActionBar",
116 | "androidSidebarBackgroundColor": "#111111",
117 | "androidSidebarForegroundColor": "#d0d0d0",
118 | "androidHideTitleInActionBar": false,
119 | "androidPullToRefreshColor": "#333333",
120 | "androidTabBarBackgroundColor": "#fefefe",
121 | "androidTabBarTextColor": "#747474",
122 | "androidTabBarIndicatorColorx": "#2f79fe",
123 | "androidShowSplash": true,
124 | "androidShowSplashMaxTime": null,
125 | "androidShowSplashForceTime": null,
126 | "disableAnimations": false,
127 | "menuAnimationDuration": 0.15,
128 | "transitionInteractiveDelayMax": 0.2
129 | },
130 | "permissions": {
131 | "usesGeolocation": false,
132 | "androidDownloadToPublicStorage": false
133 | },
134 | "services": {
135 | "oneSignal": {
136 | "active": false,
137 | "applicationId": ""
138 | },
139 | "facebook": {
140 | "active": false,
141 | "appId": "",
142 | "displayName": ""
143 | },
144 | "registration": {
145 | "active": false,
146 | "endpoints": [{
147 | "url": "https://gonative.io/example_push_endpoint",
148 | "dataType": "onesignal",
149 | "urlRegex": ".*/loginfinished"
150 | }]
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/app/src/normal/java/io/gonative/android/GoNativeWebviewClient.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.net.Uri;
7 | import android.net.http.SslError;
8 | import android.os.Build;
9 | import android.os.Message;
10 | import android.webkit.ClientCertRequest;
11 | import android.webkit.SslErrorHandler;
12 | import android.webkit.WebResourceError;
13 | import android.webkit.WebResourceRequest;
14 | import android.webkit.WebResourceResponse;
15 | import android.webkit.WebView;
16 | import android.webkit.WebViewClient;
17 |
18 | import androidx.annotation.RequiresApi;
19 |
20 | import io.gonative.gonative_core.GoNativeWebviewInterface;
21 |
22 | /**
23 | * Created by weiyin on 9/9/15.
24 | */
25 | public class GoNativeWebviewClient extends WebViewClient{
26 | private static final String TAG = GoNativeWebviewClient.class.getName();
27 | private UrlNavigation urlNavigation;
28 | private Context context;
29 |
30 | public GoNativeWebviewClient(MainActivity mainActivity, UrlNavigation urlNavigation) {
31 | this.urlNavigation = urlNavigation;
32 | this.context = mainActivity;
33 | }
34 |
35 | @Override
36 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
37 | return urlNavigation.shouldOverrideUrlLoading((GoNativeWebviewInterface)view, url);
38 | }
39 |
40 | public boolean shouldOverrideUrlLoading(WebView view, String url, boolean isReload) {
41 | return urlNavigation.shouldOverrideUrlLoading((GoNativeWebviewInterface)view, url, isReload, false);
42 | }
43 |
44 | @Override
45 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
47 | Uri uri = request.getUrl();
48 | return urlNavigation.shouldOverrideUrlLoading((GoNativeWebviewInterface)view, uri.toString(), false, request.isRedirect());
49 | }
50 | return super.shouldOverrideUrlLoading(view, request);
51 | }
52 |
53 | @Override
54 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
55 | super.onPageStarted(view, url, favicon);
56 |
57 | urlNavigation.onPageStarted(url);
58 | }
59 |
60 | @Override
61 | public void onPageFinished(WebView view, String url) {
62 | super.onPageFinished(view, url);
63 |
64 | urlNavigation.onPageFinished((GoNativeWebviewInterface)view, url);
65 | }
66 |
67 | @Override
68 | public void onFormResubmission(WebView view, Message dontResend, Message resend) {
69 | urlNavigation.onFormResubmission((GoNativeWebviewInterface)view, dontResend, resend);
70 | }
71 |
72 | @Override
73 | public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
74 | urlNavigation.doUpdateVisitedHistory((GoNativeWebviewInterface)view, url, isReload);
75 | }
76 |
77 | @Override
78 | public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
79 | urlNavigation.onReceivedError((GoNativeWebviewInterface) view, errorCode, description, failingUrl);
80 | }
81 |
82 | @TargetApi(Build.VERSION_CODES.M)
83 | @Override
84 | public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
85 | urlNavigation.onReceivedError((GoNativeWebviewInterface) view, error.getErrorCode(),
86 | error.getDescription().toString(), request.getUrl().toString());
87 | }
88 |
89 | @Override
90 | public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
91 | handler.cancel();
92 | urlNavigation.onReceivedSslError(error, view.getUrl());
93 | }
94 |
95 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
96 | @Override
97 | public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
98 | urlNavigation.onReceivedClientCertRequest(view.getUrl(), request);
99 | }
100 |
101 | @Override
102 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
103 | return urlNavigation.interceptHtml((LeanWebView)view, url);
104 | }
105 |
106 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
107 | @Override
108 | public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
109 |
110 | WebResourceResponse wr = ((GoNativeApplication) context.getApplicationContext()).mBridge.interceptHtml((MainActivity) context, request);
111 | if (wr != null) {
112 | return wr;
113 | }
114 |
115 | String method = request.getMethod();
116 | if (method == null || !method.equalsIgnoreCase("GET")) return null;
117 |
118 | android.net.Uri uri = request.getUrl();
119 | if (uri == null || !uri.getScheme().startsWith("http")) return null;
120 |
121 | return shouldInterceptRequest(view, uri.toString());
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/LoginManager.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 |
6 | import org.json.JSONObject;
7 |
8 | import java.lang.ref.WeakReference;
9 | import java.net.HttpURLConnection;
10 | import java.net.URL;
11 | import java.util.List;
12 | import java.util.Observable;
13 | import java.util.regex.Pattern;
14 |
15 | import io.gonative.gonative_core.AppConfig;
16 | import io.gonative.gonative_core.GNLog;
17 |
18 | /**
19 | * Created by weiyin on 3/16/14.
20 | */
21 | public class LoginManager extends Observable {
22 | private static final String TAG = LoginManager.class.getName();
23 |
24 | private Context context;
25 | private CheckRedirectionTask task = null;
26 |
27 | private boolean loggedIn = false;
28 |
29 | LoginManager(Context context) {
30 | this.context = context;
31 | checkLogin();
32 | }
33 |
34 | public void checkLogin() {
35 | if (task != null)
36 | task.cancel(true);
37 |
38 | String loginDetectionUrl = AppConfig.getInstance(context).loginDetectionUrl;
39 | if (loginDetectionUrl == null) {
40 | return;
41 | }
42 |
43 | task = new CheckRedirectionTask(this);
44 | task.execute(AppConfig.getInstance(context).loginDetectionUrl);
45 | }
46 |
47 | public boolean isLoggedIn() {
48 | return loggedIn;
49 | }
50 |
51 |
52 | private static class CheckRedirectionTask extends AsyncTask {
53 | private WeakReference loginManagerReference;
54 |
55 | public CheckRedirectionTask(LoginManager loginManager) {
56 | this.loginManagerReference = new WeakReference<>(loginManager);
57 | }
58 |
59 | @Override
60 | protected String doInBackground(String... urls){
61 | LoginManager loginManager = loginManagerReference.get();
62 | if (loginManager == null) return null;
63 |
64 | try {
65 | URL parsedUrl = new URL(urls[0]);
66 | HttpURLConnection connection = null;
67 | boolean wasRedirected;
68 | int numRedirects = 0;
69 | do {
70 | if (connection != null)
71 | connection.disconnect();
72 |
73 | connection = (HttpURLConnection) parsedUrl.openConnection();
74 | connection.setInstanceFollowRedirects(true);
75 | connection.setRequestProperty("User-Agent", AppConfig.getInstance(loginManager.context).userAgent);
76 |
77 | connection.connect();
78 | int responseCode = connection.getResponseCode();
79 |
80 | if (responseCode == HttpURLConnection.HTTP_MOVED_PERM ||
81 | responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
82 | wasRedirected = true;
83 | parsedUrl = new URL(parsedUrl, connection.getHeaderField("Location"));
84 | numRedirects++;
85 | } else {
86 | wasRedirected = false;
87 | }
88 | } while (!isCancelled() && wasRedirected && numRedirects < 10);
89 |
90 | String finalUrl = connection.getURL().toString();
91 | connection.disconnect();
92 | return finalUrl;
93 |
94 | } catch (Exception e) {
95 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
96 | return null;
97 | }
98 | }
99 |
100 | @Override
101 | protected void onPostExecute(String finalUrl) {
102 | LoginManager loginManager = loginManagerReference.get();
103 | if (loginManager == null) return;
104 |
105 | UrlInspector.getInstance().inspectUrl(finalUrl);
106 | String loginStatus;
107 |
108 | if (finalUrl == null) {
109 | loginManager.loggedIn = false;
110 | loginStatus = "default";
111 | loginManager.setChanged();
112 | loginManager.notifyObservers();
113 | return;
114 | }
115 |
116 | // iterate through loginDetectionRegexes
117 | AppConfig appConfig = AppConfig.getInstance(loginManager.context);
118 |
119 | List regexes = appConfig.loginDetectRegexes;
120 | for (int i = 0; i < regexes.size(); i++) {
121 | Pattern regex = regexes.get(i);
122 | if (regex.matcher(finalUrl).matches()) {
123 | JSONObject entry = appConfig.loginDetectLocations.get(i);
124 | loginManager.loggedIn = entry.optBoolean("loggedIn", false);
125 |
126 | loginStatus = AppConfig.optString(entry, "menuName");
127 | if (loginStatus == null) loginStatus = loginManager.loggedIn ? "loggedIn" : "default";
128 |
129 | loginManager.setChanged();
130 | loginManager.notifyObservers();
131 | break;
132 | }
133 | }
134 | }
135 |
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/GoNativeWindowManager.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.util.LinkedHashMap;
6 | import java.util.Map;
7 |
8 | public class GoNativeWindowManager {
9 | private final LinkedHashMap windows;
10 | private ExcessWindowsClosedListener excessWindowsClosedListener;
11 |
12 | public GoNativeWindowManager() {
13 | windows = new LinkedHashMap<>();
14 | }
15 |
16 | public void addNewWindow(String activityId, boolean isRoot) {
17 | this.windows.put(activityId, new ActivityWindow(activityId, isRoot));
18 | }
19 |
20 | public void removeWindow(String activityId) {
21 | this.windows.remove(activityId);
22 |
23 | if (excessWindowsClosedListener != null && windows.size() <= 1) {
24 | excessWindowsClosedListener.onAllExcessWindowClosed();
25 | }
26 | }
27 |
28 | public void setOnExcessWindowClosedListener(ExcessWindowsClosedListener listener) {
29 | this.excessWindowsClosedListener = listener;
30 | }
31 |
32 | public ActivityWindow getActivityWindowInfo(String activityId) {
33 | return windows.get(activityId);
34 | }
35 |
36 | public void setUrlLevel(String activityId, int urlLevel) {
37 | ActivityWindow window = windows.get(activityId);
38 | if (window != null) {
39 | window.setUrlLevels(urlLevel, window.parentUrlLevel);
40 | }
41 | }
42 |
43 | public int getUrlLevel(String activityId) {
44 | ActivityWindow window = windows.get(activityId);
45 | if (window != null) {
46 | return window.urlLevel;
47 | }
48 | return -1;
49 | }
50 |
51 | public void setParentUrlLevel(String activityId, int parentLevel) {
52 | ActivityWindow window = windows.get(activityId);
53 | if (window != null) {
54 | window.setUrlLevels(window.urlLevel, parentLevel);
55 | }
56 | }
57 |
58 | public int getParentUrlLevel(String activityId) {
59 | ActivityWindow window = windows.get(activityId);
60 | if (window != null) {
61 | return window.parentUrlLevel;
62 | }
63 | return -1;
64 | }
65 |
66 | public void setUrlLevels(String activityId, int urlLevel, int parentLevel) {
67 | ActivityWindow window = windows.get(activityId);
68 | if (window != null) {
69 | window.setUrlLevels(urlLevel, parentLevel);
70 | }
71 | }
72 |
73 | public boolean isRoot(String activityId) {
74 | ActivityWindow window = windows.get(activityId);
75 | if (window != null) {
76 | return window.isRoot;
77 | }
78 | return false;
79 | }
80 |
81 | public void setAsNewRoot(String activityId) {
82 | for (Map.Entry entry : windows.entrySet()) {
83 | ActivityWindow window = entry.getValue();
84 | if (TextUtils.equals(activityId, entry.getKey())) {
85 | window.isRoot = true;
86 | } else {
87 | window.isRoot = false;
88 | }
89 | }
90 | }
91 |
92 | public void setIgnoreInterceptMaxWindows(String activityId, boolean ignore) {
93 | ActivityWindow window = windows.get(activityId);
94 | if (window != null) {
95 | window.ignoreInterceptMaxWindows = ignore;
96 | }
97 | }
98 |
99 | public boolean isIgnoreInterceptMaxWindows(String activityId) {
100 | ActivityWindow window = windows.get(activityId);
101 | if (window != null) {
102 | return window.ignoreInterceptMaxWindows;
103 | }
104 | return false;
105 | }
106 |
107 | public int getWindowCount() {
108 | return windows.size();
109 | }
110 |
111 | // Returns ID of the next window after root as Excess window
112 | public String getExcessWindow() {
113 | for (Map.Entry entry : windows.entrySet()) {
114 | ActivityWindow window = entry.getValue();
115 | if (window.isRoot) continue;
116 | return window.id;
117 | }
118 | return null;
119 | }
120 |
121 | public static class ActivityWindow {
122 | private final String id;
123 | private boolean isRoot;
124 | private int urlLevel;
125 | private int parentUrlLevel;
126 | private boolean ignoreInterceptMaxWindows;
127 |
128 | ActivityWindow(String id, boolean isRoot) {
129 | this.id = id;
130 | this.isRoot = isRoot;
131 | this.urlLevel = -1;
132 | this.parentUrlLevel = -1;
133 | }
134 |
135 | public void setUrlLevels(int urlLevel, int parentUrlLevel) {
136 | this.urlLevel = urlLevel;
137 | this.parentUrlLevel = parentUrlLevel;
138 | }
139 |
140 | @Override
141 | public String toString() {
142 | return "id=" + id + "\n" +
143 | "isRoot=" + isRoot + "\n" +
144 | "urlLevel=" + urlLevel + "\n" +
145 | "parentUrlLevel=" + parentUrlLevel;
146 | }
147 | }
148 |
149 |
150 | interface ExcessWindowsClosedListener {
151 | void onAllExcessWindowClosed();
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/app/src/normal/java/io/gonative/android/WebkitCookieManagerProxy.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import java.io.IOException;
4 | import java.net.CookiePolicy;
5 | import java.net.CookieStore;
6 | import java.net.HttpCookie;
7 | import java.net.URI;
8 | import java.util.Arrays;
9 | import java.util.Calendar;
10 | import java.util.Date;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | import io.gonative.gonative_core.AppConfig;
15 | import io.gonative.gonative_core.LeanUtils;
16 |
17 | // this syncs cookies between webkit (webview) and java.net classes
18 | public class WebkitCookieManagerProxy extends java.net.CookieManager {
19 | private static final String TAG = WebkitCookieManagerProxy.class.getName();
20 | private android.webkit.CookieManager webkitCookieManager;
21 |
22 | public WebkitCookieManagerProxy()
23 | {
24 | this(null, null);
25 | }
26 |
27 | WebkitCookieManagerProxy(CookieStore store, CookiePolicy cookiePolicy)
28 | {
29 | super(null, cookiePolicy);
30 |
31 | this.webkitCookieManager = android.webkit.CookieManager.getInstance();
32 | }
33 |
34 | // java.net.CookieManager overrides
35 | @Override
36 | public void put(URI uri, Map> responseHeaders) throws IOException
37 | {
38 | // make sure our args are valid
39 | if ((uri == null) || (responseHeaders == null)) return;
40 |
41 | // save our url once
42 | String url = uri.toString();
43 |
44 | String expiryString = null;
45 | int sessionExpiry = AppConfig.getInstance(null).forceSessionCookieExpiry;
46 |
47 | // go over the headers
48 | for (String headerKey : responseHeaders.keySet())
49 | {
50 | // ignore headers which aren't cookie related
51 | if ((headerKey == null) || !headerKey.equalsIgnoreCase("Set-Cookie")) continue;
52 |
53 | // process each of the headers
54 | for (String headerValue : responseHeaders.get(headerKey))
55 | {
56 | boolean passOriginalHeader = true;
57 | if (sessionExpiry > 0) {
58 | List cookies = HttpCookie.parse(headerValue);
59 | for (HttpCookie cookie : cookies) {
60 | if (cookie.getMaxAge() < 0 || cookie.getDiscard()) {
61 | // this is a session cookie. Modify it and pass it to the webview.
62 | cookie.setMaxAge(sessionExpiry);
63 | cookie.setDiscard(false);
64 | if (expiryString == null) {
65 | Calendar calendar = Calendar.getInstance();
66 | calendar.add(Calendar.SECOND, sessionExpiry);
67 | Date expiryDate = calendar.getTime();
68 | expiryString = "; expires=" + LeanUtils.formatDateForCookie(expiryDate) +
69 | "; Max-Age=" + Integer.toString(sessionExpiry);
70 | }
71 |
72 | StringBuilder newHeader = new StringBuilder();
73 | newHeader.append(cookie.toString());
74 | newHeader.append(expiryString);
75 | if (cookie.getPath() != null) {
76 | newHeader.append("; path=");
77 | newHeader.append(cookie.getPath());
78 | }
79 | if (cookie.getDomain() != null) {
80 | newHeader.append("; domain=");
81 | newHeader.append(cookie.getDomain());
82 | }
83 | if (cookie.getSecure()) {
84 | newHeader.append("; secure");
85 | }
86 |
87 | this.webkitCookieManager.setCookie(url, newHeader.toString());
88 | passOriginalHeader = false;
89 | }
90 | }
91 | }
92 |
93 | if (passOriginalHeader) this.webkitCookieManager.setCookie(url, headerValue);
94 | }
95 | }
96 | }
97 |
98 | @Override
99 | public Map> get(URI uri, Map> requestHeaders) throws IOException
100 | {
101 | // make sure our args are valid
102 | if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null");
103 |
104 | // save our url once
105 | String url = uri.toString();
106 |
107 | // prepare our response
108 | Map> res = new java.util.HashMap>();
109 |
110 | // get the cookie
111 | String cookie = this.webkitCookieManager.getCookie(url);
112 |
113 | // return it
114 | if (cookie != null) res.put("Cookie", Arrays.asList(cookie));
115 | return res;
116 | }
117 |
118 | @Override
119 | public CookieStore getCookieStore()
120 | {
121 | // we don't want anyone to work with this cookie store directly
122 | throw new UnsupportedOperationException();
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/plugins.gradle:
--------------------------------------------------------------------------------
1 | import groovy.json.JsonSlurper
2 | import org.gradle.initialization.DefaultSettings
3 | import org.apache.tools.ant.taskdefs.condition.Os
4 |
5 | def generatedFileName = "PackageList.java"
6 | def generatedFilePackage = "io.gonative.android"
7 | def generatedFileContentsTemplate = """
8 | package $generatedFilePackage;
9 |
10 | import android.app.Application;
11 | import android.content.Context;
12 | import android.content.res.Resources;
13 |
14 | import io.gonative.gonative_core.BridgeModule;
15 | import java.util.Arrays;
16 | import java.util.ArrayList;
17 |
18 | {{ packageImports }}
19 |
20 | public class PackageList {
21 | private Application application;
22 |
23 | public PackageList(Application application) {
24 | this.application = application;
25 | }
26 |
27 | private Resources getResources() {
28 | return this.getApplication().getResources();
29 | }
30 |
31 | private Application getApplication() {
32 | return this.application;
33 | }
34 |
35 | private Context getApplicationContext() {
36 | return this.getApplication().getApplicationContext();
37 | }
38 |
39 | public ArrayList getPackages() {
40 | return new ArrayList<>(Arrays.asList(
41 | {{ packageClassInstances }}
42 | ));
43 | }
44 | }
45 | """
46 |
47 | class GoNativeModules {
48 | private Logger logger
49 | private ArrayList> modulesMetadata
50 |
51 | private packageName = "io.gonative.android"
52 |
53 | GoNativeModules(Logger logger) {
54 | this.logger = logger
55 | this.modulesMetadata = this.getModulesMetadata()
56 | }
57 |
58 | ArrayList> getModulesMetadata() {
59 | if (this.modulesMetadata != null) return this.modulesMetadata
60 |
61 | ArrayList> modulesMetadata = new ArrayList>()
62 |
63 | def finder = new FileNameFinder()
64 | def files = finder.getFileNames(System.getProperty("user.dir"), 'plugins/**/plugin-metadata.json')
65 | files.each { fileName ->
66 | def jsonFile = new File(fileName)
67 | def parsedJson = new JsonSlurper().parseText(jsonFile.text).plugin
68 | parsedJson["sourceDir"] = fileName.tokenize(File.separator)[-3..-2].join(File.separator)
69 | modulesMetadata.push(parsedJson)
70 | }
71 |
72 | return modulesMetadata
73 | }
74 |
75 | void addModuleProjects(DefaultSettings defaultSettings) {
76 | modulesMetadata.forEach { module ->
77 | String pluginName = module["pluginName"]
78 | String sourceDir = module["sourceDir"]
79 | this.logger.warn(sourceDir)
80 | defaultSettings.include(":${pluginName}")
81 | defaultSettings.project(":${pluginName}").projectDir = new File(defaultSettings.rootProject.projectDir, "./${sourceDir}")
82 | }
83 | }
84 |
85 | void addModuleDependencies(Project appProject) {
86 | modulesMetadata.forEach { module ->
87 | String pluginName = module["pluginName"]
88 | appProject.dependencies {
89 | implementation project(path: ":${pluginName}")
90 | }
91 | }
92 | }
93 |
94 | void generatePackagesFile(File outputDir, String generatedFileName, GString generatedFileContentsTemplate) {
95 | def packages = this.modulesMetadata
96 | String packageName = this.packageName
97 |
98 | String packageImports = ""
99 | String packageClassInstances = ""
100 |
101 | if (packages.size() > 0) {
102 | packageImports = "import ${packageName}.BuildConfig;\nimport ${packageName}.R;\n\n"
103 | packageImports = packageImports + packages.collect {
104 | "// ${it.name}\nimport ${it.packageName}.${it.classInstance};"
105 | }.join('\n')
106 | packageClassInstances = packages.collect { "new ${it.classInstance}()" }.join(",\n ")
107 | }
108 |
109 | String generatedFileContents = generatedFileContentsTemplate.toString()
110 | .replace("{{ packageImports }}", packageImports)
111 | .replace("{{ packageClassInstances }}", packageClassInstances)
112 |
113 | outputDir.mkdirs()
114 | final FileTreeBuilder treeBuilder = new FileTreeBuilder(outputDir)
115 | treeBuilder.file(generatedFileName).newWriter().withWriter { w ->
116 | w << generatedFileContents
117 | }
118 | }
119 | }
120 |
121 | def gonativeModules = new GoNativeModules(logger)
122 |
123 | ext.applyModulesSettingsGradle = { DefaultSettings defaultSettings ->
124 | gonativeModules.addModuleProjects(defaultSettings)
125 | }
126 |
127 | ext.applyNativeModulesAppBuildGradle = { Project project ->
128 | gonativeModules.addModuleDependencies(project)
129 |
130 | def generatedSrcDir = new File(buildDir, "generated/gncli/src/main/java")
131 | def generatedCodeDir = new File(generatedSrcDir, generatedFilePackage.replace('.', '/'))
132 |
133 | task generatePackageList {
134 | doLast {
135 | gonativeModules.generatePackagesFile(generatedCodeDir, generatedFileName, generatedFileContentsTemplate)
136 | }
137 | }
138 |
139 | preBuild.dependsOn generatePackageList
140 |
141 | android {
142 | sourceSets {
143 | main {
144 | java {
145 | srcDirs += generatedSrcDir
146 | }
147 | }
148 | }
149 | }
150 | }
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/widget/CircleImageView.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.RadialGradient;
8 | import android.graphics.Shader;
9 | import android.graphics.drawable.ShapeDrawable;
10 | import android.graphics.drawable.shapes.OvalShape;
11 | import android.view.View;
12 | import android.view.animation.Animation;
13 |
14 | import androidx.core.content.ContextCompat;
15 | import androidx.core.view.ViewCompat;
16 |
17 | /**
18 | * Private class created to work around issues with AnimationListeners being
19 | * called before the animation is actually complete and support shadows on older
20 | * platforms.
21 | */
22 | class CircleImageView extends androidx.appcompat.widget.AppCompatImageView {
23 |
24 | private static final int KEY_SHADOW_COLOR = 0x1E000000;
25 | private static final int FILL_SHADOW_COLOR = 0x3D000000;
26 | // PX
27 | private static final float X_OFFSET = 0f;
28 | private static final float Y_OFFSET = 1.75f;
29 | private static final float SHADOW_RADIUS = 3.5f;
30 | private static final int SHADOW_ELEVATION = 4;
31 |
32 | private Animation.AnimationListener mListener;
33 | int mShadowRadius;
34 |
35 | CircleImageView(Context context, int color) {
36 | super(context);
37 | final float density = getContext().getResources().getDisplayMetrics().density;
38 | final int shadowYOffset = (int) (density * Y_OFFSET);
39 | final int shadowXOffset = (int) (density * X_OFFSET);
40 |
41 | mShadowRadius = (int) (density * SHADOW_RADIUS);
42 |
43 | ShapeDrawable circle;
44 | if (elevationSupported()) {
45 | circle = new ShapeDrawable(new OvalShape());
46 | ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
47 | } else {
48 | OvalShape oval = new CircleImageView.OvalShadow(mShadowRadius);
49 | circle = new ShapeDrawable(oval);
50 | setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint());
51 | circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
52 | KEY_SHADOW_COLOR);
53 | final int padding = mShadowRadius;
54 | // set padding so the inner image sits correctly within the shadow.
55 | setPadding(padding, padding, padding, padding);
56 | }
57 | circle.getPaint().setColor(color);
58 | ViewCompat.setBackground(this, circle);
59 | }
60 |
61 | private boolean elevationSupported() {
62 | return android.os.Build.VERSION.SDK_INT >= 21;
63 | }
64 |
65 | @Override
66 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
67 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
68 | if (!elevationSupported()) {
69 | setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()
70 | + mShadowRadius * 2);
71 | }
72 | }
73 |
74 | public void setAnimationListener(Animation.AnimationListener listener) {
75 | mListener = listener;
76 | }
77 |
78 | @Override
79 | public void onAnimationStart() {
80 | super.onAnimationStart();
81 | if (mListener != null) {
82 | mListener.onAnimationStart(getAnimation());
83 | }
84 | }
85 |
86 | @Override
87 | public void onAnimationEnd() {
88 | super.onAnimationEnd();
89 | if (mListener != null) {
90 | mListener.onAnimationEnd(getAnimation());
91 | }
92 | }
93 |
94 | /**
95 | * Update the background color of the circle image view.
96 | *
97 | * @param colorRes Id of a color resource.
98 | */
99 | public void setBackgroundColorRes(int colorRes) {
100 | setBackgroundColor(ContextCompat.getColor(getContext(), colorRes));
101 | }
102 |
103 | @Override
104 | public void setBackgroundColor(int color) {
105 | if (getBackground() instanceof ShapeDrawable) {
106 | ((ShapeDrawable) getBackground()).getPaint().setColor(color);
107 | }
108 | }
109 |
110 | private class OvalShadow extends OvalShape {
111 | private RadialGradient mRadialGradient;
112 | private Paint mShadowPaint;
113 |
114 | OvalShadow(int shadowRadius) {
115 | super();
116 | mShadowPaint = new Paint();
117 | mShadowRadius = shadowRadius;
118 | updateRadialGradient((int) rect().width());
119 | }
120 |
121 | @Override
122 | protected void onResize(float width, float height) {
123 | super.onResize(width, height);
124 | updateRadialGradient((int) width);
125 | }
126 |
127 | @Override
128 | public void draw(Canvas canvas, Paint paint) {
129 | final int viewWidth = CircleImageView.this.getWidth();
130 | final int viewHeight = CircleImageView.this.getHeight();
131 | canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint);
132 | canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint);
133 | }
134 |
135 | private void updateRadialGradient(int diameter) {
136 | mRadialGradient = new RadialGradient(diameter / 2, diameter / 2,
137 | mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT },
138 | null, Shader.TileMode.CLAMP);
139 | mShadowPaint.setShader(mRadialGradient);
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/widget/HandleView.kt:
--------------------------------------------------------------------------------
1 | package io.gonative.android.widget
2 |
3 | import android.animation.ArgbEvaluator
4 | import android.animation.ValueAnimator
5 | import android.content.Context
6 | import android.graphics.Color
7 | import android.graphics.PorterDuff
8 | import android.graphics.drawable.Drawable
9 | import android.util.AttributeSet
10 | import android.widget.ImageView
11 | import android.widget.RelativeLayout
12 | import android.widget.TextView
13 | import androidx.annotation.ColorInt
14 | import androidx.core.content.res.ResourcesCompat
15 | import io.gonative.android.R
16 |
17 | class HandleView : RelativeLayout {
18 | private val iconView: ImageView
19 | private val textView: TextView
20 |
21 | init {
22 | inflate(context, R.layout.view_handle, this)
23 | iconView = findViewById(R.id.icon)
24 | textView = findViewById(R.id.text)
25 | }
26 |
27 | @JvmOverloads
28 | constructor(
29 | context: Context,
30 | attrs: AttributeSet? = null,
31 | defStyle: Int = 0
32 | ) : super(context, attrs, defStyle) {
33 | context.theme.obtainStyledAttributes(attrs, R.styleable.HandleView, 0, 0).apply {
34 | val backgroundDrawable = getDrawable(R.styleable.HandleView_handleBackground)
35 | ?: ResourcesCompat.getDrawable(
36 | resources,
37 | R.drawable.shape_rounded,
38 | context.theme
39 | )
40 | val iconDrawable = getDrawable(R.styleable.HandleView_iconDrawable)
41 | val text = getString(R.styleable.HandleView_text)
42 | val inactiveColor = getColor(R.styleable.HandleView_inactiveColor, inactiveColor)
43 | val activeColor = getColor(R.styleable.HandleView_activeColor, activeColor)
44 | initView(backgroundDrawable, iconDrawable, text, inactiveColor, activeColor)
45 | }
46 | }
47 |
48 | constructor(
49 | context: Context,
50 | backgroundDrawable: Drawable?,
51 | iconDrawable: Drawable?,
52 | text: String?,
53 | @ColorInt inactiveColor: Int,
54 | @ColorInt activeColor: Int,
55 | ) : super(context, null, 0) {
56 | initView(backgroundDrawable, iconDrawable, text, inactiveColor, activeColor)
57 | }
58 |
59 | var maxTextWidth: Int = Int.MIN_VALUE
60 | var inactiveColor: Int = Color.WHITE
61 | var activeColor: Int = Color.WHITE
62 |
63 | fun initView(
64 | backgroundDrawable: Drawable?,
65 | iconDrawable: Drawable?,
66 | text: String?,
67 | @ColorInt inactiveColor: Int,
68 | @ColorInt activeColor: Int
69 | ) {
70 | background = backgroundDrawable
71 | iconView.setImageDrawable(iconDrawable)
72 | setText(text)
73 | textView.layoutParams.let {
74 | it.width = 0
75 | textView.layoutParams = it
76 | }
77 |
78 | this.inactiveColor = inactiveColor
79 | this.activeColor = activeColor
80 | iconView.setColorFilter(inactiveColor)
81 | }
82 |
83 | fun setText(text: String?) {
84 | textView.layoutParams.width = LayoutParams.WRAP_CONTENT
85 | textView.text = text
86 | textView.measure(
87 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
88 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
89 | )
90 | maxTextWidth = textView.measuredWidth
91 | textView.layoutParams.let {
92 | it.width = 0
93 | textView.layoutParams = it
94 | }
95 | }
96 |
97 | fun animateShowText() {
98 | if (textView.text.isEmpty()) {
99 | return
100 | }
101 | if (textView.layoutParams.width != 0) {
102 | textView.layoutParams.let {
103 | it.width = 0
104 | textView.layoutParams = it
105 | }
106 | }
107 | val animator = ValueAnimator.ofInt(0, maxTextWidth)
108 | animator.addUpdateListener { anim ->
109 | val value = anim.animatedValue as Int
110 | textView.layoutParams.let {
111 | it.width = value
112 | textView.layoutParams = it
113 | }
114 | }
115 | animator.duration = 300
116 | animator.start()
117 | }
118 |
119 | fun animateHideText() {
120 | if (textView.text.isEmpty()) {
121 | return
122 | }
123 | val animator = ValueAnimator.ofInt(maxTextWidth, 0)
124 | animator.duration = 300
125 | animator.addUpdateListener { anim ->
126 | val value = anim.animatedValue as Int
127 | val params = textView.layoutParams
128 | params.width = value
129 | textView.layoutParams = params
130 | }
131 | animator.start()
132 | }
133 |
134 | fun animateActive() {
135 | val animator = ValueAnimator.ofObject(ArgbEvaluator(), inactiveColor, activeColor)
136 | animator.addUpdateListener { anim ->
137 | val color = anim.animatedValue as Int
138 | textView.setTextColor(color)
139 | iconView.setColorFilter(color, PorterDuff.Mode.SRC_IN)
140 | }
141 | animator.duration = 100
142 | animator.start()
143 | }
144 |
145 | fun animateInactive() {
146 | val animator = ValueAnimator.ofObject(ArgbEvaluator(), activeColor, inactiveColor)
147 | animator.addUpdateListener { anim ->
148 | val color = anim.animatedValue as Int
149 | textView.setTextColor(color)
150 | iconView.setColorFilter(color, PorterDuff.Mode.SRC_IN)
151 | }
152 | animator.duration = 200
153 | animator.start()
154 | }
155 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/Installation.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.content.pm.ApplicationInfo;
5 | import android.content.pm.PackageInfo;
6 | import android.content.pm.PackageManager;
7 | import android.os.Build;
8 | import android.telephony.SubscriptionInfo;
9 | import android.telephony.SubscriptionManager;
10 | import android.util.Log;
11 |
12 | import androidx.core.app.ActivityCompat;
13 |
14 | import java.io.File;
15 | import java.io.FileOutputStream;
16 | import java.io.IOException;
17 | import java.io.RandomAccessFile;
18 | import java.util.ArrayList;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.Locale;
22 | import java.util.Map;
23 | import java.util.TimeZone;
24 | import java.util.UUID;
25 |
26 | import io.gonative.gonative_core.AppConfig;
27 | import io.gonative.gonative_core.GNLog;
28 |
29 | /**
30 | * Created by weiyin on 8/8/14.
31 | */
32 | public class Installation {
33 | private static final String TAG = Installation.class.getName();
34 |
35 | private static String sID = null;
36 | private static final String INSTALLATION = "INSTALLATION";
37 |
38 | public synchronized static String id(Context context) {
39 | if (sID == null) {
40 | File installation = new File(context.getFilesDir(), INSTALLATION);
41 | try {
42 | if (!installation.exists())
43 | writeInstallationFile(installation);
44 | sID = readInstallationFile(installation);
45 | } catch (Exception e) {
46 | throw new RuntimeException(e);
47 | }
48 | }
49 | return sID;
50 | }
51 |
52 | public static Map getInfo(Context context) {
53 | HashMap info = new HashMap<>();
54 |
55 | info.put("platform", "android");
56 |
57 | String publicKey = AppConfig.getInstance(context).publicKey;
58 | if (publicKey == null) publicKey = "";
59 | info.put("publicKey", publicKey);
60 |
61 | String packageName = context.getPackageName();
62 | info.put("appId", packageName);
63 |
64 |
65 | PackageManager manager = context.getPackageManager();
66 | try {
67 | PackageInfo packageInfo = manager.getPackageInfo(packageName, 0);
68 | info.put("appVersion", packageInfo.versionName);
69 | info.put("appVersionCode", packageInfo.versionCode);
70 | } catch (PackageManager.NameNotFoundException e) {
71 | GNLog.getInstance().logError(TAG, e.getMessage(), e);
72 | }
73 |
74 | String distribution;
75 | boolean isDebuggable = ( 0 != ( context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
76 | if (isDebuggable) {
77 | distribution = "debug";
78 | } else {
79 | String installer = manager.getInstallerPackageName(packageName);
80 | if (installer == null) {
81 | distribution = "adhoc";
82 | } else if (installer.equals("com.android.vending") || installer.equals("com.google.market")) {
83 | distribution = "playstore";
84 | } else if (installer.equals("com.amazon.venezia")) {
85 | distribution = "amazon";
86 | } else {
87 | distribution = installer;
88 | }
89 | }
90 | info.put("distribution", distribution);
91 |
92 | info.put("language", Locale.getDefault().getLanguage());
93 | info.put("os", "Android");
94 | info.put("osVersion", Build.VERSION.RELEASE);
95 | info.put("model", Build.MANUFACTURER + " " + Build.MODEL);
96 | info.put("hardware", Build.FINGERPRINT);
97 | info.put("timeZone", TimeZone.getDefault().getID());
98 | info.put("deviceName", getDeviceName());
99 |
100 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
101 | SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
102 |
103 | if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
104 | List carriers = new ArrayList<>();
105 | for (SubscriptionInfo subscriptionInfo : subscriptionManager.getActiveSubscriptionInfoList()) {
106 | carriers.add(subscriptionInfo.getCarrierName().toString());
107 | }
108 | info.put("carrierNames", carriers);
109 | try {
110 | info.put("carrierName", carriers.get(0));
111 | } catch ( IndexOutOfBoundsException e ) {
112 | Log.w(TAG, "getInfo: No carriers registered with subscription manager");
113 | }
114 | } else {
115 | Log.w(TAG, "getInfo: Cannot get carrierNames, READ_PHONE_STATE not granted");
116 | }
117 | }
118 |
119 | info.put("installationId", Installation.id(context));
120 |
121 | return info;
122 | }
123 |
124 | private static String readInstallationFile(File installation) throws IOException {
125 | RandomAccessFile f = new RandomAccessFile(installation, "r");
126 | byte[] bytes = new byte[(int) f.length()];
127 | f.readFully(bytes);
128 | f.close();
129 | return new String(bytes);
130 | }
131 |
132 | private static void writeInstallationFile(File installation) throws IOException {
133 | FileOutputStream out = new FileOutputStream(installation);
134 | String id = UUID.randomUUID().toString();
135 | out.write(id.getBytes());
136 | out.close();
137 | }
138 |
139 | private static String getDeviceName() {
140 | String manufacturer = Build.MANUFACTURER;
141 | String model = Build.MODEL;
142 | String name;
143 | if (model.startsWith(manufacturer)) {
144 | name = model;
145 | } else {
146 | name = manufacturer + " " + model;
147 | }
148 | return name;
149 | }
150 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
75 |
76 |
83 |
84 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
108 |
111 |
112 |
113 |
114 |
117 |
120 |
121 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/app/src/main/java/io/gonative/android/RegistrationManager.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 | import android.util.Log;
6 |
7 | import org.json.JSONArray;
8 | import org.json.JSONObject;
9 |
10 | import java.io.OutputStreamWriter;
11 | import java.net.HttpURLConnection;
12 | import java.net.URL;
13 | import java.util.HashMap;
14 | import java.util.Iterator;
15 | import java.util.LinkedList;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.regex.Pattern;
19 |
20 | import io.gonative.gonative_core.GNLog;
21 | import io.gonative.gonative_core.LeanUtils;
22 |
23 | /**
24 | * Created by weiyin on 10/4/15.
25 | */
26 | public class RegistrationManager {
27 | private final static String TAG = RegistrationManager.class.getName();
28 |
29 | private Context context;
30 | private JSONObject customData;
31 | private String lastUrl;
32 |
33 | private List registrationEndpoints;
34 |
35 | RegistrationManager(Context context) {
36 | this.context = context;
37 | this.registrationEndpoints = new LinkedList<>();
38 | }
39 |
40 | public void processConfig(JSONArray endpoints) {
41 | registrationEndpoints.clear();
42 |
43 | if (endpoints == null) return;
44 |
45 | for (int i = 0; i < endpoints.length(); i++) {
46 | JSONObject endpoint = endpoints.optJSONObject(i);
47 | if (endpoint == null) continue;
48 |
49 | String url = LeanUtils.optString(endpoint, "url");
50 | if (url == null) {
51 | Log.w(TAG, "Invalid registration: endpoint url is null");
52 | continue;
53 | }
54 |
55 | List urlRegexes = LeanUtils.createRegexArrayFromStrings(endpoint.opt("urlRegex"));
56 |
57 | RegistrationEndpoint registrationEndpoint = new RegistrationEndpoint(url, urlRegexes);
58 | registrationEndpoints.add(registrationEndpoint);
59 | }
60 | }
61 |
62 | public void checkUrl(String url) {
63 | this.lastUrl = url;
64 | for (RegistrationEndpoint endpoint : registrationEndpoints) {
65 | if (LeanUtils.stringMatchesAnyRegex(url, endpoint.urlRegexes)) {
66 | endpoint.sendRegistrationInfo();
67 | }
68 | }
69 | }
70 |
71 | public void setCustomData(JSONObject customData) {
72 | this.customData = customData;
73 | registrationDataChanged();
74 | }
75 |
76 | public void sendToAllEndpoints() {
77 | for (RegistrationEndpoint endpoint : registrationEndpoints) {
78 | endpoint.sendRegistrationInfo();
79 | }
80 | }
81 |
82 | private void registrationDataChanged() {
83 | for (RegistrationEndpoint endpoint : registrationEndpoints) {
84 | if (this.lastUrl != null &&
85 | LeanUtils.stringMatchesAnyRegex(this.lastUrl, endpoint.urlRegexes)) {
86 | endpoint.sendRegistrationInfo();
87 | }
88 | }
89 | }
90 |
91 | public void subscriptionInfoChanged(){
92 | registrationDataChanged();
93 | }
94 |
95 | private class RegistrationEndpoint {
96 | private String postUrl;
97 | private List urlRegexes;
98 |
99 | RegistrationEndpoint(String postUrl, List urlRegexes) {
100 | this.postUrl = postUrl;
101 | this.urlRegexes = urlRegexes;
102 | }
103 |
104 | void sendRegistrationInfo() {
105 | new SendRegistrationTask(context, this, RegistrationManager.this).execute();
106 | }
107 | }
108 |
109 | private static class SendRegistrationTask extends AsyncTask {
110 | private RegistrationEndpoint registrationEndpoint;
111 | private RegistrationManager registrationManager;
112 | private Context context;
113 |
114 | SendRegistrationTask(Context context, RegistrationEndpoint registrationEndpoint, RegistrationManager registrationManager) {
115 | this.registrationEndpoint = registrationEndpoint;
116 | this.registrationManager = registrationManager;
117 | this.context = context;
118 | }
119 |
120 | @Override
121 | protected Void doInBackground(Void... voids) {
122 | Map toSend = new HashMap<>();
123 |
124 | toSend.putAll(Installation.getInfo(registrationManager.context));
125 |
126 | // Append provider info to Map toSend
127 | if (((GoNativeApplication) context).getAnalyticsProviderInfo() != null) {
128 | toSend.putAll(((GoNativeApplication) context).getAnalyticsProviderInfo());
129 | }
130 |
131 | if (registrationManager.customData != null) {
132 | Iterator keys = registrationManager.customData.keys();
133 | while(keys.hasNext()) {
134 | String key = keys.next();
135 | toSend.put("customData_" + key, registrationManager.customData.opt(key));
136 | }
137 | }
138 |
139 | try {
140 | JSONObject json = new JSONObject(toSend);
141 |
142 | URL url = new URL(registrationEndpoint.postUrl);
143 | HttpURLConnection connection = (HttpURLConnection) url.openConnection();
144 | connection.setRequestMethod("POST");
145 | connection.setRequestProperty("Content-Type", "application/json");
146 | connection.setDoOutput(true);
147 | OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
148 | writer.write(json.toString());
149 | writer.close();
150 | connection.connect();
151 | int result = connection.getResponseCode();
152 |
153 | if (result < 200 || result > 299) {
154 | Log.w(TAG, "Recevied status code " + result + " when posting to " + registrationEndpoint.postUrl);
155 | }
156 | } catch (Exception e) {
157 | GNLog.getInstance().logError(TAG, "Error posting to " + registrationEndpoint.postUrl, e);
158 | }
159 |
160 | return null;
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/app/src/normal/java/io/gonative/android/WebViewSetup.java:
--------------------------------------------------------------------------------
1 | package io.gonative.android;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.os.Message;
7 | import android.webkit.CookieManager;
8 | import android.webkit.WebSettings;
9 | import android.webkit.WebView;
10 |
11 | import java.util.Map;
12 |
13 | import io.gonative.gonative_core.AppConfig;
14 | import io.gonative.gonative_core.GNLog;
15 | import io.gonative.gonative_core.GoNativeWebviewInterface;
16 |
17 | /**
18 | * Created by weiyin on 9/8/15.
19 | */
20 | public class WebViewSetup {
21 | private static final String TAG = WebViewSetup.class.getName();
22 |
23 | @SuppressLint("JavascriptInterface")
24 | public static void setupWebviewForActivity(GoNativeWebviewInterface webview, MainActivity activity) {
25 | if (!(webview instanceof LeanWebView)) {
26 | GNLog.getInstance().logError(TAG, "Expected webview to be of class LeanWebView and not " + webview.getClass().getName());
27 | return;
28 | }
29 |
30 | LeanWebView wv = (LeanWebView)webview;
31 |
32 | setupWebview(wv, activity);
33 |
34 | UrlNavigation urlNavigation = new UrlNavigation(activity);
35 | urlNavigation.setCurrentWebviewUrl(webview.getUrl());
36 |
37 | wv.setWebChromeClient(new GoNativeWebChromeClient(activity, urlNavigation));
38 | wv.setWebViewClient(new GoNativeWebviewClient(activity, urlNavigation));
39 |
40 | FileDownloader fileDownloader = activity.getFileDownloader();
41 | if (fileDownloader != null) {
42 | wv.setDownloadListener(fileDownloader);
43 | fileDownloader.setUrlNavigation(urlNavigation);
44 | }
45 |
46 | ProfilePicker profilePicker = activity.getProfilePicker();
47 | wv.removeJavascriptInterface("gonative_profile_picker");
48 | if (profilePicker != null) {
49 | wv.addJavascriptInterface(profilePicker.getProfileJsBridge(), "gonative_profile_picker");
50 | }
51 |
52 | wv.removeJavascriptInterface("gonative_status_checker");
53 | wv.addJavascriptInterface(activity.getStatusCheckerBridge(), "gonative_status_checker");
54 |
55 | wv.removeJavascriptInterface("gonative_file_writer_sharer");
56 | wv.addJavascriptInterface(activity.getFileWriterSharer().getJavascriptBridge(), "gonative_file_writer_sharer");
57 |
58 | wv.removeJavascriptInterface("JSBridge");
59 | wv.addJavascriptInterface(activity.getJavascriptBridge(), "JSBridge");
60 |
61 | ((GoNativeApplication) activity.getApplication()).mBridge.onWebviewSetUp(activity, wv);
62 |
63 | if (activity.getIntent().getBooleanExtra(MainActivity.EXTRA_WEBVIEW_WINDOW_OPEN, false)) {
64 | // send to other webview
65 | Message resultMsg = ((GoNativeApplication)activity.getApplication()).getWebviewMessage();
66 | if (resultMsg != null) {
67 | WebView.WebViewTransport transport = (WebView.WebViewTransport)resultMsg.obj;
68 | if (transport != null) {
69 | transport.setWebView(wv);
70 | resultMsg.sendToTarget();
71 | }
72 | }
73 | }
74 | }
75 |
76 | @SuppressWarnings("deprecation")
77 | @SuppressLint("SetJavaScriptEnabled")
78 | public static void setupWebview(GoNativeWebviewInterface webview, Context context) {
79 | if (!(webview instanceof LeanWebView)) {
80 | GNLog.getInstance().logError(TAG, "Expected webview to be of class LeanWebView and not " + webview.getClass().getName());
81 | return;
82 | }
83 |
84 | AppConfig appConfig = AppConfig.getInstance(context);
85 |
86 | LeanWebView wv = (LeanWebView)webview;
87 | WebSettings webSettings = wv.getSettings();
88 |
89 | if (AppConfig.getInstance(context).allowZoom) {
90 | webSettings.setBuiltInZoomControls(true);
91 | }
92 | else {
93 | webSettings.setBuiltInZoomControls(false);
94 | }
95 |
96 | webSettings.setDisplayZoomControls(false);
97 | webSettings.setLoadWithOverviewMode(true);
98 | webSettings.setUseWideViewPort(true);
99 |
100 | webSettings.setJavaScriptEnabled(true);
101 | webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
102 |
103 | // font size bug fix, see https://stackoverflow.com/questions/41179357/android-webview-rem-units-scale-way-to-large-for-boxes
104 | webSettings.setMinimumFontSize(1);
105 | webSettings.setMinimumLogicalFontSize(1);
106 |
107 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
108 | webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
109 | CookieManager.getInstance().setAcceptThirdPartyCookies(wv, true);
110 | }
111 |
112 | webSettings.setDomStorageEnabled(true);
113 | webSettings.setCacheMode(appConfig.cacheMode.webSettingsCacheMode());
114 |
115 | webSettings.setDatabaseEnabled(true);
116 |
117 | webSettings.setSaveFormData(false);
118 | webSettings.setSavePassword(false);
119 | webSettings.setUserAgentString(appConfig.userAgent);
120 | webSettings.setSupportMultipleWindows(appConfig.enableWindowOpen);
121 | webSettings.setGeolocationEnabled(appConfig.usesGeolocation);
122 | webSettings.setMediaPlaybackRequiresUserGesture(false);
123 |
124 | if (appConfig.webviewTextZoom > 0) {
125 | webSettings.setTextZoom(appConfig.webviewTextZoom);
126 | }
127 | }
128 |
129 | public static void setupWebviewGlobals(Context context) {
130 | // WebView debugging
131 | if(!AppConfig.getInstance(context).geckoViewEnabled) {
132 | Map installation = Installation.getInfo(context);
133 | String dist = (String)installation.get("distribution");
134 | if (dist != null && (dist.equals("debug") || dist.equals("adhoc"))) {
135 | WebView.setWebContentsDebuggingEnabled(true);
136 | }
137 | }
138 | }
139 |
140 | public static void removeCallbacks(LeanWebView webview) {
141 | webview.setWebViewClient(null);
142 | webview.setWebChromeClient(null);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | import groovy.json.JsonSlurper
2 |
3 | apply plugin: 'com.android.application'
4 | apply plugin: 'kotlin-android'
5 | //[enabled by builder] apply plugin: 'com.google.gms.google-services'
6 | //[enabled by builder] apply plugin: 'com.google.firebase.crashlytics'
7 |
8 | ext {
9 | fbAppId = ""
10 | fbClientToken = ""
11 | onesignalAppId = ""
12 | adMobAppId = ""
13 | googleServiceInvalid = "false"
14 | auth0Domain = ""
15 | auth0Scheme = ""
16 | }
17 |
18 | task parseAppConfig {
19 | def jsonFile = file('src/main/assets/appConfig.json')
20 | def parsedJson = new JsonSlurper().parseText(jsonFile.text)
21 | if (parsedJson.services.facebook) {
22 | if (parsedJson.services.facebook.appId) {
23 | fbAppId = parsedJson.services.facebook.appId
24 | }
25 | if (parsedJson.services.facebook.clientToken) {
26 | fbClientToken = parsedJson.services.facebook.clientToken
27 | }
28 | }
29 | if (parsedJson.services.socialLogin && parsedJson.services.socialLogin.facebookLogin) {
30 | if (parsedJson.services.socialLogin.facebookLogin.appId) {
31 | fbAppId = parsedJson.services.socialLogin.facebookLogin.appId
32 | }
33 | if (parsedJson.services.socialLogin.facebookLogin.clientToken) {
34 | fbClientToken = parsedJson.services.socialLogin.facebookLogin.clientToken
35 | }
36 | }
37 | if (parsedJson.services.oneSignal && parsedJson.services.oneSignal.applicationId) {
38 | onesignalAppId = parsedJson.services.oneSignal.applicationId
39 | }
40 | if (parsedJson.services.admob && parsedJson.services.admob.admobAndroid && parsedJson.services.admob.admobAndroid.applicationId) {
41 | adMobAppId = parsedJson.services.admob.admobAndroid.applicationId
42 | }
43 | if (parsedJson.services.braze) {
44 | if (parsedJson.services.braze.androidApiKey) {
45 | gradle.ext.set("braze_api_key", parsedJson.services.braze.androidApiKey)
46 | }
47 | if (parsedJson.services.braze.androidEndpointKey) {
48 | gradle.ext.set("braze_endpoint_key", parsedJson.services.braze.androidEndpointKey)
49 | }
50 | }
51 | if (parsedJson.services.auth0) {
52 | if (parsedJson.services.auth0.domain) {
53 | auth0Domain = parsedJson.services.auth0.domain
54 | }
55 | if (parsedJson.services.auth0.scheme) {
56 | auth0Scheme = parsedJson.services.auth0.scheme
57 | }
58 | }
59 | }
60 |
61 | task checkGoogleService {
62 | plugins.withId("com.google.gms.google-services") {
63 | def googleServiceJsonFile = file('google-services.json')
64 | if (project.file(googleServiceJsonFile).exists()) {
65 | if (googleServiceJsonFile.text.isEmpty()) {
66 | googleServiceInvalid = "true"
67 | }
68 | } else {
69 | googleServiceInvalid = "true"
70 | }
71 | }
72 | }
73 |
74 | build.dependsOn parseAppConfig
75 | build.dependsOn checkGoogleService
76 |
77 | android {
78 | defaultConfig {
79 | compileSdk 33
80 | minSdkVersion 21
81 | targetSdkVersion 33
82 | applicationId "io.gonative.android"
83 | versionCode 1
84 | multiDexEnabled true
85 | vectorDrawables.useSupportLibrary = true
86 |
87 | manifestPlaceholders = [manifestApplicationId: "${applicationId}",
88 | onesignal_app_id: onesignalAppId,
89 | onesignal_google_project_number: "",
90 | admob_app_id: adMobAppId,
91 | facebook_app_id: fbAppId,
92 | facebook_client_token: fbClientToken,
93 | auth0Domain: auth0Domain, auth0Scheme: auth0Scheme ]
94 | }
95 |
96 | compileOptions {
97 | sourceCompatibility JavaVersion.VERSION_1_8
98 | targetCompatibility JavaVersion.VERSION_1_8
99 | }
100 |
101 | signingConfigs {
102 | release {
103 | storeFile file("../../release.keystore")
104 | storePassword "password"
105 | keyAlias "release"
106 | keyPassword "password"
107 | }
108 | upload {
109 | storeFile file("../../upload.keystore")
110 | storePassword "password"
111 | keyAlias "upload"
112 | keyPassword "password"
113 | }
114 | }
115 |
116 | buildTypes {
117 | debug {
118 | applicationIdSuffix ".debug"
119 | }
120 | release {
121 | minifyEnabled true
122 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
123 | zipAlignEnabled true
124 | debuggable project.getProperties().get("enableLogsInRelease").toBoolean()
125 | signingConfig signingConfigs.release
126 | }
127 | upload {
128 | minifyEnabled true
129 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
130 | zipAlignEnabled true
131 | matchingFallbacks = ['release']
132 | debuggable project.getProperties().get("enableLogsInRelease").toBoolean()
133 | signingConfig signingConfigs.upload
134 | }
135 | buildTypes.each {
136 | it.buildConfigField 'boolean', 'GOOGLE_SERVICE_INVALID', googleServiceInvalid
137 | }
138 | }
139 |
140 | flavorDimensions "webview"
141 |
142 | productFlavors {
143 | normal {
144 | dimension "webview"
145 | }
146 | }
147 | namespace 'io.gonative.android'
148 | testNamespace '${applicationId}.test'
149 | }
150 |
151 | dependencies {
152 | /**** dependencies used by all apps ****/
153 | implementation "androidx.core:core-ktx:1.10.1"
154 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
155 | implementation 'com.aurelhubert:ahbottomnavigation:2.3.4'
156 | implementation 'com.squareup:seismic:1.0.2'
157 | implementation 'androidx.webkit:webkit:1.7.0'
158 | implementation 'androidx.core:core-splashscreen:1.0.1'
159 | implementation "com.github.gonativeio:gonative-icons:$iconsVersion"
160 | implementation "com.github.gonativeio:gonative-android-core:$coreVersion"
161 | /**** end all apps ****/
162 |
163 | /**** add-on module dependencies ****/
164 | /**** end modules ****/
165 |
166 | /**** Google Android and Play Services dependencies ****/
167 | implementation 'androidx.multidex:multidex:2.0.1'
168 | implementation 'androidx.cardview:cardview:1.0.0'
169 | implementation 'androidx.browser:browser:1.5.0'
170 | implementation 'androidx.appcompat:appcompat:1.6.1'
171 | implementation 'com.google.android.material:material:1.9.0'
172 | implementation "androidx.drawerlayout:drawerlayout:1.2.0"
173 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
174 | /**** end google ****/
175 |
176 | /**** local dependencies ****/
177 | implementation fileTree(dir: 'libs', include: '*.jar')
178 | implementation fileTree(dir: 'libs', include: '*.aar')
179 | /**** end local ****/
180 | }
181 |
182 | apply from: file("../plugins.gradle"); applyNativeModulesAppBuildGradle(project)
--------------------------------------------------------------------------------