From 7aad536fbe93a159ba57f660891e1039b2b094e6 Mon Sep 17 00:00:00 2001 From: Andrei Shkrob Date: Thu, 16 Jan 2025 20:17:47 +0100 Subject: [PATCH] [android][sdk] Create OrganicMaps sdk class Signed-off-by: Andrei Shkrob --- android/app/src/main/cpp/CMakeLists.txt | 2 +- .../cpp/app/organicmaps/MwmApplication.cpp | 60 ------ .../cpp/app/organicmaps/sdk/OrganicMaps.cpp | 56 ++++++ .../java/app/organicmaps/MwmApplication.java | 154 ++------------- .../java/app/organicmaps/SplashActivity.java | 3 +- .../organicmaps/background/OsmUploadWork.java | 2 +- .../base/BaseMwmFragmentActivity.java | 2 +- .../app/organicmaps/car/CarAppSession.java | 2 +- .../location/TrackRecordingService.java | 2 +- .../routing/NavigationService.java | 2 +- .../java/app/organicmaps/sdk/OrganicMaps.java | 178 ++++++++++++++++++ .../settings/StoragePathManager.java | 5 +- .../app/organicmaps/util/StorageUtils.java | 17 +- .../main/java/app/organicmaps/util/Utils.java | 2 +- 14 files changed, 272 insertions(+), 215 deletions(-) delete mode 100644 android/app/src/main/cpp/app/organicmaps/MwmApplication.cpp create mode 100644 android/app/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp create mode 100644 android/app/src/main/java/app/organicmaps/sdk/OrganicMaps.java diff --git a/android/app/src/main/cpp/CMakeLists.txt b/android/app/src/main/cpp/CMakeLists.txt index 25a839155c..ed6d821ff8 100644 --- a/android/app/src/main/cpp/CMakeLists.txt +++ b/android/app/src/main/cpp/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRC app/organicmaps/sdk/search/DisplayedCategories.cpp app/organicmaps/sdk/search/SearchEngine.cpp app/organicmaps/sdk/search/SearchRecents.cpp + app/organicmaps/sdk/OrganicMaps.cpp app/organicmaps/core/jni_helper.cpp app/organicmaps/core/jni_java_methods.cpp app/organicmaps/core/logging.cpp @@ -38,7 +39,6 @@ set(SRC app/organicmaps/LocationState.cpp app/organicmaps/Map.cpp app/organicmaps/MapManager.cpp - app/organicmaps/MwmApplication.cpp app/organicmaps/routing/RoutingOptions.cpp app/organicmaps/settings/UnitLocale.cpp app/organicmaps/settings/MapLanguageCode.cpp diff --git a/android/app/src/main/cpp/app/organicmaps/MwmApplication.cpp b/android/app/src/main/cpp/app/organicmaps/MwmApplication.cpp deleted file mode 100644 index 88f38cfb0c..0000000000 --- a/android/app/src/main/cpp/app/organicmaps/MwmApplication.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "app/organicmaps/Framework.hpp" - -#include "app/organicmaps/platform/AndroidPlatform.hpp" - -#include "app/organicmaps/core/jni_helper.hpp" - -extern "C" -{ - // static void nativeSetSettingsDir(String settingsPath); - JNIEXPORT void JNICALL - Java_app_organicmaps_MwmApplication_nativeSetSettingsDir(JNIEnv * env, jclass clazz, jstring settingsPath) - { - android::Platform::Instance().SetSettingsDir(jni::ToNativeString(env, settingsPath)); - } - - // static void nativeInitPlatform(Context context, String apkPath, String storagePath, String privatePath, String tmpPath, - // String flavorName, String buildType, boolean isTablet); - JNIEXPORT void JNICALL - Java_app_organicmaps_MwmApplication_nativeInitPlatform(JNIEnv * env, jclass clazz, jobject context, - jstring apkPath, jstring writablePath, - jstring privatePath, jstring tmpPath, - jstring flavorName, jstring buildType, - jboolean isTablet) - { - android::Platform::Instance().Initialize(env, context, apkPath, writablePath, privatePath, tmpPath, - flavorName, buildType, isTablet); - } - - // static void nativeInitFramework(@NonNull Runnable onComplete); - JNIEXPORT void JNICALL - Java_app_organicmaps_MwmApplication_nativeInitFramework(JNIEnv * env, jclass clazz, jobject onComplete) - { - if (!g_framework) - { - g_framework = std::make_unique([onComplete = jni::make_global_ref(onComplete)]() - { - JNIEnv * env = jni::GetEnv(); - jmethodID const methodId = jni::GetMethodID(env, *onComplete, "run", "()V"); - env->CallVoidMethod(*onComplete, methodId); - }); - } - } - - // static void nativeAddLocalization(String name, String value); - JNIEXPORT void JNICALL - Java_app_organicmaps_MwmApplication_nativeAddLocalization(JNIEnv * env, jclass clazz, jstring name, jstring value) - { - g_framework->AddString(jni::ToNativeString(env, name), - jni::ToNativeString(env, value)); - } - - JNIEXPORT void JNICALL - Java_app_organicmaps_MwmApplication_nativeOnTransit(JNIEnv *, jclass, jboolean foreground) - { - if (static_cast(foreground)) - g_framework->NativeFramework()->EnterForeground(); - else - g_framework->NativeFramework()->EnterBackground(); - } -} diff --git a/android/app/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp b/android/app/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp new file mode 100644 index 0000000000..8e676c7337 --- /dev/null +++ b/android/app/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp @@ -0,0 +1,56 @@ +#include "app/organicmaps/Framework.hpp" + +#include "app/organicmaps/platform/AndroidPlatform.hpp" + +#include "app/organicmaps/core/jni_helper.hpp" + +extern "C" +{ +// static void nativeSetSettingsDir(String settingsPath); +JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeSetSettingsDir(JNIEnv * env, jclass clazz, + jstring settingsPath) +{ + android::Platform::Instance().SetSettingsDir(jni::ToNativeString(env, settingsPath)); +} + +// static void nativeInitPlatform(Context context, String apkPath, String storagePath, String privatePath, String +// tmpPath, String flavorName, String buildType, boolean isTablet); +JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeInitPlatform( + JNIEnv * env, jclass clazz, jobject context, jstring apkPath, jstring writablePath, jstring privatePath, + jstring tmpPath, jstring flavorName, jstring buildType, jboolean isTablet) +{ + android::Platform::Instance().Initialize(env, context, apkPath, writablePath, privatePath, tmpPath, flavorName, + buildType, isTablet); +} + +// static void nativeInitFramework(@NonNull Runnable onComplete); +JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeInitFramework(JNIEnv * env, jclass clazz, + jobject onComplete) +{ + if (!g_framework) + { + g_framework = std::make_unique( + [onComplete = jni::make_global_ref(onComplete)]() + { + JNIEnv * env = jni::GetEnv(); + jmethodID const methodId = jni::GetMethodID(env, *onComplete, "run", "()V"); + env->CallVoidMethod(*onComplete, methodId); + }); + } +} + +// static void nativeAddLocalization(String name, String value); +JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeAddLocalization(JNIEnv * env, jclass clazz, + jstring name, jstring value) +{ + g_framework->AddString(jni::ToNativeString(env, name), jni::ToNativeString(env, value)); +} + +JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeOnTransit(JNIEnv *, jclass, jboolean foreground) +{ + if (static_cast(foreground)) + g_framework->NativeFramework()->EnterForeground(); + else + g_framework->NativeFramework()->EnterBackground(); +} +} diff --git a/android/app/src/main/java/app/organicmaps/MwmApplication.java b/android/app/src/main/java/app/organicmaps/MwmApplication.java index fc06fdb9fe..a3623632c4 100644 --- a/android/app/src/main/java/app/organicmaps/MwmApplication.java +++ b/android/app/src/main/java/app/organicmaps/MwmApplication.java @@ -20,10 +20,9 @@ import java.io.IOException; import java.lang.ref.WeakReference; import app.organicmaps.background.OsmUploadWork; -import app.organicmaps.bookmarks.data.BookmarkManager; -import app.organicmaps.display.DisplayManager; import app.organicmaps.downloader.Android7RootCertificateWorkaround; import app.organicmaps.downloader.DownloaderNotifier; +import app.organicmaps.display.DisplayManager; import app.organicmaps.location.LocationHelper; import app.organicmaps.location.LocationState; import app.organicmaps.location.SensorHelper; @@ -31,18 +30,11 @@ import app.organicmaps.location.TrackRecorder; import app.organicmaps.location.TrackRecordingService; import app.organicmaps.maplayer.isolines.IsolinesManager; import app.organicmaps.maplayer.subway.SubwayManager; -import app.organicmaps.maplayer.traffic.TrafficManager; import app.organicmaps.routing.NavigationService; import app.organicmaps.routing.RoutingController; -import app.organicmaps.sdk.search.SearchEngine; -import app.organicmaps.settings.StoragePathManager; -import app.organicmaps.sound.TtsPlayer; +import app.organicmaps.sdk.OrganicMaps; import app.organicmaps.util.Config; import app.organicmaps.util.ConnectionState; -import app.organicmaps.util.SharedPropertiesUtils; -import app.organicmaps.util.StorageUtils; -import app.organicmaps.util.ThemeSwitcher; -import app.organicmaps.util.UiUtils; import app.organicmaps.util.Utils; import app.organicmaps.util.log.Logger; import app.organicmaps.util.log.LogsManager; @@ -52,6 +44,10 @@ public class MwmApplication extends Application implements Application.ActivityL @NonNull private static final String TAG = MwmApplication.class.getSimpleName(); + @SuppressWarnings("NotNullFieldNotInitialized") + @NonNull + private OrganicMaps mOrganicMaps; + @SuppressWarnings("NotNullFieldNotInitialized") @NonNull private SubwayManager mSubwayManager; @@ -72,9 +68,6 @@ public class MwmApplication extends Application implements Application.ActivityL @NonNull private DisplayManager mDisplayManager; - private volatile boolean mFrameworkInitialized; - private volatile boolean mPlatformInitialized; - @Nullable private WeakReference mTopActivity; @@ -115,6 +108,12 @@ public class MwmApplication extends Application implements Application.ActivityL return mDisplayManager; } + @NonNull + public OrganicMaps getOrganicMaps() + { + return mOrganicMaps; + } + @NonNull public static MwmApplication from(@NonNull Context context) { @@ -138,20 +137,12 @@ public class MwmApplication extends Application implements Application.ActivityL sInstance = this; + mOrganicMaps = new OrganicMaps(getApplicationContext()); + LogsManager.INSTANCE.initFileLogging(this); Android7RootCertificateWorkaround.initializeIfNeeded(this); - // Set configuration directory as early as possible. - // Other methods may explicitly use Config, which requires settingsDir to be set. - final String settingsPath = StorageUtils.getSettingsPath(this); - if (!StorageUtils.createDirectory(settingsPath)) - throw new AssertionError("Can't create settingsDir " + settingsPath); - Logger.d(TAG, "Settings path = " + settingsPath); - nativeSetSettingsDir(settingsPath); - - Config.init(this); - ConnectionState.INSTANCE.initialize(this); DownloaderNotifier.createNotificationChannel(this); @@ -166,117 +157,16 @@ public class MwmApplication extends Application implements Application.ActivityL mDisplayManager = new DisplayManager(); } - /** - * Initialize native core of application: platform and framework. - * - * @throws IOException - if failed to create directories. Caller must handle - * the exception and do nothing with native code if initialization is failed. - */ - public boolean init(@NonNull Runnable onComplete) throws IOException + public boolean initOrganicMaps(@NonNull Runnable onComplete) throws IOException { - initNativePlatform(); - return initNativeFramework(onComplete); + return mOrganicMaps.init(() -> { + ProcessLifecycleOwner.get().getLifecycle().addObserver(mProcessLifecycleObserver); + onComplete.run(); + }); } - private void initNativePlatform() throws IOException + private final LifecycleObserver mProcessLifecycleObserver = new DefaultLifecycleObserver() { - if (mPlatformInitialized) - return; - - final String apkPath = StorageUtils.getApkPath(this); - Logger.d(TAG, "Apk path = " + apkPath); - // Note: StoragePathManager uses Config, which requires SettingsDir to be set. - final String writablePath = StoragePathManager.findMapsStorage(this); - Logger.d(TAG, "Writable path = " + writablePath); - final String privatePath = StorageUtils.getPrivatePath(this); - Logger.d(TAG, "Private path = " + privatePath); - final String tempPath = StorageUtils.getTempPath(this); - Logger.d(TAG, "Temp path = " + tempPath); - - // If platform directories are not created it means that native part of app will not be able - // to work at all. So, we just ignore native part initialization in this case, e.g. when the - // external storage is damaged or not available (read-only). - createPlatformDirectories(writablePath, privatePath, tempPath); - - nativeInitPlatform(getApplicationContext(), - apkPath, - writablePath, - privatePath, - tempPath, - app.organicmaps.BuildConfig.FLAVOR, - app.organicmaps.BuildConfig.BUILD_TYPE, UiUtils.isTablet(this)); - Config.setStoragePath(writablePath); - Config.setStatisticsEnabled(SharedPropertiesUtils.isStatisticsEnabled(this)); - - mPlatformInitialized = true; - Logger.i(TAG, "Platform initialized"); - } - - private void createPlatformDirectories(@NonNull String writablePath, - @NonNull String privatePath, - @NonNull String tempPath) throws IOException - { - SharedPropertiesUtils.emulateBadExternalStorage(this); - - StorageUtils.requireDirectory(writablePath); - StorageUtils.requireDirectory(privatePath); - StorageUtils.requireDirectory(tempPath); - } - - private boolean initNativeFramework(@NonNull Runnable onComplete) - { - if (mFrameworkInitialized) - return false; - - nativeInitFramework(onComplete); - - initNativeStrings(); - ThemeSwitcher.INSTANCE.initialize(this); - SearchEngine.INSTANCE.initialize(); - BookmarkManager.loadBookmarks(); - TtsPlayer.INSTANCE.initialize(this); - ThemeSwitcher.INSTANCE.restart(false); - RoutingController.get().initialize(this); - TrafficManager.INSTANCE.initialize(); - SubwayManager.from(this).initialize(); - IsolinesManager.from(this).initialize(); - ProcessLifecycleOwner.get().getLifecycle().addObserver(mProcessLifecycleObserver); - - Logger.i(TAG, "Framework initialized"); - mFrameworkInitialized = true; - return true; - } - - private void initNativeStrings() - { - nativeAddLocalization("core_entrance", getString(R.string.core_entrance)); - nativeAddLocalization("core_exit", getString(R.string.core_exit)); - nativeAddLocalization("core_my_places", getString(R.string.core_my_places)); - nativeAddLocalization("core_my_position", getString(R.string.core_my_position)); - nativeAddLocalization("core_placepage_unknown_place", getString(R.string.core_placepage_unknown_place)); - nativeAddLocalization("postal_code", getString(R.string.postal_code)); - nativeAddLocalization("wifi", getString(R.string.category_wifi)); - } - - public boolean arePlatformAndCoreInitialized() - { - return mFrameworkInitialized && mPlatformInitialized; - } - - static - { - System.loadLibrary("organicmaps"); - } - - private static native void nativeSetSettingsDir(String settingsPath); - private static native void nativeInitPlatform(Context context, String apkPath, String writablePath, - String privatePath, String tmpPath, String flavorName, - String buildType, boolean isTablet); - private static native void nativeInitFramework(@NonNull Runnable onComplete); - private static native void nativeAddLocalization(String name, String value); - private static native void nativeOnTransit(boolean foreground); - - private final LifecycleObserver mProcessLifecycleObserver = new DefaultLifecycleObserver() { @Override public void onStart(@NonNull LifecycleOwner owner) { @@ -334,8 +224,6 @@ public class MwmApplication extends Application implements Application.ActivityL { Logger.d(TAG); - nativeOnTransit(true); - mLocationHelper.resumeLocationInForeground(); } @@ -343,8 +231,6 @@ public class MwmApplication extends Application implements Application.ActivityL { Logger.d(TAG); - nativeOnTransit(false); - OsmUploadWork.startActionUploadOsmChanges(this); if (!mDisplayManager.isDeviceDisplayUsed()) diff --git a/android/app/src/main/java/app/organicmaps/SplashActivity.java b/android/app/src/main/java/app/organicmaps/SplashActivity.java index 37c7cd976b..2089887c83 100644 --- a/android/app/src/main/java/app/organicmaps/SplashActivity.java +++ b/android/app/src/main/java/app/organicmaps/SplashActivity.java @@ -2,7 +2,6 @@ package app.organicmaps; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static app.organicmaps.api.Const.EXTRA_PICK_POINT; import android.content.ComponentName; import android.content.Context; @@ -146,7 +145,7 @@ public class SplashActivity extends AppCompatActivity boolean asyncContinue = false; try { - asyncContinue = app.init(this::processNavigation); + asyncContinue = app.initOrganicMaps(this::processNavigation); } catch (IOException error) { showFatalErrorDialog(R.string.dialog_error_storage_title, R.string.dialog_error_storage_message, error); diff --git a/android/app/src/main/java/app/organicmaps/background/OsmUploadWork.java b/android/app/src/main/java/app/organicmaps/background/OsmUploadWork.java index 79744a356f..52bc6ad2d5 100644 --- a/android/app/src/main/java/app/organicmaps/background/OsmUploadWork.java +++ b/android/app/src/main/java/app/organicmaps/background/OsmUploadWork.java @@ -47,7 +47,7 @@ public class OsmUploadWork extends Worker public Result doWork() { final MwmApplication app = MwmApplication.from(mContext); - if (!app.arePlatformAndCoreInitialized()) + if (!app.getOrganicMaps().arePlatformAndCoreInitialized()) { Logger.w(TAG, "Application is not initialized, ignoring " + mWorkerParameters); return Result.failure(); diff --git a/android/app/src/main/java/app/organicmaps/base/BaseMwmFragmentActivity.java b/android/app/src/main/java/app/organicmaps/base/BaseMwmFragmentActivity.java index 16cc5397c1..d6dd2184c2 100644 --- a/android/app/src/main/java/app/organicmaps/base/BaseMwmFragmentActivity.java +++ b/android/app/src/main/java/app/organicmaps/base/BaseMwmFragmentActivity.java @@ -69,7 +69,7 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity setTheme(getThemeResourceId(mThemeName)); EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT)); RtlUtils.manageRtl(this); - if (!MwmApplication.from(this).arePlatformAndCoreInitialized()) + if (!MwmApplication.from(this).getOrganicMaps().arePlatformAndCoreInitialized()) { final Intent intent = Objects.requireNonNull(getIntent()); intent.setComponent(new ComponentName(this, SplashActivity.class)); diff --git a/android/app/src/main/java/app/organicmaps/car/CarAppSession.java b/android/app/src/main/java/app/organicmaps/car/CarAppSession.java index 781a61b383..693c6a261d 100644 --- a/android/app/src/main/java/app/organicmaps/car/CarAppSession.java +++ b/android/app/src/main/java/app/organicmaps/car/CarAppSession.java @@ -163,7 +163,7 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse mInitFailed = false; try { - MwmApplication.from(getCarContext()).init(() -> { + MwmApplication.from(getCarContext()).initOrganicMaps(() -> { Config.setFirstStartDialogSeen(getCarContext()); if (DownloaderHelpers.isWorldMapsDownloadNeeded()) mScreenManager.push(new DownloadMapsScreenBuilder(getCarContext()).setDownloaderType(DownloadMapsScreenBuilder.DownloaderType.FirstLaunch).build()); diff --git a/android/app/src/main/java/app/organicmaps/location/TrackRecordingService.java b/android/app/src/main/java/app/organicmaps/location/TrackRecordingService.java index a217f355d8..eced8098d0 100644 --- a/android/app/src/main/java/app/organicmaps/location/TrackRecordingService.java +++ b/android/app/src/main/java/app/organicmaps/location/TrackRecordingService.java @@ -135,7 +135,7 @@ public class TrackRecordingService extends Service implements LocationListener @Override public int onStartCommand(@NonNull Intent intent, int flags, int startId) { - if (!MwmApplication.from(this).arePlatformAndCoreInitialized()) + if (!MwmApplication.from(this).getOrganicMaps().arePlatformAndCoreInitialized()) { Logger.w(TAG, "Application is not initialized"); stopSelf(); diff --git a/android/app/src/main/java/app/organicmaps/routing/NavigationService.java b/android/app/src/main/java/app/organicmaps/routing/NavigationService.java index 8e492dbbde..d2327c32e4 100644 --- a/android/app/src/main/java/app/organicmaps/routing/NavigationService.java +++ b/android/app/src/main/java/app/organicmaps/routing/NavigationService.java @@ -201,7 +201,7 @@ public class NavigationService extends Service implements LocationListener return START_NOT_STICKY; } - if (!MwmApplication.from(this).arePlatformAndCoreInitialized()) + if (!MwmApplication.from(this).getOrganicMaps().arePlatformAndCoreInitialized()) { // The system restarts the service if the app's process has crashed or been stopped. It would be nice to // automatically restore the last route and resume navigation. Unfortunately, the current implementation of diff --git a/android/app/src/main/java/app/organicmaps/sdk/OrganicMaps.java b/android/app/src/main/java/app/organicmaps/sdk/OrganicMaps.java new file mode 100644 index 0000000000..a4fdf61b2a --- /dev/null +++ b/android/app/src/main/java/app/organicmaps/sdk/OrganicMaps.java @@ -0,0 +1,178 @@ +package app.organicmaps.sdk; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; + +import app.organicmaps.R; +import app.organicmaps.bookmarks.data.BookmarkManager; +import app.organicmaps.maplayer.isolines.IsolinesManager; +import app.organicmaps.maplayer.subway.SubwayManager; +import app.organicmaps.maplayer.traffic.TrafficManager; +import app.organicmaps.routing.RoutingController; +import app.organicmaps.sdk.search.SearchEngine; +import app.organicmaps.settings.StoragePathManager; +import app.organicmaps.sound.TtsPlayer; +import app.organicmaps.util.Config; +import app.organicmaps.util.SharedPropertiesUtils; +import app.organicmaps.util.StorageUtils; +import app.organicmaps.util.ThemeSwitcher; +import app.organicmaps.util.UiUtils; +import app.organicmaps.util.log.Logger; + +import java.io.IOException; + +public final class OrganicMaps implements DefaultLifecycleObserver +{ + private static final String TAG = OrganicMaps.class.getSimpleName(); + + @NonNull + private final Context mContext; + + private volatile boolean mFrameworkInitialized; + private volatile boolean mPlatformInitialized; + + public OrganicMaps(@NonNull Context context) + { + mContext = context.getApplicationContext(); + + // Set configuration directory as early as possible. + // Other methods may explicitly use Config, which requires settingsDir to be set. + final String settingsPath = StorageUtils.getSettingsPath(mContext); + if (!StorageUtils.createDirectory(settingsPath)) + throw new AssertionError("Can't create settingsDir " + settingsPath); + Logger.d(TAG, "Settings path = " + settingsPath); + nativeSetSettingsDir(settingsPath); + + Config.init(mContext); + } + + /** + * Initialize native core of application: platform and framework. + * + * @throws IOException - if failed to create directories. Caller must handle + * the exception and do nothing with native code if initialization is failed. + */ + public boolean init(@NonNull Runnable onComplete) throws IOException + { + initNativePlatform(); + return initNativeFramework(onComplete); + } + + public boolean arePlatformAndCoreInitialized() + { + return mFrameworkInitialized && mPlatformInitialized; + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) + { + nativeOnTransit(true); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) + { + nativeOnTransit(false); + } + + private void initNativePlatform() throws IOException + { + if (mPlatformInitialized) + return; + + final String apkPath = StorageUtils.getApkPath(mContext); + Logger.d(TAG, "Apk path = " + apkPath); + // Note: StoragePathManager uses Config, which requires SettingsDir to be set. + final String writablePath = StoragePathManager.findMapsStorage(mContext); + Logger.d(TAG, "Writable path = " + writablePath); + final String privatePath = StorageUtils.getPrivatePath(mContext); + Logger.d(TAG, "Private path = " + privatePath); + final String tempPath = StorageUtils.getTempPath(mContext); + Logger.d(TAG, "Temp path = " + tempPath); + + // If platform directories are not created it means that native part of app will not be able + // to work at all. So, we just ignore native part initialization in this case, e.g. when the + // external storage is damaged or not available (read-only). + createPlatformDirectories(writablePath, privatePath, tempPath); + + nativeInitPlatform(mContext, + apkPath, + writablePath, + privatePath, + tempPath, + app.organicmaps.BuildConfig.FLAVOR, + app.organicmaps.BuildConfig.BUILD_TYPE, UiUtils.isTablet(mContext)); + Config.setStoragePath(writablePath); + Config.setStatisticsEnabled(SharedPropertiesUtils.isStatisticsEnabled(mContext)); + + mPlatformInitialized = true; + Logger.i(TAG, "Platform initialized"); + } + + private boolean initNativeFramework(@NonNull Runnable onComplete) + { + if (mFrameworkInitialized) + return false; + + nativeInitFramework(onComplete); + + initNativeStrings(); + ThemeSwitcher.INSTANCE.initialize(mContext); + SearchEngine.INSTANCE.initialize(); + BookmarkManager.loadBookmarks(); + TtsPlayer.INSTANCE.initialize(mContext); + ThemeSwitcher.INSTANCE.restart(false); + RoutingController.get().initialize(mContext); + TrafficManager.INSTANCE.initialize(); + SubwayManager.from(mContext).initialize(); + IsolinesManager.from(mContext).initialize(); + ProcessLifecycleOwner.get().getLifecycle().addObserver(this); + + Logger.i(TAG, "Framework initialized"); + mFrameworkInitialized = true; + return true; + } + + private void createPlatformDirectories(@NonNull String writablePath, + @NonNull String privatePath, + @NonNull String tempPath) throws IOException + { + SharedPropertiesUtils.emulateBadExternalStorage(mContext); + + StorageUtils.requireDirectory(writablePath); + StorageUtils.requireDirectory(privatePath); + StorageUtils.requireDirectory(tempPath); + } + + private void initNativeStrings() + { + nativeAddLocalization("core_entrance", mContext.getString(R.string.core_entrance)); + nativeAddLocalization("core_exit", mContext.getString(R.string.core_exit)); + nativeAddLocalization("core_my_places", mContext.getString(R.string.core_my_places)); + nativeAddLocalization("core_my_position", mContext.getString(R.string.core_my_position)); + nativeAddLocalization("core_placepage_unknown_place", mContext.getString(R.string.core_placepage_unknown_place)); + nativeAddLocalization("postal_code", mContext.getString(R.string.postal_code)); + nativeAddLocalization("wifi", mContext.getString(R.string.category_wifi)); + } + + private static native void nativeSetSettingsDir(String settingsPath); + + private static native void nativeInitPlatform(Context context, String apkPath, String writablePath, + String privatePath, String tmpPath, String flavorName, + String buildType, boolean isTablet); + + private static native void nativeInitFramework(@NonNull Runnable onComplete); + + private static native void nativeAddLocalization(String name, String value); + + private static native void nativeOnTransit(boolean foreground); + + static + { + System.loadLibrary("organicmaps"); + } +} diff --git a/android/app/src/main/java/app/organicmaps/settings/StoragePathManager.java b/android/app/src/main/java/app/organicmaps/settings/StoragePathManager.java index 749c60dbbb..3cc28453ff 100644 --- a/android/app/src/main/java/app/organicmaps/settings/StoragePathManager.java +++ b/android/app/src/main/java/app/organicmaps/settings/StoragePathManager.java @@ -1,6 +1,5 @@ package app.organicmaps.settings; -import android.app.Application; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -306,9 +305,9 @@ public class StoragePathManager * Checks the currently configured storage first, then scans other storages. * If no map files found uses getDefaultStorage(). */ - public static String findMapsStorage(@NonNull Application application) + public static String findMapsStorage(@NonNull Context context) { - StoragePathManager mgr = new StoragePathManager(application); + StoragePathManager mgr = new StoragePathManager(context); mgr.scanAvailableStorages(); String path; final List storages = mgr.mStorages; diff --git a/android/app/src/main/java/app/organicmaps/util/StorageUtils.java b/android/app/src/main/java/app/organicmaps/util/StorageUtils.java index 5e279c01fd..943eed24ca 100644 --- a/android/app/src/main/java/app/organicmaps/util/StorageUtils.java +++ b/android/app/src/main/java/app/organicmaps/util/StorageUtils.java @@ -1,6 +1,5 @@ package app.organicmaps.util; -import android.app.Application; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; @@ -91,11 +90,11 @@ public class StorageUtils } @NonNull - public static String getApkPath(@NonNull Application application) + public static String getApkPath(@NonNull Context context) { try { - return Utils.getApplicationInfo(application.getPackageManager(), BuildConfig.APPLICATION_ID, 0).sourceDir; + return Utils.getApplicationInfo(context.getPackageManager(), BuildConfig.APPLICATION_ID, 0).sourceDir; } catch (final PackageManager.NameNotFoundException e) { @@ -113,21 +112,21 @@ public class StorageUtils } @NonNull - public static String getSettingsPath(@NonNull Application application) + public static String getSettingsPath(@NonNull Context context) { - return addTrailingSeparator(application.getFilesDir().getAbsolutePath()); + return addTrailingSeparator(context.getFilesDir().getAbsolutePath()); } @NonNull - public static String getPrivatePath(@NonNull Application application) + public static String getPrivatePath(@NonNull Context context) { - return addTrailingSeparator(application.getFilesDir().getAbsolutePath()); + return addTrailingSeparator(context.getFilesDir().getAbsolutePath()); } @NonNull - public static String getTempPath(@NonNull Application application) + public static String getTempPath(@NonNull Context context) { - return addTrailingSeparator(application.getCacheDir().getAbsolutePath()); + return addTrailingSeparator(context.getCacheDir().getAbsolutePath()); } public static boolean createDirectory(@NonNull final String path) diff --git a/android/app/src/main/java/app/organicmaps/util/Utils.java b/android/app/src/main/java/app/organicmaps/util/Utils.java index a13374805e..a729635b52 100644 --- a/android/app/src/main/java/app/organicmaps/util/Utils.java +++ b/android/app/src/main/java/app/organicmaps/util/Utils.java @@ -583,7 +583,7 @@ public class Utils public static void detachFragmentIfCoreNotInitialized(@NonNull Context context, @NonNull Fragment fragment) { - if (MwmApplication.from(context).arePlatformAndCoreInitialized()) + if (MwmApplication.from(context).getOrganicMaps().arePlatformAndCoreInitialized()) return; FragmentManager manager = fragment.getFragmentManager();