From 416c95654964acdc0874aaf166b08db2eda222f0 Mon Sep 17 00:00:00 2001 From: Roman Tsisyk Date: Sun, 5 Jun 2022 11:02:52 +0300 Subject: [PATCH] [android] Public API for choosing location by using the app ```java Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("om://location")); startActivityForResult(intent, REQ_CODE_LOCATION); ``` Closes #2674 --- .../maps/DownloadResourcesLegacyActivity.java | 19 +- .../src/com/mapswithme/maps/MwmActivity.java | 55 +++++- .../com/mapswithme/maps/SplashActivity.java | 24 ++- .../mapswithme/maps/api/ParsingResult.java | 12 +- .../com/mapswithme/maps/intent/Factory.java | 15 +- .../maps/widget/placepage/PlacePageView.java | 4 +- api/example/android/.gitignore | 15 ++ api/example/android/app/.gitignore | 1 + api/example/android/app/build.gradle | 33 ++++ api/example/android/app/proguard-rules.pro | 21 ++ .../android/app/src/main/AndroidManifest.xml | 23 +++ .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 21236 bytes .../organicmaps/api/example/MainActivity.java | 59 ++++++ .../res/drawable/ic_launcher_foreground.xml | 15 ++ .../app/src/main/res/layout/activity_main.xml | 29 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1944 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3970 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1346 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2294 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2577 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5460 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 4229 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 9035 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5858 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 13088 bytes .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 6 + api/example/android/build.gradle | 9 + api/example/android/gradle.properties | 21 ++ .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + api/example/android/gradlew | 185 ++++++++++++++++++ api/example/android/gradlew.bat | 89 +++++++++ api/example/android/settings.gradle | 16 ++ map/mwm_url.cpp | 6 + map/mwm_url.hpp | 13 +- 38 files changed, 655 insertions(+), 35 deletions(-) create mode 100644 api/example/android/.gitignore create mode 100644 api/example/android/app/.gitignore create mode 100644 api/example/android/app/build.gradle create mode 100644 api/example/android/app/proguard-rules.pro create mode 100644 api/example/android/app/src/main/AndroidManifest.xml create mode 100644 api/example/android/app/src/main/ic_launcher-playstore.png create mode 100644 api/example/android/app/src/main/java/app/organicmaps/api/example/MainActivity.java create mode 100644 api/example/android/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 api/example/android/app/src/main/res/layout/activity_main.xml create mode 100644 api/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 api/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 api/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 api/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 api/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 api/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 api/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 api/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 api/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 api/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 api/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 api/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 api/example/android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 api/example/android/app/src/main/res/values/strings.xml create mode 100644 api/example/android/build.gradle create mode 100644 api/example/android/gradle.properties create mode 100644 api/example/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 api/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 api/example/android/gradlew create mode 100644 api/example/android/gradlew.bat create mode 100644 api/example/android/settings.gradle diff --git a/android/src/com/mapswithme/maps/DownloadResourcesLegacyActivity.java b/android/src/com/mapswithme/maps/DownloadResourcesLegacyActivity.java index a53a9b9d33..e111a3f7fd 100644 --- a/android/src/com/mapswithme/maps/DownloadResourcesLegacyActivity.java +++ b/android/src/com/mapswithme/maps/DownloadResourcesLegacyActivity.java @@ -46,6 +46,8 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity imp private static final String ERROR_LOADING_DIALOG_TAG = "error_loading_dialog"; private static final int ERROR_LOADING_DIALOG_REQ_CODE = 234; + private static final int REQ_CODE_API_RESULT = 10; + public static final String EXTRA_COUNTRY = "country"; // Error codes, should match the same codes in JNI @@ -394,13 +396,28 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity imp intent.putExtra(MwmActivity.EXTRA_TASK, mMapTaskToForward); intent.putExtra(MwmActivity.EXTRA_LAUNCH_BY_DEEP_LINK, true); mMapTaskToForward = null; + + // Wait for the result from MwmActivity for API callers + startActivityForResult(intent, REQ_CODE_API_RESULT); + return; } startActivity(intent); - finish(); } + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + switch (requestCode) + { + case REQ_CODE_API_RESULT: + setResult(resultCode, data); + finish(); + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + private void finishFilesDownload(int result) { if (result == ERR_NO_MORE_FILES) diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index b86ee05e23..378f7d2a7b 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -78,7 +78,6 @@ import com.mapswithme.maps.search.SearchFragment; import com.mapswithme.maps.settings.DrivingOptionsActivity; import com.mapswithme.maps.settings.RoadType; import com.mapswithme.maps.settings.SettingsActivity; -import com.mapswithme.maps.settings.StoragePathManager; import com.mapswithme.maps.settings.UnitLocale; import com.mapswithme.maps.sound.TtsPlayer; import com.mapswithme.maps.widget.menu.MainMenu; @@ -158,6 +157,13 @@ public class MwmActivity extends BaseMwmFragmentActivity @SuppressWarnings("NullableProblems") @NonNull private View mPositionChooser; + enum PositionChooserMode { + NONE, + EDITOR, + API + }; + @NonNull + private PositionChooserMode mPositionChooserMode = PositionChooserMode.NONE; private RoutingPlanInplaceController mRoutingPlanInplaceController; @@ -499,15 +505,34 @@ public class MwmActivity extends BaseMwmFragmentActivity final Toolbar toolbar = mPositionChooser.findViewById(R.id.toolbar_position_chooser); UiUtils.extendViewWithStatusBar(toolbar); UiUtils.showHomeUpButton(toolbar); - toolbar.setNavigationOnClickListener(v -> closePositionChooser()); + toolbar.setNavigationOnClickListener(v -> { + closePositionChooser(); + if (mPositionChooserMode == PositionChooserMode.API) + finish(); + }); mPositionChooser.findViewById(R.id.done).setOnClickListener( v -> { + switch (mPositionChooserMode) + { + case API: + Intent apiResult = new Intent(); + double[] center = Framework.nativeGetScreenRectCenter(); + apiResult.putExtra("lat", center[0]); + apiResult.putExtra("lon", center[1]); + setResult(Activity.RESULT_OK, apiResult); + finish(); + break; + case EDITOR: + if (Framework.nativeIsDownloadedMapAtScreenCenter()) + startActivity(new Intent(MwmActivity.this, FeatureCategoryActivity.class)); + else + DialogUtils.showAlertDialog(MwmActivity.this, R.string.message_invalid_feature_position); + break; + case NONE: + throw new IllegalStateException("Unexpected mPositionChooserMode"); + } closePositionChooser(); - if (Framework.nativeIsDownloadedMapAtScreenCenter()) - startActivity(new Intent(MwmActivity.this, FeatureCategoryActivity.class)); - else - DialogUtils.showAlertDialog(MwmActivity.this, R.string.message_invalid_feature_position); }); UiUtils.hide(mPositionChooser); } @@ -536,8 +561,19 @@ public class MwmActivity extends BaseMwmFragmentActivity mSearchController.show(); } - public void showPositionChooser(boolean isBusiness, boolean applyPosition) + public void showPositionChooserForAPI() { + showPositionChooser(PositionChooserMode.API, false, false); + } + + public void showPositionChooserForEditor(boolean isBusiness, boolean applyPosition) + { + showPositionChooser(PositionChooserMode.EDITOR, isBusiness, applyPosition); + } + + private void showPositionChooser(PositionChooserMode mode, boolean isBusiness, boolean applyPosition) + { + mPositionChooserMode = mode; closeFloatingToolbarsAndPanels(false); UiUtils.show(mPositionChooser); setFullscreen(true); @@ -549,6 +585,9 @@ public class MwmActivity extends BaseMwmFragmentActivity UiUtils.hide(mPositionChooser); Framework.nativeTurnOffChoosePositionMode(); setFullscreen(false); + if (mPositionChooserMode == PositionChooserMode.API) + finish(); + mPositionChooserMode = PositionChooserMode.NONE; } private void initMap(boolean isLaunchByDeepLink) @@ -2022,7 +2061,7 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onAddPlaceOptionSelected() { closeFloatingPanels(); - showPositionChooser(false, false); + showPositionChooserForEditor(false, false); } public void onDownloadMapsOptionSelected() diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index fc091b66dc..de21eed4cc 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -10,7 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import com.mapswithme.maps.base.BaseActivity; import com.mapswithme.maps.base.BaseActivityDelegate; @@ -24,11 +23,13 @@ import com.mapswithme.util.concurrency.UiThread; import java.io.IOException; -public class SplashActivity extends AppCompatActivity implements BaseActivity +public class SplashActivity extends Activity implements BaseActivity { private static final String EXTRA_ACTIVITY_TO_START = "extra_activity_to_start"; public static final String EXTRA_INITIAL_INTENT = "extra_initial_intent"; private static final int REQUEST_PERMISSIONS = 1; + private static final int REQ_CODE_API_RESULT = 10; + private static final long DELAY = 100; private boolean mCanceled = false; @@ -190,12 +191,31 @@ public class SplashActivity extends AppCompatActivity implements BaseActivity input.getParcelableExtra(EXTRA_INITIAL_INTENT) : input; result.putExtra(EXTRA_INITIAL_INTENT, initialIntent); + if (!initialIntent.hasCategory(Intent.CATEGORY_LAUNCHER)) + { + // Wait for the result from MwmActivity for API callers + startActivityForResult(result, REQ_CODE_API_RESULT); + return; + } } Counters.setFirstStartDialogSeen(this); startActivity(result); finish(); } + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + switch (requestCode) + { + case REQ_CODE_API_RESULT: + // Propagate the result to API callers + setResult(resultCode, data); + finish(); + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + @Override @NonNull public Activity get() diff --git a/android/src/com/mapswithme/maps/api/ParsingResult.java b/android/src/com/mapswithme/maps/api/ParsingResult.java index 6f44493be9..12b8329614 100644 --- a/android/src/com/mapswithme/maps/api/ParsingResult.java +++ b/android/src/com/mapswithme/maps/api/ParsingResult.java @@ -8,8 +8,7 @@ import java.lang.annotation.RetentionPolicy; public class ParsingResult { @Retention(RetentionPolicy.SOURCE) - @IntDef({ TYPE_INCORRECT, TYPE_MAP, TYPE_ROUTE, TYPE_SEARCH, TYPE_LEAD, TYPE_CATALOGUE, - TYPE_CATALOGUE_PATH, TYPE_SUBSCRIPTION}) + @IntDef({ TYPE_INCORRECT, TYPE_MAP, TYPE_ROUTE, TYPE_SEARCH, TYPE_LOCATION}) public @interface UrlType {} // Represents url_scheme::ParsedMapApi::UrlType from c++ part. @@ -17,10 +16,11 @@ public class ParsingResult public static final int TYPE_MAP = 1; public static final int TYPE_ROUTE = 2; public static final int TYPE_SEARCH = 3; - public static final int TYPE_LEAD = 4; - public static final int TYPE_CATALOGUE = 5; - public static final int TYPE_CATALOGUE_PATH = 6; - public static final int TYPE_SUBSCRIPTION = 7; + //public static final int TYPE_LEAD = 4; + //public static final int TYPE_CATALOGUE = 5; + //public static final int TYPE_CATALOGUE_PATH = 6; + //public static final int TYPE_SUBSCRIPTION = 7; + public static final int TYPE_LOCATION = 8; private final int mUrlType; private final boolean mSuccess; diff --git a/android/src/com/mapswithme/maps/intent/Factory.java b/android/src/com/mapswithme/maps/intent/Factory.java index 5f35a0af37..e403f19f89 100644 --- a/android/src/com/mapswithme/maps/intent/Factory.java +++ b/android/src/com/mapswithme/maps/intent/Factory.java @@ -2,11 +2,9 @@ package com.mapswithme.maps.intent; import android.content.ContentResolver; import android.content.Intent; -import android.location.Location; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -31,15 +29,10 @@ import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.maps.search.SearchActivity; import com.mapswithme.maps.search.SearchEngine; -import com.mapswithme.util.CrashlyticsUtils; import com.mapswithme.util.KeyValue; import com.mapswithme.util.StorageUtils; -import com.mapswithme.util.StringUtils; -import com.mapswithme.util.UTM; import com.mapswithme.util.Utils; import com.mapswithme.util.concurrency.ThreadPool; -import com.mapswithme.util.log.Logger; -import com.mapswithme.util.log.LoggerFactory; import java.io.File; import java.util.List; @@ -326,9 +319,6 @@ public class Factory switch (result.getUrlType()) { case ParsingResult.TYPE_INCORRECT: - case ParsingResult.TYPE_CATALOGUE: - case ParsingResult.TYPE_CATALOGUE_PATH: - case ParsingResult.TYPE_SUBSCRIPTION: return false; case ParsingResult.TYPE_MAP: @@ -358,8 +348,9 @@ public class Factory } SearchActivity.start(target, request.mQuery, request.mLocale, request.mIsSearchOnMap); return true; - case ParsingResult.TYPE_LEAD: - return true; + case ParsingResult.TYPE_LOCATION: + target.showPositionChooserForAPI(); + return true; } return false; diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index a4573f4058..0fd7742e22 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -1272,12 +1272,12 @@ public class PlacePageView extends NestedScrollViewClickFixed private void addOrganisation() { - getActivity().showPositionChooser(true, false); + getActivity().showPositionChooserForEditor(true, false); } private void addPlace() { - getActivity().showPositionChooser(false, true); + getActivity().showPositionChooserForEditor(false, true); } @Override diff --git a/api/example/android/.gitignore b/api/example/android/.gitignore new file mode 100644 index 0000000000..aa724b7707 --- /dev/null +++ b/api/example/android/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/api/example/android/app/.gitignore b/api/example/android/app/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/api/example/android/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/api/example/android/app/build.gradle b/api/example/android/app/build.gradle new file mode 100644 index 0000000000..0c20ceba96 --- /dev/null +++ b/api/example/android/app/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 31 + + defaultConfig { + applicationId "app.organicmaps.api.example" + minSdk 21 + targetSdk 31 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' +} \ No newline at end of file diff --git a/api/example/android/app/proguard-rules.pro b/api/example/android/app/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/api/example/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/api/example/android/app/src/main/AndroidManifest.xml b/api/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..41bf1aea45 --- /dev/null +++ b/api/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/example/android/app/src/main/ic_launcher-playstore.png b/api/example/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..bbee5a53e7d434e1ccfe31938264b9ed19d3348a GIT binary patch literal 21236 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 literal 0 HcmV?d00001 diff --git a/api/example/android/app/src/main/java/app/organicmaps/api/example/MainActivity.java b/api/example/android/app/src/main/java/app/organicmaps/api/example/MainActivity.java new file mode 100644 index 0000000000..a4f851e917 --- /dev/null +++ b/api/example/android/app/src/main/java/app/organicmaps/api/example/MainActivity.java @@ -0,0 +1,59 @@ +package app.organicmaps.api.example; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.widget.Toast; + +import app.organicmaps.api.example.databinding.ActivityMainBinding; + +import java.util.Locale; + +public class MainActivity extends Activity +{ + private ActivityMainBinding binding; + + private static final int REQ_CODE_LOCATION = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + binding.locationTest.setOnClickListener(view -> { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("om://location")); + startActivityForResult(intent, REQ_CODE_LOCATION); + }); + } + + protected void onActivityResult(int requestCode, + int resultCode, + Intent data) + { + switch (requestCode) + { + case REQ_CODE_LOCATION: + if (resultCode == RESULT_CANCELED) + { + Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show(); + return; + } + else if (resultCode != RESULT_OK) + { + throw new AssertionError("Unsupported resultCode: " + resultCode); + } + + double lat = data.getDoubleExtra("lat", 0.0); + double lon = data.getDoubleExtra("lon", 0.0); + String message = String.format(Locale.ENGLISH, "Result: lat=%.4f lon=%.4f", lat, lon); + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + } + } +} \ No newline at end of file diff --git a/api/example/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/api/example/android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000..d98c1df227 --- /dev/null +++ b/api/example/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/api/example/android/app/src/main/res/layout/activity_main.xml b/api/example/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..e5cace6681 --- /dev/null +++ b/api/example/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,29 @@ + + + +