From d620f6e68872d64ce19b0c3ac02751627ed4849a Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Tue, 25 Oct 2016 16:25:35 +0400 Subject: [PATCH 01/16] [android] up target api to 23 (+4 squashed commits) Squashed commits: [1773b27] [android] Review fixes [9a09696] [android] Dynamic permissions. [0bdcdd6] [android] dynamic permissions [5306cfd] [android] up target api to 23 [android] settings refactoring [strings] added new strings [strings] generated translations --- .../com/cocosw/bottomsheet/BottomSheet.java | 4 +- .../BottomSheet/src/main/res/values/attrs.xml | 2 +- android/build.gradle | 12 +- android/gradle.properties | 4 +- .../res/layout-w1020dp/activity_settings.xml | 23 + android/res/layout/activity_settings.xml | 17 + android/res/layout/toolbar_extended.xml | 22 + android/res/values-ru/strings.xml | 10 + android/res/values-w1020dp-land/dimens.xml | 4 + android/res/values-w1020dp/dimens.xml | 2 + android/res/values/dimens.xml | 4 + android/res/values/donottranslate.xml | 7 + android/res/values/strings.xml | 10 + android/res/values/themes-base.xml | 2 + android/res/xml-v21/prefs_map.xml | 61 -- android/res/xml-v21/prefs_misc.xml | 40 - android/res/xml-v21/prefs_route.xml | 32 - android/res/xml/prefs_headers.xml | 33 - android/res/xml/prefs_main.xml | 158 ++++ android/res/xml/prefs_map.xml | 53 -- android/res/xml/prefs_misc.xml | 32 - android/res/xml/prefs_route.xml | 24 - .../maps/DownloadResourcesActivity.java | 12 +- .../src/com/mapswithme/maps/MwmActivity.java | 117 +-- .../com/mapswithme/maps/MwmApplication.java | 26 +- .../maps/base/BaseActivityDelegate.java | 22 +- .../maps/base/BaseMwmDialogFragment.java | 77 ++ .../mapswithme/maps/base/BaseMwmFragment.java | 70 ++ .../maps/base/BaseMwmFragmentActivity.java | 135 +++- .../maps/base/BaseMwmListFragment.java | 66 ++ .../maps/base/BaseMwmRecyclerFragment.java | 68 +- .../maps/base/BaseMwmToolbarFragment.java | 5 +- .../maps/base/BaseToolbarActivity.java | 7 +- .../bookmarks/BookmarkCategoriesFragment.java | 45 +- .../maps/bookmarks/BookmarksListFragment.java | 62 +- .../maps/bookmarks/data/Bookmark.java | 3 +- .../maps/bookmarks/data/BookmarkManager.java | 5 - .../maps/downloader/BottomPanel.java | 11 +- .../maps/downloader/DownloaderFragment.java | 37 +- .../maps/downloader/OnmapDownloader.java | 11 +- .../maps/editor/EditorFragment.java | 11 +- .../maps/editor/EditorHostFragment.java | 11 +- .../maps/editor/FeatureCategoryFragment.java | 8 +- .../maps/editor/StreetFragment.java | 11 +- .../maps/location/AndroidNativeProvider.java | 28 +- .../location/GoogleFusedLocationProvider.java | 18 +- .../maps/location/LocationHelper.java | 11 +- .../maps/search/SearchCategoriesFragment.java | 5 +- .../maps/search/SearchFragment.java | 12 +- .../maps/search/SearchHistoryAdapter.java | 1 + .../maps/search/SearchHistoryFragment.java | 27 +- .../mapswithme/maps/search/SearchRecents.java | 5 - .../maps/settings/AboutFragment.java | 16 +- .../maps/settings/BaseSettingsFragment.java | 43 +- .../settings/BaseXmlSettingsFragment.java | 30 +- .../maps/settings/CopyrightFragment.java | 14 +- .../maps/settings/HelpFragment.java | 12 - .../maps/settings/MapPrefsFragment.java | 235 ------ .../maps/settings/MiscPrefsFragment.java | 146 ---- .../maps/settings/RoutePrefsFragment.java | 216 ----- .../maps/settings/SettingsActivity.java | 306 ++----- .../maps/settings/SettingsPrefsFragment.java | 756 ++++++++++++++++++ .../maps/settings/StoragePathFragment.java | 14 - .../placepage/EditBookmarkFragment.java | 4 +- .../placepage/EditDescriptionFragment.java | 11 +- android/src/com/mapswithme/util/Utils.java | 41 +- .../ar.lproj/Localizable.strings | 16 + .../cs.lproj/Localizable.strings | 16 + .../da.lproj/Localizable.strings | 16 + .../de.lproj/Localizable.strings | 16 + .../en-GB.lproj/Localizable.strings | 16 + .../en.lproj/Localizable.strings | 16 + .../es.lproj/Localizable.strings | 16 + .../fi.lproj/Localizable.strings | 16 + .../fr.lproj/Localizable.strings | 16 + .../hu.lproj/Localizable.strings | 16 + .../id.lproj/Localizable.strings | 16 + .../it.lproj/Localizable.strings | 16 + .../ja.lproj/Localizable.strings | 16 + .../ko.lproj/Localizable.strings | 16 + .../nb.lproj/Localizable.strings | 16 + .../nl.lproj/Localizable.strings | 16 + .../pl.lproj/Localizable.strings | 16 + .../pt.lproj/Localizable.strings | 16 + .../ro.lproj/Localizable.strings | 16 + .../ru.lproj/Localizable.strings | 16 + .../sk.lproj/Localizable.strings | 16 + .../sv.lproj/Localizable.strings | 16 + .../th.lproj/Localizable.strings | 16 + .../tr.lproj/Localizable.strings | 16 + .../uk.lproj/Localizable.strings | 16 + .../vi.lproj/Localizable.strings | 16 + .../zh-Hans.lproj/Localizable.strings | 16 + .../zh-Hant.lproj/Localizable.strings | 16 + strings.txt | 32 + 95 files changed, 2379 insertions(+), 1428 deletions(-) create mode 100644 android/res/layout-w1020dp/activity_settings.xml create mode 100644 android/res/layout/activity_settings.xml create mode 100644 android/res/layout/toolbar_extended.xml create mode 100644 android/res/values-w1020dp-land/dimens.xml delete mode 100644 android/res/xml-v21/prefs_map.xml delete mode 100644 android/res/xml-v21/prefs_misc.xml delete mode 100644 android/res/xml-v21/prefs_route.xml delete mode 100644 android/res/xml/prefs_headers.xml create mode 100644 android/res/xml/prefs_main.xml delete mode 100644 android/res/xml/prefs_map.xml delete mode 100644 android/res/xml/prefs_misc.xml delete mode 100644 android/res/xml/prefs_route.xml delete mode 100644 android/src/com/mapswithme/maps/settings/MapPrefsFragment.java delete mode 100644 android/src/com/mapswithme/maps/settings/MiscPrefsFragment.java delete mode 100644 android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java create mode 100644 android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/BottomSheet.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/BottomSheet.java index 4d6e9df115..33eb1f131a 100644 --- a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/BottomSheet.java +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/BottomSheet.java @@ -115,7 +115,7 @@ public class BottomSheet extends Dialog implements DialogInterface { super(context, theme); TypedArray a = getContext() - .obtainStyledAttributes(null, R.styleable.BottomSheet, R.attr.bottomSheetStyle, 0); + .obtainStyledAttributes(null, R.styleable.BottomSheet, R.attr.customBottomSheetStyle, 0); try { more = a.getDrawable(R.styleable.BottomSheet_bs_moreDrawable); close = a.getDrawable(R.styleable.BottomSheet_bs_closeDrawable); @@ -625,7 +625,7 @@ public class BottomSheet extends Dialog implements DialogInterface { */ public Builder(@NonNull Activity context) { this(context, R.style.BottomSheet_Dialog); - TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.bottomSheetStyle}); + TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.customBottomSheetStyle }); try { theme = ta.getResourceId(0, R.style.BottomSheet_Dialog); } finally { diff --git a/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml b/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml index efeacc3378..a526689586 100644 --- a/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml +++ b/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml @@ -2,7 +2,7 @@ - + diff --git a/android/build.gradle b/android/build.gradle index ba07aadafb..107dfb4fbf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,13 +31,15 @@ apply plugin: 'io.fabric' dependencies { // android support libs - compile ('com.android.support:support-v4:23.2.1') { + compile ('com.android.support:support-v4:23.4.0') { force = true; } - compile 'com.android.support:appcompat-v7:23.2.1' - compile 'com.android.support:recyclerview-v7:23.2.1' - compile 'com.android.support:design:23.2.1' - compile 'com.android.support:cardview-v7:23.2.1' + compile 'com.android.support:appcompat-v7:23.4.0' + compile 'com.android.support:recyclerview-v7:23.4.0' + compile 'com.android.support:design:23.4.0' + compile 'com.android.support:cardview-v7:23.4.0' + compile 'com.android.support:preference-v7:23.4.0' + compile 'com.android.support:preference-v14:23.4.0' // google play services compile 'com.google.android.gms:play-services-location:10.0.1' compile 'com.google.android.gms:play-services-analytics:10.0.1' diff --git a/android/gradle.properties b/android/gradle.properties index 390096495e..1ae416be9f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,7 +1,7 @@ propMinSdkVersion=15 # TODO use 23 target and build tools version, when ProGuard problem will be fixed # https://code.google.com/p/android/issues/detail?id=184567 -propTargetSdkVersion=22 +propTargetSdkVersion=23 propCompileSdkVersion=23 propBuildToolsVersion=25.0.2 propVersionCode=724 @@ -23,3 +23,5 @@ propObbWorlds ../data/World.mwm \ ../data/WorldCoasts_obsolete.mwm propObbWorldsOutput build/worlds.obb propObbFontsOutput build/fonts.obb + +org.gradle.jvmargs=-Xmx1536M diff --git a/android/res/layout-w1020dp/activity_settings.xml b/android/res/layout-w1020dp/activity_settings.xml new file mode 100644 index 0000000000..7806acc20d --- /dev/null +++ b/android/res/layout-w1020dp/activity_settings.xml @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/android/res/layout/activity_settings.xml b/android/res/layout/activity_settings.xml new file mode 100644 index 0000000000..4df7f04a25 --- /dev/null +++ b/android/res/layout/activity_settings.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/android/res/layout/toolbar_extended.xml b/android/res/layout/toolbar_extended.xml new file mode 100644 index 0000000000..f0ac7db8bf --- /dev/null +++ b/android/res/layout/toolbar_extended.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/res/values-ru/strings.xml b/android/res/values-ru/strings.xml index 7a6e778020..28ca8dd1d7 100644 --- a/android/res/values-ru/strings.xml +++ b/android/res/values-ru/strings.xml @@ -300,6 +300,14 @@ Длина Поделиться местоположением Поиск + + Общие настройки + + Информация + + Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). + + For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device\'s settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. Карта @@ -1095,4 +1103,6 @@ Хостел Дом отдыха Мотель + Вкл. + Выкл. diff --git a/android/res/values-w1020dp-land/dimens.xml b/android/res/values-w1020dp-land/dimens.xml new file mode 100644 index 0000000000..7fa394973d --- /dev/null +++ b/android/res/values-w1020dp-land/dimens.xml @@ -0,0 +1,4 @@ + + + 672dp + \ No newline at end of file diff --git a/android/res/values-w1020dp/dimens.xml b/android/res/values-w1020dp/dimens.xml index ee03656052..9e5c7c170d 100644 --- a/android/res/values-w1020dp/dimens.xml +++ b/android/res/values-w1020dp/dimens.xml @@ -6,4 +6,6 @@ -72dp + + 560dp diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index d4e8d30e1c..36061cf6d8 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -184,4 +184,8 @@ 128dp 20dp + + 8dp + + 72dp diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml index 4ce38ca510..a41dcd4d10 100644 --- a/android/res/values/donottranslate.xml +++ b/android/res/values/donottranslate.xml @@ -40,18 +40,25 @@ Settings MapsMePrefs MapStyle + TtsScreen TtsEnabled TtsLanguage + TtsInfo AutoDownloadMap 3D 3DBuildings + TrackScreen TrackRecord + TrackRecordTime DisplayShowcase Osm profile AutoZoom LargeFontSize UseMobileData TrafficSimplifiedColors + GeneralSettings + Navigation + Information %1$s: %2$s %2$s :%1$s diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 8c80b1a2fd..00d393313b 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -302,6 +302,14 @@ Length Share My Location Search + + General settings + + Information + + Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). + + For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device\'s settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. Map @@ -1107,4 +1115,6 @@ Motel Create ad campaign View ad campaign + On + Off diff --git a/android/res/values/themes-base.xml b/android/res/values/themes-base.xml index a4f4e1448a..e805959318 100644 --- a/android/res/values/themes-base.xml +++ b/android/res/values/themes-base.xml @@ -100,6 +100,7 @@ @color/warm_gray @color/search_local_ads_customer_result + @style/PreferenceThemeOverlay.v14.Material @@ -203,5 +204,6 @@ @color/warm_gray_night @color/search_local_ads_customer_result_night + @style/PreferenceThemeOverlay.v14.Material diff --git a/android/res/xml-v21/prefs_map.xml b/android/res/xml-v21/prefs_map.xml deleted file mode 100644 index 1affa5f8a2..0000000000 --- a/android/res/xml-v21/prefs_map.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/xml-v21/prefs_misc.xml b/android/res/xml-v21/prefs_misc.xml deleted file mode 100644 index b9eabbd3ec..0000000000 --- a/android/res/xml-v21/prefs_misc.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - diff --git a/android/res/xml-v21/prefs_route.xml b/android/res/xml-v21/prefs_route.xml deleted file mode 100644 index 1a75689965..0000000000 --- a/android/res/xml-v21/prefs_route.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/xml/prefs_headers.xml b/android/res/xml/prefs_headers.xml deleted file mode 100644 index cb251afd69..0000000000 --- a/android/res/xml/prefs_headers.xml +++ /dev/null @@ -1,33 +0,0 @@ - - -
- -
- -
- -
- -
- -
- \ No newline at end of file diff --git a/android/res/xml/prefs_main.xml b/android/res/xml/prefs_main.xml new file mode 100644 index 0000000000..2c7cfbb4f3 --- /dev/null +++ b/android/res/xml/prefs_main.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/xml/prefs_map.xml b/android/res/xml/prefs_map.xml deleted file mode 100644 index ae014c6a23..0000000000 --- a/android/res/xml/prefs_map.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/xml/prefs_misc.xml b/android/res/xml/prefs_misc.xml deleted file mode 100644 index 7e50633223..0000000000 --- a/android/res/xml/prefs_misc.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/android/res/xml/prefs_route.xml b/android/res/xml/prefs_route.xml deleted file mode 100644 index e1884eae88..0000000000 --- a/android/res/xml/prefs_route.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java index 4dbbeb93dd..8a3c915536 100644 --- a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java +++ b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java @@ -7,6 +7,8 @@ import android.graphics.Color; import android.location.Location; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.text.TextUtils; import android.view.View; @@ -210,10 +212,11 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity } }; + @CallSuper @Override - protected void onCreate(Bundle savedInstanceState) + protected void safeOnCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.safeOnCreate(savedInstanceState); setContentView(R.layout.activity_download_resources); initViewsAndListeners(); @@ -246,11 +249,10 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity } } + @CallSuper @Override - protected void onResume() + protected void safeOnResume() { - super.onResume(); - if (!isFinishing()) LocationHelper.INSTANCE.addListener(mLocationListener, true); } diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index d619033919..729cbb38e8 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -10,6 +10,7 @@ import android.graphics.Rect; import android.location.Location; import android.os.Build; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; @@ -139,6 +140,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Nullable private MapFragment mMapFragment; + @Nullable private PlacePageView mPlacePage; private RoutingPlanInplaceController mRoutingPlanInplaceController; @@ -148,6 +150,7 @@ public class MwmActivity extends BaseMwmFragmentActivity private MainMenu mMainMenu; private PanelAnimator mPanelAnimator; + @Nullable private OnmapDownloader mOnmapDownloader; private FadeView mFadeView; @@ -246,7 +249,7 @@ public class MwmActivity extends BaseMwmFragmentActivity int oldLeft, int oldTop, int oldRight, int oldBottom) { mScreenFullRect = new Rect(left, top, right, bottom); - if (mPlacePageVisible && mPlacePage.GetPreview().getVisibility() != View.VISIBLE) + if (mPlacePageVisible && (mPlacePage == null || mPlacePage.GetPreview().getVisibility() != View.VISIBLE)) mPlacePageVisible = false; recalculateVisibleRect(mScreenFullRect); } @@ -259,7 +262,7 @@ public class MwmActivity extends BaseMwmFragmentActivity int orientation = MwmActivity.this.getResources().getConfiguration().orientation; Rect rect = new Rect(r.left, r.top, r.right, r.bottom); - if (mPlacePageVisible) + if (mPlacePage != null && mPlacePageVisible) { int[] loc = new int[2]; mPlacePage.GetPreview().getLocationOnScreen(loc); @@ -447,14 +450,13 @@ public class MwmActivity extends BaseMwmFragmentActivity } @SuppressLint("InlinedApi") + @CallSuper @Override - public void onCreate(@Nullable Bundle savedInstanceState) + protected void safeOnCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - + super.safeOnCreate(savedInstanceState); if (savedInstanceState != null) mLocationErrorDialogAnnoying = savedInstanceState.getBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING); - mIsFragmentContainer = getResources().getBoolean(R.bool.tabletLayout); if (!mIsFragmentContainer && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) @@ -490,8 +492,11 @@ public class MwmActivity extends BaseMwmFragmentActivity initNavigationButtons(); mPlacePage = (PlacePageView) findViewById(R.id.info_box); - mPlacePage.setOnVisibilityChangedListener(this); - mPlacePage.setOnAnimationListener(this); + if (mPlacePage != null) + { + mPlacePage.setOnVisibilityChangedListener(this); + mPlacePage.setOnAnimationListener(this); + } if (!mIsFragmentContainer) { @@ -549,6 +554,9 @@ public class MwmActivity extends BaseMwmFragmentActivity private void initPositionChooser() { mPositionChooser = findViewById(R.id.position_chooser); + if (mPositionChooser == null) + return; + final Toolbar toolbar = (Toolbar) mPositionChooser.findViewById(R.id.toolbar_position_chooser); UiUtils.extendViewWithStatusBar(toolbar); UiUtils.showHomeUpButton(toolbar); @@ -615,8 +623,11 @@ public class MwmActivity extends BaseMwmFragmentActivity } View container = findViewById(R.id.map_fragment_container); - container.setOnTouchListener(this); - mRootView = (ViewGroup) container.getParent(); + if (container != null) + { + container.setOnTouchListener(this); + mRootView = (ViewGroup) container.getParent(); + } } public void detachMap(@NonNull FragmentTransaction transaction) @@ -643,6 +654,9 @@ public class MwmActivity extends BaseMwmFragmentActivity private void initNavigationButtons() { View frame = findViewById(R.id.navigation_buttons); + if (frame == null) + return; + View zoomIn = frame.findViewById(R.id.nav_zoom_in); zoomIn.setOnClickListener(this); View zoomOut = frame.findViewById(R.id.nav_zoom_out); @@ -658,7 +672,7 @@ public class MwmActivity extends BaseMwmFragmentActivity public boolean closePlacePage() { - if (mPlacePage.isHidden()) + if (mPlacePage == null || mPlacePage.isHidden()) return false; mPlacePage.hide(); @@ -723,7 +737,7 @@ public class MwmActivity extends BaseMwmFragmentActivity { RoutingController.get().prepare(endPoint); - if (mPlacePage.isDocked() || !mPlacePage.isFloating()) + if (mPlacePage != null && (mPlacePage.isDocked() || !mPlacePage.isFloating())) closePlacePage(); } }); @@ -758,7 +772,7 @@ public class MwmActivity extends BaseMwmFragmentActivity case TOGGLE: if (!mMainMenu.isOpen()) { - if (mPlacePage.isDocked() && closePlacePage()) + if (mPlacePage == null || (mPlacePage.isDocked() && closePlacePage())) return; if (closeSidePanel()) @@ -857,7 +871,7 @@ public class MwmActivity extends BaseMwmFragmentActivity return; } - if (mPlacePage.isDocked()) + if (mPlacePage != null && mPlacePage.isDocked()) mPlacePage.setLeftAnimationTrackListener(mMainMenu.getLeftAnimationTrackListener()); } @@ -881,7 +895,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override protected void onSaveInstanceState(Bundle outState) { - if (!mPlacePage.isHidden()) + if (mPlacePage != null && !mPlacePage.isHidden()) { outState.putInt(STATE_PP, mPlacePage.getState().ordinal()); outState.putParcelable(STATE_MAP_OBJECT, mPlacePage.getMapObject()); @@ -914,12 +928,12 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) + protected void safeOnRestoreInstanceState(@NonNull Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); + super.safeOnRestoreInstanceState(savedInstanceState); final State state = State.values()[savedInstanceState.getInt(STATE_PP, 0)]; - if (state != State.HIDDEN) + if (mPlacePage != null && state != State.HIDDEN) { mPlacePageRestored = true; mPlacePage.setMapObject((MapObject) savedInstanceState.getParcelable(STATE_MAP_OBJECT), true, @@ -1005,11 +1019,9 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - protected void onResume() + protected void safeOnResume() { - super.onResume(); - - mPlacePageRestored = mPlacePage.getState() != State.HIDDEN; + mPlacePageRestored = mPlacePage != null && mPlacePage.getState() != State.HIDDEN; mSearchController.refreshToolbar(); mMainMenu.onResume(new Runnable() { @@ -1023,7 +1035,8 @@ public class MwmActivity extends BaseMwmFragmentActivity } } }); - mOnmapDownloader.onResume(); + if (mOnmapDownloader != null) + mOnmapDownloader.onResume(); if (mNavigationController != null) mNavigationController.onResume(); if (mNavAnimationController != null) @@ -1041,10 +1054,8 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - protected void onResumeFragments() + protected void safeOnResumeFragments() { - super.onResumeFragments(); - if (!RoutingController.get().isNavigating()) { mFirstStart = FirstStartFragment.showOn(this); @@ -1061,7 +1072,8 @@ public class MwmActivity extends BaseMwmFragmentActivity } RoutingController.get().restore(); - mPlacePage.restore(); + if (mPlacePage != null) + mPlacePage.restore(); } @@ -1071,8 +1083,10 @@ public class MwmActivity extends BaseMwmFragmentActivity { TtsPlayer.INSTANCE.stop(); LikesManager.INSTANCE.cancelDialogs(); - mOnmapDownloader.onPause(); - mPlacePage.onActivityPause(); + if (mOnmapDownloader != null) + mOnmapDownloader.onPause(); + if (mPlacePage != null) + mPlacePage.onActivityPause(); super.onPause(); } @@ -1081,7 +1095,11 @@ public class MwmActivity extends BaseMwmFragmentActivity { super.onStart(); RoutingController.get().attach(this); + } + @Override + protected void safeOnStart() + { if (MapFragment.nativeIsEngineCreated()) LocationHelper.INSTANCE.attach(this); if (mTrafficButtonController != null) @@ -1205,16 +1223,19 @@ public class MwmActivity extends BaseMwmFragmentActivity setFullscreen(false); - mPlacePage.setMapObject(object, true, new PlacePageView.SetMapObjectListener() + if (mPlacePage != null) { - @Override - public void onSetMapObjectComplete() + mPlacePage.setMapObject(object, true, new PlacePageView.SetMapObjectListener() { - if (!mPlacePageRestored) - mPlacePage.setState(State.PREVIEW); - mPlacePageRestored = false; - } - }); + @Override + public void onSetMapObjectComplete() + { + if (!mPlacePageRestored) + mPlacePage.setState(State.PREVIEW); + mPlacePageRestored = false; + } + }); + } if (UiUtils.isVisible(mFadeView)) mFadeView.fadeOut(); @@ -1233,7 +1254,8 @@ public class MwmActivity extends BaseMwmFragmentActivity } else { - mPlacePage.hide(); + if (mPlacePage != null) + mPlacePage.hide(); } } @@ -1327,7 +1349,8 @@ public class MwmActivity extends BaseMwmFragmentActivity else { Framework.nativeDeactivatePopup(); - mPlacePage.setMapObject(null, false, null); + if (mPlacePage != null) + mPlacePage.setMapObject(null, false, null); } } @@ -1371,7 +1394,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public boolean onTouch(View view, MotionEvent event) { - return mPlacePage.hideOnTouch() || + return (mPlacePage != null && mPlacePage.hideOnTouch()) || (mMapFragment != null && mMapFragment.onTouch(view, event)); } @@ -1632,7 +1655,8 @@ public class MwmActivity extends BaseMwmFragmentActivity updateSearchBar(); } - mPlacePage.refreshViews(); + if (mPlacePage != null) + mPlacePage.refreshViews(); } private void adjustCompassAndTraffic(int offsetY) @@ -1689,11 +1713,13 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public void showNavigation(boolean show) { - mPlacePage.refreshViews(); + if (mPlacePage != null) + mPlacePage.refreshViews(); if (mNavigationController != null) mNavigationController.show(show); refreshFade(); - mOnmapDownloader.updateState(false); + if (mOnmapDownloader != null) + mOnmapDownloader.updateState(false); adjustCompass(UiUtils.getCompassYOffset(this)); } @@ -1801,7 +1827,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public void onLocationUpdated(@NonNull Location location) { - if (!mPlacePage.isHidden()) + if (mPlacePage != null && !mPlacePage.isHidden()) mPlacePage.refreshLocation(location); if (!RoutingController.get().isNavigating()) @@ -1817,7 +1843,8 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onCompassUpdated(@NonNull CompassData compass) { MapFragment.nativeCompassUpdated(compass.getMagneticNorth(), compass.getTrueNorth(), false); - mPlacePage.refreshAzimuth(compass.getNorth()); + if (mPlacePage != null) + mPlacePage.refreshAzimuth(compass.getNorth()); if (mNavigationController != null) mNavigationController.updateNorth(compass.getNorth()); } diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index 1053f93fcd..af8293af93 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -7,10 +7,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Environment; import android.os.Handler; import android.os.Message; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.support.multidex.MultiDex; +import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; @@ -58,6 +58,7 @@ public class MwmApplication extends Application private boolean mAreCountersInitialized; private boolean mIsFrameworkInitialized; + private boolean mIsPlatformInitialized; private Handler mMainLoopHandler; private final Object mMainQueueToken = new Object(); @@ -147,10 +148,20 @@ public class MwmApplication extends Application mMainLoopHandler = new Handler(getMainLooper()); initCrashlytics(); - final boolean isInstallationIdFound = - setInstallationIdToCrashlytics(); initPushWoosh(); + + mPrefs = getSharedPreferences(getString(R.string.pref_file_name), MODE_PRIVATE); + mBackgroundTracker = new AppBackgroundTracker(); + } + + public void initNativePlatform() + { + if (mIsPlatformInitialized) + return; + + final boolean isInstallationIdFound = setInstallationIdToCrashlytics(); + initTracker(); String settingsPath = getSettingsPath(); @@ -174,6 +185,8 @@ public class MwmApplication extends Application mBackgroundTracker.addListener(mBackgroundListener); TrackRecorder.init(); Editor.init(); + + mIsPlatformInitialized = true; } public void initNativeCore() @@ -251,6 +264,11 @@ public class MwmApplication extends Application return mIsFrameworkInitialized; } + public boolean isPlatformInitialized() + { + return mIsPlatformInitialized; + } + public String getApkPath() { try @@ -358,7 +376,7 @@ public class MwmApplication extends Application { mAreCountersInitialized = true; Config.updateLaunchCounter(); - PreferenceManager.setDefaultValues(this, R.xml.prefs_misc, false); + PreferenceManager.setDefaultValues(this, R.xml.prefs_main, false); } } diff --git a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java index fa821b27c3..2d69367e0d 100644 --- a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java +++ b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java @@ -1,21 +1,31 @@ package com.mapswithme.maps.base; import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.Config; import com.mapswithme.util.UiUtils; +import com.mapswithme.util.Utils; import com.mapswithme.util.ViewServer; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.statistics.Statistics; import com.my.tracker.MyTracker; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; + public class BaseActivityDelegate { + @NonNull private final BaseActivity mActivity; + @Nullable private String mThemeName; - public BaseActivityDelegate(BaseActivity activity) + public BaseActivityDelegate(@NonNull BaseActivity activity) { mActivity = activity; } @@ -38,18 +48,24 @@ public class BaseActivityDelegate public void onStart() { + if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) + return; Statistics.INSTANCE.startActivity(mActivity.get()); MyTracker.onStartActivity(mActivity.get()); } public void onStop() { + if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) + return; Statistics.INSTANCE.stopActivity(mActivity.get()); MyTracker.onStopActivity(mActivity.get()); } public void onResume() { + if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) + return; org.alohalytics.Statistics.logEvent("$onResume", mActivity.getClass().getSimpleName() + ":" + UiUtils.deviceOrientationAsString(mActivity.get())); ViewServer.get(mActivity.get()).setFocusedWindow(mActivity.get()); @@ -57,12 +73,14 @@ public class BaseActivityDelegate public void onPause() { + if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) + return; org.alohalytics.Statistics.logEvent("$onPause", mActivity.getClass().getSimpleName()); } public void onPostResume() { - if (mThemeName.equals(Config.getCurrentUiTheme())) + if (mThemeName == null || mThemeName.equals(Config.getCurrentUiTheme()) || !Utils.isWriteExternalGranted(mActivity.get())) return; // Workaround described in https://code.google.com/p/android/issues/detail?id=93731 diff --git a/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java index a6728747f5..be559b7756 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java @@ -1,15 +1,28 @@ package com.mapswithme.maps.base; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; import android.support.v4.app.DialogFragment; +import android.view.View; +import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.util.ThemeUtils; +import com.mapswithme.util.Utils; + +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class BaseMwmDialogFragment extends DialogFragment { + @Nullable + private View mView; + @Nullable + private Bundle mSavedInstanceState; + protected final @StyleRes int getFullscreenTheme() { return (ThemeUtils.isNightTheme() ? R.style.MwmTheme_DialogFragment_Fullscreen_Night @@ -30,12 +43,20 @@ public class BaseMwmDialogFragment extends DialogFragment public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mSavedInstanceState = savedInstanceState; int style = getStyle(); int theme = getCustomTheme(); if (style != STYLE_NORMAL || theme != 0) //noinspection WrongConstant setStyle(style, theme); + + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnCreate(savedInstanceState); + } + + protected void safeOnCreate(@Nullable Bundle savedInstanceState) + { } @Override @@ -44,6 +65,13 @@ public class BaseMwmDialogFragment extends DialogFragment super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", getClass().getSimpleName() + ":" + com.mapswithme.util.UiUtils.deviceOrientationAsString(getActivity())); + + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnResume(); + } + + protected void safeOnResume() + { } @Override @@ -53,6 +81,55 @@ public class BaseMwmDialogFragment extends DialogFragment org.alohalytics.Statistics.logEvent("$onPause", getClass().getSimpleName()); } + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + mView = view; + mSavedInstanceState = savedInstanceState; + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnViewCreated(view, savedInstanceState); + } + + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + } + + @CallSuper + @Override + public void onDestroyView() + { + mView = null; + mSavedInstanceState = null; + super.onDestroyView(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + + if (isWriteGranted) + safeOnCreate(mSavedInstanceState); + + if (isWriteGranted && mView != null) + { + safeOnViewCreated(mView, mSavedInstanceState); + safeOnResume(); + } + } + public BaseMwmFragmentActivity getMwmActivity() { return (BaseMwmFragmentActivity) getActivity(); diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java index a79049469d..5a01b91df7 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java @@ -1,15 +1,38 @@ package com.mapswithme.maps.base; +import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.view.View; + +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.util.Utils; + +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class BaseMwmFragment extends Fragment { + @Nullable + private View mView; + @Nullable + private Bundle mSavedInstanceState; + @Override public void onResume() { super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", this.getClass().getSimpleName() + ":" + com.mapswithme.util.UiUtils.deviceOrientationAsString(getActivity())); + + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnResume(); + } + + protected void safeOnResume() + { } @Override @@ -19,6 +42,53 @@ public class BaseMwmFragment extends Fragment org.alohalytics.Statistics.logEvent("$onPause", this.getClass().getSimpleName()); } + @CallSuper + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + mView = view; + mSavedInstanceState = savedInstanceState; + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnViewCreated(view, savedInstanceState); + } + + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + } + + @CallSuper + @Override + public void onDestroyView() + { + mView = null; + mSavedInstanceState = null; + super.onDestroyView(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + + if (isWriteGranted && mView != null) + { + safeOnViewCreated(mView, mSavedInstanceState); + safeOnResume(); + } + } + public BaseMwmFragmentActivity getMwmActivity() { return (BaseMwmFragmentActivity) getActivity(); diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java index 5fb29c0232..e73b965541 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java @@ -1,10 +1,10 @@ package com.mapswithme.maps.base; import android.app.Activity; -import android.content.Intent; import android.media.AudioManager; import android.os.Bundle; import android.support.annotation.ColorRes; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; @@ -16,17 +16,38 @@ import android.view.MenuItem; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.util.Config; +import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.GET_ACCOUNTS; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + public class BaseMwmFragmentActivity extends AppCompatActivity implements BaseActivity { + private static final int REQUEST_PERMISSIONS_START = 1; + private static final int REQUEST_PERMISSIONS_RESUME = 2; + + private static final String[] START_PERMISSIONS = new String[] + { + WRITE_EXTERNAL_STORAGE, + ACCESS_COARSE_LOCATION, + ACCESS_FINE_LOCATION, + GET_ACCOUNTS + }; + private static final String[] RESUME_PERMISSIONS = new String[] {WRITE_EXTERNAL_STORAGE}; + private final BaseActivityDelegate mBaseDelegate = new BaseActivityDelegate(this); @Nullable private Bundle mSavedInstanceState; + private boolean mRequestedFromOnCreate = false; + private boolean mFromInstanceState = false; @Override public Activity get() @@ -48,22 +69,38 @@ public class BaseMwmFragmentActivity extends AppCompatActivity } @Override - protected void onCreate(Bundle savedInstanceState) + protected void onCreate(@Nullable Bundle savedInstanceState) { + mFromInstanceState = savedInstanceState != null; + if (!Utils.checkPermissions(this, MwmApplication.get().isPlatformInitialized() + ? RESUME_PERMISSIONS : START_PERMISSIONS, + REQUEST_PERMISSIONS_START)) + { + mRequestedFromOnCreate = true; + super.onCreate(savedInstanceState); + return; + } + + MwmApplication.get().initNativePlatform(); mBaseDelegate.onCreate(); super.onCreate(savedInstanceState); + safeOnCreate(savedInstanceState); + } + + @CallSuper + protected void safeOnCreate(@Nullable Bundle savedInstanceState) + { + setVolumeControlStream(AudioManager.STREAM_MUSIC); + final int layoutId = getContentLayoutResId(); + if (layoutId != 0) + setContentView(layoutId); if (useTransparentStatusBar()) UiUtils.setupStatusBar(this); if (useColorStatusBar()) UiUtils.setupColorStatusBar(this, getStatusBarColor()); - setVolumeControlStream(AudioManager.STREAM_MUSIC); - final int layoutId = getContentLayoutResId(); - if (layoutId != 0) - setContentView(layoutId); - // Use full-screen on Kindle Fire only if (Utils.isAmazonDevice()) { @@ -74,6 +111,8 @@ public class BaseMwmFragmentActivity extends AppCompatActivity MwmApplication.get().initNativeCore(); MwmApplication.get().initCounters(); + LocationHelper.INSTANCE.init(); + attachDefaultFragment(); } @@ -119,6 +158,12 @@ public class BaseMwmFragmentActivity extends AppCompatActivity { super.onStart(); mBaseDelegate.onStart(); + if (MwmApplication.get().isPlatformInitialized()) + safeOnStart(); + } + + protected void safeOnStart() + { } @Override @@ -133,6 +178,12 @@ public class BaseMwmFragmentActivity extends AppCompatActivity { super.onRestoreInstanceState(savedInstanceState); mSavedInstanceState = savedInstanceState; + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) + safeOnRestoreInstanceState(savedInstanceState); + } + + protected void safeOnRestoreInstanceState(@NonNull Bundle savedInstanceState) + { } @Nullable @@ -156,15 +207,41 @@ public class BaseMwmFragmentActivity extends AppCompatActivity protected void onResume() { super.onResume(); + if (!mRequestedFromOnCreate && !Utils.checkPermissions(this, RESUME_PERMISSIONS, + REQUEST_PERMISSIONS_RESUME)) + { + return; + } + mBaseDelegate.onResume(); + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) + safeOnResume(); + } + + protected void safeOnResume() + { } @Override - protected void onPostResume() { + protected void onPostResume() + { super.onPostResume(); mBaseDelegate.onPostResume(); } + @CallSuper + @Override + protected void onResumeFragments() + { + super.onResumeFragments(); + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) + safeOnResumeFragments(); + } + + protected void safeOnResumeFragments() + { + } + @Override protected void onPause() { @@ -172,6 +249,48 @@ public class BaseMwmFragmentActivity extends AppCompatActivity mBaseDelegate.onPause(); } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + if (isWriteGranted) + { + if (requestCode == REQUEST_PERMISSIONS_START) + { + MwmApplication.get().initNativePlatform(); + mBaseDelegate.onCreate(); + safeOnCreate(mSavedInstanceState); + mBaseDelegate.onStart(); + if (mSavedInstanceState != null) + safeOnRestoreInstanceState(mSavedInstanceState); + mBaseDelegate.onResume(); + safeOnResume(); + if (!mFromInstanceState) + safeOnResumeFragments(); + } + else if (requestCode == REQUEST_PERMISSIONS_RESUME) + { + safeOnResume(); + safeOnResumeFragments(); + } + } + else + { + finish(); + } + } + protected Toolbar getToolbar() { return (Toolbar) findViewById(R.id.toolbar); diff --git a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java index 2a715b2113..172571a922 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java @@ -1,24 +1,56 @@ package com.mapswithme.maps.base; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; import android.support.v7.widget.Toolbar; import android.view.View; +import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + @Deprecated public abstract class BaseMwmListFragment extends ListFragment { private Toolbar mToolbar; + @Nullable + private View mView; + @Nullable + private Bundle mSavedInstanceState; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnCreate(savedInstanceState); + } + + protected void safeOnCreate(@Nullable Bundle savedInstanceState) + { + } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + mView = view; + mSavedInstanceState = savedInstanceState; + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnViewCreated(view, savedInstanceState); + } + + @CallSuper + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { mToolbar = (Toolbar) view.findViewById(R.id.toolbar); if (mToolbar != null) { @@ -34,6 +66,33 @@ public abstract class BaseMwmListFragment extends ListFragment } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + + if (isWriteGranted) + { + safeOnCreate(mSavedInstanceState); + if (mView != null) + { + safeOnViewCreated(mView, mSavedInstanceState); + safeOnResume(); + } + } + } + public Toolbar getToolbar() { return mToolbar; @@ -45,6 +104,13 @@ public abstract class BaseMwmListFragment extends ListFragment super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", getClass().getSimpleName() + ":" + UiUtils.deviceOrientationAsString(getActivity())); + + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnResume(); + } + + protected void safeOnResume() + { } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index f79b7cdc63..0438a2bb0a 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -1,8 +1,10 @@ package com.mapswithme.maps.base; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -11,17 +13,27 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.widget.PlaceholderView; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + public abstract class BaseMwmRecyclerFragment extends Fragment { private Toolbar mToolbar; + @Nullable private RecyclerView mRecycler; private PlaceholderView mPlaceholder; + @Nullable + private View mView; + @Nullable + private Bundle mSavedInstanceState; + protected abstract RecyclerView.Adapter createAdapter(); protected @LayoutRes int getLayoutRes() @@ -29,9 +41,10 @@ public abstract class BaseMwmRecyclerFragment extends Fragment return R.layout.fragment_recycler; } + @Nullable protected RecyclerView.Adapter getAdapter() { - return mRecycler.getAdapter(); + return mRecycler != null ? mRecycler.getAdapter() : null; } @Override @@ -45,6 +58,15 @@ public abstract class BaseMwmRecyclerFragment extends Fragment { super.onViewCreated(view, savedInstanceState); + mView = view; + mSavedInstanceState = savedInstanceState; + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnViewCreated(view, savedInstanceState); + } + + @CallSuper + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { mToolbar = (Toolbar) view.findViewById(R.id.toolbar); if (mToolbar != null) { @@ -72,6 +94,50 @@ public abstract class BaseMwmRecyclerFragment extends Fragment setupPlaceholder(mPlaceholder); } + @CallSuper + @Override + public void onDestroyView() + { + mView = null; + mSavedInstanceState = null; + super.onDestroyView(); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) + { + super.onActivityCreated(savedInstanceState); + if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) + safeOnActivityCreated(savedInstanceState); + } + + protected void safeOnActivityCreated(@Nullable Bundle savedInstanceState) + { + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + + if (isWriteGranted && mView != null) + { + safeOnViewCreated(mView, mSavedInstanceState); + safeOnActivityCreated(mSavedInstanceState); + } + } + public Toolbar getToolbar() { return mToolbar; diff --git a/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java index 6113b363d9..a2548098c8 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java @@ -1,6 +1,7 @@ package com.mapswithme.maps.base; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; @@ -11,10 +12,10 @@ public class BaseMwmToolbarFragment extends BaseMwmFragment { protected ToolbarController mToolbarController; + @CallSuper @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); mToolbarController = onCreateToolbarController(view); } diff --git a/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java b/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java index 6763a02378..c295c161ea 100644 --- a/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java @@ -1,6 +1,8 @@ package com.mapswithme.maps.base; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.app.Fragment; import android.support.v7.widget.Toolbar; @@ -10,10 +12,11 @@ import com.mapswithme.util.UiUtils; public abstract class BaseToolbarActivity extends BaseMwmFragmentActivity { + @CallSuper @Override - protected void onCreate(Bundle state) + protected void safeOnCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(state); + super.safeOnCreate(savedInstanceState); Toolbar toolbar = getToolbar(); UiUtils.extendViewWithStatusBar(toolbar); diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java index 5b2807242f..b5931fa4cb 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java @@ -2,8 +2,10 @@ package com.mapswithme.maps.bookmarks; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.MenuItem; import android.view.View; @@ -39,39 +41,47 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment return new BookmarkCategoriesAdapter(getActivity()); } + @Nullable @Override protected BookmarkCategoriesAdapter getAdapter() { - return (BookmarkCategoriesAdapter)super.getAdapter(); + RecyclerView.Adapter adapter = super.getAdapter(); + return adapter != null ? (BookmarkCategoriesAdapter)adapter : null; } + @CallSuper @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); - getAdapter().setOnClickListener(this); - getAdapter().setOnLongClickListener(this); - getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() + if (getAdapter() != null) { - @Override - public void onChanged() + getAdapter().setOnClickListener(this); + getAdapter().setOnLongClickListener(this); + getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - updateResultsPlaceholder(); - } - }); + @Override + public void onChanged() + { + updateResultsPlaceholder(); + } + }); + } } private void updateResultsPlaceholder() { - showPlaceholder(getAdapter().getItemCount() == 0); + if (getAdapter() != null) + showPlaceholder(getAdapter().getItemCount() == 0); } @Override public void onResume() { super.onResume(); - getAdapter().notifyDataSetChanged(); + if (getAdapter() != null) + getAdapter().notifyDataSetChanged(); } @Override @@ -86,7 +96,8 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment { final BookmarkCategory category = BookmarkManager.INSTANCE.getCategory(mSelectedPosition); category.setName(text); - getAdapter().notifyDataSetChanged(); + if (getAdapter() != null) + getAdapter().notifyDataSetChanged(); } @Override @@ -96,7 +107,8 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment { case R.id.set_show: BookmarkManager.INSTANCE.toggleCategoryVisibility(mSelectedPosition); - getAdapter().notifyDataSetChanged(); + if (getAdapter() != null) + getAdapter().notifyDataSetChanged(); break; case R.id.set_share: @@ -105,7 +117,8 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment case R.id.set_delete: BookmarkManager.INSTANCE.nativeDeleteCategory(mSelectedPosition); - getAdapter().notifyDataSetChanged(); + if (getAdapter() != null) + getAdapter().notifyDataSetChanged(); break; case R.id.set_edit: diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java index a01cba32d4..b117183aa4 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java @@ -2,7 +2,10 @@ package com.mapswithme.maps.bookmarks; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.Menu; @@ -13,7 +16,6 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; -import com.mapswithme.maps.Framework; import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmListFragment; @@ -36,13 +38,13 @@ public class BookmarksListFragment extends BaseMwmListFragment private BookmarkCategory mCategory; private int mCategoryIndex; private int mSelectedPosition; + @Nullable private BookmarkListAdapter mAdapter; + @CallSuper @Override - public void onCreate(Bundle savedInstanceState) + protected void safeOnCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mCategoryIndex = getArguments().getInt(ChooseBookmarkCategoryFragment.CATEGORY_ID, -1); mCategory = BookmarkManager.INSTANCE.getCategory(mCategoryIndex); } @@ -53,19 +55,23 @@ public class BookmarksListFragment extends BaseMwmListFragment return inflater.inflate(R.layout.simple_list, container, false); } + @CallSuper @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); initList(); setHasOptionsMenu(true); - ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(mCategory.getName()); + ActionBar bar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + if (bar != null) + bar.setTitle(mCategory.getName()); } @Override - public void onResume() + protected void safeOnResume() { - super.onResume(); + if (mAdapter == null) + return; mAdapter.startLocationUpdate(); mAdapter.notifyDataSetChanged(); @@ -76,7 +82,8 @@ public class BookmarksListFragment extends BaseMwmListFragment { super.onPause(); - mAdapter.stopLocationUpdate(); + if (mAdapter != null) + mAdapter.stopLocationUpdate(); } private void initList() @@ -92,20 +99,23 @@ public class BookmarksListFragment extends BaseMwmListFragment { final Intent i = new Intent(getActivity(), MwmActivity.class); - switch (mAdapter.getItemViewType(position)) + if (mAdapter != null) { - case BookmarkListAdapter.TYPE_SECTION: - return; - case BookmarkListAdapter.TYPE_BOOKMARK: - final Bookmark bookmark = (Bookmark) mAdapter.getItem(position); - i.putExtra(MwmActivity.EXTRA_TASK, - new MwmActivity.ShowBookmarkTask(mCategoryIndex, bookmark.getBookmarkId())); - break; - case BookmarkListAdapter.TYPE_TRACK: - final Track track = (Track) mAdapter.getItem(position); - i.putExtra(MwmActivity.EXTRA_TASK, - new MwmActivity.ShowTrackTask(track.getCategoryId(), track.getTrackId())); - break; + switch (mAdapter.getItemViewType(position)) + { + case BookmarkListAdapter.TYPE_SECTION: + return; + case BookmarkListAdapter.TYPE_BOOKMARK: + final Bookmark bookmark = (Bookmark) mAdapter.getItem(position); + i.putExtra(MwmActivity.EXTRA_TASK, + new MwmActivity.ShowBookmarkTask(mCategoryIndex, bookmark.getBookmarkId())); + break; + case BookmarkListAdapter.TYPE_TRACK: + final Track track = (Track) mAdapter.getItem(position); + i.putExtra(MwmActivity.EXTRA_TASK, + new MwmActivity.ShowTrackTask(track.getCategoryId(), track.getTrackId())); + break; + } } i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -115,6 +125,9 @@ public class BookmarksListFragment extends BaseMwmListFragment @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + if (mAdapter == null) + return false; + mSelectedPosition = position; final Object item = mAdapter.getItem(mSelectedPosition); int type = mAdapter.getItemViewType(mSelectedPosition); @@ -160,6 +173,9 @@ public class BookmarksListFragment extends BaseMwmListFragment @Override public boolean onMenuItemClick(MenuItem menuItem) { + if (mAdapter == null) + return false; + Bookmark item = (Bookmark) mAdapter.getItem(mSelectedPosition); switch (menuItem.getItemId()) diff --git a/android/src/com/mapswithme/maps/bookmarks/data/Bookmark.java b/android/src/com/mapswithme/maps/bookmarks/data/Bookmark.java index b0349eec74..9343e432c2 100644 --- a/android/src/com/mapswithme/maps/bookmarks/data/Bookmark.java +++ b/android/src/com/mapswithme/maps/bookmarks/data/Bookmark.java @@ -137,7 +137,8 @@ public class Bookmark extends MapObject if (!title.equals(getTitle()) || icon != mIcon || !description.equals(getBookmarkDescription())) { - nativeSetBookmarkParams(mCategoryId, mBookmarkId, title, icon.getType(), description); + nativeSetBookmarkParams(mCategoryId, mBookmarkId, title, icon != null ? icon.getType() : "", + description); mTitle = title; } } diff --git a/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java b/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java index c237296e3d..5dd519822e 100644 --- a/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java +++ b/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java @@ -39,11 +39,6 @@ public enum BookmarkManager return ICONS.get(0); } - BookmarkManager() - { - nativeLoadBookmarks(); - } - public void deleteBookmark(Bookmark bmk) { nativeDeleteBookmark(bmk.getCategoryId(), bmk.getBookmarkId()); diff --git a/android/src/com/mapswithme/maps/downloader/BottomPanel.java b/android/src/com/mapswithme/maps/downloader/BottomPanel.java index a2f4fc51ac..feab04b563 100644 --- a/android/src/com/mapswithme/maps/downloader/BottomPanel.java +++ b/android/src/com/mapswithme/maps/downloader/BottomPanel.java @@ -82,7 +82,8 @@ class BottomPanel @Override public void onClick(View v) { - mFragment.getAdapter().setAvailableMapsMode(); + if (mFragment.getAdapter() != null ) + mFragment.getAdapter().setAvailableMapsMode(); update(); } }); @@ -114,15 +115,15 @@ class BottomPanel public void update() { DownloaderAdapter adapter = mFragment.getAdapter(); - boolean search = adapter.isSearchResultsMode(); + boolean search = adapter != null && adapter.isSearchResultsMode(); boolean show = !search; - UiUtils.showIf(show && adapter.isMyMapsMode(), mFab); + UiUtils.showIf(show && adapter != null && adapter.isMyMapsMode(), mFab); if (show) { - String root = adapter.getCurrentRootId(); - if (adapter.isMyMapsMode()) + String root = adapter != null ? adapter.getCurrentRootId() : ""; + if (adapter != null && adapter.isMyMapsMode()) { int status = MapManager.nativeGetStatus(root); switch (status) diff --git a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java index 298aedb23d..12ff82a0d8 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java +++ b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java @@ -2,6 +2,7 @@ package com.mapswithme.maps.downloader; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; @@ -17,7 +18,6 @@ import com.mapswithme.maps.base.OnBackPressListener; import com.mapswithme.maps.search.NativeMapSearchListener; import com.mapswithme.maps.search.SearchEngine; import com.mapswithme.maps.widget.PlaceholderView; -import com.mapswithme.util.UiUtils; public class DownloaderFragment extends BaseMwmRecyclerFragment implements OnBackPressListener @@ -25,6 +25,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment private DownloaderToolbarController mToolbarController; private BottomPanel mBottomPanel; + @Nullable private DownloaderAdapter mAdapter; private long mCurrentSearch; @@ -57,7 +58,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment rs.add(item); } - mAdapter.setSearchResultsMode(rs, mToolbarController.getQuery()); + if (mAdapter != null) + mAdapter.setSearchResultsMode(rs, mToolbarController.getQuery()); if (isLast) onSearchEnd(); @@ -75,7 +77,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment mCurrentSearch = System.nanoTime(); SearchEngine.searchMaps(mToolbarController.getQuery(), mCurrentSearch); mToolbarController.showProgress(true); - mAdapter.clearAdsAndCancelMyTarget(); + if (mAdapter != null) + mAdapter.clearAdsAndCancelMyTarget(); } void clearSearchQuery() @@ -85,7 +88,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment void cancelSearch() { - if (!mAdapter.isSearchResultsMode()) + if (mAdapter == null || !mAdapter.isSearchResultsMode()) return; mAdapter.resetSearchResultsMode(); @@ -105,6 +108,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment mBottomPanel.update(); } + @CallSuper @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -112,10 +116,9 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); } - @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); mSubscriberSlot = MapManager.nativeSubscribe(new MapManager.StorageCallback() { @Override @@ -132,8 +135,11 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment SearchEngine.INSTANCE.addMapListener(mSearchListener); getRecyclerView().addOnScrollListener(mScrollListener); - mAdapter.refreshData(); - mAdapter.attach(); + if (mAdapter != null) + { + mAdapter.refreshData(); + mAdapter.attach(); + } mBottomPanel = new BottomPanel(this, view); mToolbarController = new DownloaderToolbarController(view, getActivity(), this); @@ -145,7 +151,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment public void onDestroyView() { super.onDestroyView(); - mAdapter.detach(); + if (mAdapter != null) + mAdapter.detach(); mAdapter = null; if (mSubscriberSlot != 0) @@ -174,7 +181,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment return true; } - return mAdapter.goUpwards(); + return mAdapter != null && mAdapter.goUpwards(); } @Override @@ -200,20 +207,22 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment } @Override + @Nullable public DownloaderAdapter getAdapter() { return mAdapter; } - @NonNull String getCurrentRoot() + @NonNull + String getCurrentRoot() { - return mAdapter.getCurrentRootId(); + return mAdapter != null ? mAdapter.getCurrentRootId() : ""; } @Override protected void setupPlaceholder(@NonNull PlaceholderView placeholder) { - if (mAdapter.isSearchResultsMode()) + if (mAdapter != null && mAdapter.isSearchResultsMode()) placeholder.setContent(R.drawable.img_search_nothing_found_light, R.string.search_not_found, R.string.search_not_found_query); else diff --git a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java index fef316c9ff..4cf0617bc4 100644 --- a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java +++ b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java @@ -253,15 +253,18 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener { MapManager.nativeUnsubscribe(mStorageSubscriptionSlot); mStorageSubscriptionSlot = 0; - } - MapManager.nativeUnsubscribeOnCountryChanged(); + MapManager.nativeUnsubscribeOnCountryChanged(); + } } public void onResume() { - mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback); - MapManager.nativeSubscribeOnCountryChanged(mCountryChangedListener); + if (mStorageSubscriptionSlot == 0) + { + mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback); + MapManager.nativeSubscribeOnCountryChanged(mCountryChangedListener); + } } public static void setAutodownloadLocked(boolean locked) diff --git a/android/src/com/mapswithme/maps/editor/EditorFragment.java b/android/src/com/mapswithme/maps/editor/EditorFragment.java index 0e569818bc..f1d6aacd73 100644 --- a/android/src/com/mapswithme/maps/editor/EditorFragment.java +++ b/android/src/com/mapswithme/maps/editor/EditorFragment.java @@ -4,6 +4,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -114,6 +115,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe private EditorHostFragment mParent; + private boolean mIsViewCreated = false; + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) @@ -121,10 +124,11 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe return inflater.inflate(R.layout.fragment_editor, container, false); } + @CallSuper @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + mIsViewCreated = true; mParent = (EditorHostFragment) getParentFragment(); @@ -206,7 +210,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - setEdits(); + if (mIsViewCreated) + setEdits(); } boolean setEdits() diff --git a/android/src/com/mapswithme/maps/editor/EditorHostFragment.java b/android/src/com/mapswithme/maps/editor/EditorHostFragment.java index 6bc9f44f28..11fae484d3 100644 --- a/android/src/com/mapswithme/maps/editor/EditorHostFragment.java +++ b/android/src/com/mapswithme/maps/editor/EditorHostFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; @@ -118,10 +119,12 @@ public class EditorHostFragment extends BaseMwmToolbarFragment return inflater.inflate(R.layout.fragment_editor_host, container, false); } + @CallSuper @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); + mToolbarController.findViewById(R.id.save).setOnClickListener(this); mToolbarController.getToolbar().setNavigationOnClickListener(new View.OnClickListener() { @@ -153,7 +156,9 @@ public class EditorHostFragment extends BaseMwmToolbarFragment @Override protected void onTextChanged(String query) { - ((CuisineFragment) getChildFragmentManager().findFragmentByTag(CuisineFragment.class.getName())).setFilter(query); + Fragment fragment = getChildFragmentManager().findFragmentByTag(CuisineFragment.class.getName()); + if (fragment != null) + ((CuisineFragment) fragment).setFilter(query); } }; } diff --git a/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java b/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java index 46ea0001bd..7153d2e801 100644 --- a/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java +++ b/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java @@ -1,6 +1,9 @@ package com.mapswithme.maps.editor; import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -29,10 +32,11 @@ public class FeatureCategoryFragment extends BaseMwmRecyclerFragment return inflater.inflate(R.layout.fragment_categories, container, false); } + @CallSuper @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); if (getArguments() != null && getArguments().containsKey(FeatureCategoryActivity.EXTRA_FEATURE_CATEGORY)) mSelectedCategory = getArguments().getParcelable(FeatureCategoryActivity.EXTRA_FEATURE_CATEGORY); diff --git a/android/src/com/mapswithme/maps/editor/StreetFragment.java b/android/src/com/mapswithme/maps/editor/StreetFragment.java index 0d1ade85ea..c4a5addd03 100644 --- a/android/src/com/mapswithme/maps/editor/StreetFragment.java +++ b/android/src/com/mapswithme/maps/editor/StreetFragment.java @@ -1,7 +1,9 @@ package com.mapswithme.maps.editor; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -18,10 +20,17 @@ public class StreetFragment extends BaseMwmRecyclerFragment implements EditTextD @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mSelectedString = Editor.nativeGetStreet(); return super.onCreateView(inflater, container, savedInstanceState); } + @CallSuper + @Override + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + mSelectedString = Editor.nativeGetStreet(); + super.safeOnViewCreated(view, savedInstanceState); + } + @Override public void onSaveInstanceState(Bundle outState) { diff --git a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java index 01191ec8f2..495f831994 100644 --- a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java +++ b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java @@ -51,7 +51,14 @@ class AndroidNativeProvider extends BaseLocationProvider long interval = LocationHelper.INSTANCE.getInterval(); LOGGER.d(TAG, "Request Android native provider '" + provider + "' to get locations at this interval = " + interval + " ms"); - mLocationManager.requestLocationUpdates(provider, interval, 0, listener); + try + { + mLocationManager.requestLocationUpdates(provider, interval, 0, listener); + } + catch (SecurityException e) + { + e.printStackTrace(); + } mListeners.add(listener); } @@ -110,14 +117,21 @@ class AndroidNativeProvider extends BaseLocationProvider private static Location findBestNotExpiredLocation(LocationManager manager, List providers, long expirationMillis) { Location res = null; - for (final String pr : providers) + try { - final Location last = manager.getLastKnownLocation(pr); - if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) - continue; + for (final String pr : providers) + { + final Location last = manager.getLastKnownLocation(pr); + if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) + continue; - if (res == null || res.getAccuracy() > last.getAccuracy()) - res = last; + if (res == null || res.getAccuracy() > last.getAccuracy()) + res = last; + } + } + catch (SecurityException e) + { + e.printStackTrace(); } return res; } diff --git a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java index d45b16b58d..768f250d0a 100644 --- a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java +++ b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java @@ -128,11 +128,19 @@ class GoogleFusedLocationProvider extends BaseLocationProvider if (!mGoogleApiClient.isConnected()) return; - LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mListener); - LocationHelper.INSTANCE.startSensors(); - Location last = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); - if (last != null) - mListener.onLocationChanged(last); + try + { + LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mListener); + LocationHelper.INSTANCE.startSensors(); + Location last = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); + if (last != null) + mListener.onLocationChanged(last); + } + catch (SecurityException e) + { + e.printStackTrace(); + stop(); + } } @Override diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index dafac37106..d715da5636 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -159,7 +159,7 @@ public enum LocationHelper }; - LocationHelper() + public void init() { mLogger.d(LocationHelper.class.getSimpleName(), "ctor()"); } @@ -167,9 +167,12 @@ public enum LocationHelper @UiThread public void initialize() { - initProvider(); - LocationState.nativeSetListener(mMyPositionModeListener); - MwmApplication.backgroundTracker().addListener(mOnTransition); + if (MwmApplication.get().isFrameworkInitialized()) + { + initProvider(); + LocationState.nativeSetListener(mMyPositionModeListener); + MwmApplication.backgroundTracker().addListener(mOnTransition); + } } private void initProvider() diff --git a/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java b/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java index e0a546e062..d2cdc9b737 100644 --- a/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java @@ -22,10 +22,9 @@ public class SearchCategoriesFragment extends BaseMwmRecyclerFragment return R.layout.fragment_search_categories; } - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) + + protected void safeOnActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); ((SearchFragment) getParentFragment()).setRecyclerScrollListener(getRecyclerView()); } diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 36c3cf50dd..627ab2bd6f 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -8,6 +8,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CollapsingToolbarLayout; +import android.support.annotation.CallSuper; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -19,7 +20,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.mapswithme.maps.Framework; import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; @@ -286,10 +286,11 @@ public class SearchFragment extends BaseMwmFragment return inflater.inflate(R.layout.fragment_search, container, false); } + @CallSuper @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + super.safeOnViewCreated(view, savedInstanceState); readArguments(); ViewGroup root = (ViewGroup) view; @@ -388,10 +389,9 @@ public class SearchFragment extends BaseMwmFragment mFilterController.onSaveState(outState); } - @Override - public void onResume() + + protected void safeOnResume() { - super.onResume(); LocationHelper.INSTANCE.addListener(mLocationListener, true); mAppBarLayout.addOnOffsetChangedListener(mOffsetListener); } diff --git a/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java b/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java index 2b022069e5..40843a69d3 100644 --- a/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java +++ b/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java @@ -35,6 +35,7 @@ class SearchHistoryAdapter extends RecyclerView.Adapter sRecents = new ArrayList<>(); - static - { - refresh(); - } - private SearchRecents() {} public static void refresh() diff --git a/android/src/com/mapswithme/maps/settings/AboutFragment.java b/android/src/com/mapswithme/maps/settings/AboutFragment.java index 4bf2e94014..0e4d397d93 100644 --- a/android/src/com/mapswithme/maps/settings/AboutFragment.java +++ b/android/src/com/mapswithme/maps/settings/AboutFragment.java @@ -42,12 +42,12 @@ public class AboutFragment extends BaseSettingsFragment return R.layout.about; } - @Override - protected BaseShadowController createShadowController() - { - clearPaddings(); - return new ScrollViewShadowController((ObservableScrollView) mFrame.findViewById(R.id.content_frame)); - } +// @Override +// protected BaseShadowController createShadowController() +// { +// clearPaddings(); +// return new ScrollViewShadowController((ObservableScrollView) mFrame.findViewById(R.id.content_frame)); +// } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) @@ -127,7 +127,9 @@ public class AboutFragment extends BaseSettingsFragment case R.id.copyright: Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.COPYRIGHT); AlohaHelper.logClick(AlohaHelper.Settings.COPYRIGHT); - getSettingsActivity().switchToFragment(CopyrightFragment.class, R.string.copyright); +// getSettingsActivity().switchToFragment(CopyrightFragment.class, R.string.copyright); + getSettingsActivity().replaceFragment(CopyrightFragment.class, + getString(R.string.copyright), null); break; } } catch (ActivityNotFoundException e) diff --git a/android/src/com/mapswithme/maps/settings/BaseSettingsFragment.java b/android/src/com/mapswithme/maps/settings/BaseSettingsFragment.java index 87385a9b11..b68d384217 100644 --- a/android/src/com/mapswithme/maps/settings/BaseSettingsFragment.java +++ b/android/src/com/mapswithme/maps/settings/BaseSettingsFragment.java @@ -1,27 +1,21 @@ package com.mapswithme.maps.settings; -import android.app.Fragment; import android.graphics.Rect; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.support.annotation.LayoutRes; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.mapswithme.maps.R; -import com.mapswithme.maps.widget.BaseShadowController; -import com.mapswithme.util.UiUtils; +import com.mapswithme.maps.base.BaseMwmFragment; -abstract class BaseSettingsFragment extends Fragment +abstract class BaseSettingsFragment extends BaseMwmFragment { protected View mFrame; - private BaseShadowController mShadowController; private final Rect mSavedPaddings = new Rect(); protected abstract @LayoutRes int getLayoutRes(); - protected abstract BaseShadowController createShadowController(); private void savePaddings() { @@ -53,12 +47,6 @@ abstract class BaseSettingsFragment extends Fragment super.onActivityCreated(savedInstanceState); savePaddings(); - if (((PreferenceActivity)getActivity()).onIsMultiPane()) - { - mShadowController = createShadowController(); - if (mShadowController != null) - mShadowController.attach(); - } } @Override @@ -67,33 +55,6 @@ abstract class BaseSettingsFragment extends Fragment super.onDestroyView(); restorePaddings(); - if (mShadowController != null) - mShadowController.detach(); - } - - @Override - public void onResume() - { - super.onResume(); - org.alohalytics.Statistics.logEvent("$onResume", getClass().getSimpleName() + ":" + - UiUtils.deviceOrientationAsString(getActivity())); - } - - @Override - public void onPause() - { - super.onPause(); - org.alohalytics.Statistics.logEvent("$onPause", getClass().getSimpleName() + ":" + - UiUtils.deviceOrientationAsString(getActivity())); - } - - protected static void adjustMargins(View view) - { - int margin = UiUtils.dimen(R.dimen.margin_half); - ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); - lp.leftMargin = margin; - lp.rightMargin = margin; - view.setLayoutParams(lp); } protected SettingsActivity getSettingsActivity() diff --git a/android/src/com/mapswithme/maps/settings/BaseXmlSettingsFragment.java b/android/src/com/mapswithme/maps/settings/BaseXmlSettingsFragment.java index cb81146ae2..d7e6453e57 100644 --- a/android/src/com/mapswithme/maps/settings/BaseXmlSettingsFragment.java +++ b/android/src/com/mapswithme/maps/settings/BaseXmlSettingsFragment.java @@ -1,19 +1,25 @@ package com.mapswithme.maps.settings; import android.os.Bundle; -import android.preference.PreferenceFragment; +import android.support.annotation.Nullable; import android.support.annotation.XmlRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.view.View; + +import com.mapswithme.maps.R; +import com.mapswithme.util.Config; +import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; -abstract class BaseXmlSettingsFragment extends PreferenceFragment +abstract class BaseXmlSettingsFragment extends PreferenceFragmentCompat { protected abstract @XmlRes int getXmlResources(); @Override - public void onCreate(Bundle savedInstanceState) + public void onCreatePreferences(Bundle bundle, String root) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(getXmlResources()); + setPreferencesFromResource(getXmlResources(), root); } @Override @@ -32,6 +38,20 @@ abstract class BaseXmlSettingsFragment extends PreferenceFragment UiUtils.deviceOrientationAsString(getActivity())); } + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + + String theme = Config.getCurrentUiTheme(); + int color; + if (ThemeUtils.isDefaultTheme(theme)) + color = ContextCompat.getColor(getContext(), R.color.bg_cards); + else + color = ContextCompat.getColor(getContext(), R.color.bg_cards_night); + view.setBackgroundColor(color); + } + protected SettingsActivity getSettingsActivity() { return (SettingsActivity) getActivity(); diff --git a/android/src/com/mapswithme/maps/settings/CopyrightFragment.java b/android/src/com/mapswithme/maps/settings/CopyrightFragment.java index 0fcaa363b1..3a9bfe732d 100644 --- a/android/src/com/mapswithme/maps/settings/CopyrightFragment.java +++ b/android/src/com/mapswithme/maps/settings/CopyrightFragment.java @@ -9,9 +9,6 @@ import android.view.ViewGroup; import com.mapswithme.maps.R; import com.mapswithme.maps.WebContainerDelegate; import com.mapswithme.maps.base.OnBackPressListener; -import com.mapswithme.maps.widget.BaseShadowController; -import com.mapswithme.maps.widget.ObservableWebView; -import com.mapswithme.maps.widget.WebViewShadowController; import com.mapswithme.util.Constants; public class CopyrightFragment extends BaseSettingsFragment @@ -25,14 +22,6 @@ public class CopyrightFragment extends BaseSettingsFragment return R.layout.fragment_prefs_copyright; } - @Override - protected BaseShadowController createShadowController() - { - clearPaddings(); - adjustMargins(mDelegate.getWebView()); - return new WebViewShadowController((ObservableWebView)mDelegate.getWebView()); - } - @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -54,7 +43,8 @@ public class CopyrightFragment extends BaseSettingsFragment public boolean onBackPressed() { if (!mDelegate.onBackPressed()) - getSettingsActivity().switchToFragment(AboutFragment.class, R.string.about_menu_title); + getSettingsActivity().replaceFragment(AboutFragment.class, + getString(R.string.about_menu_title), null); return true; } diff --git a/android/src/com/mapswithme/maps/settings/HelpFragment.java b/android/src/com/mapswithme/maps/settings/HelpFragment.java index 2a6bc78853..3a8ed91cb4 100644 --- a/android/src/com/mapswithme/maps/settings/HelpFragment.java +++ b/android/src/com/mapswithme/maps/settings/HelpFragment.java @@ -13,9 +13,6 @@ import android.widget.TextView; import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.R; import com.mapswithme.maps.WebContainerDelegate; -import com.mapswithme.maps.widget.BaseShadowController; -import com.mapswithme.maps.widget.ObservableWebView; -import com.mapswithme.maps.widget.WebViewShadowController; import com.mapswithme.util.Constants; import com.mapswithme.util.Utils; import com.mapswithme.util.statistics.AlohaHelper; @@ -31,15 +28,6 @@ public class HelpFragment extends BaseSettingsFragment return R.layout.fragment_prefs_help; } - @Override - protected BaseShadowController createShadowController() - { - clearPaddings(); - adjustMargins(mDelegate.getWebView()); - return new WebViewShadowController((ObservableWebView)mDelegate.getWebView()) - .addBottomShadow(); - } - @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { diff --git a/android/src/com/mapswithme/maps/settings/MapPrefsFragment.java b/android/src/com/mapswithme/maps/settings/MapPrefsFragment.java deleted file mode 100644 index 20fdea77cc..0000000000 --- a/android/src/com/mapswithme/maps/settings/MapPrefsFragment.java +++ /dev/null @@ -1,235 +0,0 @@ -package com.mapswithme.maps.settings; - -import android.app.Activity; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.TwoStatePreference; -import android.support.v7.app.AlertDialog; - -import java.util.List; - -import com.mapswithme.maps.Framework; -import com.mapswithme.maps.R; -import com.mapswithme.maps.downloader.MapManager; -import com.mapswithme.maps.downloader.OnmapDownloader; -import com.mapswithme.maps.location.TrackRecorder; -import com.mapswithme.util.Config; -import com.mapswithme.util.ThemeSwitcher; -import com.mapswithme.util.concurrency.UiThread; -import com.mapswithme.util.statistics.AlohaHelper; -import com.mapswithme.util.statistics.Statistics; - -public class MapPrefsFragment extends BaseXmlSettingsFragment -{ - private final StoragePathManager mPathManager = new StoragePathManager(); - private Preference mStoragePref; - - private boolean singleStorageOnly() - { - return !mPathManager.hasMoreThanOneStorage(); - } - - private void updateStoragePrefs() - { - Preference old = findPreference(getString(R.string.pref_storage)); - - if (singleStorageOnly()) - { - if (old != null) - getPreferenceScreen().removePreference(old); - } - else - { - if (old == null) - getPreferenceScreen().addPreference(mStoragePref); - } - } - - @Override - protected int getXmlResources() - { - return R.xml.prefs_map; - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - mStoragePref = findPreference(getString(R.string.pref_storage)); - updateStoragePrefs(); - - mStoragePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() - { - @Override - public boolean onPreferenceClick(Preference preference) - { - if (MapManager.nativeIsDownloading()) - new AlertDialog.Builder(getActivity()) - .setTitle(getString(R.string.downloading_is_active)) - .setMessage(getString(R.string.cant_change_this_setting)) - .setPositiveButton(getString(R.string.ok), null) - .show(); - else - getSettingsActivity().switchToFragment(StoragePathFragment.class, R.string.maps_storage); - - return true; - } - }); - - Preference pref = findPreference(getString(R.string.pref_munits)); - ((ListPreference)pref).setValue(String.valueOf(UnitLocale.getUnits())); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - UnitLocale.setUnits(Integer.parseInt((String) newValue)); - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.UNITS); - AlohaHelper.logClick(AlohaHelper.Settings.CHANGE_UNITS); - return true; - } - }); - - pref = findPreference(getString(R.string.pref_show_zoom_buttons)); - ((TwoStatePreference)pref).setChecked(Config.showZoomButtons()); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.ZOOM); - Config.setShowZoomButtons((Boolean) newValue); - return true; - } - }); - - String curTheme = Config.getUiThemeSettings(); - final ListPreference stylePref = (ListPreference)findPreference(getString(R.string.pref_map_style)); - stylePref.setValue(curTheme); - stylePref.setSummary(stylePref.getEntry()); - stylePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - String themeName = (String)newValue; - if (!Config.setUiThemeSettings(themeName)) - return true; - - ThemeSwitcher.restart(false); - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.MAP_STYLE, - Statistics.params().add(Statistics.EventParam.NAME, themeName)); - - UiThread.runLater(new Runnable() - { - @Override - public void run() - { - stylePref.setSummary(stylePref.getEntry()); - } - }); - - return true; - } - }); - - TwoStatePreference prefAutodownload = (TwoStatePreference)findPreference(getString(R.string.pref_autodownload)); - prefAutodownload.setChecked(Config.isAutodownloadEnabled()); - prefAutodownload.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - boolean value = (Boolean)newValue; - Config.setAutodownloadEnabled(value); - - if (value) - OnmapDownloader.setAutodownloadLocked(false); - - return true; - } - }); - - pref = findPreference(getString(R.string.pref_large_fonts_size)); - ((TwoStatePreference)pref).setChecked(Config.isLargeFontsSize()); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - boolean oldVal = Config.isLargeFontsSize(); - boolean newVal = (Boolean) newValue; - if (oldVal != newVal) - Config.setLargeFontsSize(newVal); - - return true; - } - }); - - final Framework.Params3dMode _3d = new Framework.Params3dMode(); - Framework.nativeGet3dMode(_3d); - - final TwoStatePreference pref3dBuildings = (TwoStatePreference)findPreference(getString(R.string.pref_3d_buildings)); - pref3dBuildings.setChecked(_3d.buildings); - - pref3dBuildings.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Framework.nativeSet3dMode(_3d.enabled, (Boolean)newValue); - return true; - } - }); - - final ListPreference trackPref = (ListPreference)findPreference(getString(R.string.pref_track_record)); - String value = (TrackRecorder.isEnabled() ? String.valueOf(TrackRecorder.getDuration()) : "0"); - trackPref.setValue(value); - trackPref.setSummary(trackPref.getEntry()); - trackPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) - { - int value = Integer.valueOf((String)newValue); - TrackRecorder.setEnabled(value != 0); - - if (value != 0) - TrackRecorder.setDuration(value); - - UiThread.runLater(new Runnable() - { - @Override - public void run() - { - trackPref.setSummary(trackPref.getEntry()); - } - }); - return true; - } - }); - } - - @Override - public void onAttach(Activity activity) - { - super.onAttach(activity); - mPathManager.startExternalStorageWatching(activity, new StoragePathManager.OnStorageListChangedListener() - { - @Override - public void onStorageListChanged(List storageItems, int currentStorageIndex) - { - updateStoragePrefs(); - } - }, null); - } - - @Override - public void onDetach() - { - super.onDetach(); - mPathManager.stopExternalStorageWatching(); - } -} diff --git a/android/src/com/mapswithme/maps/settings/MiscPrefsFragment.java b/android/src/com/mapswithme/maps/settings/MiscPrefsFragment.java deleted file mode 100644 index bdf6631fd5..0000000000 --- a/android/src/com/mapswithme/maps/settings/MiscPrefsFragment.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.mapswithme.maps.settings; - -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.TwoStatePreference; - -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.mapswithme.maps.BuildConfig; -import com.mapswithme.maps.MwmApplication; -import com.mapswithme.maps.R; -import com.mapswithme.maps.location.LocationHelper; -import com.mapswithme.maps.search.SearchFragment; -import com.mapswithme.util.Config; -import com.mapswithme.util.log.LoggerFactory; -import com.mapswithme.util.NetworkPolicy; -import com.mapswithme.util.concurrency.UiThread; -import com.mapswithme.util.statistics.MytargetHelper; -import com.mapswithme.util.statistics.Statistics; - -public class MiscPrefsFragment extends BaseXmlSettingsFragment -{ - @Override - protected int getXmlResources() - { - return R.xml.prefs_misc; - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - Preference pref = findPreference(getString(R.string.pref_send_statistics)); - ((TwoStatePreference)pref).setChecked(Config.isStatisticsEnabled()); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Statistics.INSTANCE.setStatEnabled((Boolean) newValue); - return true; - } - }); - - pref = findPreference(getString(R.string.pref_play_services)); - - if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(MwmApplication.get()) != ConnectionResult.SUCCESS) - getPreferenceScreen().removePreference(pref); - else - { - ((TwoStatePreference) pref).setChecked(Config.useGoogleServices()); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - boolean oldVal = Config.useGoogleServices(); - boolean newVal = ((Boolean) newValue).booleanValue(); - if (oldVal != newVal) - { - Config.setUseGoogleService(newVal); - LocationHelper.INSTANCE.restart(); - } - return true; - } - }); - } - - if (!MytargetHelper.isShowcaseSwitchedOnServer()) - getPreferenceScreen().removePreference(findPreference(getString(R.string.pref_showcase_switched_on))); - - pref = findPreference(getString(R.string.pref_enable_logging)); - if (!MwmApplication.prefs().getBoolean(SearchFragment.PREFS_SHOW_ENABLE_LOGGING_SETTING, - BuildConfig.BUILD_TYPE.equals("beta"))) - { - getPreferenceScreen().removePreference(pref); - } - else - { - final boolean isLoggingEnabled = LoggerFactory.INSTANCE.isFileLoggingEnabled(); - ((TwoStatePreference) pref).setChecked(isLoggingEnabled); - pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - boolean oldVal = isLoggingEnabled; - boolean newVal = (Boolean) newValue; - if (oldVal != newVal) - { - LoggerFactory.INSTANCE.setFileLoggingEnabled(newVal); - } - return true; - } - }); - } - - int curValue = Config.getUseMobileDataSettings(); - final ListPreference mobilePref = (ListPreference)findPreference( - getString(R.string.pref_use_mobile_data)); - if (curValue != NetworkPolicy.NOT_TODAY && curValue != NetworkPolicy.TODAY) - { - mobilePref.setValue(String.valueOf(curValue)); - mobilePref.setSummary(mobilePref.getEntry()); - } - else - { - mobilePref.setSummary(getString(R.string.mobile_data_description)); - } - mobilePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - String valueStr = (String)newValue; - switch (Integer.parseInt(valueStr)) - { - case NetworkPolicy.ASK: - Config.setUseMobileDataSettings(NetworkPolicy.ASK); - break; - case NetworkPolicy.ALWAYS: - Config.setUseMobileDataSettings(NetworkPolicy.ALWAYS); - break; - case NetworkPolicy.NEVER: - Config.setUseMobileDataSettings(NetworkPolicy.NEVER); - break; - default: - throw new AssertionError("Wrong NetworkPolicy type!"); - } - - UiThread.runLater(new Runnable() - { - @Override - public void run() - { - mobilePref.setSummary(mobilePref.getEntry()); - } - }); - - return true; - } - }); - } -} diff --git a/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java b/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java deleted file mode 100644 index 30a2990c6c..0000000000 --- a/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.mapswithme.maps.settings; - -import android.content.Intent; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.preference.TwoStatePreference; -import android.speech.tts.TextToSpeech; -import android.support.annotation.NonNull; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.mapswithme.maps.Framework; -import com.mapswithme.maps.R; -import com.mapswithme.maps.sound.LanguageData; -import com.mapswithme.maps.sound.TtsPlayer; -import com.mapswithme.util.Config; -import com.mapswithme.util.statistics.Statistics; - -public class RoutePrefsFragment extends PreferenceFragment -{ - private static final int REQUEST_INSTALL_DATA = 1; - - private TwoStatePreference mPrefEnabled; - private ListPreference mPrefLanguages; - - private final Map mLanguages = new HashMap<>(); - private LanguageData mCurrentLanguage; - private String mSelectedLanguage; - - private final Preference.OnPreferenceChangeListener mEnabledListener = new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.VOICE_ENABLED, Statistics.params().add(Statistics.EventParam.ENABLED, newValue.toString())); - boolean set = (Boolean)newValue; - if (!set) - { - TtsPlayer.setEnabled(false); - mPrefLanguages.setEnabled(false); - return true; - } - - if (mCurrentLanguage != null && mCurrentLanguage.downloaded) - { - setLanguage(mCurrentLanguage); - return true; - } - - mPrefLanguages.setEnabled(true); - getPreferenceScreen().onItemClick(null, null, mPrefLanguages.getOrder(), 0); - mPrefLanguages.setEnabled(false); - return false; - } - }; - - private final Preference.OnPreferenceChangeListener mLangListener = new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - if (newValue == null) - return false; - - mSelectedLanguage = (String)newValue; - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.VOICE_LANGUAGE, Statistics.params().add(Statistics.EventParam.LANGUAGE, mSelectedLanguage)); - LanguageData lang = mLanguages.get(mSelectedLanguage); - if (lang == null) - return false; - - if (lang.downloaded) - setLanguage(lang); - else - startActivityForResult(new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA); - - return false; - } - }; - - private void enableListeners(boolean enable) - { - mPrefEnabled.setOnPreferenceChangeListener(enable ? mEnabledListener : null); - mPrefLanguages.setOnPreferenceChangeListener(enable ? mLangListener : null); - } - - private void setLanguage(@NonNull LanguageData lang) - { - Config.setTtsEnabled(true); - TtsPlayer.INSTANCE.setLanguage(lang); - mPrefLanguages.setSummary(lang.name); - - update(); - } - - private void update() - { - enableListeners(false); - - List languages = TtsPlayer.INSTANCE.refreshLanguages(); - mLanguages.clear(); - mCurrentLanguage = null; - - if (languages.isEmpty()) - { - mPrefEnabled.setChecked(false); - mPrefEnabled.setEnabled(false); - mPrefEnabled.setSummary(R.string.pref_tts_unavailable); - mPrefLanguages.setEnabled(false); - mPrefLanguages.setSummary(null); - - enableListeners(true); - return; - } - - mPrefEnabled.setChecked(TtsPlayer.INSTANCE.isEnabled()); - mPrefEnabled.setSummary(null); - - final CharSequence[] entries = new CharSequence[languages.size()]; - final CharSequence[] values = new CharSequence[languages.size()]; - for (int i = 0; i < languages.size(); i++) - { - LanguageData lang = languages.get(i); - entries[i] = lang.name; - values[i] = lang.internalCode; - - mLanguages.put(lang.internalCode, lang); - } - - mPrefLanguages.setEntries(entries); - mPrefLanguages.setEntryValues(values); - - mCurrentLanguage = TtsPlayer.getSelectedLanguage(languages); - boolean available = (mCurrentLanguage != null && mCurrentLanguage.downloaded); - mPrefLanguages.setEnabled(available && TtsPlayer.INSTANCE.isEnabled()); - mPrefLanguages.setSummary(available ? mCurrentLanguage.name : null); - mPrefLanguages.setValue(available ? mCurrentLanguage.internalCode : null); - mPrefEnabled.setChecked(available && TtsPlayer.INSTANCE.isEnabled()); - - enableListeners(true); - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.prefs_route); - - mPrefEnabled = (TwoStatePreference) findPreference(getString(R.string.pref_tts_enabled)); - mPrefLanguages = (ListPreference) findPreference(getString(R.string.pref_tts_language)); - - final Framework.Params3dMode _3d = new Framework.Params3dMode(); - Framework.nativeGet3dMode(_3d); - - final TwoStatePreference pref3d = (TwoStatePreference)findPreference(getString(R.string.pref_3d)); - pref3d.setChecked(_3d.enabled); - - pref3d.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Framework.nativeSet3dMode((Boolean) newValue, _3d.buildings); - return true; - } - }); - - boolean autozoomEnabled = Framework.nativeGetAutoZoomEnabled(); - final TwoStatePreference prefAutoZoom = (TwoStatePreference)findPreference(getString(R.string.pref_auto_zoom)); - prefAutoZoom.setChecked(autozoomEnabled); - prefAutoZoom.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Framework.nativeSetAutoZoomEnabled((Boolean)newValue); - return true; - } - }); - - boolean simplifiedColorsEnabled = Framework.nativeGetSimplifiedTrafficColorsEnabled(); - final TwoStatePreference prefSimplifiedColors = (TwoStatePreference)findPreference( - getString(R.string.pref_traffic_simplified_colors)); - prefSimplifiedColors.setChecked(simplifiedColorsEnabled); - prefSimplifiedColors.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) - { - Framework.nativeSetSimplifiedTrafficColorsEnabled((Boolean)newValue); - return true; - } - }); - - update(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) - { - // Do not check resultCode here as it is always RESULT_CANCELED - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_INSTALL_DATA) - { - update(); - - LanguageData lang = mLanguages.get(mSelectedLanguage); - if (lang != null && lang.downloaded) - setLanguage(lang); - } - } -} diff --git a/android/src/com/mapswithme/maps/settings/SettingsActivity.java b/android/src/com/mapswithme/maps/settings/SettingsActivity.java index 35c79307fd..bd55a918b6 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsActivity.java +++ b/android/src/com/mapswithme/maps/settings/SettingsActivity.java @@ -1,303 +1,89 @@ package com.mapswithme.maps.settings; -import android.app.Activity; -import android.app.Fragment; -import android.content.Intent; -import android.content.res.Configuration; -import android.media.AudioManager; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.support.annotation.StyleRes; -import android.support.v7.app.AppCompatDelegate; -import android.support.v7.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; -import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; -import com.mapswithme.maps.base.BaseActivity; -import com.mapswithme.maps.base.BaseActivityDelegate; -import com.mapswithme.maps.base.OnBackPressListener; -import com.mapswithme.maps.editor.OsmOAuth; -import com.mapswithme.maps.editor.ProfileActivity; -import com.mapswithme.util.FragmentListHelper; -import com.mapswithme.util.ThemeUtils; -import com.mapswithme.util.UiUtils; -import com.mapswithme.util.statistics.AlohaHelper; -import com.mapswithme.util.statistics.Statistics; +import com.mapswithme.maps.base.BaseToolbarActivity; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class SettingsActivity extends PreferenceActivity - implements BaseActivity +public class SettingsActivity extends BaseToolbarActivity + implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, + PreferenceFragmentCompat.OnPreferenceStartScreenCallback { - private final BaseActivityDelegate mActivityDelegate = new BaseActivityDelegate(this); - private final FragmentListHelper mFragmentListHelper = new FragmentListHelper(); - private AppCompatDelegate mDelegate; - private CharSequence mNextBreadcrumb; - private final Map mHeaders = new HashMap<>(); + @Nullable + private String mLastTitle; @Override - public Activity get() + protected int getContentLayoutResId() { - return this; + return R.layout.activity_settings; } @Override - @StyleRes - public int getThemeResourceId(@NonNull String theme) + protected Class getFragmentClass() { - if (ThemeUtils.isDefaultTheme(theme)) - return R.style.MwmTheme_Settings; - - if (ThemeUtils.isNightTheme(theme)) - return R.style.MwmTheme_Night_Settings; - - throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme); - } - - private AppCompatDelegate getAppDelegate() - { - if (mDelegate == null) - mDelegate = AppCompatDelegate.create(this, null); - - return mDelegate; + return SettingsPrefsFragment.class; } @Override - public void onAttachFragment(Fragment fragment) + @SuppressWarnings("unchecked") + public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { - mFragmentListHelper.onAttachFragment(fragment); - } - - @Override - public void onBuildHeaders(List
target) - { - loadHeadersFromResource(R.xml.prefs_headers, target); - - mHeaders.clear(); - for (Header h : target) + String title = TextUtils.isEmpty(pref.getTitle()) ? null : pref.getTitle().toString(); + try { - mHeaders.put(h.id, h); - // Hack to change profile header to username, if user is logged in. - if (h.id == R.id.osm_profile && OsmOAuth.isAuthorized()) - { - h.titleRes = 0; - h.title = OsmOAuth.getUsername(); - } - } - } - - @Override - public void onHeaderClick(@NonNull Header header, int position) - { - if (header.id == R.id.group_map) - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.GROUP_MAP); - else if (header.id == R.id.group_route) - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.GROUP_ROUTE); - else if (header.id == R.id.group_misc) - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.GROUP_MISC); - else if (header.id == R.id.help) + Class fragment = (Class) Class.forName(pref.getFragment()); + replaceFragment(fragment, title, pref.getExtras()); + } catch (ClassNotFoundException e) { - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.HELP); - AlohaHelper.logClick(AlohaHelper.Settings.HELP); + e.printStackTrace(); } - else if (header.id == R.id.about) - { - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.ABOUT); - AlohaHelper.logClick(AlohaHelper.Settings.ABOUT); - } - else if (header.id == R.id.osm_profile) - { - Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.OSM_PROFILE); - startActivity(new Intent(this, ProfileActivity.class)); - } - - super.onHeaderClick(header, position); - } - - @Override - protected void onCreate(Bundle savedInstanceState) - { - mActivityDelegate.onCreate(); - getAppDelegate().installViewFactory(); - getAppDelegate().onCreate(savedInstanceState); - - super.onCreate(savedInstanceState); - UiUtils.setupStatusBar(this); - setVolumeControlStream(AudioManager.STREAM_MUSIC); - - // Hack to attach Toolbar and make it work on native PreferenceActivity - ViewGroup root = (ViewGroup)findViewById(android.R.id.list).getParent().getParent().getParent(); - View toolbarHolder = LayoutInflater.from(this).inflate(R.layout.toolbar_default, root, false); - Toolbar toolbar = (Toolbar) toolbarHolder.findViewById(R.id.toolbar); - UiUtils.extendViewWithStatusBar(toolbar); - UiUtils.showHomeUpButton(toolbar); - - // First, add toolbar view to UI. - root.addView(toolbarHolder, 0); - // Second, attach it as ActionBar (it does not add view, so we need previous step). - getAppDelegate().setSupportActionBar(toolbar); - - MwmApplication.get().initNativeCore(); - MwmApplication.get().initCounters(); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) - { - super.onPostCreate(savedInstanceState); - mActivityDelegate.onPostCreate(); - getAppDelegate().onPostCreate(savedInstanceState); - } - - @Override - public void setContentView(int layoutResID) - { - getAppDelegate().setContentView(layoutResID); - } - - @Override - public void setContentView(View view) - { - getAppDelegate().setContentView(view); - } - - @Override - public void setContentView(View view, ViewGroup.LayoutParams params) - { - getAppDelegate().setContentView(view, params); - } - - @Override - public void addContentView(View view, ViewGroup.LayoutParams params) - { - getAppDelegate().addContentView(view, params); - } - - @Override - protected void onTitleChanged(CharSequence title, int color) - { - super.onTitleChanged(title, color); - getAppDelegate().setTitle(title); - } - - @Override - public void invalidateOptionsMenu() - { - super.invalidateOptionsMenu(); - getAppDelegate().invalidateOptionsMenu(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) - { - super.onConfigurationChanged(newConfig); - getAppDelegate().onConfigurationChanged(newConfig); - } - - @Override - protected void onDestroy() - { - super.onDestroy(); - mActivityDelegate.onDestroy(); - getAppDelegate().onDestroy(); - } - - @Override - protected boolean isValidFragment(String fragmentName) - { return true; } @Override - protected void onStart() + public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) { - super.onStart(); - mActivityDelegate.onStart(); - invalidateHeaders(); + Bundle args = new Bundle(); + args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey()); + replaceFragment(SettingsPrefsFragment.class, preferenceScreen.getTitle().toString(), args); + return true; } - @Override - protected void onStop() + public void replaceFragment(@NonNull Class fragmentClass, + @Nullable String title, @Nullable Bundle args) { - super.onStop(); - mActivityDelegate.onStop(); - getAppDelegate().onStop(); - } + final int resId = getFragmentContentResId(); + if (resId <= 0 || findViewById(resId) == null) + throw new IllegalStateException("Fragment can't be added, since getFragmentContentResId() isn't implemented or returns wrong resourceId."); - @Override - protected void onResume() - { - super.onResume(); - mActivityDelegate.onResume(); - } + String name = fragmentClass.getName(); + final Fragment fragment = Fragment.instantiate(this, name, args); + getSupportFragmentManager().beginTransaction() + .replace(resId, fragment, name) + .addToBackStack(null) + .commitAllowingStateLoss(); + getSupportFragmentManager().executePendingTransactions(); - @Override - protected void onPostResume() - { - super.onPostResume(); - mActivityDelegate.onPostResume(); - getAppDelegate().onPostResume(); - } - - @Override - protected void onPause() - { - super.onPause(); - mActivityDelegate.onPause(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - if (item.getItemId() == android.R.id.home) + if(title != null) { - super.onBackPressed(); - return true; + mLastTitle = getToolbar().getTitle().toString(); + getToolbar().setTitle(title); } - - return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { - for (Fragment f : mFragmentListHelper.getFragments()) - if ((f instanceof OnBackPressListener) && ((OnBackPressListener)f).onBackPressed()) - return; + if (mLastTitle != null) + getToolbar().setTitle(mLastTitle); super.onBackPressed(); } - - @Override - public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) - { - if (mNextBreadcrumb != null) - { - title = mNextBreadcrumb; - mNextBreadcrumb = null; - } - - super.showBreadCrumbs(title, shortTitle); - } - - public void switchToHeader(long id) - { - Header h = mHeaders.get(id); - if (h != null) - switchToHeader(h); - } - - public void switchToFragment(Class fragmentClass, @StringRes int breadcrumb) - { - mNextBreadcrumb = getString(breadcrumb); - switchToHeader(fragmentClass.getName(), null); - } } diff --git a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java new file mode 100644 index 0000000000..faf492902e --- /dev/null +++ b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java @@ -0,0 +1,756 @@ +package com.mapswithme.maps.settings; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.speech.tts.TextToSpeech; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.TwoStatePreference; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.mapswithme.maps.BuildConfig; +import com.mapswithme.maps.Framework; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.R; +import com.mapswithme.maps.downloader.MapManager; +import com.mapswithme.maps.downloader.OnmapDownloader; +import com.mapswithme.maps.editor.ProfileActivity; +import com.mapswithme.maps.location.LocationHelper; +import com.mapswithme.maps.location.TrackRecorder; +import com.mapswithme.maps.search.SearchFragment; +import com.mapswithme.maps.sound.LanguageData; +import com.mapswithme.maps.sound.TtsPlayer; +import com.mapswithme.util.Config; +import com.mapswithme.util.NetworkPolicy; +import com.mapswithme.util.ThemeSwitcher; +import com.mapswithme.util.concurrency.UiThread; +import com.mapswithme.util.log.LoggerFactory; +import com.mapswithme.util.statistics.AlohaHelper; +import com.mapswithme.util.statistics.MytargetHelper; +import com.mapswithme.util.statistics.Statistics; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SettingsPrefsFragment extends BaseXmlSettingsFragment +{ + private static final int REQUEST_INSTALL_DATA = 1; + + @NonNull + private final StoragePathManager mPathManager = new StoragePathManager(); + @Nullable + private Preference mStoragePref; + + @Nullable + private TwoStatePreference mPrefEnabled; + @Nullable + private ListPreference mPrefLanguages; + @Nullable + private Preference mLangInfo; + + @NonNull + private final Map mLanguages = new HashMap<>(); + private LanguageData mCurrentLanguage; + private String mSelectedLanguage; + + private boolean singleStorageOnly() + { + return !mPathManager.hasMoreThanOneStorage(); + } + + private void updateStoragePrefs() + { + Preference old = findPreference(getString(R.string.pref_storage)); + + if (singleStorageOnly()) + { + if (old != null) + getPreferenceScreen().removePreference(old); + } + else + { + if (old == null && mStoragePref != null) + getPreferenceScreen().addPreference(mStoragePref); + } + } + + private final Preference.OnPreferenceChangeListener mEnabledListener = new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.VOICE_ENABLED, Statistics.params().add(Statistics.EventParam.ENABLED, newValue.toString())); + Preference root = findPreference(getString(R.string.pref_tts_screen)); + boolean set = (Boolean)newValue; + if (!set) + { + TtsPlayer.setEnabled(false); + if (mPrefLanguages != null) + mPrefLanguages.setEnabled(false); + if (mLangInfo != null) + mLangInfo.setSummary(R.string.prefs_languages_information_off); + + if (root != null) + root.setSummary(R.string.off); + + if (mPrefEnabled != null) + mPrefEnabled.setTitle(R.string.off); + return true; + } + + if (mLangInfo != null) + mLangInfo.setSummary(R.string.prefs_languages_information); + if (root != null) + root.setSummary(R.string.on); + if (mPrefEnabled != null) + mPrefEnabled.setTitle(R.string.on); + + if (mCurrentLanguage != null && mCurrentLanguage.downloaded) + { + setLanguage(mCurrentLanguage); + return true; + } + + return false; + } + }; + + private final Preference.OnPreferenceChangeListener mLangListener = new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + if (newValue == null) + return false; + + mSelectedLanguage = (String)newValue; + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.VOICE_LANGUAGE, Statistics.params().add(Statistics.EventParam.LANGUAGE, mSelectedLanguage)); + LanguageData lang = mLanguages.get(mSelectedLanguage); + if (lang == null) + return false; + + if (lang.downloaded) + setLanguage(lang); + else + startActivityForResult(new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA); + + return false; + } + }; + + private void enableListeners(boolean enable) + { + if (mPrefEnabled != null) + mPrefEnabled.setOnPreferenceChangeListener(enable ? mEnabledListener : null); + if (mPrefLanguages != null) + mPrefLanguages.setOnPreferenceChangeListener(enable ? mLangListener : null); + } + + private void setLanguage(@NonNull LanguageData lang) + { + Config.setTtsEnabled(true); + TtsPlayer.INSTANCE.setLanguage(lang); + if (mPrefLanguages != null) + mPrefLanguages.setSummary(lang.name); + + updateTts(); + } + + private void updateTts() + { + if (mPrefEnabled == null || mPrefLanguages == null || mLangInfo == null) + return; + + enableListeners(false); + + List languages = TtsPlayer.INSTANCE.refreshLanguages(); + mLanguages.clear(); + mCurrentLanguage = null; + + Preference root = findPreference(getString(R.string.pref_tts_screen)); + + if (languages.isEmpty()) + { + mPrefEnabled.setChecked(false); + mPrefEnabled.setEnabled(false); + mPrefEnabled.setSummary(R.string.pref_tts_unavailable); + mPrefEnabled.setTitle(R.string.off); + mPrefLanguages.setEnabled(false); + mPrefLanguages.setSummary(null); + mLangInfo.setSummary(R.string.prefs_languages_information_off); + if (root != null) + root.setSummary(R.string.off); + + enableListeners(true); + return; + } + + boolean enabled = TtsPlayer.INSTANCE.isEnabled(); + mPrefEnabled.setChecked(enabled); + mPrefEnabled.setSummary(null); + mPrefEnabled.setTitle(enabled ? R.string.on : R.string.off); + mLangInfo.setSummary(enabled ? R.string.prefs_languages_information + : R.string.prefs_languages_information_off); + + if (root != null) + root.setSummary(enabled ? R.string.on : R.string.off); + + final CharSequence[] entries = new CharSequence[languages.size()]; + final CharSequence[] values = new CharSequence[languages.size()]; + for (int i = 0; i < languages.size(); i++) + { + LanguageData lang = languages.get(i); + entries[i] = lang.name; + values[i] = lang.internalCode; + + mLanguages.put(lang.internalCode, lang); + } + + mPrefLanguages.setEntries(entries); + mPrefLanguages.setEntryValues(values); + + mCurrentLanguage = TtsPlayer.getSelectedLanguage(languages); + boolean available = (mCurrentLanguage != null && mCurrentLanguage.downloaded); + mPrefLanguages.setEnabled(available && TtsPlayer.INSTANCE.isEnabled()); + mPrefLanguages.setSummary(available ? mCurrentLanguage.name : null); + mPrefLanguages.setValue(available ? mCurrentLanguage.internalCode : null); + mPrefEnabled.setChecked(available && TtsPlayer.INSTANCE.isEnabled()); + + enableListeners(true); + } + + @Override + public Fragment getCallbackFragment() + { + return this; + } + + @Override + protected int getXmlResources() + { + return R.xml.prefs_main; + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + mStoragePref = findPreference(getString(R.string.pref_storage)); + mPrefEnabled = (TwoStatePreference) findPreference(getString(R.string.pref_tts_enabled)); + mPrefLanguages = (ListPreference) findPreference(getString(R.string.pref_tts_language)); + mLangInfo = findPreference(getString(R.string.pref_tts_info)); + updateStoragePrefs(); + initStoragePrefCallbacks(); + initMeasureUnitsPrefsCallbacks(); + initZoomPrefsCallbacks(); + initMapStylePrefsCallbacks(); + initAutoDownloadPrefsCallbacks(); + initLargeFontSizePrefsCallbacks(); + init3dModePrefsCallbacks(); + initPerspectivePrefsCallbacks(); + initTrackRecordPrefsCallbacks(); + initStatisticsPrefsCallback(); + initPlayServicesPrefsCallbacks(); + initAutoZoomPrefsCallbacks(); + initSimplifiedTrafficColorsPrefsCallbacks(); + + if (!MytargetHelper.isShowcaseSwitchedOnServer()) + getPreferenceScreen().removePreference(findPreference(getString(R.string.pref_showcase_switched_on))); + + initLoggingEnabledPrefsCallbacks(); + initUseMobileDataPrefsCallbacks(); + + updateTts(); + } + + @Override + public void onResume() + { + super.onResume(); + + initTrackRecordPrefsCallbacks(); + updateTts(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) + { + // Do not check resultCode here as it is always RESULT_CANCELED + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_INSTALL_DATA) + { + updateTts(); + + LanguageData lang = mLanguages.get(mSelectedLanguage); + if (lang != null && lang.downloaded) + setLanguage(lang); + } + } + + @Override + public boolean onPreferenceTreeClick(Preference preference) + { + if (preference.getKey() != null && preference.getKey().equals(getString(R.string.pref_help))) + { + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.HELP); + AlohaHelper.logClick(AlohaHelper.Settings.HELP); + } + else if (preference.getKey() != null && preference.getKey().equals(getString(R.string.pref_about))) + { + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.ABOUT); + AlohaHelper.logClick(AlohaHelper.Settings.ABOUT); + } + else if (preference.getKey() != null && preference.getKey().equals(getString(R.string.pref_osm_profile))) + { + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.OSM_PROFILE); + startActivity(new Intent(getActivity(), ProfileActivity.class)); + } + return super.onPreferenceTreeClick(preference); + } + + private void initSimplifiedTrafficColorsPrefsCallbacks() + { + boolean simplifiedColorsEnabled = Framework.nativeGetSimplifiedTrafficColorsEnabled(); + final TwoStatePreference prefSimplifiedColors = (TwoStatePreference)findPreference( + getString(R.string.pref_traffic_simplified_colors)); + prefSimplifiedColors.setChecked(simplifiedColorsEnabled); + prefSimplifiedColors.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSetSimplifiedTrafficColorsEnabled((Boolean)newValue); + return true; + } + }); + } + + private void initLargeFontSizePrefsCallbacks() + { + Preference pref = findPreference(getString(R.string.pref_large_fonts_size)); + ((TwoStatePreference)pref).setChecked(Config.isLargeFontsSize()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + boolean oldVal = Config.isLargeFontsSize(); + boolean newVal = (Boolean) newValue; + if (oldVal != newVal) + Config.setLargeFontsSize(newVal); + + return true; + } + }); + } + + private void initUseMobileDataPrefsCallbacks() + { + int curValue = Config.getUseMobileDataSettings(); + final ListPreference mobilePref = (ListPreference)findPreference( + getString(R.string.pref_use_mobile_data)); + if (curValue != NetworkPolicy.NOT_TODAY && curValue != NetworkPolicy.TODAY) + { + mobilePref.setValue(String.valueOf(curValue)); + mobilePref.setSummary(mobilePref.getEntry()); + } + else + { + mobilePref.setSummary(getString(R.string.mobile_data_description)); + } + mobilePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + String valueStr = (String)newValue; + switch (Integer.parseInt(valueStr)) + { + case NetworkPolicy.ASK: + Config.setUseMobileDataSettings(NetworkPolicy.ASK); + break; + case NetworkPolicy.ALWAYS: + Config.setUseMobileDataSettings(NetworkPolicy.ALWAYS); + break; + case NetworkPolicy.NEVER: + Config.setUseMobileDataSettings(NetworkPolicy.NEVER); + break; + default: + throw new AssertionError("Wrong NetworkPolicy type!"); + } + + UiThread.runLater(new Runnable() + { + @Override + public void run() + { + mobilePref.setSummary(mobilePref.getEntry()); + } + }); + + return true; + } + }); + } + + private void initLoggingEnabledPrefsCallbacks() + { + Preference pref = findPreference(getString(R.string.pref_enable_logging)); + if (!MwmApplication.prefs().getBoolean(SearchFragment.PREFS_SHOW_ENABLE_LOGGING_SETTING, + BuildConfig.BUILD_TYPE.equals("beta"))) + { + getPreferenceScreen().removePreference(pref); + } + else + { + final boolean isLoggingEnabled = LoggerFactory.INSTANCE.isFileLoggingEnabled(); + ((TwoStatePreference) pref).setChecked(isLoggingEnabled); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + boolean oldVal = isLoggingEnabled; + boolean newVal = (Boolean) newValue; + if (oldVal != newVal) + { + LoggerFactory.INSTANCE.setFileLoggingEnabled(newVal); + } + return true; + } + }); + } + } + + private void initAutoZoomPrefsCallbacks() + { + final TwoStatePreference pref = (TwoStatePreference)findPreference(getString(R.string.pref_auto_zoom)); + if (pref == null) + return; + + boolean autozoomEnabled = Framework.nativeGetAutoZoomEnabled(); + pref.setChecked(autozoomEnabled); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSetAutoZoomEnabled((Boolean)newValue); + return true; + } + }); + } + + private void initPlayServicesPrefsCallbacks() + { + Preference pref = findPreference(getString(R.string.pref_play_services)); + if (pref == null) + return; + + if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(MwmApplication.get()) != ConnectionResult.SUCCESS) + getPreferenceScreen().removePreference(pref); + else + { + ((TwoStatePreference) pref).setChecked(Config.useGoogleServices()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + boolean oldVal = Config.useGoogleServices(); + boolean newVal = (Boolean) newValue; + if (oldVal != newVal) + { + Config.setUseGoogleService(newVal); + LocationHelper.INSTANCE.restart(); + } + return true; + } + }); + } + } + + private void initStatisticsPrefsCallback() + { + Preference pref = findPreference(getString(R.string.pref_send_statistics)); + if (pref == null) + return; + + ((TwoStatePreference)pref).setChecked(Config.isStatisticsEnabled()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Statistics.INSTANCE.setStatEnabled((Boolean) newValue); + return true; + } + }); + } + + private void initTrackRecordPrefsCallbacks() + { + final ListPreference trackPref = (ListPreference)findPreference(getString(R.string.pref_track_record)); + final Preference pref = findPreference(getString(R.string.pref_track_record_time)); + final Preference root = findPreference(getString(R.string.pref_track_screen)); + if (trackPref == null || pref == null) + return; + + boolean enabled = TrackRecorder.isEnabled(); + ((TwoStatePreference)pref).setChecked(enabled); + trackPref.setEnabled(enabled); + if (root != null) + root.setSummary(enabled ? R.string.on : R.string.off); + pref.setTitle(enabled ? R.string.on : R.string.off); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + boolean enabled = (Boolean) newValue; + TrackRecorder.setEnabled(enabled); + Statistics.INSTANCE.setStatEnabled(enabled); + trackPref.setEnabled(enabled); + if (root != null) + root.setSummary(enabled ? R.string.on : R.string.off); + pref.setTitle(enabled ? R.string.on : R.string.off); + trackPref.performClick(); + return true; + } + }); + + String value = (enabled ? String.valueOf(TrackRecorder.getDuration()) : "0"); + trackPref.setValue(value); + trackPref.setSummary(trackPref.getEntry()); + trackPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) + { + int value = Integer.valueOf((String)newValue); + boolean enabled = value != 0; + if (enabled) + TrackRecorder.setDuration(value); + TrackRecorder.setEnabled(enabled); + ((TwoStatePreference) pref).setChecked(enabled); + trackPref.setEnabled(enabled); + if (root != null) + root.setSummary(enabled ? R.string.on : R.string.off); + pref.setTitle(enabled ? R.string.on : R.string.off); + + UiThread.runLater(new Runnable() + { + @Override + public void run() + { + trackPref.setSummary(trackPref.getEntry()); + } + }); + return true; + } + }); + } + + private void init3dModePrefsCallbacks() + { + final TwoStatePreference pref = (TwoStatePreference)findPreference(getString(R.string.pref_3d_buildings)); + if (pref == null) + return; + + final Framework.Params3dMode _3d = new Framework.Params3dMode(); + Framework.nativeGet3dMode(_3d); + + pref.setChecked(_3d.buildings); + + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSet3dMode(_3d.enabled, (Boolean)newValue); + return true; + } + }); + } + + private void initPerspectivePrefsCallbacks() + { + final TwoStatePreference pref = (TwoStatePreference)findPreference(getString(R.string.pref_3d)); + if (pref == null) + return; + + final Framework.Params3dMode _3d = new Framework.Params3dMode(); + Framework.nativeGet3dMode(_3d); + + pref.setChecked(_3d.enabled); + + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSet3dMode((Boolean) newValue, _3d.buildings); + return true; + } + }); + } + + private void initAutoDownloadPrefsCallbacks() + { + TwoStatePreference pref = (TwoStatePreference)findPreference(getString(R.string.pref_autodownload)); + if (pref == null) + return; + + pref.setChecked(Config.isAutodownloadEnabled()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + boolean value = (Boolean)newValue; + Config.setAutodownloadEnabled(value); + + if (value) + OnmapDownloader.setAutodownloadLocked(false); + + return true; + } + }); + } + + private void initMapStylePrefsCallbacks() + { + final ListPreference pref = (ListPreference)findPreference(getString(R.string.pref_map_style)); + if (pref == null) + return; + + String curTheme = Config.getUiThemeSettings(); + pref.setValue(curTheme); + pref.setSummary(pref.getEntry()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + String themeName = (String)newValue; + if (!Config.setUiThemeSettings(themeName)) + return true; + + ThemeSwitcher.restart(false); + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.MAP_STYLE, + Statistics.params().add(Statistics.EventParam.NAME, themeName)); + + UiThread.runLater(new Runnable() + { + @Override + public void run() + { + pref.setSummary(pref.getEntry()); + } + }); + + return true; + } + }); + } + + private void initZoomPrefsCallbacks() + { + Preference pref = findPreference(getString(R.string.pref_show_zoom_buttons)); + if (pref == null) + return; + + ((TwoStatePreference)pref).setChecked(Config.showZoomButtons()); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.ZOOM); + Config.setShowZoomButtons((Boolean) newValue); + return true; + } + }); + } + + private void initMeasureUnitsPrefsCallbacks() + { + Preference pref = findPreference(getString(R.string.pref_munits)); + if (pref == null) + return; + + ((ListPreference)pref).setValue(String.valueOf(UnitLocale.getUnits())); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + UnitLocale.setUnits(Integer.parseInt((String) newValue)); + Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.UNITS); + AlohaHelper.logClick(AlohaHelper.Settings.CHANGE_UNITS); + return true; + } + }); + } + + private void initStoragePrefCallbacks() + { + if (mStoragePref == null) + return; + + mStoragePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() + { + @Override + public boolean onPreferenceClick(Preference preference) + { + if (MapManager.nativeIsDownloading()) + new AlertDialog.Builder(getActivity()) + .setTitle(getString(R.string.downloading_is_active)) + .setMessage(getString(R.string.cant_change_this_setting)) + .setPositiveButton(getString(R.string.ok), null) + .show(); + else +// getSettingsActivity().switchToFragment(StoragePathFragment.class, R.string.maps_storage); + getSettingsActivity().replaceFragment(StoragePathFragment.class, + getString(R.string.maps_storage), null); + + return true; + } + }); + } + + @Override + public void onAttach(Context context) + { + super.onAttach(context); + + if (!(context instanceof Activity)) + return; + + mPathManager.startExternalStorageWatching((Activity) context, new StoragePathManager.OnStorageListChangedListener() + { + @Override + public void onStorageListChanged(List storageItems, int currentStorageIndex) + { + updateStoragePrefs(); + } + }, null); + } + + @Override + public void onDetach() + { + super.onDetach(); + mPathManager.stopExternalStorageWatching(); + } +} diff --git a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java index 4a5a50ed83..3bbcb0eff5 100644 --- a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java +++ b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java @@ -16,7 +16,6 @@ import java.util.Locale; import com.mapswithme.maps.R; import com.mapswithme.maps.base.OnBackPressListener; -import com.mapswithme.maps.widget.BaseShadowController; import com.mapswithme.util.Constants; import com.mapswithme.util.Utils; @@ -36,12 +35,6 @@ public class StoragePathFragment extends BaseSettingsFragment return R.layout.fragment_prefs_storage; } - @Override - protected BaseShadowController createShadowController() - { - return null; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -148,13 +141,6 @@ public class StoragePathFragment extends BaseSettingsFragment @Override public boolean onBackPressed() { - SettingsActivity activity = (SettingsActivity)getActivity(); - if (activity.onIsMultiPane()) - { - activity.switchToHeader(R.id.group_map); - return true; - } - return false; } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java b/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java index 6656f4c0c2..4357cf1f12 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java +++ b/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java @@ -78,10 +78,8 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View. } @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - final Bundle args = getArguments(); mCategoryId = args.getInt(EXTRA_CATEGORY_ID); int bookmarkId = args.getInt(EXTRA_BOOKMARK_ID); diff --git a/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java b/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java index 05e92ee566..ab2e584d85 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java +++ b/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java @@ -1,6 +1,7 @@ package com.mapswithme.maps.widget.placepage; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.Toolbar; import android.text.Html; @@ -49,14 +50,14 @@ public class EditDescriptionFragment extends BaseMwmDialogFragment } @Override - public void onViewCreated(View view, Bundle savedInstanceState) + protected void safeOnViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mBookmark = getArguments().getParcelable(EXTRA_BOOKMARK); - String description = mBookmark.getBookmarkDescription(); + String description = null; + if (mBookmark != null) + description = mBookmark.getBookmarkDescription(); - if (StringUtils.nativeIsHtml(description)) + if (description != null && StringUtils.nativeIsHtml(description)) { final String descriptionNoSimpleTags = StringUtils.removeEditTextHtmlTags(description); if (!StringUtils.nativeIsHtml(descriptionNoSimpleTags)) diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index 2f2e4fb4e5..e06afe70da 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -14,6 +14,7 @@ import android.support.annotation.DimenRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.NavUtils; import android.support.v7.app.AlertDialog; import android.text.SpannableStringBuilder; @@ -40,6 +41,8 @@ import java.util.Currency; import java.util.Locale; import java.util.Map; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; + public class Utils { private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); @@ -400,7 +403,8 @@ public class Utils try { activity.startActivity(intent); - } catch (ActivityNotFoundException e) + } + catch (ActivityNotFoundException e) { AlohaHelper.logException(e); } @@ -408,4 +412,39 @@ public class Utils }); } } + + public static boolean checkPermissions(@NonNull Activity activity, @NonNull String[] permissions, + int requestCode) + { + if (Build.VERSION.SDK_INT >= 23) + { + boolean isGranted = false; + for (String permission: permissions) + { + isGranted = activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; + if (!isGranted) + break; + } + if (isGranted) + { + return true; + } + else + { + ActivityCompat.requestPermissions(activity, permissions, requestCode); + return false; + } + } + + //permission is automatically granted on sdk<23 upon installation + return true; + } + + public static boolean isWriteExternalGranted(@NonNull Activity activity) + { + if (Build.VERSION.SDK_INT >= 23) + return activity.checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + + return true; + } } diff --git a/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings index 105cc45e44..3a1ef44bf6 100644 --- a/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "بحث"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "الخريطة"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "منتج"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings index 257fcc5475..014019656a 100644 --- a/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Vyhledávání"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Letovisko"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings index 58180ef40b..a91c7f5036 100644 --- a/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Søg"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kort"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings index f4ab6e1eca..a127f751cf 100644 --- a/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Suche"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Karte"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings index 3360b3dc82..755b96c205 100644 --- a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Search"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Map"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings index 354ec24978..d12acbe404 100644 --- a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Search"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Map"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings index ab29ef75aa..a0e5081a9a 100644 --- a/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Buscar"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings index c6638a399a..8c6a43368a 100644 --- a/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Haku"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kartta"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Lomakohteet"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings index 6d95a9cde9..a9e081311a 100644 --- a/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Rechercher"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Carte"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Complexe touristique"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings index 001d4ab753..8d2c8f58d5 100644 --- a/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Keresés"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Térkép"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort hotel"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings index b69aba129e..f13a26539d 100644 --- a/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Cari"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Peta"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Sanggraloka"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings index 9848bfd2ce..b070b8851d 100644 --- a/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Cerca"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mappa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings index dfabbc74ed..568735b60b 100644 --- a/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "検索"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地図"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "リゾート"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings index c8f0563e0b..117c84dcaf 100644 --- a/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "검색"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "지도"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "리조트"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings index 96252709b6..bde13cb3e1 100644 --- a/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Søk"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kart"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Ferieresort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings index 362b44abf6..963a26c42b 100644 --- a/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Zoek"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kaart"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Complex"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings index 92bf85cf6d..5c8aa19950 100644 --- a/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Wyszukaj"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Dom wczasowy"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings index bb33331cfd..3e24d2483c 100644 --- a/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Pesquisar"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings index 366246c11a..11a461f482 100644 --- a/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Căutare"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Hartă"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Stațiuni"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings index e41a66518e..dac5301ed3 100644 --- a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Поиск"; +/* Settings general group in settings screen */ +"prefs_group_general" = "Общие настройки"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Информация"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Карта"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Дом отдыха"; "search_hotel_filter_motel" = "Мотель"; + +"on" = "Вкл."; + +"off" = "Выкл."; diff --git a/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings index cfd9e7edb7..138799e784 100644 --- a/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Vyhľadávanie"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Rezort"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings index 7a6a7ac155..8f9f42f50e 100644 --- a/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Sök"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Karta"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Resorter"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings index c17d68a652..d120d388ca 100644 --- a/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "ค้นหา"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "แผนที่"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "รีสอร์ต"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings index 23304911f0..15cd2d50fb 100644 --- a/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Ara"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Harita"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Tatil köyü"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings index fa357a9aa0..7292a3f6a4 100644 --- a/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Пошук"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Карта"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Будинок відпочинку"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings index bf599eeaf0..b2c263a551 100644 --- a/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "Tìm kiếm"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Bản đồ"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "Khu nghỉ dưỡng"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings index 530fd4d7d0..bffb9135a1 100644 --- a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "搜索"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地图"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "度假村"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings index 6e00dafacf..ce1a3aadff 100644 --- a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings @@ -467,6 +467,18 @@ "menu_search" = "搜尋"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地圖"; @@ -1870,3 +1882,7 @@ "search_hotel_filter_resort" = "度假酒店"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/strings.txt b/strings.txt index 210b7270e2..2591974923 100644 --- a/strings.txt +++ b/strings.txt @@ -5098,6 +5098,30 @@ he = חפוש sk = Vyhľadávanie + [prefs_group_general] + comment = Settings general group in settings screen + tags = android + en = General settings + ru = Общие настройки + + [prefs_group_information] + comment = Settings information group in settings screen + tags = android + en = Information + ru = Информация + + [prefs_languages_information] + comment = Settings languages information in voice instructions screen + tags = android + en = Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). + ru = Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). + + [prefs_languages_information_off] + comment = Settings languages off information in voice instructions screen + tags = android + en = For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. + ru = For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. + [prefs_group_map] comment = Settings screen: "Map" category title tags = android @@ -23361,3 +23385,11 @@ [view_campaign_button] en = View ad campagain + + [on] + en = On + ru = Вкл. + + [off] + en = Off + ru = Выкл. From 3547acb16c60d24008a0d081813a1c45e1434576 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Tue, 28 Mar 2017 11:08:54 +0400 Subject: [PATCH 02/16] [android] Fixed crash after branch rebase --- .../maps/location/LocationHelper.java | 9 +++------ .../maps/settings/SettingsPrefsFragment.java | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index d715da5636..63c676823a 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -167,12 +167,9 @@ public enum LocationHelper @UiThread public void initialize() { - if (MwmApplication.get().isFrameworkInitialized()) - { - initProvider(); - LocationState.nativeSetListener(mMyPositionModeListener); - MwmApplication.backgroundTracker().addListener(mOnTransition); - } + initProvider(); + LocationState.nativeSetListener(mMyPositionModeListener); + MwmApplication.backgroundTracker().addListener(mOnTransition); } private void initProvider() diff --git a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java index faf492902e..bdb8bcf264 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java +++ b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java @@ -320,9 +320,12 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private void initSimplifiedTrafficColorsPrefsCallbacks() { - boolean simplifiedColorsEnabled = Framework.nativeGetSimplifiedTrafficColorsEnabled(); final TwoStatePreference prefSimplifiedColors = (TwoStatePreference)findPreference( getString(R.string.pref_traffic_simplified_colors)); + if (prefSimplifiedColors == null) + return; + + boolean simplifiedColorsEnabled = Framework.nativeGetSimplifiedTrafficColorsEnabled(); prefSimplifiedColors.setChecked(simplifiedColorsEnabled); prefSimplifiedColors.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -338,6 +341,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private void initLargeFontSizePrefsCallbacks() { Preference pref = findPreference(getString(R.string.pref_large_fonts_size)); + if (pref == null) + return; + ((TwoStatePreference)pref).setChecked(Config.isLargeFontsSize()); pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -356,9 +362,13 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private void initUseMobileDataPrefsCallbacks() { - int curValue = Config.getUseMobileDataSettings(); final ListPreference mobilePref = (ListPreference)findPreference( getString(R.string.pref_use_mobile_data)); + if (mobilePref == null) + return; + + int curValue = Config.getUseMobileDataSettings(); + if (curValue != NetworkPolicy.NOT_TODAY && curValue != NetworkPolicy.TODAY) { mobilePref.setValue(String.valueOf(curValue)); @@ -406,6 +416,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private void initLoggingEnabledPrefsCallbacks() { Preference pref = findPreference(getString(R.string.pref_enable_logging)); + if (pref == null) + return; + if (!MwmApplication.prefs().getBoolean(SearchFragment.PREFS_SHOW_ENABLE_LOGGING_SETTING, BuildConfig.BUILD_TYPE.equals("beta"))) { From 51410622d93c03f5ad7fca39eb7e788b643d0ff6 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 30 Mar 2017 12:03:02 +0400 Subject: [PATCH 03/16] [android] Added splash screen. --- android/AndroidManifest.xml | 48 +++-- android/res/drawable-hdpi/img_logo.png | Bin 0 -> 12388 bytes android/res/drawable-mdpi/img_logo.png | Bin 0 -> 7976 bytes android/res/drawable-xhdpi/img_logo.png | Bin 0 -> 16492 bytes android/res/drawable-xxhdpi/img_logo.png | Bin 0 -> 26826 bytes android/res/drawable-xxxhdpi/img_logo.png | Bin 0 -> 38050 bytes android/res/layout/activity_splash.xml | 13 ++ .../maps/DownloadResourcesActivity.java | 13 +- .../src/com/mapswithme/maps/MapFragment.java | 2 +- .../src/com/mapswithme/maps/MwmActivity.java | 70 +++---- .../com/mapswithme/maps/MwmApplication.java | 26 +-- .../com/mapswithme/maps/SplashActivity.java | 198 ++++++++++++++++++ .../maps/ads/GooglePlusDialogFragment.java | 4 +- .../com/mapswithme/maps/ads/LikesManager.java | 10 +- .../maps/ads/RateStoreDialogFragment.java | 6 +- .../ConnectivityChangedReceiver.java | 3 +- .../maps/base/BaseActivityDelegate.java | 21 +- .../maps/base/BaseMwmDialogFragment.java | 82 -------- .../mapswithme/maps/base/BaseMwmFragment.java | 82 ++------ .../maps/base/BaseMwmFragmentActivity.java | 168 ++++----------- .../maps/base/BaseMwmListFragment.java | 71 +------ .../maps/base/BaseMwmRecyclerFragment.java | 77 ++----- .../maps/base/BaseMwmToolbarFragment.java | 4 +- .../maps/base/BaseToolbarActivity.java | 23 +- .../bookmarks/BookmarkCategoriesFragment.java | 4 +- .../maps/bookmarks/BookmarksListFragment.java | 10 +- .../maps/downloader/DownloaderFragment.java | 4 +- .../maps/editor/EditorFragment.java | 2 +- .../maps/editor/EditorHostFragment.java | 4 +- .../maps/editor/FeatureCategoryFragment.java | 4 +- .../maps/editor/StreetFragment.java | 4 +- .../maps/news/BaseNewsFragment.java | 17 +- .../maps/news/FirstStartFragment.java | 14 +- .../mapswithme/maps/news/NewsFragment.java | 14 +- .../maps/search/SearchFragment.java | 7 +- .../maps/search/SearchHistoryFragment.java | 7 +- .../placepage/EditBookmarkFragment.java | 2 +- .../placepage/EditDescriptionFragment.java | 2 +- android/src/com/mapswithme/util/Config.java | 136 +----------- android/src/com/mapswithme/util/Counters.java | 176 ++++++++++++++++ .../util/MultipleTrackerReferrerReceiver.java | 2 +- android/src/com/mapswithme/util/Utils.java | 13 +- .../util/statistics/Statistics.java | 3 +- 43 files changed, 629 insertions(+), 717 deletions(-) create mode 100644 android/res/drawable-hdpi/img_logo.png create mode 100644 android/res/drawable-mdpi/img_logo.png create mode 100644 android/res/drawable-xhdpi/img_logo.png create mode 100644 android/res/drawable-xxhdpi/img_logo.png create mode 100644 android/res/drawable-xxxhdpi/img_logo.png create mode 100644 android/res/layout/activity_splash.xml create mode 100644 android/src/com/mapswithme/maps/SplashActivity.java create mode 100644 android/src/com/mapswithme/util/Counters.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index e9ebc34f0b..a5611e315b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -82,9 +82,9 @@ android:value="ERROR" /--> + android:name="com.mapswithme.maps.SplashActivity" + android:configChanges="orientation|screenLayout|screenSize" + android:label="@string/app_name"> @@ -117,8 +117,8 @@ + android:host="ge0.me" + android:scheme="http"/> @@ -128,11 +128,11 @@ + android:host="maps.google.com" + android:scheme="https"/> + android:host="maps.google.com" + android:scheme="http"/> @@ -145,40 +145,44 @@ + android:host="*" + android:mimeType="application/vnd.google-earth.kmz"/> + android:host="*" + android:mimeType="application/vnd.google-earth.kml+xml"/> + android:host="*" + android:mimeType="*/*" + android:pathPattern=".*\\.kmz" + android:scheme="file"/> + android:host="*" + android:mimeType="*/*" + android:pathPattern=".*\\.kml" + android:scheme="file"/> - + + PyQ9!W$&RCodHT?c#=RsMf(cT)+WgwTuRX?Wh#3Y%QRFeJ+^$%{fc*gs8VLDfexkd~J>1P%jwrGmLhL}1u?#M< z45}i7hI$4QSt{dV68$D}#uG&*CUDLL%GW~qoAFC|o4F{~Q2uI4tEPMUbyPA@+$=a) zX-cxIq+n3NK7*Rw>5H)mK<5ycuzhNOv88z=4bl-9=rm|)u=5dA%n+K9-rq9tI$sRl1%_XK74*j{(xpj>o`$hr-_`8qnUVE zkXK^%m*UaYI5*!dC^nz!S3u#X-e30jNIGLVhI4Nix+Vf66P-_Qyn#7zgH&`_HI2+4U{@^aiB@ggSjeB#STMgi|J zse)F91GN8mOLRD1?4OnK=5oHgH6{(QP^F!g0P;5H^>zzvDh-H>XwV;Tp>GnErqGAq zOBMZxIUFw(XRrA*DwSHWl1@bcxx21RWp#Te5nF>ne`7ZY6k zrPae%mqon+F|4pt5I}C(lp~qwxtUm;b7|nG#Sq_?ubOlA8T~IiCTsMn&LC0h&;&5C zbjlb(6lRiSWCF1|9lEbZQx+ksoY{Q}QeKA~6cbAG&LkQAOq%V- zSq0JY+74O`DoD@pa5_tU{H5%dYU8yZ{@UGP2w zwu05Ul{U9Y)<9(^Z3StX9WLj!MZ;EqACDM_I|7(kGC9w~#D~ZbY;Zgxyd$=m*do{) zJ(lcr%`F&Sa3G%g7&im}bA?uq_$bZzXT=kOJ5yVU5Gu(!_11McYo4cTTA-~DaX|pN zrITlfOt{ZxcdS34q!x1aZc0MJ^n#%Ud#v7KOA`R``aK&P-Txxfg$Y(iq}@Jm47BMF z*k6jX*S_uj#9DWjC4k&5d8aUsxRMlFgRF%zyEP$WfRphF+Cyx^Z@nz!oGc{XkCw2%7I2eB{DaUoaRa`YKK>!m< z@}`o^>^YkG6FbCLwmT6J1H=-&%F;5=FYdp*Az;QRJd8O3g@+ZD$c1H6GWQB1f@!hpxLvPRFu?aX9FB8}vR8i-ixx$l04^-MFuhi2C?tOW53%U5U8nUh zK!flg?F*(9m}ATy-SjwQPJ+GU7xivKnk-~(aER3nziQTXtp$J z1TdlMvaaI3ed|fuV|!~w^9#pOT_6Gnb4*${V)g6MYPKU970oD{ktFV`S!WOaXka^j z>mar`LEyqGk{L{tx?%bXKpS3XQ)%lfH25b)sZVw_agG6Kja?!?t#Cx)cV>6R^c8@l zJx|+%-|WxWvC1a{$nF%g@lCkAn%y%~2_UzWm!kyc2yQyQ?N+fH;X$zbyN9kr~w(Dg|nbzFhS=HTXy~ z8f%x?i7`OlWsEx~zM8$d*hnlGy8;}ne9|8LMt&ZFTMGyAmebN1oEkJ$E>9h!OUL|+Uz9Q-^A(O%&DB(#*U31EEL zoPQnEC0TIEA)hk&=YlZIjJKGC1 zfOn-kx*Q(v@l9b7z{IlrBLuPfe!rsT5oB(r9hV(0dpqW8OYs{Aw_oy3kmg}AbMJFaU5$lIBh;jR}q(Z;uz-ikUx{!~XQ zTQYDVZEA;x)tzehKDM=KD=TmLnbo*!q%txSGNrx4kfgzW%cKv?7VcWYKCl16Zyx&r zV>J3_6pdKBOb;$}5ddv`mlCC2tEYmFl9!yd*_{Jsvtzm(9kfdru)A7!u`SKp*tVu^ z^t+X9rRxKp1AgBfk(|T+nt3TZJVpDU%c|DB?5gssS*u9CQZ{H}fLNge&V+75)(%~< zUo(S7V#owAVf)nnLK972o%B$;i!v@?=V#<=ps+0o1N8mc_t{qs->@x&rY2GU;p!l= zt9oC~&gp)(W<4vao@c9T3N@eEiH9)YVDnzhUVB%l_z0P25}TTC>8yi4)0N34_t5s~ zCq*E8Ved=q%JRRn=c`|0KQ#Ur*5Jnn6X@@W%H`~%gCA>tzX?6YYo^%DO?7dtFzkfg8Zo6uL%N_Ta3Wp$o9Niz*a9QxEC#5{Pj z-mYPY_NWyb!KN<`<>inFfSi+WCcZW$lma_i9!PyoNjpiiTmP(j*6_iPieMJ;`u;cB zc^T(=)qB8wfDKF>pql_-a&cC-Gg(%0mNe)w_+32kr+15P_I<;D*?;!EBCTp(4YB-< zaO3nn(;s=E?+ZcgO9EqoIts~8YrS~2<@;<>1CQ%+EK79wnxAiLE@!Xpf5W6&8T+Vi zBO8=BuzdrKjr2Z}BIyOp_P6)Hg^fu&LD#Dw5N!DI|3%%G?6JxxrPV=KAu-AfDP>J< z9mec^!I}_s6HL(^7LkMd9l;9fsL%i2`%2AC>Epzvym8>Im~}hT)r;(l$i|37vMG)f zwsP+(pS?!{PhfZSzm0*2BZV6Gezs)SJ?y8ZO_3HAmG>#QRz-43!q9?21&x6!47dUy z97Sz&L#Gsb%1G-)15FEz=KHnkKpG@N0}}g7gB>ab z{|+{~%P4q_rI)~kyK&1Z9W-o^gc_=B!@W09F8(S4d5Td$HGT_ehaU@cM5|u{piM6)vxmzJ z#b6v3=3yS6R>RaeQt0aFN&|gJyQwtT8O-!f?hL7DDA;EIGLO}IYQ6G<9lC44T)zr6 zugqcGz@Oo!zSpx`cf^4quqY1ry6pS2sGG0(>#AP@AV1$zNTxK*-;13kT{c-je=+m# zXxS;5GbGR^u~}>h2@Ohqz0Fj5DCal4jvj405)aarOFOMviFQ!8M5`f>SEVpFRKWR7pSHoKrX9fN)?PO__3A2Wu z$%Y5wL4N>#9b(F%#JXtl>;B%;lfJ33gx&eed>(P6Uv*z@q)P5@muWU2EQq`=v0!RU_4pyG0%G)V#A&%4nnO~P@^kDqDRvCKJerWiS z-Sx`?)~GVG=;rLk77o0N_6vjc78!Pa_&@YX=wp4S9WvdiYiWSn?h{&_)g%C3UzI12 z&!yH2oiWXuUvG17)WfeEfwS)#zGn-5SxjtKvsZCFoIThg8vKJuxfN|Vq08~Cc>k-> zu2y$_BoP7|6gmMMdO>$Z9p!}q3H{lr>A!1ZV`y`lnV>BSjaGlZuK$KD+I2T;(b#ib z*Y}#341VwT7w_$URLo0+tspL-G!*!XC)7hNqP@^ zJ~{Y*?B3lEkRaAq6sxQTMsiCAE@1shj2dm2wt2dbG%DI{2&q2Ke>pCD)M2C(pyB;1 z_z(b{>OxW8#AANV5?@!4(Pxv<7nWx!BuNPJb=dOXv2%`(atA~(tN-on*e=mW$A`5a zu?O}%Ov)Uu@5Nxf3CEg7!CS+Rm2B~#1*A(D5Y-O`MI81g@c>Sz>*RG=uY96xtC!Y= zV*T`VyM+fSgAZXSU$WwLKN=X9Wd4O+-}hPuyZNKLjFz;hh{NOHKAfIc7rHo8*-0eV z{)}7>m9ljVzK`oZAuGz^QJr1aAK1Vis(93A@DC)GZ3zwXF1SCHqaYUUrDc$mcIfZZ8E zimnBu@Pd!B5^6j(Y$maDP}W5Qernvr-l}~k8dVum2PwsT_O`0Q?wi4onoTNXNaA4j z@Zbm7AG@6qvI#+;req{!NKWtlD59GZWFo+Z)J%AF|LamLAo@^V)qs@)dxLO_GCi8D5c*1&VB}q~lVQ=|uoE`}#7hnFHU;J~N33*47lUrz)TJ!7)6*7SRS*Yh}x!XA4LN zph*O@r`c{25qwKL|2)!0hvWC`)4?bv(8Q9+>MO~_=T~Uo13>)g5k*tZVfwW?W3I5_ zyZ5jqe&cLLT1zS!l%3plti<-z3J2SZ16u;g3^cCb$Blx5eXH4uz0dm;2T?3+d%8Hg z_&k#nGKeJ_O(7XSKDf~}MwA=nGQ7B)b^_4aAVj%gAyo(U zyI%LckP6nwgs_K;i5*hU_K?fpLqvepAQE_v2L8XRU-l_tbm}NJZ@?T~!FBx+0nDZE z*g2P#M@R(dD?Gx~#(=#kV#f503w`=v)dUigAFVuh_*Vkp{0~-bw6a~3dQ4E>ntlyW zsKvyCLu;%Ai3oPzFNq$N>7iafQ~59U3b8(F0LwCmJh`Bx(+h}9y=dTk2HPbifCy&M z#u$Fg;Q(|J_fTKaXC**lcK4vg61%8OxAHZloOR8O&=(!c=!9V*ZVU{BI!ckTWnpI1&K`92hH{ye~ZUeHK@%Os5B z($z#jdzo1~=1P(3G+y(Yw7&LzO@$m-!zF_jNp?~Y7(g5yFF-Q3v2t4_0uY5}?Rb20 zE{pvBHuX0#YY9Y^GA{=IvdSkYn#AkMWa;{33cYeWxf??J z94Ww5c_I1ls)^trh=4S=TgjF`6nOiLdml&lrk-nr+vJ) z5dh=u2^MLfW-|~!GnN3Y@*>(8L;s)z5W)SsA7Wq?{DB|}l7j1PQgo@|_gznAxAeQo z5M72`_RypuQd`yH(>5ZQD}`^A^J@LRP9a~GRy;-;32&VQ%7t(MtD+L%7ZEQ&JsKgm zRU!Z;3UNUIX2;@}krL>)8<`m(Is%;4l>j1u?al|aA1afjYq(P4+4|ZIKF`ib|AXXX z8>%m`PXPfEAg;x#1&0|Wz}y3oB;Z=91w}Q*Qiw+Nv#g}y(tZdyP(wUfhLTCdF!`+E zHUdykgt}u^x({b#Xh!=ntAfwQY^Vef!Gn7qp&+>*v?oJqf_(zc{!#-D*|@IHwLxu_ z)bHuJA527mjj@^t5Pc4YY-%D{y|0ixO%d?b35RAR70&PSvU$Qt=wn-*}4YYY|6|d zo+89;touJN69y=?AjR6#vWFG!U+2?=Q7ONn7(;U?FhH`;Gi3rSZojDi%9MCX%6g2t z=1lyqWZ9;V9j&#k?HdB43OBQyb^D9i^2%q-tU_1Wh|mTtteOSD<`IWtsNwvc`Rvln zi`B_dxgV4?NZP=5r*n#xfGE&!*S1>+v_Co~^=KOWvq>SB)SedTVt5>RhzX81uE!yW z@xlHm5f4XfEyFQoo@idnM39}76|mY1Go~?wT8||_p|^xYm~f^K1SfPkPV$J;qm#fU zIbMK^qBQY4n835+rP0cE!gTn_isj0rm|mxm!5X44BO0?7AduA53~NN1LF^)kGX3cs zMy39iLLlCzc_xBZ-{drCqT>Z9@S!yB9U^DF4Tu$!?4RhfBa* z8pd*3fGBEj?tfeBQPk6*lPIL)SN$2aVN+bzv{`q%4oe*5m#B0XX8H?aPl_Ara*miW z5)A2)rq^du1!c{frQom#0UiyBidHucz}AN3>RLR%GCwxO;J@Ls94mlM;*pAF?7iCc zewzT%EjGqSQE**7Q8BO)hFfVk=NR~HjEN8S)E;=;L3_0f6YDU%Xhe8W1LE}K0Ebwt z1c;lDs1oJP-ak?K6rKL^hUPP~6D5TlX{;flRfC!xf{*c?6bk*v#-G$#1KwxF zlSbIn(hGWk*hYN{h)&;!P{^E_e!9qgEbgm~-9)flqSeFKF9 zxtpvI_mSKk#|9|@_j@U*6?_KFW{0GBLObWM-L1QoO$hlKo>eeghJ(A=0EOJ*-S=o7 zwB4qYxKwhv9FulDgVIjv=Zihf#|J-QN2DB15v@bpE@Co@2jC9A7JGo(sj=GnZ434S z5z@}+b~+o^_4kK}z}ub$e0Y_+%2XoQ(^{b!OiM4izH0bd^5Qa+2%3dv$&2f2I=@&A z=k+|7U7R_+JsBrOBIF7-?7l>>Q95&=PY@zN88D}SM%?N^O87z$z$|KLU^Sn`dIHrv zPC7iEWYvg*u(>&UM6j!67n?~lKUNwe00Xm&qYE3EV!S@GNob;I(hJ%5jsNv&Ds&PT z_L}CC-bMt+OGJRxRt7l3U?srW0D*`AMtz@>yQo?-E|+7aUm}1a5Wrk&ROCkJQS4d2 z#J&u&YOpzw{=oMY(jXljlU*>li~^X9;2eP; zn|wF1m-PCJ&k?N3dO-wm&cQ%do)z3i1RIH1K?Hr2uXtUT(S)RCPrWi&`|6Xb@1~;Oi)w*kahyaqA3Sw8_LmVj)z^4|XPW5?QMj`E+hHn}A zWdsq_dFt7mU+!WhWILsX%QG)yc|9kplYQ=?-5!%RMw19Ol8AH!tyq2i5J5GapY|o$ z1z8fbF*TP4%sTA^0`$k2Nj)!|@nY&gRO`2Wt?InReJM0tOsBVTiS{5j;JK?ElrB7%Cw zsqr#91P7(Qv>K}cc2HW5Kk9rjzvqtEK>*kU#}x~NLn8i|euhuT5U@j-{a`h!1RQMy zX-zmpP}*2Zv+11_Gqhe=q-ob60_ZxzA%gv+lfWvqt)<+neFzwQZJ(>z+1=0bN)P1* zCLUw`5CP&2AvhOUQYApT^~%0ALcM*Xr0fYY^eQ#i=b!*!%DWB=RZX<%R?9=+sy@&wfPOJoKA}Aw!qF|;W zdfQ-he$K^JL;xeNqk`i`5hf!cnH)4uOFvb<_4x%6Kn$o3-3_fRq9jJB#}==CMH09i9sBQ2r61DC7r|`cZF9=Ks>kgyP2JmezI43BsYP>$ZzxtBfpo< zT!5a#%D^4Yl~@7(N{tLn#b=@3G}7P>Ce6SZ0Z;_})U?T~xBRNZ{z=ngXfy@kZ?589Xn@};?G+r;i5B)Va`0Z>lm+o8*zhBjW|o;bB(hUuoA#0 z00E0Vv@*O?`<@Y1npTFx!Iy&o&Z8QJm{yBP1tT&soXR0T4+J0rm;$Kf;aCN*FHo)o zi1&GS0F&lG^_ck#M1cLIGAJ0@k7#ezDxCz3+HkZJM8jr58xf4vJWymKDa#Nd1!B_G z7T*YSm*YVYKrSIFe7T!hSPr4w-=rKxM*{T^nsaoQQDpn>_OWc-d58!wqkTfYvWVV* zja?*w84wbe8~R?yre&N@VFOR1`J4IZDD>9xzs4UJ?D( zyrW5T40GTgR8HzUX;b_)DWPDU8L9mW+z1y^2$F$G10>c8dkjRB!)%OAI~?UB5e=g4 z)veVOx#0z-)=5B#hJd>M1OtI^6YeuPjQkfGL;xv}i&i70COO=C%z7_*7{3E6z=>3W z|J|fnwHr__jtIhezM%rlCoqeui2x$RNCqbAN8FuzC{S#ILtCYSAXQMvm6LWl5`c+; z`p8tUfe2foM{F8N8wPulnSj#QS4#m^8+6XR`w!)C$*L<4Hq?+z1p>qZG8$k=ho}@rm{5ewAO^FQUFd8yjnoBWSC`$j7=}{_E$`xOb!^yGs0L zvi_5BRso#H1&0(P0X8_nph;m~94v1r&!wPNBzm+8$V33=c*_G9+X$dwP{BSjm8`OS zgZ(rRhH82M=W#t#^)EfIgp?Pi!@=A!wL&C@RCs_)IAmi*)$;*zME6l!82JTEeU?IW zTMA%xc~mqpJCfa8dsr(=oK5XiP&o!8$zbP8`Ac+=lw$pizQQjr2Q zylPloQ^@{R^_)66X7>ogh`2-QvlL?1AdQM$f>Yvr+WY``8$YCx&+n>{|{QFcn2 zuX)3onziiNsuiIYWqQ80F!FSkLedb^>(X5T1qMw>VG;pI)5*wdkH{f0;2(7tWPul8 zrw-{#pwQ`Hc`!iKfY_IF2DF{W1@mLr;=>#{5`fnB)tc8x^Snultpk<|@l2Igl;M<* z!@F@ROW(vkl6f*LTVc+j1lT>n2oQEqRsh5lg>nuCbk+jCeeJN-_oGd1ar__&B6(=M z0`>vW_NoCq`BPQ@)I5?1ks}bwP#F(rO3ysW`0UxeE7V2lx<@z>BR!&UN+r%Cz|pM$ zm>a{&6E;HXa|dwt7!-O}^oEI7>>*ly6uNqPXakIQBo@fQ1!+T|s}Kui*4hVP@FQ53 z)_GhoeVFOzJT4g6U6y%?WqJobdI`n$mj}PxFc85_eXnOYUP%o_6g+r&U~o3dbxaw0B}Pd^6$xs@%FoiY;5Yu7r5;_t_Ro$8s~ArhZwGb z{+wX6`aGe>cy?9hsjKPlz-WukQV~h{MCE>-BdJZ#=MJmIKbc zCi-?>VAG?Us&lS89MfI8+cr6K({jv+MiH^4eDbB#yZ5 zh^~a4JY|M>y~bg5f62Va(B{L-DZ|;5*sL!NEf-NLB%OpGUjn_Afe4`IXp5k|#p`vm zBWQnH{~cSj`)=#;{)7c$55FwRTD4S(koG+Q5|ahz`bKJcyu(Eq7qIg)@=fW+S=~ut zP6KQi&7?*E%o=X)ccY}-GNL@W3=qLJ#Ph@69S4Xg0nX}h0K!V4M<}2yL+ctxKs{ac zj3kWEdTxO<9}dTB1^98Si|)w_WaUb7qk3~DW=w#MaO|=l5KyaD*@#9 zH}$*T@WC&49z+1v3IVD%tAYn(k5EZr>)_(v_g?dd0&i15%#v8vaWn=MN>Dr*LqfY>VmF zoYf5G13y&P<3j+H@LVfnx2ZG6!+o@UP|xW02M7+NL+A7i01)cmDNt>sGq0X+^0z)$ zO01jOGz8~`!E6M|G=Id1ure_B#;PIxlkFA4(L?VNFF=#ks-@XWo7L_0A%Mc1!k@;M z=27?p@dR~dtKY{WCjIFk0+PW$yywx7Rsx*Ib!ChU)&AD|3TY)!%je%e_<_`R5CJ9; zd7y7=Y8&u~K@@4kj98Byrzot@YJ(2}AQR0@&(W+9cLV@l6iaisa&#>Og_8CMGDE;- z^C2RFKm!Hmab22uaVSNamgmaems3mWXcRJ-Qvz5SxQW&Yb;{%paZUZ*w01~9{yI2i=7zbh&|Dg6m$#V?s zj23jn!7ugG!JgCVFVZwu0QdXqOd za2Sf$2*k|@$%5+ze|`5W|H>`R8&7Ny5kY*x za0*TM@ZkGn+UO4te#{=A1F6+S0QW$|@PWIa5rD%$Ft{qg*4E`tc* z?c$t5zbAuet^j!X*z8enepU8u1qlfH$c4qj?-sIo!AXvYP#h~bwd*O;N^sxq2fc`( zyt$ms-Z7Ug95i1y5g;U@G{_GPcno$N0%$`p3~;~f58m{<0^nh8*`)i4ef$Tm;|Weq zJ4rGvh;^G0Df!;r4|ov)+BBHFycUt8i`Gg2etj1m=Y#>S9PpvU9g1~-tyVwH{s)p% zT>XfpZ}iu;fCMnPZ1TWXLEJ){%S3+#ST+6LK}%v9E>OmcKO#WfoVzHPEwr?f*zwSS z-$tth&Ooyvatx4OV_{LwnmK_=3P=EWk@f;l(q7=IK!sTGS%xcvJ$Tr?KEAZEXsbUW zh(%jEOPwV8tV>RH4VD7G1?Y580+_UI>JWDe9r)x&Vich4m_N!+$|BFIc?{=UMH3Kh zdMhcBlq?Lq)@+C#28jK+hc>^r2dgnC0iXa9i9SP`;md**X2s`-%Rif9|AZ2}R&V=Z zLV$zm+E|!h2Ru%$w;^^IAoHh&WS%gjAiJO{SSvyzfC-!PvqZtYi9WcO^=Podt@0e_ z2;j7qKX*IZZ~XpXZS}H;A%4A0zFr=)QzV16w=+K{UCe_;BML(j-h@N|KxS@9-V3xD zHrB%=%3ld4%&^5dkxq?Qo@=Xa1|00ASzRG=gnZq~roQSvblQ8)n^F>7Lkor$gv4`( zP5|4cjHJ`%exOxATh||F7$2B0fOG}@SOyVA3LR_#X$($UN65j7R_`N6dag#Ap4ew<0^gjR7+;;Bc^O0+~Gct6Mh#Ea%Hx$-V6F{tC3yZ45*k1H_gW6lE2@ z8BV>r2>_LleZ_~g3akjHLi-&X1BPON&im!bNjHRBW>^FuRhE`=3+=kC7yLb3OxW+m zB?gEanXL?VtG5$j6F_nQfA1w<@LTm(Xy>;vU>F8C`zkrx`FJGd>CywJD$V+zyv>Y|-%55yhztdj&w}X!9W^aDE|C(O#1~YZq^0z#9Xc-x2BH zM}ZNo0CI~amQ6W}PG)(7f-)FVo?Nc|YhyqY1DqEXXRn!r30U*Fj>L#o0J)xZ*{k28 zk-t#Bw|{L6n1ulvXXWWhDVG>A_|Zl~2ml2gn>~6SNrl$q+6Ef~W?%reKHTNZUpaVX zjZx(r%Cbz@I<1e`+Vl;XP4_dZfp$4I24oD-=`Pn0>l2aGM=neLHIx8Q=!DYArxF3I zk5}gz88P;+#W2A6s#kLgry1QU!&o4>VbBuOR(O_tZ~xjDh!g`fOP8c{cU=)_Ia==< zP5`K+IJ@v3YVXwy5sXxQrS z&Fr7yn_zWg^DFbynrhtZX=8l6`k8%iV?YZ7B<^fwPH|#!*4j6;o*S8F+6o{y61|YxL{xCpnv%t7`aWn=$+F&{XppwFYh1H3i|B;rp?YOtW#(-Z8a6W^g zI{e%3bAum5g#gfII4irH{7hQjb{pIWySNU50kZ9%WwfFz7-~%41E?D?dF$lCt*yda zWW_W>{mj1aoERXs$3x@cT2gd98Ep(iodD3myq$R&?gmy&+TyX%?yy~-wJ<<~zmXKD z(@1x;E*33|8VjWC9QFhh!s2w=7#Ati?d#5l0lv@SaGnvX!4GW5WCf5vbH14GY$|Jg zoPsRVDb6-@E)4LWd7|S_#Y0zbieryEZ_)e3_jn6(4}pdm%sh0i*j;z^_{ z*o=D{Iu-^X=^=)D4xK*wrgjy zaz7_-o;pYnnjaurqI@~c{*4U=VB$_Q|BXdCg>mWr8kYnhf9mmD&OM!ZJWEMhbhw;m z|3(P|w8^D`e?LphTwL6Lc|(+HHN2X*CIDdLO7Y5sU1e2tKmxHvB1<dJy=n4o3iT zcjt-oTwj-dccH-9ZT22P#!BeF#OBa>(BeOtGvWT?;RV}6e>1*vc31+CKLbgM_@vS) zlLRi_Vtb&;Sn>Z$17a^ZDm|6VU5^xG7gYJr*)g7Ucmhycq|Go+JpuMMMbak*!gN@>yrNra>=| z)a-rQ*}h2j`KzsDq8v^9cUl6Fkqj~Fp3QsDCidteN*hnVDe|)p`Agnd)UTIPMAyoK z5e2_=$Uf@mRHr8ZC9W>nc1iMncio8|!84A;spGiF$I$AKsLT_0*PL(VqPT%O_=Y6G z^}apmy}zJM0N(iUx`Rlyv4yQ5m3!r`=c7(qmlLp*d2A#n(?PEj^3=ljSGrPy8;z>k7RCodHT?u?t)zv@u%_JE@5)wAa0tnf_U2z5UQ>(325(t>qT58d{)K*lg zR@uWQDz2Yat+u!!Y9k2{w6%U!t0F3jvZy3shlD*W*^`-h-}%4c4KvA`{mq*R&fyo{ zd-vUY&VBcud+*uq9YH?=D3*)ktu@wUxji9Sw%d|zc59L#Ia$aeiG;9{pnAU6%MvwM z+NAn;DZV~8C%2(Ql-N)7<`E~#2XZHytXr(vRvYEXg7Snc=gWkv;H#V-=ywVdZCqvx z|5XY>Rk9FOl3l8dm&C@>+|vDl;)~Y&U6uh#Gn?m7QF++Yq{8Akw86;QH;?r!}-T70a@?S{CvpBvV=#+9Y6<+=!JV zqz_1v-yW8I=0{y=>4hAVY|HR8eWXY^GLsjr}OxLd&tNFLexk^i^(i4T0VHm zIwMI3Ln~?-ps;$}**v}9&eJT9cuEcC<04+Sbmm#%d$(?8&UtTGeGgq;+`!{)`h&*k2d0iP6!&TE%0|t zT9H@&e7L_StPD`Fxo9NW-)mS06gVS;Pw7R zuDCf|mH#o|--R#mtETV*0$DFW2%bJ3K5lN zab0HsxM;twUiS=l{_UF7Vu?Yw0C!ZqBurzMDw2)G4g60+UCa8&iUf`H3v6bP9C)>NQHoIZ zfFR%{plDZ7FRVYY&JU=a!>*K)?35WsMjVr8;v?ylV52mTZTgFcEA4hE( z`^!M71jGP^)okJ=`KmyP#PS={0>Wm0v1oHqULe%`W`I%EMHeyRS%DyrB zZj;|a)Rn)K{mQ6>aaFB-o=3mQU&PUt@1(esXmG+HrVQ- z@eBH<@mpfmD%z?ZSbbzVYfaUD^7;eO7H}+b;V1zL)6hrZqnEMtO!tlgTo4SYRu^LM%`QV#=*CODjzL{ST$T$kzYQ>SABfS0Crm| zj6G4``EeqS?&&v){*-=q(4CLQV#5ku-|IRmO6%GYY`k2LW}&^%7Xx^m0Y+DjyV^#! zcYGmrJ0X! zLA1+unzZ9yrA%jxT_r_qA0%bvKb`w@tEU=!Wq^XpF{@d!Jl9jo$9=Y5ay}*35Rp+tnTg|yM z3O0@Txoo3%Jr#_W^zKr3`rDwtQj&|1x4vlu&8nG8t+GeP*Q|bKTn0UR^3x;<$I1wK z;D#+XDVl<2fewkh?owuaCJl)1Pidx9WjQ%uJxD(^Z=@r($ZQVg6>Ud)Zr_|2#0%{m zs;fMDRs-!~IK+F?ZUaJSCi)mFouKZiR%V6!;YwAa$tjce$f?q^d?TdlHGB%6}F zG5|UqGQ(MkXVS?DCzD0$yzK$Y$-3qZ^!kCtw6Ueq&?aRb98x zjl04o+uqkI5N&BwG9I9dk}hyo>)E}}(?5^A=X?(ICP_-7HfA3DsnAX8MuWI>VN3Y< z=qJQ-k@5=a=A?di(WNQ=yXDL3meMN+UNhF-AjB5lf0)nv53TAqsaMSzfQ=zM`nv|C z$Mtio=&FV<+@1q{Fbgx~Tk>eI#bGoWoG_R`qru)l(|65scD{*BbZ`138ksyIAQEGH zj-eD&3O%*gKu@|X4_;HX_Sam|S2|X<69W`(8Fz-g-R3__cf@G$Ge)N?SmjPk?)uv8 z9vzDg&W451GFZ8H)dbYcN32I^T1^RUYu%Ah+8m7*#1f=+s&3sMX%8tk+QWTI|?W#oS~7#pYkVwuUnYXGU|yY|uT{VORUTx?X2NXe=0A!% zzrr;`G~cJPs)Y{5h*mos(>knqp~AlQ{YvNWY2TxuN)QP&Gjkf9o_JcQsGa3MtzStG z?|#(z9R3@zr(2#miz9~|QY;hfFf8N=;jct+6#AHZ(|+p#XW{d^pG(jHOhSJL<({@Z zG=)e0e(QcGJ*a!CeZZL*;!Pe4o5q6NudT|C@O`I!$Ju%~gt5(PeUV=}(~Pa0gBsTB zfe8cXT=wK$k@6EN?m3Qb>3cI>+jA^W_7bgUeHs9r9p>B%lg`(*V`^FdpS){29dKbb z(6jQtPwPLUQ@fo)edGEdDP(|$@|{EP24%Adp15qTdS>I>YI)}j(9qU=1v?Pp)fYxy zyR%>ZXFYyO=O>+~s9>Jf>+5>`g2$ntMF$rv)%&>q6E;=J>;$@7mziKE#|2>IBSruK zo4}-gchXDyU!h&?HPpfa8%)D`E(L>l`C8j#;CYflT!nL$)n&jr+Vdt3g_>Zv-G4;V zaGIAjTj_j^EML>?X^E#&9M2MKo7XFyw4-eY!TJYdkgu{z0T2IeWfc#dubOP3tIx}t zMW8o=olL76zow(Mqlz5>Gr`bqL)`oZ9?*JC{+w+V<#o%*RfzRkK%bWuI%t1q<1-s; zfX>MP`4fjc!OuPQq2V8v!qibT@uWXcf`<9j8-`O8Pa&vd>d1kLhG341k}eF22{yHC zW+P7t)!XY8vJfsG%$h?dTLvo6&@8~HU%4^CN*bat!Aah#>hax5W}-)SKc+C0$4udS zv91$N*^yE;n`Tgfm|AZ{JN_Uei3KPIraOBubu#%&l)~g)GB|i zf!{a(z$1SqHQJjL1kf}d%9^Wq^i-U#*Hn%dz;ZPpet?n;htuqudGu|gg9%;pdbruQ?Sm0Ggc~$fzvoYktN^Bd{#6cLNW!*$Q=bV=T&utYwF}4> z6ToFWgKhh&B@T|Bhx*T>^!RiqF0C6d!Gp{M1CKEQoJMnMA5;QLweq{By?6NE^d$QL zAh-ff={A&RXO&P6+cFFV-dDI?)C>nr2Lq6NrjZqZP^BjzeCv08ZgisjyzxtV$O{u7 z`nN=J8Mi8BGB^$ZBfqboafCh0J!l>;6DP+zWCWE75J2JyRvdnpp4oRiF5`@Dr_-#= znTk0&+mh|6SlH6pO|0~}g#Gxygz43uc1(wGUIf z-0n=n&FY4)Xm;&9r@Oa*d=`)VxzszZw_8464?S2*nA?B05(9v|pfk62z7kK+vFy@A z%jvKCU}AB=*@D$Tl?JX zME_;uSIh*DIGF&h;s=>2)a3yqcs`H(G;^A>OdvO40;r0J6#%GA0Bu2qx9iI2@B3d; zAL%@{U{24N;->lJBt&2aAUPkEGUeZqcM&g1p-W`WPxOA%%-%YzeZhupd&ka4YIVy~ z{Oyp|0kv!~gqIMy_G7!BaH?JK9n9kqoMKM#h0^PLsIZ6$_@?PwI>ZJf&{^5=8L#Wv z^j|hqsYWXxftvb2#(fkgxo@h5!D;E?I0#!or%sW1^tCXp(G~AJ+}9ABYmz zt2Q%px~`6uj%43C{7-s*-wPd+mFGN5uCo5R#t~{}3?Q-IR;uSxwm-QM@MP6bY}45c|d zZR^?jAd+lKrn!ta``C2uYTdgmpiGl7{IARht&@5P`6yG z5B1Z0+yLtOD&%{}H+j4jts@NQso$5^GMgikj0U?z=@K_lveAT9dW!lKpdm#q>+8Dg zPz61wu>`;N8{R=&2z|u2^8BZ@JK-l;H?tR8B#%~nwG*XuXxRRlb zcnes?o&<|9Oc_(!#wPK~#a_UB`Kg!3)(XQ5bovtm7bw;S>k+E=gouv zpw|w*?kgMZweC|kY>Z$D-jn;;Clb!U;q&waLc8k!>&$*F$1uP!1?^xRGh3alPO<)d zR=-NIAa?XKpdfrA&|F-DUYrQSbi*`1VIk1?B>)U?l%-}@YZ`QDm{e8uEtqe9?ZE5w z`oTB+E{fn12%{}QE(fqDz@rCsPlaFg`X&9g_ju(Ig614H_2#nH0l7#=#OQ+`t`=pcPku&^eM_$?G_N;UGsa!w>LT!zyL_ z7=l$@0paJa_*~zG<6aPbYs$ZK=xjy~ZNO0I!=~4ByAM+=VrYCAQdA~@Aq9q-Oh1_b zX4d70R|H*uFabP+*k*<}17FZ)Z=-p&57Adm$1Q$vNWnX)1z>`UlSU}vYzKL-po0lM zCzzoJBse^aD2ITScaQu_3q^R+B(V-Nz&(sTAv_d3V}+sQr@Sp2plMCxX%b=GS-jcO z6F~BQcHi^5G#UaxziawlnF+ix0hS90kl!{RcLCql_ZAw>W@v8!>)%Lb0vLO+VH8XN z&m}gKA_fuwTSD2PrA}+5H+*`$;~C%+2!VGnGQ%~2dfu~-KuFU(0{HlY( z1dRJTrh%YSFag5CpaBd_7^ututJ!~nS;7clx$}MY1q{7~i3R2g)1h_{h+rOtYgGkh zz2pXhnpSs=2_P_`aWGw~pe1>$??SzzV>WQC03>+VnV`|us04)m(6Uh>f#B4;Pnt+q zraERZf9V1zaAURs!>LS=9+%FZ#euZBWeZieZt@phX!(SA$CP*H&>I+w=Xo|X5?)IW zVaJW?NmDbbOyEg4j0T&XYjAwu$*KtAP?T}2g51-0`le{{D$^?k~ z#oj-R5`=r9ffgTp({D)`J9rT?HaX+Z1oaGpCHCq2b8rNh0FwLZ-Ap+7qbM zMKTpiyp(Ozs+bXJTlv5yNb;TlJ7?x(mMEJ=Jmv9}j*)srkItcvFt)%3;V-COJ&f3? znGXcLCEOD|3r8C(u5q4ej0VX>+gbmY98!(Z)R`UvGO@+W8$dV4uG~NYQ!2v7wZK!l zR$es?LfTM^5=EsZ>2}4jGBbGNDdGf;0@l9lJw@L zJNE;6%BPi%k^7~FFm=d!m{dt#tDL7a>^Z>Zkp^Ct!T}B@m`Z0Ro)Hf8opH+Y_Lm2} z+#wBI0ua4)a=kTpsZ&aTK9^pmDk#Bt=s*!vSG8v?5vrJyzoWC zYPS17qU;a`ezY;!8U&-VgyRWu7_`3tIRkXm)4hC^!|9>e8>kA47x1oSsFLbT0LH}0 zAz;*S0NNn@;E??k2H>zDgDel7KdXtiW$iw83J1>PnY?QnZEM@EWQ2Eaa>o6gj(}Aq z=RlaCGiB|RR8tRSM#8Bhu7IuUQ~5v&)nkZbXTb#6F|0Qja+aWQ^tr+SY}VAzpdLhP zU_{|Oo(DKG7Q1`_P`hv*4@RFBKxl&cq^IPJO)K+xd)UB)fm$S$gP~lR09(qZ@>H!S z6F@$J(JQW1Z<@hxbW@ZYfe&feQ*v);$IicEksBf-QY{mr^kI({BcJWXe{=o}x1 zF<~^qm0a0esYLjMQfF)oozFXje7T?zr+~<%mF&}prU4f=!r9?DirnU zBy(S7S9eBlCQ|a=K8NfN?D-57u6_H&APzMNj zki2m!M<4(un2|A+u;(sNfPn|Qf&<;Dg!6=SnX+p-!Q$u%5K@MdNf5#O$8C2i=LRFj&=3$}sVf~fp|7B7?6b0Lzuh&1 zQTGU?R-{@)%)^Uk}4eY<_lthV-6t~lSHG@%C9Mpv97(LJ81rt2LOrY$|;}b&w z_^}2^?uBWi{Q5L(AKT78gv~s4BIoYFsiC(5gUen-7&Kf&$HMF1RYy%&|-pX)5a=6pXyJ^8M!Y{ z@9*=4Png6{Dssv{=$KEF0g9@MhFXa>@GRg|6FU|R*E8$^3kE#o76u*)Ev(nDuwm*( zY~hN-@9n+N9t*U0;%AUyYOp@uUWw}e<)A~k_3^00g@#!{O`&x*OS_O?rJHPvRgyY{3 zJ={?{a9yt*I`)=`>eoiJM=&&a?O)An|M{NEdhJ}61m)SFL<9 zz-#?pYveuL6MZ3yy^o$2z<^CnNhZz}`VPf~s}Kt*=x4xR-j_=z``EfOujzY^ZfWWG5N z(ta}lkb<*VD|jpr0ob>=(m%?{Ehe7RiqLJa> zHNC4Mw=CeuZm)(QPmKhZakH>~!&bsRp7O-f4Qv4+USE+{K0X+V0nY#+Qkq{{V-_a- zjAv^wL}O+2YXKg?U!|oa-4-mypk@G-*yzf!MRp;VMk07bt;3J`%1iM^O14~Fk@3cX zVA~<689*hrEWfOrRl{4==h)S_7J#uwG9~;pl+H(OLt+3R;7tY#S^C7j!WbCT0z7RW z5azfmm*u<}vWY%q2EaG7$LUd$B*Ozq4QlVGBEy62h{;4(EgxK}%b52%}UCElk5}pc@JyMRn@Vf@!hWJvHQSVfY|}yCJ38c6;=kor}9!DMpuu!kN3gPZ&_6Ub{k7RD!=K3yn-?;_2z1~O>iRT#6`Jf*58Nl@ui#Hed zXtr4&=Xv4ANbWaqty8qzb4MKDle_O(mS6s=k##bZ0n`sGs2+PEuML0U<-zw+9%`Wz z^vWaN#%=M}WJ}^S<*+V;zcV5XfNw03OQf%=*WSuA0m}w*PlLZZTF8ASNv1oN=Pvms zQWc6A1E?Qdw5zBWp9?+9F6ehW5-e)^iG1Zo-^x<{tYvv+uR=zOoTah?N@eTAJ0PLS+4J*>K>7~>=-4l)E^|&B^bc9lMAZHoyun!Ud>CyWvo4njI_MK4Y*Yn;zJ(oA0$ZT6@i9jyM9|F z?zS-JDqWtC<&!xtT+@y}A-Z>zDOk>EXXe3AfYfum9exyu zf8klK<{wwAl7(DpmZ{2|Yu?-yrgg5n-*Cb)fInXj2h|OGZ%91SWKA-~w^+@Y=K2$@ eWBq9dE`5)5&0000Pyg07*naRCodHT?c#=RsR3I*>nO)NNAy#02XX`dZM27tON)cyLZ@7?=PNR=>eoz zQSoeddiE9}nBdvYvv*Mx3ncX30wJXD?#%!9o3LS%%-h}Bo!On4eJ`JUc6a8@d%yRc zdB6F+-}@B+Q3L`4fw)D0#qG!)Ht<5FFsX5qD~Gwg*-Tcly^4~}z?-coQVxL95pQ%7 z4mnwtAc-mJcl@m+fdU>p-UPrJ@pluj1K%0cDFBs{43&&YyA=kNiX>G+YS!+u+^6dt z24CC`M=$^z-TKk1FYYZX4TsLQD1!G%~;P)6)-s_l@_Q5NCU)gJ~0oapT!2nFUZ-tu* zQyc3&=c0^Th@$UE6z$VZ>dJ}DdKhCLA#TM=$CQ$?+*fxyagbIRlVAV_-UTt~4FvOG zQyx@+ek>{M4Z#c!f@0J+!2k?Ea?I*6!@aU}HSVKdY70%h0b`Cu6AYLenL(MPNY5k< zmfjQFjgj94!2rU<@X`&JX6-d7(@W3wf36`0B-pM?!2kl>GP194 zsPJBiBL2syJmO1Q^?ea=;S_9pKTDUL}=QBF$>F2nL{WM`+^LS9q>M>2fme z+4t6{A`aOA0bCl?C=y$kn&Nt(xOed$8;~4z5(NYBgM5NAA#q#9?yCS~n)rSEPKmEJ zMgSjjvO0XUV{+<)!oaBYjT8(3@hDP?q-7O9{{`3jMYxsMw>3oKmtzoMP>oxN3ox0dCVd}3g;wrVQw7{C?Vujo)!T~ma6_&1?u zpQ7Z567L8wR>E8fHV?5yCcm^=4e9Qz4m3NPHCu5k13=f@&h_=4f1{Fblr^CjR9PMY#(r_Jgp1zF zE2bESjx3I30Har3aI%+qU%|=W%aM0Y3@T~}pfpgAMnpH22M=+fc%E z#LLsxgHWA!R_~mL`}(&=FDp*4r$n5O2L#Y=;GGmH;jhJc#k=f0UfVJNN~Y1I$gkmB z{||P)0fKh>B7jU_8!ibhD;>1>BfE~mmP>%qt1lSQsL0=m$#2)k&KY#$^VdU>l@G|) z#I7T<9RtW;J#LIFd;g1WyPfR{x}e)R2+(emtiV(Gs|se?aa6Wi0*ov#xE=|9Ol9SGkow-*Q1LurChf~u?+)2N1oZTB2Tpis)ES4MF0(l-iGup7nbEdU1tXn zEYASYxF=y*#ZOP;TmMQs04it{2%zQQw=6y7d^@Bxvor%B-~GMS``^GVzVpOmB@nO` z0*qB8gLID7la^SP0Tfmgc4?43ZwpPotsZ+ptRDiHr(g$$9y<^H2!8N8Y^kr7WB}?O zKX~6m1D`>bLQ4=M5YUN0bD$ArTv^_d_jO(xmFkvb0Qu_+`$MDqbIib#8x;gZCjtSJ z5kMy3mLw_vt-M9=ncS7`<}JklMyp-$!wQ#Sy^eK%K(anM?CZzOoXj zmN5h>TvM1Kdm4n;Z>6V0&|(+@_)<&5_!MuCUQ;;Cutp7N$Px?yqxYmXcsxrHAdzO< zfSW3suqXlwWXT@Sd+IXRA|%F~0nk2wL%pXMwf!eq1QS7qK)^Hv$bSHuJReY?TGKkw z(Qr%|0QppZU-8p(_<6n#nnhKCfUOb0=Y3#9ljprL>&9osdLS`n0HZ1j9zn7HvRH#4 z`WFa98v&FAj_@|^TbjRhLRz%@_U|ud3}9r%m>H-Tndnc9_%0BLCj^iQoCy1Oy-9&> zVm0oVFo033#@q-p6vb-LqF;f4RS-ZXaPGE>UC+~UI2JTTodJw0FZeU=hdvUE!HPZw z0#-x-C;ug*R*#()3)-T}bqZG%_N-@~A93B^B^HAfeF_AuiU7J1%Zkf&*3yA*ei%&} zqb32U+kN19O-%l1-d@p{T@XMiKw@6+D-_;7nlwj^0iaL+JY4UHz5Zz4UD21V5I`o- zRc>f_jb4_~B0MS#U{uA}Q8>8Uqcteek3hh-2q>Q0St_ctAjs*oCEeCBmjHh@l0eaxHUchi!N8OOILQaL zO!$6DeIZFQMrpjyTMchi7Q=t`y$c@2HmUBk!E(q^N=NQW$~bgM|L3ZV8j@iQ026m! ziFW?a8bz3Br#%84rH*iH`fxa|!?AF5+ELKK)uDYQgOBWV?}Uf8KLWqj|7LK@qPegT zVC>nYgG;Us>spPA3}FBi+Q{SHS1ufVG!Qo6PzR|4jO$zg`I#djMM^fHMSV?>1n|$D zOXzAHs-!x2CeA;zQ;m~H55Bic4Z3Fvk2n<*y05x1j#8wFXT;lj>^kRFA%ef!>< zk0h`JxS`v1&^NgcJhAg>UNvz%8VIOXg7QLKY9EI##AqrIS#d>nIY55JxWmxk=NW9r z@GZqiX|wb7sVoKV%Do-(vql=Wu8$Otyp%lT^NOk{jI=FreMAW4zP|U3jjI-}{4t_d z@Z!iPt?Z@fK88pVkq*Ww*LP3s4iEIX5B`{byeUI!kQ-pTdk0jbqEBzRH2Y#WF@1#I z%S3&H5kNjXkCN#`+H6D<7sGpvl@;ZTNQn^FA4H%ND*9&koB`dFA`cr+Yxhl!n_x}j zI#{dzuZ4Au>!D7rYw0kFCBb2-ha$5%4o>fM8tNEQTFz;GL9r8V>3$QeUcVZ4cy?-4 z5yzr{07_zAF`3Q`?CRD?x(izgNI?shRV@D?vR@--@rts61$*B?6^2a|K} z1`_#s<=dL?VD{Fzywl=%6cE7s zPJR;U|Gx1yczsW?sgs|E@OACC@M`7j0Y`ImhogXYD@2JM0;mgcxdCQ{O97v~b6UJ& z`xPCks%zGg5`t4j9M*}zk!eS$YyUu(t--bZq)k(xUTFwa&Cqj+ED@gU_Ydfr$ah4W zzx96jqUK9O`}H-J%#wj5Bl2mW&+NqPW=AH}Rgi5$rDu=o$h6C|3zpaZ1k2HxsZnY2 zT^>K5&Bj5;qH)j9j20#;dRqwo5 zK#=9ig69W3qv`_c)SU+0Qf#1Y#pd0HjkwLIXQx4H49U0^Iko2$I0_{pqskApKf?37 zUsQKQjB3-;?Wj_KYgk#{qF?pUsY?P_4_hu5lV1M-?6$I{ zyNuHB=^1Cp5&$MI9EJOiL-jN+>IO$3Cov~rrSEFL4|qNnCu!myRL(UA9_f?ZTUE^Y z=viL(Gu*rFey&oEJRE8A4@Un2v&)E%CqPa@HcZ+)4VoORBqY0l2IjdL@w_8cOhaY> zimcojYNMiF=m_K_1ZDsvddIR*_%Ky5cOb%0e|*dLKwpJG_5sV#IBCw-`CQRoHFrjf zM~Ye$tui=uFx=dIBHXv_!Dw|ElYWp1Oh6xjdx{4Z?+b;1kQhLI#n@vMSr#53p&+WW zPKK)!_X>*H7tOF-JraAUVm!qUBrQBmeA2Y*Dc{t53$!)JQw(O{7Tz43{C$)A>S@-H zx+ix$2}o(_Xq|upof^EJzan6dh5}kh3;-1Qx=1#Oxz2D zoS-&IWFK%KW+1(ZY5h*3eI(_J{a?cU+a3Zh{%IB3QJB+v7WBrrg|R9ZXI}`US0U4B z2bT#rHe(n-;m*SJhP|FkQTy*;yka*r(z0t6(wuK<+^l-%)yRA%p>?hQG5__tZ^E}- zulv^z_d7WUQRpxk|FlXruEFQ_ngzK@xmu^AcFe_G@YkHnVLs|7oLW#}e;DfQPAna? z_={j;3@QOMR=F?5sn6e}4%UM>9}s~mH1Z+)e)8m7gL~`i8rP|xB&zb>q`U^{4cj&% zFvS)Kv|i`6rRVs6_x%qZ-SHTAx1uYCC7;uKHgr$yrl(oc>Ymtf1mx|_g9>z_a%xe$ zs?;8o0R%M!!brc%kfR*qRG?x#E;51%$9$F6>&;ek{nYh3z1_2PuGfti`@2=|s~&{B zwuk}E!8d;(b7J1LW*+}f)w>wp>?t$5h;7@9HFQc&&3&csE7~IF0qO!u00)$RV)C1K zD}zmb|Mfa4=lUD8UN>S$OZUDFPwn~_w<$8^r8j?eLT7HBSe+mf<|n|x3_wv*_BYmF zf%pd01q5UOX!wJcf1*SSfwt>)n()0*Sx0vARx2fY-hyX$KgT7jPf~AmaGDhpsf0_E z-wCq6API_=U5+gZ(EJU^0Juv4viw5{U=v1x)MZ)ZSTGG9n7Up+@HTH2<@@6r+5Gjo z5yN<+@=bWL@?~yQ{m^rdA{zzT;^bZ&#S_o%dI1*gEs0`}(eEmmxdtv8yok%?xUZJc z72{48li&Co))~d&`MvIe9!WinZ#)VuzP$U@p!?`9ba0}7tCvA3fz7b3PJbx$H7N?^ z6P(Md%e_me`KJp-(SZQjy_1RT@HD>KWDokVKfHZmR1JJ|3?X1udSixSL_=crl+Dv& zzjuGY_F{c9m>)*0=`*H(Ip93jTmlp-g|7Mv4+fc2_>mxtq0Y9gP$V2Oi)}WdX->jP z`|ITWM&)(PtM^jnE8OuNk}?#EdQFG42#s&J9TAYA_T@ajH0DvyHM!(WtL zh*26Ccdh(;!%CR3c_z&4iA=zicCa&xeM4OkJc-$WC`qW^LbeT4HqTH^f33;{D7os- z^(*ua%~tgl*?S=pT%<*`CIgVcd%jlXcsbS?4Qh_*a10!a|A%6fnsE2mFU-C`HNtse z_e+}4t!!8Y)3?loSv_Z1k_pI)43F)296qT2P?LT-Na4&VfGBS}a6K`BP1E5XGy%4v zQjqijPGDy?OHWa28r~kK@w#I z!-T?IQEWR3Ri#xbJ(cPMcoJsYF)FpURzG@=c@-}_g1U+i_kYB_lyn_ab0@(eX@@`; z^lJQ;qpFG71GtJi_hFq!YUl|8Fn=s-p{o9z$l#~pA<&PC8Iv8&-jJv8+YH6R6_3G9KNPz<#S6QIfe;I;+uNzG?m zERM&w{-mCFwlJ-M=snm4O@69FY--vJ3ozVTLlgt8YJy|_ zK@1os|60EA_vLN7YR3TbCLW5$1IoE=D?0V}c^T)Z5o3%<#T5vnLsExg2<=q(5hK@X z71|vj260ly6I&*gRz0(iQ97}Ew>VDAIAP3I#QtylEQH(j;yQQ_iAver?CwN&=v$zk8wjNvzba zV%rPt{`;nGH^S)|r$v1jLsN%9I)=ldID1-!+=F8w2~hBnd$!JlWp(_}@@HW3(3>$a zD7n-ID&(&~o_}9sa1gCRS&6^Hv}Uv%h%^R)R{MdDRWz(_X0j2uSO;p~!sk-9SM7X% z)uX>}@~5%Yo$PjV&lAx8e}aDnCw{-G%Xm0HGj^+-A;<(eVisWDWFsa(ONEorZIyz8 zm|ZAc_^d5+P!I7lx2tn9&Vq@UFECi@GG7CvSD~#^iV%chs(goAK2;`A^F55{_{XR* zftB^myDt9Oky{9ggD*@`R&rGZM_7L;Uxc)PW(>WRs~t3V?MO}tn=YIjGoIY zl+K&1>1d*uSt!>3jLowGCeom!8-MG1z3#XIr*=98ZtebeTx_*|%zWe7?B1e^;-vpD zyZl=B8%*C^)G9T)*?n5AeVMhN->vVA(!PS#1`!E8v>iupF{_l3$ONwM$``nNhBW(3 zVgj|8VR#zq9DZxy=anR8d0p3Qbx(dD#;0`pld5asF@Y`at(fv`nwsXzca71XKi92* z8Czy?)2dO(@FbRGbhnmk4;6tU$jQ&2uoebDCs0&#bl}gioyVB6&F2rO-tV*&xuGjR z64Wnr55Lv3p-x`v|Ng`{HfE)a9fXCxRStDl?hCRN_S)1wgq8$X9{3XvvNt9*QEnTP^cj7=TA+CpsAbiUSv} zNpCc8IXClcxUuUE+^#k@ZB)0c0mjO)RE8NPYElsj(&?=OhU~aG3ke4z@Jsl4c01Z@;St}{Og#n)U965&TQ7S^XQCp51mBpyEjb*vVh|ivQrq>xqx>%;^V}K@9c3qmkDfFnZVSD zn80WIKgZkz_i@9iXJT%GxxMdcm6#-MBmf4j=6MSPkQK$a)T|LdCdM}~G-ZhKjhfKn zSs7=-O{l0OPeQFilQ$egnw{>QaM$K3KLSx?Uf#eR z?j5KYoB})DySQ!ePJCSb2|R#i%)IdA>e1^v-fqoePj(>}={#ct)yASSi6hdv3JK(IaZElpe0H~%h=_S=-!f*@-mMl4S=}dD z@f_(Jr^*04iqdkxcfv`Z(Xgg711~aFCevHqav<0qIwcpO3B+R(H2H7G?YYxBooWX8 zA+`BOl?mL*Wdg*wh|z85NH5e$8HOi+d?#73YZrO}(3?M)r)NCG2t0sL%W_|30A5LM zIiL-py^oRskX_5^I5`7Z)A9Du$%u%y)p*`+^wy)CbvZu$4=^b=FcY994=rWd z779=Pbb#Chy%Kxkn|}^;36h4L+NJ0~@la%TP~WFA04B5My%2|velu{@-7@jJS459NBh)Ip2CmiewS03^J>U5F2dP7;;L@Wm)Yns8`{^qNzH znAH0A(7)LK6?(f(2VO!^(sWPDoeY0SKQ>a548NXenv#1rcM?+c9+N$5R`;6nSB=?%O2F8rZH)p-l&yyH)aiLB zqCl3eEhivx)Me)f?9;BN0rn}Mu z!J&EXu^msqd;8wkJm;i?EIDyNS-;|S()e}bQ_wwtuNrmGs3SF`u>usqOlo;FQW*1@ z7>3;Hlb`4xj=@+-N@*K$$Tzk?lb`H8$hM$tkIX&XwBBTQdhN*1;3`nTbECJx#-|=gIEgV8Mq|v(8D#v*C0si!*U_>Db zL1x!N0bD3td!Rx%T;1hLj5Zwkb}z+;q}>3=7e!I$y@aAbj^hW*8DKC&8qQ^wI84gG z`P=RXFa8B8q&dGE?E(C^&jQuA(m4uG{?PV^xuMg1v{J~i8CzxtbaL|54dpXV^Z;t! z58MV+;+6r>!@x773FSeFRh|D;f2?aKfRc|86QJ!gyA;|1xNqBo@X7wqLL!;sDAHE% zCbWKxvG%8l{)^d#AJx4LNany~8Mw8H?+s{` zak>>Z%tLqHK)DIDXeFyh@{}YK=txRDNC3F)ufF)n5GZaWfV`AE)Oz}CpISd6K0_KK zf>E??*|=YCSG@xZcl-kdUc7x_6qN~}Ni8`DS+z9DO>obadE5*`ME7wWj^!!_Db_zR zfenot!yaP4@2g`CGIK7hkZ zW}#B(k}%w>W8(m}y@6{Q*Q(Ahfnw8<&pbJvScwU|xo0u_YuB@FhZFX1S}&7_U7*N7 zVK*M;h4mPoya*%rQjSDk8I?Iw&E&)|dc{$=J3v;A$((4u!~`ZI6IdT66L7p|Ald$G zh76L2Bi}pW-7$+Dr#E~JhQe+(q~V99+7U)D3bTg#YUS(j{O%VGZ(dz56BCGVy=(+w z6rOzg=9%zo{cqgn#$^@2wOy}nb)L2eXY`!LWdh_3H3g*|@{H75;vqJG*XvCt29Tr| z96Q%vi&6j;3QHBpkVa213bXKdVfRa_(~DW{`_{{mD$OFHkHV8vKE#y`tGLZv)VUDG zclnFv`C(~?A7BF7@3Cqb+sCl#>o6anp5k~30Kt%zB$WYZBc}%~8`ULOSzj4JOdvp5 zs^(x6QvwE~@U><1=QF$h4aIvF8QpBV_Iykr5~W$YMngP6F4`ofQ&Lo3xjg$)xIE`l z-f8tXF@YkK1jtE9s}K{Q90-&YG}J=ggrOK(h1#AK>oK`82_*m}z8FB8hs_t3if8_* z`k`ZhO08fNX53Va&xp#tf5MWzrDim**=F;4d0N=(Wz9x|93(Sr(zUD)Af8{D^H;bi zyD&&g&8H7fI}D0?@tMGWl?hCTH4V)YV6aA0q!j3>5j!y0b#^*0Ljp+v94(5SMxujO ztp|X@Qca>@6!@oAn*Gqtx2B`X{|^jp^>)>Nw8${^SS0IZy@dI~laqHQuUy;ZYM@L` zdI~uMk;fx>K57+;kwD80e<@I_p6{5`^#BUU$r8E&#+43$EdMAWX4?`hER}ySiniAp z@mp48cx1=InEIlJyI$5ae%f78tr7!W^Z62f|q|he~vFcyiLIw2$paZP1#V zaw1Twv`7l2`cj#Il&aZGtz4@{O{&09ae<2CEdpi5OS=G#fk1HLxj;&%U}33@wx;vh zynh;^&3iQNhu-yaC^Ur{o}6+b7*U7`%<74f0GA2WshN=jB}aAo5`cl!NIHPHV*pxu z0RM;X*bc)32BR>_nl4-&RixH*q1e3pXo=Nl zv#1=p(oBm^+J6reo}6}H%nVOH7z3fq?o|X76-leqArqLor3hAWn7|+hCktWVCJ7+^ zmH;G+s}*(Fu$~qVbpj znITU@Ch&Xx%63C>vTLxyQcwawF`(V39e#kk>DD%eW)yOChob_9rScilglbI}ip{&# z;14D$Ytg&1ewDg}2o|246tB@Jlm?taqjlg!N22>OWAn^*Ou)$uK!Qe+0F>Hj+yJZf zp=WJ-REHx2I-dBLKrm~%P;B0*GiZrq_SnSarA{e1N9VgJel5e zD%bJEXHCa*L9OxDs0yb*Ht%}qiACLJCQtVC%XTr+_tlU7l~lVk8s!ZQ{n zG$t^s#W9J(i&J7z#}x*RFLr;imUIBS9XGfT2Bb#<@HIy<^_#uVw5_LUtxI94+IpWg z+q_SMipIeE&c1%a{iMt`JP|kCHrrlDqVjZ{;9BctO6V~Y{rMXe?Nudd<7c5l?y$7j zh9@TjpSidk=qPC?#HPFhMVn` z{~t`~dJSCAc`W2-o)6gx*+6~_B$*gp$aaCoe;C@|cS-D`Hvdh{w=j3>z2Lz=twM~6 z#BZyFC#QQuOAxwOS`3TE@fnVh?ls!W1dLOlHD0S9L!+zp<&Uj=(lun_p~s<8>{P20 zY&3nO2>b(vmdXhsYi!%TyCM@HyYtWS;n8~F>4BnieUtjY1KS>gP43NYn=s^ehARUe z=yNZe(&=Q#aCK^FlOo#CL^}(OYACJt1{0VMjk}o%913Jncki|bG#Mfh)d}CUl#jdrN+XAk@jm0&oVQdo z_=_H!d6HHry?8WB4(cH0^_~O0Z4z9wZ*m`%36R7R4@G9p^Il3#NC?d$#(}6%JUqAT zj(B9j98V(dpiKi^5^{8>I3Ju6z1)BleR+QGxsaP=FFSfl2}Y7YrYo}z;?Dc&VoaU) zQgQ#{?I3GN%f|vlA2HUkzP*9QtIwy3t z>kv3}ADr(h15hOP8>gn@50mXay;1cRau^{$a`KeZDzxUF(B&G_Omiur>4NQ#1f`wm ze00ZSS|l2BY+&+0RGMjP&WQ=k+A;^0+n5RDCgsAsUiYY$m4*OjjYe9mnp6gWB7l9_ zbcBGCF-tHnzP4SEXgg!`EFimhtwQnXi_qaEKkIze0xq57eWrD-o@10cn{3v#l3oyP zlvjXk^TT0qq0lijWr%81A4qdf22rz#3Do`+N|TnXL%SLCaCd;VHl0w=G^FKbxM)Cs zm^i$u5!`p@W``&|ZYra=4{^*VWQS~SA#Ez#h!|wN{P5#4Eo8$0P*sP3%44_zwU+n)1$dRUP zQB7)Vz5v3xht0MIePik9>Zk^hSy69a<)&{eF2%KA)nmU`&~H@&cscLoYIF;ZTZ5oA ze%XS2AZdMmU$YEmZ=DOgiR^QDA-e$oLgu=2&=H1g;>{{+FyTqb-nZbjJ#WB&t3Nc& z1exeRD(y%$6OVtu&0v~yl0xp;IuCxR{n0E4OmCOsIXIytfz%=aG%o?vQL&bTP8g(~ zF{}AB2zj}c6jq3wy)X5`k-q!mxJ^u(uvLPqq zkzjt~{gTnT3Q z-sGpC8}o6Wn9N7@6uUW|oYwIaxUI)6K<>q%mSCE5VgeLE=)j5E+hBEI|y{GPR@k4Z?wQ!8~( z>aMy4lLw<#Atpfj6;iTVwe0ikvqav;Nb#W;yw3#K*H-I0%cQtv0P8SM0GZY<-m?V$ zx$7ApKJ7kp6LVoczSYRb-iQ)x`^<@y2tw(;pKKvf+ePy8Z-2IaJs_R*bCTm6swtZ$spIU8R-1$QIYnLmu zPDOl-f~k@uz>Bl*V*(VZ*9wIs!UfwO)r=Z!1^V>TfcrNewY{bl12}N3+_&vP-R{n25dFgLm%z@6 zT>LS?7yF9Ed5bsuAEWQ>7yx6EU;ut7k!2s{lp|a4Xp~^vXJ*lGRnB-Q%)Y>^=3BK( zOn`O=NSUfthzUHfZ2^3$GvRDnu_%<3g(QI4&+9T%$9toE~ zd?Uck-6q1HGtUgS2}7NwIVB#Ar=`5(N<#r z(Y>kdY1{wCt=zJwbDn%%Yas2G08}Y40ZQZ5EI1F>{L^wLYkIqB0ciFy+CHPOR6Naj zIQ>hg`nj$9&2UD>>ET>%_%+!Wa*N{c^+X#BtwNFjY2*J_{jt{Ru#fF@?}9~pO5o~s z*QvH2W@T{-n?S=ZWL#wi*UDq$sxf5yIKnU*Y|;=V`nVTk+FPyA!#Ww`5AKLCx~~gd za7T~Z;6!}uTCI=>K4sH%*zDe-Ng7ElfA4l_InJ-Fvp5z_W>rL*EgNq zc&Fsvt!Bfu;<)^2YEG#>4K@W}0u#|kfzqSZV3Z#JNlp`;GCq+}ktl%jo;3EYA&a@D zj9dml;N`C>_z;ya)>tS;@owJU{T58Q9o<}ddWKi&u2d6|P-Ngx3PmWMib~C+(vAqF zx+UsVd8#nv`t*QeI_;3`!Xzo8a1g-w|2c`7-Ja|J9Cuqk;M*O!!)u(T9eud1Yp~S@ zOTGxC7$nD}V8s`q*iMt5*6Vc`ZvEVhvr*vZzoW$D4|4YzTM}sU)71%qnn+Gac%#AV zei-jZTA+GPJ6GPI228c|uzZ?x%1X4wy_HKVg(g2d?J(~7Sf4n!USEsJOK{EJ(7ayP ziY^>$TJ6?<0=WJijAlN2J#b}ydBL;Dk*^Hakdw~SHrdMqUw~*^0EUBMD4KH$=y7_d zQ^RTA&}-_vb+}$%uVyJ)i-A~ay}nVb*A2z^U}LO&>5!5k2Y+jS3n&55B{&(LLWN3P zF#y_kURJxz(s}^j?WD9-jB>-w1Ie1MJ&r9r3EIl1FWa3}7U+ogn}IS+RaJrD-8UZ;so z6Wk}lQ;vcv@4i5niF0Nmz@U+(r}iq#eRX%RZh}ex)PTg4r(^{l57wBI&Qo-n9XUH~ zljlAwArsSd&E{&(NfLMja|BY<*Fbydf$kx#*Ecn8ZY9Fgn!R2l8&IHj#5r>jK*j0e zP$oZ75t0OOS;b|Ys^z*Z_zZndn4^mTyGHsB#B*E znpaXBDmeiSf&3dO8(6#%#mBfa3?iF!3RX$+;p|i>g3+~It_HFKm4$ zQp%AwenZm+XJoQ58$2hq&)7r7tXS8ZD&!@A52umkW8Oto^4Y%M@$`9g+EMD(AO-4( zRUy%y*68a0fi*j=*GbDSN*p7=*sj#ngua;gqh1dRx*5ReRTrG>1y4vCMm>bsr7p#= zBY*n5ltHGFO#AEP_PZ7>=bHD}*Q#c+d&GL(RNU>_IIiU=9hsdv z4t%lui?w<&35x-YSv_XBM~3fE>J008y+m5IK1J7|cy<)G@7T0sfa2Q)E57}8-&&pC z?qm;7Ir2mi2!w+GD*ygoHn=3L;5}ha;QaD2Z=#esE*yfLcnw9{8Japojdf4ia4B^- zl`3zQY8FdQJEW)+`|D1;`>`7nzWq1$W4#A2%v;zP4i#ZDfYB9&hsug)1+G;?-bxII zKyi&gKp?7ZgmUb+!80J;^p2e}lvQpDm&oZnV4e1Skv1W@s}6*K)9 zw(K^90T9)$q~yQhhi*%xpd%I)1Oftq76dRx=Z(BHUoDk%d@+mxEb0H+dQ?nI*3q}9 zDi9C|=s*DHS8<>$KRTLfscIMlpcaSc9r+l3eA&{v_#zMx2pEC@F0m^UB)HL##{3!y z+Y->PjaFat*Nq#BF#|A)FeJA+A$|x11Oj0rAW3jVY2K2T!ft?bp`j80^-o` zrMO-o5RV8jm|B)s@`;&4H;n-h(L_8`lxb!TK(s9oa3TWu;(WVw(BgZ|9-FBQfQWx< zaLIg(8}uKu2O`=R2sj4;wjm`o;V;MpOpo+xIs>3Vq^2fbL6J+GbA!Z41OnzDfJ(on z1W+!-=)QZ*>7jM2rZWKQjs_!v>p~oyyLI>Cmq0)u&3j}P907}cnWrG(ljOHMs#sFwgYUEPJ-i+n|MPC8|TOhz#1yM;GFaNxxD;3!?SYE^1Oiq?0Ov!ElptMD+U42((WWu#4B+Lwm#ZaL@>!@9 z+!XD>iv9!wmP3F+Bc_)aT{>XV&#|B_>I{Ge`PQJfFqF;f4KLRKPPbeK+qAQ)Z|MfYM1W@Duud*Q}*P7M7 z-gaXy0r&_;j`(j$k?!#Q7M}tEry+oA{zbzFA9Yfa~kWF;srxbN!!d&~sfh>sn3+(01_otH+$Hc%c}NcW8S~{1ymU6#)j)l(Hd< z=USCMA6=Gb07U4hHRFy|yz<+MqIC5UExwqV%;Q%N7iPC>~QW zW}sJL?;#Ty*lGmghd{t02#{AFb3tM0ppthjLQ+sNY{LL(Bn7Jrx_Z3I+ZZVA*q|d5 z6$AoNL;#h2JJGED{L+DozK>$J5qD)-C7{;GN(Pne%*^O`Dso!ea9FKzh-1z|0Jp7I zyO=Vv2Kp>U~;LGTX6qYz*N#2q*v7{s1zHPMx zXgkRK^0AkKB0q^sf{ty^iQfVNGZ8?MUslkm=h#6H3OgLldix(xBoqO%%0zD&j1MaLZvXNp~8KyqQK4eybXeCyC8sX z{`V6wRX6FQ>_T%OIvv3Pd;`ocFBplEz;g;H*}h}qQy^dv0v;x@sTjZFK4b!>XY4VE z=Ag|uiUH7Q3s)8PY+#<3(O%$`praQR1Og!-fRkiBNYW)`d5gaep}MWA*lrgf7`nwn zinpZXC7dCFG!r?sH&}gfULX(<0nB5Ol8|_m6DL27+i?Dp*}RpLd)D;D0kHJ-L*XbtsB&kD)Xm`=1lv1p-_I7`z8b ziHAFV^3(VoEdlu7;L+vdj+YhrX;cs%;eT9w7YO(ez?TGS_LZA49>uG^V^05cv;^RP z^C&qfS`ZFLP43-zqwD?8iSKca00RZzgwJ$LP8}Lglb`N-+(-cakNDWNWBa<@^1Zm1 zbCLgX@jd5MU9f&m1%0SA@_m!UP`EyxHC z3sPO2jxhqL+}nW>dH!d|S0%xUR5! zW0U7>R9u~n`hv4W>_T)#At!xFF_+csnf&rKcFTe33rAS(~ z_UGYfdVDfk9gaYU)Dg%KdhoAs$QcIJxSs!pDeOy}{GVqfcKqDR>9ZYzI(Ha?0dS#- z{)znch5eb^GXj~y2z)gjgG+|P&`r&@Nl$2-4pCgGNU$8=@?S{_(wE2dANC81^&&b` z^dB$H9t8u?gvA;MQGv)tZMd)>b9;y3_hHBohM}>`P`qjV@OOqas6;Br>{d}WpwhD( zzpudHYbz8Mla(;OHRDp3ll#C9ujrsKb|8}l-5L`Jk8k8MavaIJr%-y1tn zoR3?d`f%~J!gYcHSm7?1NW%nWLSoIvnsm49PHvDP38!h2qR7d3pUAm0Z7 zpa6L(aZNAakrA?=rq;aJ>A2E+x=_ks4Gnw?@gG4e4d26{us=ql)rbVXlKR$W3#sla z+Xe)Wwsb+E4e1T0($Z`Rp$s%B*mAI|Oz$;8POwJlGXLf3m@n_{Ltm!%>8k7G^IE#^ zs>m%qfFlM#+YR9Y{r@6`2l7_PWzQNIHMao2r=@o9?Cv4~{(O^Av$Yhsc=f@lzx>LL zW$Ht3AnX;NL`-~NdM(kjouPy!2{+=_96!N~Ud`<6{Y$dhkB>2Lrahk`_|L@eG6>vuF<9!9G)`OWcZ%OlQ+DI4FMd-^GP#-A8!Y+yc`X#g#?GqMveH72=AdcAu= z6&F991Ul~2?j;#-O;3MW5Dt4z4tn8E`R0(=TdEb2eAe)er8Eyg={pd|9OK(7#p>p4AF zN0J;0kG3t+|8>v$WJtq$5tV^|Ul}Zl_$V;|@Wgk4)B+-#ncq1=4`_}bv>0J%FQHC1 zyZJ8xDYo+FhUrRwkwI>yuz3~Ze5jWW=zhh5#OWn+&E;Qz7!LM-QWLAF8`$+ephUd? zdg3}|c0mr_h`$TZBX2Ot@LT`Jvls`u7fIOi+@xE2^b0q+K;Z@YvH_I1P%O=WL|!&e zt?ko}>2|mm?i>~5n7nv_W6{*0y)~F`P^z`wb=nFNqBM!L7+T|&t$HW`4aSsZ?<83w z9k|^3#eC{U6zfoFGsM72PVe@8Mf7|=ifxd+D?S^Ue(ho_t?9(I6kNPqYyC*=rNWx# zR5W!P;Y1>TH06&8-#4kA_2rY$x;Y5&vGguJBy2UegyEO~O*V$nLa&!EmTA8_ZgYNP zEe#W6$0l87NWxA~5ZC?~`(rp7Edt*$syOjn_ldR})Ga2HR^y`g*J9^v!>d37pfdKUiar--_+ z+pwoP_a=mr4h?{&;%FzZ9W}a)x*X^9D68)b8T(8}P2FRaWbf-YJGIeIUObV7Hv;OG zxJ+x};A|P*Zbj^Hb&?T!5hXPHsuyblo(i_6$R!XKg&+yHak9GWO2N;$fdlQ0@O}Mv-2muT^g!*@>M(;kT&ABq}^eVrji5|nH zrG~-kzj%P3c^-wk+ z^N>AQFPA9Y)F4I(l&*Cbha{>8$S_Hp>$cx+v7V!4hl&KYqUf3t5(8Ioy8A8PQ4+7| zVDG zTd@Ip-%gtI?_h*m?n>U>sB+)6aDk@7VR&;AuWr9s@1^&p=cf6*e#WFDLL;q$mEPN> zw%YtTuCo4%N49y=A|%tT3He6DKxgCV7yf)|&yD|3ZVJSf7;_yNFpe8;qBLi$#txn^ z(nA2svmj(UTwD}51z2N*(}pO*O^`8oZt*Xo6@Q<^@NN=S+;bU`*2D<>Xr zHUN57#t#cT6+n|OPWTjsqZ*M?8cw7&SPk0`-#pz(m&Z+BPYUa8Oo%x!pZCu8LE7^f zfjB*or}KLBeX`Z(7^oW!wR~)|-1uSM-$&CMhWWq)J-}Y%KH?$6Gmc?(|It_p?Kh<; z94OeC|18%Zd$4USvg)Wx$8tT`0VU7zCnfjp6nQR?5q3hpy!MvO zXkD6c+{CB5-2r-Cu-6Y%+@9rz7TO%#_TYnD+F-i=aT1^m6aZYt@MBvtt1(PFr;2$$ zLQf1|85~Kee>b$2{_IA?@#M-KBdG-Y)Jy_TpI{h3Fu`IOrZhgAL3UMg}wLJ`__I{LcrH$!pmecGR#rG3&Yu>&166W^!)8mX_-*h)4qo2->HRj{FlB>IKVkc zY%3p<^sYi%{_ybgvjM<6j7|=#FeGblgvvOaZxdR?KpAR7h}`e&&+(KC__qO$PR+Wt z8eMFFr?L4z7t6d{k_&d@9Aw{r{NQ81AF8N4lv}R5Ndb%4C+^y)z}Hd4(7*52 z#cI08g_}ti3?wNoGyi$y>0Aw*#2WzR<8cUXdy_;}S_pPXyS4U<{ik&lMD8q;R(xc8 z%-o1E9uX>}G*I=`t&Yr4dhgHN&lQW85jb2Slm$=>jvVvtKNS{8mk?4#RUciFbO#7< z`&|+=-vxF@cU4ak;hBS#f&dk+y!wVCI{!u)zFMM&EA>jvq5<9?Q5d`rh}-n&8ls_4 zy3phyi-F-ln5O!+JvuzaZBFK75}MwOf71r4<4=BNZ&qv}#DK+1QM?thK{xy}LXjC^L}$!vPWnpt!7<3YyWc=Wb7Ap!PbLr-7DRhq8uo87(pFb;ox3Z?xBh++RKfU~TD!tsK_ z(3kty6do`$H2GJqE&8G)I`7ZDW`0-#Bys$aoW<`$qv@I?P?j8!Oa|%T&ZU;XfuHA( z_#_h_bM>w)LNV1Tw11((er)3nvU2DE<9<^P;|-ac!xuEihT=`@4>RQH{g;jguCN0t zaF_w`Nbt%fTgIPdXL!QqMgGr%M&M#2Oc-xeU9`5IC;F;jyZ{i^+Qw_N&e3y*>9QIz z?g@{57!I}+G^xTq>ABt_6R``mu~w-e5*J`T`dPIgtYPeOFop+2C=OlRsqHII zBvg5C2JS|*XepvsSc51eN#`-`r8Gn2lKbPYsihPU_5PU7u1Z<%wJ~z^y%wxcudZA zBd7I=H*$08phP&f=db^`0t^ex^(hqXRhemmjqOH#Jf(?^9`ISgm9bge8xOx0f2}mf z+cFBiotq>a!+xy$tJ0vqOg+k0Y(C0B33R;GIoc~(;vI#jU>T?^SPf#|IQp9#j{Ah% z=1D88lexcID1y_G`=g*!1E5(;O2f&|Teoi{-^- zF|x%kD!-Kdk-8%~-;eVo!vi^J>J2>cc<2e**bMwmEjZRSa4IDL`0Z6XylLTsZ;^op ze2*WOOkRY_gH|!t|5w~msT|b${iyE5Z_n2`>5lBbV-5>`kE5FYYIlZs>A1Y30>(!Cg5Nw^cp{{ zym1PP=E;_7J`K*&zef5HS|zgbVQSaRvlR1AQa<{7o3W&@htW=ceE z-ts+-IK3L}gmUghLOFd^iPz;qUbvqNYKl024ZC2o_6IB1iFZ_D9xPw09e)oWA165I z($}eq>)>UU=@tTcQJ(dE8%rGAR=ig^ZHVc>YiyqL%_hxV?_c{f9LsRPGn7D>j3jA} z+?Vi&<_=5jACjb}fu}N;CZA4k{IyumNbBibAcwP!?PMwmP#t_Gy#6h71kT zD=KSxyvhJppvrcA^!s4z($sCxaN12ahgdt92Y`!60wduD98thV=W&pgXzzKzV8Uk$A2NhF#4 z`x9wr2F@WZwW70u%lz-IFdsS^0)Y9#c5ZQVit67TvDq2>lNY(Ku0FRj>nVQq1s_3EBuC}X1tBI}L64cNPsBn*h6J-btc^qW?&%Ap79kj5W>9E4oVm}5}QDGN-e!Wb$+XbYtg8XBPFc)9CAE6ghjG8HteLOQ<_F!% zM!`i`z-7dzVB}s}$!#+1q=lMNpI!2gPerGuj$)g1f+&_)87Rpbj8e0ks^-EZlN>M0 zhZi?}+ov&5C}h3*A~MyYQo@qX{0`G(xwU=6QQ-n!u^5jgnNo70H3ehF3W7_^l*0nJ z6DcnC&m96o;%}c+iHWL*AYKvxkx)BoX3?K?#|>X|PY%7u&nRG{a~JrWFFq=n4b~KS zUgW|)Lz1PsPexH_9zu2|k~1>83(b_aXF~hu*DN0%Rv{6C6s1IhrFbQfb=u6r*>~Z; zg#JuXKvnb{bC;^xaY`Vfn8RfB&}rfBlu$lGdE1=H`~lqb#sRWizhB^>cPn9_WvY}^`6E`^>G2A z=hgfy*@}PvEe!4Rayryqc^@T2(EQD{XG(VL$1u=mnM3?&w0FyS6XbK0qzc-sMmu`108|kpc&$Kf67(lW=6UJ#B zTUCu~K7pif0M&myq|L^2{SVcu$rdwDu`n#lUf;??kL$GfBLf=1Kn@Sf>7$N^`8VkvO zPznE;JURPo6bGe`D|edfE==R2vnd>O0<`vIV_EaKP_l`@a=li(D^;)or)Cz!e@|=M zKXp1u5+PHjr+L%UtO*kH0#x$>;032<6hf_Mp`+-)C1UHr^Q#-H^}$vxhxAaf!IBt) z>QMp`pc7=Bdx0Fa3bf^X(WJ7PIFb&(s0qZx9{}l@i>nE;UVi|bwVRs(|`X`@FJ^4@RmniIRAM1 zi)^+G;HvgoZRV5ZFU*xZ9#=?03kN0FLue5B9+TEilRN9iq5oR9Q>hNWIo8HpF4U?d+5nY*)3>b}xIe zpJk*wx0UF5&3V_)C8=qpd#|r6{!C7*ju)5i$Z3DD!km69)_F&5)#P;0 z8K!y@fK66VyRPZ&tL z7+A4ZyS%CDm;Nrb62(-`VY70ockLjk9k^L6V6Ex)n0>wI08ePm9c%1R%0DQL;F{~; z4)FB@TkK%wy(8H;1c|p?`>3|B9y$nvrkPfqgZ#xpM_rM_!2r(jEI9rse_*Byk6V!jh(r^wzsuEUr&s z)?#n2?UOKG7I#U>2eI!SZvp{=*DljkaQL$lqB%|j;Ok8rmFF*c<;pB1z@8#KjgSzM zs+6ap+$`rQn)YzOeqUGKr?AqdX7kHIU>;+`ClF_Wi84*u} zM0Y~+^XAU~&8xik*1O3T?~+GsK!BbqGncu6e4h`5y3z_|zN|E)aNhb?m9$zMYK$oM zT0%Ph4q|Gro$M!rw%s|l5S3RaN9|S*{6-H?U*cZQ02Tf|f*zeLBC!;Ooa^&pe>+7Dlpmw{w?qY6z z%wO_hZ}~;2hE~@U!oG)8A6#2`1?-|}n=M_n7jAFUw-B^=5|j`jK4}xw0@UztuS;bk zN#2qJi8}kE2!;MgRfgAp^z|2^n81va{3;UQ^Lyd2hnON*tnS)H_s6NC>F2E=C6~w~ z`+0N@*#e1x@5YGuDk0G-5R=)kG^A$SauG_Vei#_K8rNPtu)>~>w8-2PO%AUA^e-yW zoNHYdG;5y!!#Q>0!AJiL8TH`sdOcH#mg>8o6h;ZP!f9=@eTIlhX~AkoVLSN4>A@-- zwq|SpU|}N07HUXc9&z_#t_S6pmu_o#JRQ8nQ-AnyXts*ru-edwg1^|fZgs^+^}SP6 zjdHQc&V#=XAr-4<8Rh|K-e)ZD3&R1R?jBXC)lXjU&DI0^Xup}_a%oCfjA}d0A#|>a z8+RG|7nOC2nhY$yvv=d~X=lGQ)#^L&(D4tXz!4)aEh13PAs3`+N zi@dM0D}ZGj)V;ma^m;bY*y2;>+)b2jT+M(roz44uVP;c>h5Yk3)P~NF@#nK`2d49B zg0-bCRa`CqaN~m>AJz~s|I#cEC=g64i$);A;_$*nf!x142PzY`iu8wVaRGu_3vM4g zjtBaTXNBS2E^J`bUb(F1B%i;3c6Bb{GxYdyoQM0-@X^Gw-~L@_J(H?=k_xo#UMMVQ zNXjOw>Z86AX+jxSM8dq2*uEA^@)HcAfi_E6%T{WTQqsodo5rgbJchgOWAPZq2$ik~P zvGpD~0@dNPmDTf#WlN*04=D1|{7hp;Ac-g&O5PUfob6SvBA z0pt{rGk$`mK8Y`)`i&`AUALyFGq=zPv#uTP5S!vLlNlDvZfy+tlS#!FyydCgR+t&e zevfOjbwZY%^|G2`DIx}u<;#QgwcljwVSq5&VaIWeO-G+L_M3|#N*CH=;}`f*;9h@T30EyTXHr;7inWG5Y6Ie>oY^anjL> zdHA&Dx)I*Fu&*#P_UmNDPwZ*&EIU)Lp4=%3e9gSlcI{ohrs)2XaodKVmNOy$5_$GK z^#*!#o$~n%_Lu&>W@1%G+7{ba!3o8$kqjSs;DE+#*MKt6$@4_*dKFERW@^yi{J`KB(Pz=wip?M4tkpDC?&3D+sC{ ziJi{%2AcL}OTbmBcbW^m47Z?fcMfCk(sU zKcf0P+3I}XYi|76$+7m17d@R1m=GqITwuyO`7>U!SRJj_A5xc|61#71+daAkD8rJ}#L{dVBjSuy6p?jGy%Koo(_ z!YjHfSMg86uh--L41I#kR_2(YNJBLl2wm@@>^@A&i;_7WxJpW)1+8WC^(kjN{@DCy zVw3z=4s{i<#^7=Yut&yI2>38~h2`x9 z>-&fGf!Fg(>1h#|)A;xiHBMD{qK+BuSwu!`zACsS^-}yT0_kzF4s_>3e7eTpfs{Wp!I3I4(m$U*{Y%k+E*QY8(quwcjfsz*GcbG ztIjiFePw{5&igZ87rH>MKI=DM;V{G5?E(5-mT&BI`H6I#{eUjO;;=UdbuBqg_()K*>TC~WGL=irfuo-eOo@W~iDyUJdToIl?t z3u`mdIP;u<_tt+ZOxbyfFf7sp;m8XG`B%S(cgpx=!0gwNc*e6pAj+m9QT6%0#hwHp$1_!Kd<|Oo!Z(Y3pMw_%me%rFLKs-A|T=kz@`ihY6U3hn9 zr>I_Dhvw_OuTrSxyrrcp(G_j~{I$z`iZKfjET*S$swDDR-gtmgSW*g0lA@xcT_E|3 zTaR@xN4q{w9bN&(X1KaF= z+h53)CkSWpsR9?>?-Dubz$(AKb}xoEKm!QgSiq|!f8v66<5b^Ie3me2a^+t(=0>=r zvnM2BS(ru+lF>vU@S)vS2N<^Q|E5%~PU|wI&9f|Ajl6I_g-iiy^zL#JwhWZlze?Pl zd>XI-gayAeo(Rz}3>hZPGSPvP*z54T+IEcY9^(QbvGFYGOd)>}ERk~7i`^NyWRkJ- zo2;TleS|xqy7UoHcf|%$S=;DvVU*tFZi0?CFrofVCIIENc25z2*v)((^X@|OuSN4- zOm|8re~-RhJn-cTa;o{eId(_0kaGD^N!h~A{&;uP!OXX87oxx&W005N8p=cB7k zF18KlE_Z(|>lKuI>}~{gpEe?9I&fp7y_P4)k_aTEU*UY77bjttJv7xE*9oDcba~vU zDG;=7V60m$4D#VKJ(CKmL7_ckbUPAcy^rs`9@kGK&vqPG1{9VIXw9@<%a&37OzSmL zG*wUal2W-?$cRWY*y1ga{gP^KYPtAUfJNLe_G)-_avfgQLKvyGn6IlM2|+fi*HrJZ zoOjX9Nr3OJ5Pw`zY;g~fn8PVubl<}Ue8PSt>>x@prmdlJ`Kal_3 zGlzyqvHQmw4s6Uo@t8!d+2E@dXd8`)Vhni^C7m4(D6bIP%l;6r6d#m2o@%?LtL~~J z^5c^}G7i-0V+|-j8r53iZ_1w)wKnYR$5=eKTC|rPte`ga(?IJZnwJW_gBLY)?vu_^qs-M` zsg_C@j3LB9Gi_fh2Ch&tU|+~`GyTFzFQ9IWP`>qgF8g1)4;vi+vDnwY*Wp}brBlysnXMKP?c4CaZo zxtT|aM>=X9aRjrmxwY|ep+;_xXc8sWcE{e9Z>HMSR!qtn_4x>bS8kP*aXE!)4Asd% z69af6@Ag>W4U=eLoOQcS9OAHpxVPwgg!|huCuKdyOyHq0t>U8|DV7<&$r5AGklBOHY)-id0pE2u;i^-9ig;M&wAB_okpfrcd*`V^Bun_^M z2^9^f&t%<`-!!0x-?SNC)+mNib$-+DQ+$1_IH;GxFf!m=9b@RioKI`ZF69B2JIMPvl~}RteZ-Pbyqy(aTQ;ZZ_o*TVuSAV z?Uv*$Sr8Xr6xw?lqKkKT;xv*^XMRQoQZu6tzMRIWVRvE0k75=!G-T)gKBjAi)A3#j zm?rX!I)&!EZuMdJKE_>7Reb*aK2fh5^TGv!^+BeYDvl#wRMZx?pVQ(>R&Qj&AYa%| zJ~PHM{}n7Q-%(+uq#3riev1UZ1*i3ftK>fmzuR;LsD9gXQ{K;7DhUBVudz-6ZoOGU zY8t859QYR*o0MNFLJqjf$IxeKXG{wzOZrzpSel~6JO2nuWW`T+ijvdy5dM@)04_0N zRNjlb$=Mz;^ahurH5f~{fz_H6SiwBP+*GzIo#RZ5_rSiBD;EBzOaPcCphseP zJ^)5LULP$-7GH2j_wG+ev&okO4}&nMD3UBIMpOrbXL2j_iwtMU1ZKPt`VPj>V13%o zM9?e)cz`aJa3In-!}va*L&5}6#+yFqjjfH8YoPIKTaZWC%gEFp_~CQ*48>6^<{-^Q z0KwkC3d^xs*FlI)AB238w|+-;XE+wULjfvq3Ysg5e(&KL4oZ>nEXuM~dt1hR?eggf zhl|mlknRsIXrE`xVaPngLucz9o4)pZ;9Az=Z}!rw2M32w2CI`Gpx_ zxLr-Lyv&2(=f-ZhacFj|5;5=|!4jH7&bT~)P9P7N5*(G6r}Xy|u9FkYoqHMpLW2J5 zX2c57Wzj_>QI5;C84Ji`ax00S4?RRhzd-Wo)oOCqI92Ra?_mIw=RjZG;8ztavGh2I z8YZ~TrU@-P^9>Cb8j-6yP5w@1KX2$7X@3}!;KB48X|#9(G?bt=wWfeNm`{m=Up?=e zXaAo_G-m{%9=v-o0+=Y%d^$dXEaR~h6Tn3jSoIP@hfq9& zkG7FoOfK!nCuzRpUI{?*NCI{~-9hKb$Ayn`QF5a|o=nXopJ207-IfIG=AUa0-^|7I zyokl-Yk~z+FM}o&o>H4^SJf%@q6#Ox%&vO;{KaAveOZd@c4!*Z#s^eWIzMyhMg zYhuatE8Lk}NZ`}9t3Hx>eg{C5P+vbUD23s({_A6C7@#CQCk#_|%YCs^Emv=ZuY`VB zfIX>Ls7#tS2{kt2=t#A1?`NTfR-bJ~h(!K$p9(B#>&bZTXJIk5yovAn^Y+4N}q_AEyD&`Zu-Z_nzMdbocQ45HCO5I#3c^ z&zSw5EoMofg-SIkr7`B6K1+=EO8@ZoXgt!CRJ0rB^|ibeGu!&jjMH@2B*K+~wGP(& zsgA^Jkb-GEBQ2ktrO&7k#};B&SXMO zWm^BtqpbokEd=<{v zC|1HedDJqzYr--*Tq>WniI|knIfI7$wc*HF8zo}7w6#jY1bu3=hFx9-RTwtm8e)cp zk#)-lvLjvSWf=1vllBBB)t!oMC;lGPuJ{;K0T^n%uf>}}-7{NhGYhU@`KCYsYR5af z5m3vpP^kGP4e@I@wNp-HWM(X7AOMl9ENSkcboLBFsT3sv$d3 zQJe2;SDR1){S@t+D!gRVbL)K~RDH=yM0MC_+{d(dvfVMj2h^Zwjmvd7QhV5YSLf7# z8kD`xh9Ln3Cw9hl+UA0blD2A@zYVd0b!A;VaRJ>zFhIfVY)s>Vs+p3{DvDQx!>3*< z69&{ET?+Djrn&X)4vy5; zJJxDI!3elLktZ2D;$YE%jH`I3M+4osz8!rfNao8Jq6(rco?FY9?E1Gq* zzZ)pBCq$q{$+w0}kDL@57!L=GStp6u&E=CA(0R2We1#;*_pg*UQ*;NpfV{ z-Dq7o@DW&&>3+D;>D{ z_>ySP{Iu!|VsGk%Rnl?2VQ6VB{Mof~s}sV;R`FQQqwelbUuS+((MXTVb zgarpr+L_PZ6>o}Nk1GuHm!&Uc`Sm%2Mf)_v;@7DN(SkzMK2(UKs#0g!o|%l!JE=HV z*kE@_83Q?xaq{=_E}MiQDXans5)*nUWi*-+)F_e_w+&>TQ{)gNoZkZhe^1lP?^P@i z?ct-W!j)TqYk2Qm7qTww*C94%^3x<&&*_7#(|4>5mG#jMWA92_tp(JZZ~y~9yP}6>9Q;*!EA>Upb}EU ze44p@F(gfQY=+QR(7udoK|AU_v0VJDtxpOMK8Tm)BItsX*ys<<7;$L!N>s%|`U=k( z35=WAuJmEMv!+lRqIOmj(I^^qdi;rsOSVVEmLE)WL2 zl3u-iKzWj{M`75oApoT$jz>yZmUSF`FP@@WLJ)#!%jX9oEB(L-TI1#i(2EH&xM(iX zcjc|k`jeeYn0SvHoZTM3c0=n6nk7Aq6QffUS3U@j?2 z;YlOQe{OvQ#O@7EAxuYt$8TeBL6YVXsF%G2h&XEcTCdiK#l@(68@9kVwbd>n0-qwX zkj{0Luxa%>`)k{=)(fnog)Tfm@VSz{E{~@?Cj8bpS{i79zzb107I-2%@I{6rtxsH@_6cVn5FS^vkq^>%#wcWD+o@OQm4f za;D;Vfe#^sMM!(&Iv)9k3<{(0U$qeKby^p+Bg{B6LJt^4(DD%WzF4xdj{$3{AL%a4 zuNWusmuS^atHlypDwHAQd0vK(ETUaC-ybP=v^W5tw|>AoY7g#SE@fU7;za{XEZ|(w zdstj(U6prs>z$L`Uk2fvWy*g~JM#I$sDjKykeTI336qDN%vMz3DiG4gX{i30)i!}0 z7qD35fa2Llu!Lg{75?SVu3xv;ZZ18om zXq>fBHco-_FZ*RfSL_G?UQYu;Fzhk5uL$+Ph_vxawez)IQx@I(VuSx4cF|X3p z56gw776pUqWRN$AR5)S2=!^r3g^%^STMl~A8gB)tG6)K3y359a)UN^Sr=9RX=1DS5 zMi-Cw=Fcp1C0!^+kN<_kzq}=)p5Iw!Gj)m*Fb$7R)@2_HHFtY(^_(~IluW|d z$;o~oV@}4~(&MBx)zABRiao!TIKbBDua9w7>s<{_j}*xyEW!=XHq>2U)|86@ck2T| z$>cc<_I3E!5BQz1LXF@L*=jC~I+-_tU>;?R(lbnI;;Yt1ky->30yQ1ynC8044y?we zqcgdz(C(GzQ&@7c%9YpJOurUJrRhzYH&ncHOqc1Het%)@=TPusIL$qM`-jVqOR66N zs%>;DvkZUp((60!xqf9Jh~sS4xqjhQI-uc@!(9pX&HW zY1sHovz<4*ZuM1tI%1jgy35R-3Vk>x&EgH>c>BgqCx4-GSF?FKWQEn&80cM&CtXqD z&LRg{kFy|pUh{>*$$ix)a4aB*?mB8!2G36VIFgo@FU=ps^5)kA)8Gc_na%PA0j>*uCapc2#wb<64*J?4#+2!M`ouMOZ zzF1krd5cC*^lvhNALkEoo9VQc>%M6afUH{=HLA)DR)N;RqpBA2{>e~YHEiFKGs*wn zzJYBPYc?ER8ROWg8Ky092`rGT^slW4I6PrkYK@6MJ6n5#+_06M%C^1}e%UtGdJW%P z8SUeFt6*6~FCRy7WZRsG*A|P(+yx()BOdC4*4sP$t;ni9!GH3lZp|Ts;u|IUR59OL zlXZijV9u`xcS6Y4YZxd&29K-gfwR=$06W7F2@{W@Pyh;qNr)4wN4 z37Cn6X}`Urtl?!DULJ>Te`mknE;QRet9UOso4@bUJBU38IQG{Pe3+Kfyej!tQ-nn~ zEFZA{84(A3a|+N!qywb|xJ$vA*{zJkIw3v?zgnkW4U~{cFu-`~IL?=fRU8l%esNl{ zn{Rzvmw=wy`yqjzpq~ww`G*Gjm{N8r^Hwv&sLGw=#GA%&`or>Qh8a;JZ}vCrbyq+Y z?`A`1#e)?=f{tHI%f>M6A7j-jiFF16#Sm=WC0$hBM&k7LHufd_Ydqy`cxOeeepHtR z8UWdn6sH_{Lyw6dG(pORAj1%cUGC`h70p)#RELerzGTo?N%NgKtGuiC1@MB6+gW29 zaEGl+%<0eKa|yYTHAFBP9WY;S`1`?jwwYd%k(5(N}tBb|<1vk;) zTv=5D0NC}cXsu!lP>9-c8dPch8#_u+_M=j`7=50ySeOC{N>G&FN|I-jMfT#Kdi8Iw za#i(SyV1nU)oKeH1n{ZT=(BFUdb$x5E+?ocIb~pmb#lUdc1BjEJOs{m3a~Ry=FD?5 zc8^TnD;Lb#K7Wbm#?1~o2rXyIbQcv4ex9bLaW>B*<8nF_e5s&pHlEjvlQ1Df1)fg< zkx82#apfAzJ;rHI4$*{+gM$zF-ANV>=Mc}HCWu09+>_gSo~~LF2;eM|fgD~mFq}DL z*{EI$?dH=&^JaObc%}v-Ql(SFVMT198XRd+%Q3y<$I^C}kTCh2ceo*pG2B+JIEb2n{+h7_%~{?7KZtbaQj4?uunjeINy97!5uW{+ZqzpTEWgRM zfL|6LNlmpYa)h)F^Q*=U+Uhr4VH^q8oiI}g+j5)}05n@Dcc;2p+zK4L0<15S^)}#~ zq6XJ9oL1XcYZ3+eu>RYm2!fBWA}Dy4m#{6KJ25SHXg15M`l@<4uL~0s3#m27X0pT` zauZe`KNFdzd6*@U@$l`jf$zwn#1d2l1aq_mh_K>-bM<7W{TP6>6gr=%k8fJFPO_)* z)JxLjsl#yFFz;WTzvR$HX5*_j&y*!?e(q(jDd{PU8&pfLDD>|(X&8K#eEUN7FR9rX z#(#6|Tr~r#PoH%*xYbH|6+HF+Ix=<(0Zi}ObyD<=pDfUchd|vzO5rQhn>cK}zuYsL z+zm%`6Dw>f>NYcXXuWR-N|g_m794ffxtGH-r*#v*8^f0uM}IGtS=HWn=uZ8$vq{1m zm+A4(Qp2fFv#66yLWG(5rRX~z_WtRk@l`1r30 zwzdCxlf@d(5tmv@j)|O$!7-$G5FJK~7dN#!B^zCY7q1c0lgL;rSMD;```-+={U?`; z9xfnZ?DI@_y_vpi<1b_QJB3lo{d%% z3QX0Dmk5Z7_Thap>c6?)MVs7Iy6L&VT#zfVd5$NCWcJ5-(}+R zlz_D4MY_s(#|s?|6va#`48WeXRxV-kHUXQ-!TOqdb$5QjdyKU&z!5!;0?do76mniJ z2$e|QT&d@jk2GiCVEBYHhqacX^X>_pl1#lE7793@W&rO!(i%`jOy2s_j4gI&xaq>? zMa$zfH~6$GgM$B2L`%Swk`#-^(2OQqQY{{f?1*0U!$9k`ABi2M zp1eM)auj9|ZeNx(>C%p%k{hDTbHMzCu}ExJ7HP3lb2ME(e#s{VFo$c>n&| zq+gdZM8}%x1C?< z*G(iW&BPj@)eW_%@-~pR4ll%{I9dIBdIKX2+>FrEC78!%ad32&TcgXiRyfW-kp?d)8<#4j|_1R@gpk?t{-4xK-oCZhzn6otxTC8O?zA zbD5&tOoUj9=$vVnvttkAn0+Ooc8&liwc?tc0gx?TfFOyogbX3x`lwW zxyVxP{$wl9AgC#DDqIdCCeKf9?c50cGn>`)E|-fH%^<^ zyMOqH54xb4bxxw+`{g*nA9@gC?h<6 zW!4tM<=0B!NcyP6zm#!`D7R4h`L9HNy(3W*B*(KoYdqVT)&Abo_C%2Gr~C_NFVFFV)~&9m#VlHl_Qgkh0Ap|4i`@0 zc+D&}ZGmB%57|@E2A0W>K3%cflKN8}z&tZd3@cg+fJ7&^yb0MYfkY()5f(|}q2;p!{t)!Bk+d%N<>& z6`TFEg{F1t)E88Nm)wg4A|K~$8_6PqaqrghaUt^ije~S>Af4hDt5ad)XA_LA0Qi^2 z9r@hvO!-AzL!8X}lgSObDS?R7orP2056^`vs_yi)Jwy+{O46~6S#tfq`Qzf=*280o z6q)q%13{V?;&nHsSuv+KQjg4(k=YSJ8BWp_e<)4MP*rO6?bhZS{lvCb(B zW3=pUP%#*58d~BD*s!o&y!CLoMe0f{gtT$0Z$=32#iQR!Cit-sQ z*oW{~xJM)@uu>=Lm>DNVLPw3*!g|b_-#lhUz`9Dm@fQ*pM&V9d6E+h6F*83cEoI5l z&G?SYz28@2?QO0MWO!~>%8N6!!G4J#GUm8nGB}A<0*s%)n#Q$!9!l)Tl3Ugu$tZ|TglGkJ8R_ZYeNCYBR{_BllxzHZ%BWf)A8?bvgcf%!*Fa@^1XI%p zsx$Bx>(L(V$b(bKt#koHru71ZdSqZO$=;(T%{-BHs$0BpWFp5$h>Uq9ss2;)D zs)SK7kvOp9i1XIpBd3=WCJu?2+P}S#T*2`+QIhMi!hFg-WZKp`Rg-z* zb@;iT)N3Vb7_&VN2hPB=Kh`-J7fCN^7WEr8lekHlqq{$2Hf=TDZuE!rm%`%Fd|7%J z_4!}(>=+vlrMPbiT))2%JUu`F&sR5cTpu`aoh#q1eU`(G38~BL{YLPV2rIhjEq+_W zizUp24_rfz!iK^tLfIUjH=G1LMC{c4sb0hpPO7W5kp19e9|qn2v*33bIKU5;jKKgs z4B$Gv_>&O~o>+K_ih|udjqrj4dU+7tcMnj1xRId14@=POJ%u~obNyL~x}=Nnn-Ux1 z+;nn^$nMv&DO|wsVWoK>PPWL=ofjl4X$>o=Pt1=P44_>)6VCO>wdFRTK`kJCfGK${ zaE1pB=2(`l>50jv{394Za7aQ_N^3DUH2Y9-j0&HN&Ev2}^FX}LCco-xvQ>DkZnQl{ z&y-hHmFsCt7jgX9eRGiKT5>p06agNu3V8>>b`C6cxsPzfilSRV6UNeYxluGW($+- z%xj9;V%`4HbrAuMBU@bw0teUZ8UB1uD>~+ioJ>i>H?1bSvZEZZVhD2#8CTBU4K=O2 z$FeOERK6uGT84Py)V0LKI9Qc*?K+Sa>`qfv!8Xp;e>~&mN4)M-bfNTT*h5rIvhRQzW*7n!&f*#7ep2rr|En#* zF+kitybUeZpsu|2wSEhmk*2PC(lU1c`RY+o>uW$G>8RHD6~}mo!1U+g;g+lxx1*d9 zX~czvo$9Dwfi!*cVTU)x9y`Vn{ghBqU$H%|~_-(6Fb z)MnD2t7v9zf*q4sk1B}eJ$+|&L`0>pYkW{IBNM|AkFXT%XQt~hCFMVnkx9e+zYNo1 zpf6;PCpA!ioI?FsxmSI3K8(l$iD z4Et=NS>Mt_vX@OSGeha>_@&DN`Jh{s-v)0d3cl9#kgPRCeA^pr-KcSJbq#5X%b+14 z`qO~FmN6_fB9p}6vhDZA?sgbon%-5ZpT6e#Qd%l0l$hg68YeS}^bLzBNhBDjcVz(E zjIjwxp-UX%P;os;99 z&E5$!l&~@dPjNWu!b=v};jR+ycr$sl`*fMp^cmIaFI%gw&`8a|p4*}>8;K9|oDQ5( zp~fl|9aZcX--#0?r`)Qczzt+ks0$+^s-hhQhY$FpmX#POz zt1q;^rR`|op4eSETuWSI7$y4>7|mS89P%^#Xvb9UDX`m-xF9v(KYt}{yFsK>>L^X_ zS*aifd3Y`X36PZQ^Lp{X9BjWsCRtYMl=DR$pTxY=*r=!_oGV_|m5VztMwOFI^xsWT z<@?F@e04~INBd2zq0~PG&Q1`sq|YLNF?hIjR80YNFKh({EJWOj(Hef%_bV3ClzDZ0eWg1mPU9;K}YU*oWx(N#{4r6vDMGjJ9;3OBLrqwBt&W)gxn` z#?H)GWL@u@7iNA8m15tPTglU4QhUrPhZ)(gVQ=iW+?B=C`H9LI5UpM&q=u3YX)F1y zd7vXXEth<~iF{Aem)4m?zNk)7GK+N2=Rj>r-rI>ThFX;dse(t~*RMB3KrN(cj z%M+f;c(EzR>u6ssp{(nXL`Oi|HH&N1`P9MQrv>;1eXhoM9vgA-Y6}<%5f1q2W{&p4 zjIEodL-Hx-kO@aU&B+7c)%M4twHz(tbixjZ>$6vF+EL)f7L#M zf#C?+)JT3alTsFNo3p|yu;j~+sO`~A<^Sp>e`WN-zRKaJmoZpa`eFB3=abKyWLc@f zJIcxaD#}k+WRkjN?s+JMeB5b&un9tq6s2Hn-S`{JaVIiv3(6P*6l}033O5&7H!cl@ zj5>%vVBcx46ZT3_x|m))HGvdoDX05-jXd_v<>w&V?xwK4bNQ92B3iT{kDf=rQLxa+ z9Es@Jm;hq=B=?{0!mHfNaDsdzMLO7^x+IzNmS2|Schx5wtp5D(>#VZrWtLBzOFAlV zisc4YmxLyqyW(V)*>hpX`Bq>jaXI|extqw7jtGYMs?LaP!n%5{3xiPdnB&gVdir3S zD7v?nWH*}iZxj7ktlspp4+PN2UN3SzPTxgM2(D&2YS$JfL;d+tMR~;LgA7utFQu*} z2UGYo%Q3^YEKe>AFOpqVq#U+fAQ7n6p*yL>nq$gGc06VSV21d5g=M5D8LuD31xi>~ z%#D9x&;NrP?jC=#kG-^YR}fJ=j2Da%&z0#~zxuK+AeGcJtb*!6SKBT{wZcT^?$GJC z(xb)9l;wyvpWdL3V4taXLS`n32~n;dvzVOoxAhq|GW7(gUD}7i7Mi#qgX@Hck_1%QRNK{k zx%)-7i`=2EXe<+JrjE&v>}bc6mL>w|Axr1_K#)b{U!JV*!|^A?lEw!`ttR5eAUqFNq0U z0|8`}p@WmN1XK7pkQkl6>v;)ilVES+&14N4SayMe>rKwC#kR^8kyDIOm}JD}*%5j3 z`r6lD8S$F>7#bgL)So2oq7bKsw!OXjJ3sT&=oBX~OlccxsF`ZJ_i7hC5{L-*cG^-Q zui@kau|C80PO|L%^AD-Gw00sC#yDX63iJqC9#gPlkYmqwX2jk;nkCJWLIGP+OgqBv zq3Ueq!2U&NZ64}}PqiibfSE0ZSY+gMe;$3k42^dHmN8O4H@_(*AD*5kjgeSosFsmW zZxYye#2j#w`s?wFkY{9{kN8VkDX@eXPyXb=j=tC&A$Wvsxa)&iVmyM4hgwdc0002lXXQOd`2; zXXsx{JjltPMoL*JPQ38PW>7PWMYEzND;hd9bZFC=5Hb0F5&e*CGMBR78is@Bq#hnW zm_dB?mE?m=?wv7jbEfww@mKn}-Pt+$2w9g)YM7c!VL?JRleC)A7e?FxsV=-;QEB2t z06|HX5+8()+V&`G>qtEe-pD41&wcQsOnSUh{xh|jMai@NhehyG`rLHAKa2s!Mp$ZM z!=80|TFK`o)M0XN;vpf@uO`R1T%+9i`e95Uo@XKi8#v`{beurbNI5}@ZN8j^i-`T@ zJlXo9X;^?!RjG^dBOQ80tAn0ZdV~ZABDjX@``8lte-?MxW^c!#H-jLvepO_-vhMH| zOesl_ucO$iU{5dM2N0rlCq^;|A?HlC!CjjJq-AX&e)L}(w7XC7u#(WHD^lVXFBd~zanmE>^ z0-pzzn}GXjrMOClmgcAQ@EO~~Ee0Bha_U_SL^KEY<@=5i_U)-{9hTj8!m#rZTNU@OJ zDihfrgC?5KW1%@NK;VLrx5uKDYTXxxdJ3J0h&uA7~YQYqf3O z@ESFkCpNZm6H0b%(IcfR_~S*uEf%p*2S?G&LhN6wpd~t%kbI+LQ5!~;W?hIC(_qeitt%@)EfNJ_4#OlgiKEC>)bAHjYyw6wD?FkdlW zin(`WJ7Rpacr-shn;v@A1bii{zj(;FOb4kycR{}H%ZmuZYPhWCGLd}-K`!LWYY-T~ z;>c`594JGAW!>BUvTt@2jz=dt?mEknAuoM@%1oF%Q|EI#a}}qSJ+#i(j17k2TTG)b z+*Ep!0ZkiCilPfOSh~{ESLAX>p1VSxvjmB6hcU^C?qL0i6FnoWh}pvGlIvQh6$0Cl zrP7_<8B`_&vWb8Z5fcpaTV10G<6Hm3J;IWI3Jy2U2dX5NR5~sCVf(2}6xfa~%}dy+ zOijoSAGH{z?@GnNOm}|xBu4j5l=d=zZDX0^44UBxrHwWb&8WNh`{4oj`F9LYIbaW@ z94*|vkAHUh*XmX8E6%w;3stGF6Xj*McKJ`X3T0gOP7<&|W>GpF#9wxaz360k;<)6d z6jyVM7z{0kaY`(;GTsjUN6^hBfGGQxHlBM#GQZ=M1BT%i$PAR^@lv-&qpW|FB?W+> zt>NxxqxaRALa2EX8NO=`Hy_0Bo7Kyf|jxSjF_ks2Jj`0O4vd6p&WAW zR(wHn)IF*9;*3Yxlxu)voO!i`M;w;~;5qdxd-8PI*%aqc;vYPUJme*Y68Y0HmIXq` z+O(I%Ujqeo0FCOd8TmVDt4SM^N6S?IJJudNF`I|N;KHc|J2F7f2!LA!y^BoiX|i76gMail#PJprInhZXo8Wko)ZlK8yy9|(+@AhB@fp4=tV<);J#aJy51 zF-?+sF&&h(z<(ID8nC-$OMXQp{}gNBC0;!T!Bh%uzrp_5yT0`8-LD#dD6}AnJR2>1BEy2ESgR@O zdD(XR3E()Y(LgKlK@Dq%(~22)Av73GFGcr+>Tyfc3OVO_?>|vz;E7C)`npL`As-X5 z0m`+~eggaz?&Tid*t8bQfB#4vLrF{1&015sksRwafYeRYPIa7G&dpk(6gltvrw%;= z*$YAOMi4h3F{WEV_A z{P_<;hXK9#wZ!n??8_>BwG&wxUA$;9F#!8s`#YaZ(_WGUPoq)&pE5(`fz#Utp1#08 zb!OY-GmG`0dBkbT9>~!jMkfKhPX8V{Q2?eYH%EF;?Y?gBs11!ngYzT1mCwY-3!EnX zmR0Xp#i*Bayp#)lSK}q(L1+gwSAeu0d{Jh^{pD zwiwwzsrn;>N$>*1EUYOY$k-k$(P3rPOP5XQLzjPl?T7}dRMhoXh8y@5QV9Vt-2R}* zZ&cpf^~iWL3_Qzf315VRNzrw(cHN8`h7qYGfcqHW;j#E&t}DnG{s3jFF(5xQI;Gkc3dB zEO<7|NT9w{=ktuah_!~F&V?8_UN{t@bM^bx+jWo1FBe2lQb~aC*R`?2&z1r1&R*9s zm;boq;XsIJjWQW$uR*OOEP!G*q6ihBmwg1|eKYM8ng9L!KSQ^bZ(~j5{uT#A za`FJSgdk^?>@h<9bvWh<=tIW_KO#V?g<(4$Z*=KL5Lm>}5f@5WqM z#}X(tk{|l;hnM`$s=Aq6A&?tFVnh0*n?I!MBKqnwKa%%GL8vkq`x1X$Mj}7q>N0C|M*jmF9(zkq@|8> zXENn9a60r(DQF0b4U`fC0_<$CLHDp@mh!OX%z)%{DR4|)0HR#(`oq;(Q(w8yh&jov-mwh#^EHxRNMi(F?w zsEji9SjMO8B!4cIbXR1TtOdcZpQ4mskb}4_b+-rS4c`Xuzf%MQ96@#0n?ydspFRU7 zurVbjRCP2-^CA)p#<*_pHBAaqy1od;3`pAj9Tt|VM_fhL_CwoHzC0=-dZO8OgO(9< zcIukRb5Dqsha;)(1mTREsrM9U2F|FIfl?`y!*HQu|0|uC*J6}(*+UWwn-2F%41{eN zsXyLxqvp9?+>G%87C(mgGcUE)dQNnf;x#WQ3bx(@Vp(JW(|H4A>b`Wq-5ZwYeJK?+ z06aa_&5DSnX<>hpIM^9F(S&tBH8CMZ!yL?+{^o89D`e)++qD*I0r>l>_oqHHF=I`z z%7}QlW5)!zTOq6s_3zuqt8CY^3&UhmJpmQW&TGB2o8c=*S*>Sx4rw5 zc>y7Sf<7PyWVIK4$#0}0=k6UvDOb!NJb6dW26RizmXIGMgVq&|Y>HQh@dgQnicRmK&Q8EoLg;aR=p4oJN;kMZ9Hhx8$W z(TJVplW*xu3g=DdubRH5n$NGs)&dwuu=od=-tmTRQr&zJGC1O1HU==)qq}jI2VR^5 zdd;KtstRltV0Q=aN032VS@vZ^)PLh$oLK0hxmgJ4KXt?d+}FFjTz|iUWZcy7taofy zAZwnSRno=S#Qja*qXFEXTw4-wDut(w4hjfMjO7l~*I5Iv`|7215hMap6&CxY*icTO z8A*7FO{Bt#X9qo(^_PVE{%-0?WLW-=YD3`pI!8D8Y<4j5tUt@=p7gJ%*WuwdKda2z z!aUXKE$J%bEwT(QoEGr5hTDx=%Wq!#*6r2ZCv7??(x51aQi^$N$G;aIBn>GEVxem| z`!T)z&`5^h!9Dh!*3D1)|%OaVy; zb6p8Sw>V$t=`%R~BuV@(h5+Qe^FnpA?Bclwz5<`>wrWN%AGDbvxIFtD*&%$} z-%;(!3_=S)DT3*1C}jmo9A)q>;r*7$(wd6c3QtICK$iSsFPTBn#=uDy>EnWgMX+PFI603O=49d zPNVa4>$S9$JH1)bAM^V8O3PZ z(uFhH4Z}I}WAduwT4dI%B+h2Y%wOhrd}k z(NC5_;RpL4fWi+WecnGpsIHlt2@;nT^+RmltP+~|oDM(pQWPd15%eVmMg*786~qGf zF4l~aQxqXv8f9WzZMe6DG$1T}=%JDe8(vwi*BKbxgTd=lB8c-3i-O6DJB zY+x)>=&C(6>mNbDiwEBL&~4tAf=Ylq)$~_Ev3wBYz)Ne1*U_*ny`B(kgB_-!G<>ih zTBxdxdsp*vmEjwvl<=ygA7iA>+$-!G1=ZNrRu_eYTKUm@(lOq?SUE0mrslugspNOX z^7>lcgc$*$E^l18In3LOmdRty=0E9hkeFEV#*IKT2_ybea^+D;+sc6zpSgt8- z8b>*vbwcI$p&OM}s_3!7`dadaT1832;2)BK_dB;ygac;B)13*(>|`4cXSZS{TrF{f zpWhqXXp3XJR**Flfc?L}e&9j?S;cu-Iy0qW0Q^ywezn8xc773#F97UqIy{+1e1J3DpT6Qt5 zvZT3pUxvxIrUSjc7QBiG*CJv7{@&Ri3fR!lQ0=v;^|!V0wmCNmx~<7-T>F6hxO+F; z`U&7-0j2;d!=dK?-^UFz!yhxH$s}mCC@7{tHF)9b!pFH*^e=C-WwupxI!UxXi8uxl zVJsUKnp@DH0 zA4K&xVSoaC5uB*FQ)hi!K7~tXc(9?inzG$Pq@T#amE*S!3;uh(lx5NOS>&3ZJJ!H& z0noV$07=#;0E&a>Kt`C5C*8SG&8^2Ff*dzXA~!mo$rea2Jm6U{5QWlZOo+}g`<``m`NJ`&l4x3!!e5d5oY*zOuHh;8&~|G^uu-quiIVK zq;^w5bF4oM5Bd=TKrKTEP&HvaCTy_lSNd|MIoPxO{>~tvmOgglScMTAD>*9s+v9Hj zI?o2Z>X>^;MC0qNXyiSgn(9;o3KSSQO2i%^;rxnJpjG9J+N)XJ6cxI%-d_ffHK=3t z_X}N@HrffwWO0d}J&8Fb&0#K2q4`~!_VZ`epT&{w)L+Lt5rE(yTM;Z5mXzRe^x(U2 z1=nO*TJo!vqn3Mv+u0dG?CQJ@$f6UReB+v(1-*=_~3uu;cX-ej_8ZxdrM3 z20q6IrwC~8%iQ<-&(p@Nl_ZFbmpX=RFG31!7qYn+b>Lo*`Q#4?-UResBd;IxrR|kH zdeaTrRf1z7OnEL-;|Uv-8`)l~9PvqYMhQL28=~}y4CZj&nz0MTdy3vp=$(IuxUmyH zbkeBXnd``pki?~mX|@J;Bmjuv)!IOuJaW*A=u3g0D^A_$^oP7=S3*WXtXmVJ7sUA} zrK7q{3BcULoZpjeIoP1V5SPzIdtW*TMwAd!bG}|HAEwXe$Z>a7M@mWc_qYf0#lhtu zNpg$`!Kh`PUD2B18x0KHj;v-pN|c7nV+a!zk%Q_s!=^qa$}C>BZ0OI!q4^pNi>1!;xxZ&AN5rt z$x6s4u`W9BD1o$4b5I_0=InD9$ZGoP`H=_*c_AY`-)*Kr6zZbx;1MkVdGMO2hUe|> zuU%HNza}sBhgTs2K!GdpA5E!&(v8bDpFvKFgo`Iq+_Z_#H;=r=lFux_AUQxhW^iW< z@o;y@&RWNAM-1$*3#~Ft8-?8FTegb{UeMLdvA?KjL)AOECb9)MhPQFDvj7Mh3E0PO z=yM@VgwUDFygDL>mdKP*E2F*@4!u>SknmR6S517;gavCOslQ%fmb0Bqh7u~nQrcWO z4o57Q34+kviG0FumLlU|0MscV0G3K3DbPyn@LlSQquEf~8`~8TSy|g33cb8c6hJAu z>Bx{sx_Hl~oF#_{=q&?9xkT_IYFrlB0-(%1odS|B^+%keVGisF zf^bvRqa!$dkX`oRmqGf4osx!6SGg|(WI^-*eQ_yB(nmPUpjl3#wV9MB4~K!wQc;w9q7TVLB*DIYpzDbx;3o78F7PWCMgSb)7Ld zyPHNfD`mya+)kNa&lyC9qheJ@A|U8(@CIO`+`5pgl{CoS0L0CR03)8%lCl||h^}2Y zyEPvuTHf#YED?;c99GA{v>}V5@Kqgb{WybbFYV`7GR!d4`4>0xjQ{6mT}9{KYWI0k zH1b!@!~}$)_Rt6eG!X{vCAp|D_>VT1Kx5D!v7!`GL!5Vsl*>ENsYyk5A8~Srv|MRB z970bVffTH8^HwYC<^8Py+IVu84?(G#xUrp(O*mj5 z!{^BP#xO%6`>WN@6gS$YRXzte3R!B60y5Ge&TN-D++ziw?%?wDpa$ND_344P9aZR2 z9xv!$9Cz~L2IlwO64iz)Goi3j_0XKXXu!J)L+rFs*I)Cm8r-qvaFE4A1yHPHd7)g4 zHuXh=%oLI_fL_4)SKB*(UW zFUR0Pd3+5(cz&&ZC#V?l6%}$axN8InhCw&R>VE0zC}eRJU?6nz&?%aP>ArsDTg+NQ zx3?rHlTbuxY*hA3PxJne4_J}q0*zafQUaA>0AL*b2t41yrpu<8jw=m&)GjG@4rS)% zE0o6cJv94KL*eB<5BCM%ELnGX|*;c`lI--wW|0U>D?bL{Nbd)u+%+< z{R3nFJo{03@aOfR1~gR6jphJz+zAAEOOa(PXPai48j$P$Qc0@O062*Jc}*f~C|57j z&}&orRpNm_yaFE@n7?NduQJPOwNr|YT3?b!2>?zAr-@-4Ni>tN4V+)A!M(^&HapOX z>$7|aepU={Me}45YDxR6JYbZ3pHp#_! z!~mQjQMUa?BaAgh66qhI%eB1^%MycGlI9cQy&?lHBMr zu{LR%hYO`&(-c4xMK5uL>tKy;Y{s;Krg3RE7{N3qQOR-PKyqd{d#qiLIu#r0QAI+K z9ra;LTDITHaoxsiW>rV^vR`1t>X>(5f;v&@>(}%@>+j!a@NvcVpm2aD0QflGvwT`f zSz{&(hA!zC$Op0k*m6qTW7ueT!!8yYE)xa5X7W&>Ru%y5K^C>(K`4Oj5C-U_sawO4nM{F8>q1O`kOK-h z2=!?j<`8e+-inis>5ncX?t<{;!!-p!lPEe46BeV%sG#_2_eGc`frm2-kZIS}a2BZrK)&0^4)hSn&C1?DGsgse2@ep`QVe)uA|PQ!R?`lIk+~JNHsJkt|L!@)xCo5$*g%Ck zf^4lC2yrwPER#*@6Ne~4=ao!icTj5w3y7fq;UutWLLEKaPv>nP&@dw$u}6$k)m5P| za@ncvGg&?zs|MjBv=j4xhseYSe&P6qMVuaIa2&g(7z+qNMKhO_6VX+6QD5nZ&6!{$ zNYq;XzqCSt7i34S6IE3}UWsz;#r6?(U(E5}Z(B=z>vnE3S52L2jUf*n)5OF1i8_OY zA^@ek|OaQ{)g`x6u}(g?I*rG9L#$2j;f#A(hWTmKp`e) zOCFz_FgGlx9^Cy0_M!0&$`Mpkf>KV^>(nLukE*+j9ZIskj4ws>!MA!}92dTinMFPY z@IA`&-%t66>v?=$=_*|l| z3>2Tm4gi~AC#|D!1#6`s^7y;o9@{C+|7C0_ElZ&@Om4ljnI%PxJ?Ha7aV!`n2}&cS z=~}9Z{=BZipK~SuCout#3?X6#PX4O4u=16t9Q`81V9C5 zDrjDN>s{uJ8!}@j%&zZIordKW?xU7qMyH!vM(eyhS83Ee&A}e>05wF$ z(C5{59@=a9cUmu%O++;S;1U{zoB7buJ32zUUz-1Ar6^yXGAU;A)s~0;{I6DPQ(RF{ zCIBoxvl7!i{Nv=FQch%L`@cIXGcc%irHpxQ$1Ft{$8XLL1quQoE?%x>W*tIE6_=x2 z3UlJhe^!S&GzdH*Y5smSM#Ax`y2F!^Vn{>*W&h?}_DA&fG6(jv=j{^kZIg)q0mAST zlBWq1R}|MWdgL+smyp2m&(buU7ygC1KB%Lx^5UOV7&IV@u|^Vgz4YrS!VYDF z@`&;f!|6&KOpY#ZDrKj zzWAUvX5c79^+ove>POJrOWYA-kWNmPuw-N38!OKmja6q1E1_R+)-ji^E;c z2^e_wOk&cVI%*E7fuS*NqeMOvk`Xdc-30L(Kaf0f)B$CcaP~dW9B~e zh*G~z{vSq&ZHQ{M#1N(DlZT|MtgPVEZ425VX14f_8~;0C&TnFq{n-DW1VcDnd{U=Q zw6wZN)!34{A2SI65no_o?d@DdQaIR$3DNd+nq&_=j{hA^na=`rqy2;IiOD|NGO4E?JqCDlL||SUZq_ll#3+*Urng z#5sg&sY6YI|1|as#9St$zq_LUBkh-K?{Udj^)2q|ZZ{1(7Nl^&8fJ$7GX%svL>0g1 zId=`Y?3-ZC<|zRtZvfwnhhcfMso_v;@C52IfVo}D4qW~r8eR!W3 zi@XEM5}W)d5e6ng#7Fq3CSHy;aV&ecVt7X|2PG5?AMz3Sd7w_S$(};zIQcJ3gdm_W zB9~j6VwM8=A2T_CRq~BbAx!)~>XFk3di2V^{|wz8k{38#{93*NqB}bYhRe&mV&jNA z#tdV!k}=f$Pp$AE3|KzqCa(KxR87?ydd9?ny32O@dQYwI0}j=B|G}`NjELyzLQ31- zp`&&3Mvtz{qytt-65BI*@8g348iN0=wHpQ42Nm_tmvoUl9t!%qH_@98hR5BLC>b@p z(7b<$7yniRC%{22V2t=$&lsN>DBRVW{G7E=*2={-L|b^u>VLLo4whgZc}MqzN0+*?9%>k zD@7I%pUDgClb92XX-)qbLih*IM)Jk?NKLQz43km+xr!D@l5U={KABme2{Eau+9~ZE z8Q6Sw{Wia*Nbwn%J&+!4|AC{T+#7-()PUo%ny?&mb-!(}m$QZi_uF4s_9eX&@i)W^ zNYD81F$qX--fX&Fnt!$@%Q^p-H7vyT*&Cac^MObA6RXYtK^g~F29qRlRK-!fGPQhR zW9MWKN~)DPunIWIb--XS#Q2|hLR4X}t%c7GX4PRO>8yZ8*2X|9Ltn~;d)~4zS~`Zw zkcTZpq7`5v?W-j5?W5a%#$HFK+oJv3#E4^||AB5m0Hj>ZIa0ccGSEJ5B{EI{ zn15af&Qn?!_n%nQ{=Xx^AW$8K?lK=!>K9&`0T7i5PmX>leDkcMS^%UjSHqumACxpN4BDS=ux-j&*s{Omtb^Jd!K?zB+xc;+qGE|@PJ%uz}x>@Xi zFKLZaKk1#d6?h;N7Ds|cse8xBEP?j)Lm#ZYN9+}YQA{LRG5YqJ-|YfGIERYEHAB9! znP%lRrwufB%^X}F1!sA#*wvZIik80nJ@k;T7AH$jYDvfNeBQ}R+)zfD!33hpAZPOo z12>iuyYp4l7ul$vxRLXf^Kx&;nd)I*nsFzE$$n%<<}QBf&du*OCVob5k_C;n1Cy{0 zjsX&xFoJ7A_$BkeJVn_BQsgCXTbhBybSNnuIDM1~(!fHFn?Q%#>H3wPN|GcPKP>-| z0>fl}yCp7Ng3P&m45w$lmbZ2M0gPG99Y)VW%_c{^#?tPG~tI7q1C^Br2Mxdhs->4wYZauLjZD= zOb)}~EbrLVk8WC1?qa^wk7%d^++y8&8sx5k3-^|{h=FyEyP`$VPc7G*aYoJ0|V;sY0F}#0ur2&JzmVB zW;S4+SQz~a`e}YIj~icU-s`?ftNyK%H!IGE<3kbEjn=ViR<(d#Y1yRmQ<$26ibd=pf$`A6K7>;%B7;{f&Ud2f6EU9vSXPo`^uVaW*7 zA^?--oeTKG_>SVOFN>mNfdV%yT7}z@Gs6G-0>S%i(I_7)x&)|zC@2Mu&LFw{|>+s3h zPa9Suc*Gh~{^lh(7_eXL2sT04GQh<*43o1eKC2oM zs0gM1o5_uH6&<3`~^GG8||l$FvJZl=;wgMh9DDbn*~)xMh-u zycw$uRlhNHFa_SJPI8ELoTu*{`mY#WRyqljh*m^B(Pr(9+5Pk`sJ>EecXK)%hna8- zvgqf%)PUF6^Bx#g?FY;oMn>3C9F!4Q-Ai>JLi((`7ddb4j*-jAD0rFA*$ zjj;*8H&au`dd$r$v*sR3eErI9IqcDsrAHJMNkLSkb|FX2MWf3=8Xg(qjpi^7L9j~lseLa0VjNYHTp~`eg~8lLvjovl9gOJzeeWbeQYt;M z4bmRH5P9qfj~{%WH_-Yr6)Ej2LX+hpwt6A{LQ|-`{&(yJPsE0I>u2x!S$bx3aWG4t z>s(B7Y%cz%MGIyW%|WGP3(wp;zeptxLQcaGSZY0;w3PMd&jt@~BxfZpEXR~RlRrh| z%pYF(oKuMKDf5TPvVMdF@AxNILh(4g5kkK%(;n>O`czf4?y z&1c%b>rOh z?RVpSc90h%S${+s0wZkT+QSxO_cUR69#$GghB#DTsXEl0t3TAd^Y*bZBYSq^qEAO5 z4Y0A&iiq399ei~=dx)dKo0Z>4zmH0NB|r_eBJ?L<4T2~-Ms~nTj2rzA{vT>fI_W)D zsOmR-c7l$^gIb?z`FV`v7QZ)q&DRG;YR3z?Mr@1L9>y!axu$+&@xx}Azk(I%#s{DZlT0hE3)qM z<2f+H!p%*+EX4U4UnG~af!DRiSTWEM8uaFt77wibTO-LN1*fh2#z^!}1G6%s0HWwG zm%!!cu-BZQE?Womk=oC)yK*kIg5+a-=n)bHu@_7rdmECQuo~@#i5lEzo?TXKdCy(r zdaLYY$1IM1OngJOGX2w%L3z0fZ4l;$nXUo@ZZ)+mw*d3?S**b39g$dnGHGvr>u~q3 zJOYI$dB%TyattboZ z?{B`x>tUrU+`kHXx3*nycpn-Ugz*8RTGB(R4{>4SH%}uCkIZj37t}QJdqg< z*B6ClbQdq(eL>u-_rzSqJ~YY<24D<}`D^w_DJq9rzO!7;V6yZV6}xV8mE=S;SckJC z5rhYE?RwYZdxx=}Tu!JP1%3>!y+L`IagaW3ee zfFpn6j@DhKmOC%b7_#u)7u|T{EtD#$R0Bndd#FN_$FWU z?h0+)JgM+mZ44yK&RanEMC-Ec9@D_!o}c5J4vX4Y=xdoDCX1d6aj!;$6=L%x0jPYQ zK1r^zO9!vPT^YsVIUaZDSRuizpu9yr9!wKyE-hohzNPrTiMoc{Q>Gt9aZnQUy`fvEH z$=@tEmL0w4Q$PCt?B$#zdfN|3COAZ4gLn-$?I4_Y~1~+aR{!80za=cX>QN!5dOUHSP4GU{7$%W>Q~&e z)6Ps%f}3)uGCmlaP3eMyBF2Fy^?#QabT^oZ6X8)9vUyb8UM)kp6$x;`qZ-YOE~+mP zjFNzdJlr8&U2G{aS*Ry)CltnS%dgsvEU?x)u^DvEE3lmT_Oeun^lm5W`_1IoMxYR( zzJF=RQ*6C!t5HEVug4^~nO}@!EOo_C?J^P?x%X*(LzQ+NJlo z1oO=7((oe*;e$GUkDM&*hACgCP0sQ&A7X-5kyXV3hW(S%1(&*;YxfkK+IU4_k(sNu zvY`4Ee~GB7(`Tj0WSn*hcH#p2fXpmmJPw+#FR{@XvnBHY>+k09WFOz!k$+<{A}~yL z;?=>vf6hSKs|?7O1j1g>bAIBevdv{+u3M6!3}Jxw{!FSc`t`TO(=Fi1+r{;IHgToBY|oHN1+2#{$ZX z4C=pVr|X3wfXLIfISz&fvxf~h(kjwx`fH8V-TUjG5APf~<_v8aliyZuocVDywpuqg z?V06=xX`A$q)}Bzmv8KuMo(*XzD#)keg5_KG>%s;ru1p&%QMR%L+^d8e$Rx6IkmLwfrlxwCzQU|x0OyX?Ny1C-^s$t zALdzFYrhuh6|%u*!VXY)zJ-kkhM&eP0O$JxdE}_Csx&m|hds6&%Pcvw`D4&vjnAo!8)Mxo zu9<|)-@bP`H3;^4N#SpMIwLDBZ7UQ7xtzeE)88OX#&qp27H#62mjw8I)k*9H+Y#k+ z#ew`GRoiWm(xwVwzalhpRcRo5+XVv81tPdxRkZIsnE`|E=whyhLxN8~(C+uUfV0!s zmLA#ZG7{yj?LG5x35j&vICHc;YrEY+ZxSx*yDq_lrSE#WeRM(EvgP6 zz3fyC(W`z&hg>)YBk;EsEAg>S;?DsW%O~Z=ezbECfDxBztS=X8cqd&xPmPQnP-Iui z-Q@u`UpVnII@}7@zCW|cYWNnvW3InNW0yo7t|TG3#_!@Np~B>hcKEYsWofU}doiOs zsVK{tIz4zh8o+no*+ZV|kL`68Vmh}ikII=hXiIvJm;A80?gT*j^5#)+bxQOC-P)NY zGGV169NYYp>mQ?GF4Vc@hn#+1#_=06T9(hp+e!tR64)RuZgkOCh+bw zp#cu6ei8?sk9LR|C*g?CdHx@b?CWyz3+#6)%+F9ecH05nakOS z?5=g=tRhy5_tHkZ>VWJggHyTvr5c{~O!;HX(Ngz`+j-)}gFycMfaG93sF=V#fPz*s z1`TX@>5CXdx<3peIbT&z{S3447%@W?fXj|@G2@Xw$ab{K+fgP+Qm{B_{ca>Lud#*R zNy%i|_l9Y`55c@5AR~s&08?$8`y{@j!*8d~-@m6V1O@#8X&RF9@4Cy381rT9L&eCk zez(*#H&r3LHR1u3Xrc9QF^~PiVz)UnDhH7=Nxb`uk~VA za6X@hX>Q0;^mFc8*^ZUp8zgnS(P_sh#_Mu%?*F6EC;WLyeURVH#xi{fvjEYnn6Usa z?)&2PW?CpBUQH0AMTNi;HVB3Ru>z4-d+jUWBilEOgrzQDP+C>N=UuUJwi-&l5nW4W zF7c%KA*m&=82gq~#XZJ{DRHs!TL`4phsDnN5zt?%e`=ET2Gz+|qV&>oo&Ulydt-z? zt_W}w(GZhA_aPYL0@vGucOzjvCw>!R1Pj}k-MC;TpotOmvTj|-$GoSLLfS-Xl+y4z zwM#Yw0ln0#N=3Ce@H05yEb9W$=WzIefU1CE&QXheiV#Ak5$cnvdh7Z(I3;8JrDi{G za&O*;qDH4eRlH;>?T^|S?~seA1oRHC^CEyjfXPdY9_?^c0Xn>k6#lfF>ukmw=5DNv zD2I4RR3SU-WquFuoMo1Ymd!_g`bCcIt(2ePycNv+PzPC(X<$SEU2lYd)t6!LBi_C> zRw*zndEy5zK4a`K1mGcF)q@^vehQGOnz~vtFlwa9sqisvc)>3AsBode#pL(bjzE`A zeQ-3u<*O8kebu3r!Tq7n{`NIf9h$&bpH~Am#a1nwd9w*n2sy~Zbb*XFad<~T6eq%; z?1y^uJ+D+Aj){QyiIFpxMNql$x-;eVdf+l>0Dihz`L#agAXi2ZLCU*1-1~-Bqp>S3 z)8G0+7p}jdSX`{Jt0lO(IfMzrZ4?uDjIsF25iGzlGP)wNK|ngeWCwD2XrYflygzKh zAFe43N_*<%*T<7bQfnd~g{WN{?xb!wt9rpmBtYIdU0I1Vt>R;DqW?wM*M$LTklu)W z4Sfv!TvVC+AVR{I=>=*lGv2GGTiB%p&I30onYqUivZ710zNajZ$~fbUW|Ba{VXR8h zDI4%D^N4I|yC0jHGpnQPk$CeIHj0gAOn?oLBrUa#Z)h30i3kB-@wq)*R8 zYm(ltkH<+a_5Ngzqv@Uu^e5&2P=C*)iIwSqmlTl}n^JU_9%pjwU(JN61UP=^Li;@m zX#zd^f&cT25-1NZZ$w!fV)m@_zSlxqHVW1vbf{UZ0VJetG=2pNj_W`f)~r z+y0F0H@AFd7#nlHzRlL&5vcFTu(sTYZ#fjPXX>V zHa|W$BqvMCNxar_P9@m)0n1d}Y4n)hd9MKpW?y>oH)CE$^&QMXA9ZE{E|q$zL?3a` z)1rYj0il0^i+jOuq;Qv#Ql7?@&Rk^v{_gl=7~vS`wjfx_^QRU!XXY%_jM~KxRv?3xVvPF@9p8VGiRg%iA zJ4;u6zM$`CnYlZYo@jIn6L^9i03LbDliM2`EHLu7jE{1aHbaaoL7NJv@RbT^jZ{=Y zoZXYguNWl7tO}x9Q_g%YNNq!q*~P-pi{+SXY>o;omu+&hqV0)g1hc~C_*TlcpgoGg zg*MonF61f6;d1*!nbjLnA&{9B>_S%jRRKk3Y`%{ z;*k1pxygA&pUU6#u+X3*rw)u7ME1DEn2rQgW1pbKFxiv{VtX^E(5O{6$qmSUn3s^P zx&xiZE)e|ImF1IW9$^vfoc(=uXt|P|!|kodJogU6o8SW`J0fp<+}Em!GUP_1!DjB`U1< z<(vXZjxU=1zA`*g1j!VBgf~2DARh6xsM(L(=Jsu^r?YuCG)kf%!_7s!-yw=@(+v#3 z&Uh;cs$hWyurI1VRxJt_4a)*w{>mM*#Ix(Z$ZygCFI#A90EJX zE4+_%!tiKAbd{jUGtv07GE^Bbf?2xi;oK?73`xoP`oL`vMcT6r@0 zWj|GEu&d+c@AKQ2XdCj*FG?3=HVxqGDPAButx(=N;zR4XDVUX|%fO=f`H;SAbYIiYo#y9495}1krWi1*#IIY`w8cg?_fwbWE`>4^Q-DUiYcpOZ_ z^k8ngta)XSav4irZkNp%g=<->aHh5en}fqVixdDoAY0Z2pUoPZC)rNfBY0vl%>ksS zhIpY05-b!nhor*KeG4=32o8L3Wy|9a;KeK0Z!pE^0FWH+V*|ysHXCi)B6SDS-{D{{ybl~2zqR`w7HE=BV7QVat?>(h*;{b zkfJK!?CSZP=*aN9%E(XW!jT`V8m&cVTR(zC-X>c}T3U+E30!Z(@0`TfN53WBPLHQL zNfW`7iBbywxo zJZ{s?3n5Mxr|a!wdVDt!MU-baTr%)CT=%S~HWGrJY zer!2_@g$#zikQG?I(DWGI2KvQJOA~|ff`6WTB_49Zl9I9G!m}B zgpYaM@rgeO+mGpKFZ;*1QKk^^L;Z*Zg~8e%R?xe@>(!SE7Rr;)qFp!Ni>0Xo8tq^I z!OF9RCqzb5{B5m6cQ6{eOr3jfeJ8;HmkT*(02=&RN*PWJLP;C1a9H`ps8lZRRY5Uf z-sd2zUGl!Kzu$sK^pB?+NO_({g9A)2BB&}7K$Fv79p21G&fE)GB)*UmoYlor=@>7U zWc&+tgeZ~}h9r>5sC2?cXMuI3^W>d`Gg)9ALa|7zZxYV}LTwTfthY%UieZRkA;L@0@=cMBUTh?c2P={lam7=k59GM`Y8^ z6z-I$sI%_EEqCj?j^#K_DGXHn&KF;E*9Pq-TD-@>7=Rnp*+Mx<*iq$QZAA0LLF{AE zKx*vi$HeX?x09F6Th`XVGZjGY!5!aUH{C>)i+gVXTY@3Hz*Y6}-3EJ+bc)$+QP9-P z3>fj(Luw7^b#b~st6d8{7|+&MSxTnwjh@K3&j-QVb-``SF^N~r$E&mv-%j4d)3 zHQ)vXFys`#AHmsjd{g!40OTm|5(UO|&IaCzw+(cG`d zQbya(PjAnYW2s`zmA804r|v{Z-ndt#F0J3E%IC1Ar0CJY=XtD%8MHucdT1)ifl(9c zco+`Zkt$-;*`h`-Nz=`kQBtVJ!-6m9B+e1R^O3zW=$1-I%|hH+sR4+|^hSVigmY$> zz}lfAXcI4|pv#^SI6e%}kg@ENH~uMf^q~Pb9IMn%nJLEgK~lqBH*mE03^pMXmSCrd ztrV}}TJl*rhiPwcqy!sHB>Ap8J)PHvFv=P=6J>Y7YGDfBxP3diC&$5eA7H*qAdWLz zSh4T!UyJvt{fh*2kEvJe=`23(*A1gF93+V}L6Q&5DQ3=sx?Hyg}wp z;#%C)*n)MD@R^XCSs>~<5mI+|xi2*MNu|D7Z5)`=AK{6Xf z4@a;S_ZTqFZ9kR~#;jCtm)MOBeh*J0@^hNx(PUZtavV`zb3FK+kN3zu!=GHE#4etf zwd44yOwv^*LHII{%U3KA_D=_|G@31K>Rc7TPb#kb>qib0RBz~liH@Zi!My}Gvog2m z^c+56uP{K?rG<}cm{1NQQgBL_emo-!$5&urp$#w_V{9<+kj5X^sYlFEPgE0>P{@eg zGfW8RJd?%FOkGv&ARJ+iC(^4A>YrN^Kqp7i=D^+BFKIHCxdys*2C;^GJ|i|07esFt zYw67D=uq<4iSq*Fdb2kU)53Y5Zb2^t*S&Z0n(?oi#0kZ!h8x!jf670l$?)Kr&2puV zR6p*E^&by!LEG;#*hy>XE|1uy?Ty#k?ZB)3N=ND~s$7zns@UzR zKz4T`aAO|IEa#JC%as#m&Bh8W<}DUzw32vc7d`1?cgsjo>Pz+03o@ZmSwZqR4y6l- zk9}Voa=az;Wv~Ekce&;SXUuxBda2rPHM%^1UuXU-oG=$zmFIkF#}3nMe9c7kFgxM0 zaE_@fXsF2gZSb9ahVie@X#qDIOIiTyHWFT>JW+L#Tb7->!e7TOyLgXv?a1JQwcc=M zb7h+;ok7Z2pnj>R~!8F7d)DOb~Awt!}OLaKotS_?x+|FWy?sVRT1u_{D@^~b*}!NVk=K8)#J`lfxH+|vvvHtQAfxdB?{&5 zx*526DIe6nkFtM=q4^m7XzwWY>EjyOR>!dpb?1d8F6Za1VT{i7KlJgj>O=&H;Vly&8Fd~d@EXHs(21nr4w+R(a1BGEm#Zxxnl0ty%&^g z{~e7`X{v4ye_yMH4-Amf#PocTRrQ`eM0McQcD0-)%LlB{pSgR1Gq!N)$g)_Nop|?L zXm$Sh*Sk{ZX1fg%>Ls?geQ)Js`+}F5CA6%eOroN^EQ=8@`F~LRlSL`9$4rW~TCt7) z`qr_M`@w@~Hs%rkhm>IE7d4E_uTbyNp*Ou8YbFH5#m(~?R}-+DPlV(8H*Cftpl_o( zPX{q*UwkID@sRa)N1wVvQ`r9$KOI^Q757EvA^JkQA8E!)G0sNEo3!XG3>ML_{oqG!YW2QZe^H1lGp=5 z2R%8s^}Ld)P4{F4FdX5-IM#o{A90-Wldlkp@hs_hvcqt&e89sieH_D|j}Kl}b+mi8 zbInTskxZKFG>Cg^J|*8sy)XYBz{>&`xF6m}QelrKp1m>u-fb5z&S~2zd;OByqplm3 zx#CX5OA_#cco}X?rX->8H)(G+&mt}5BE6hEs2x>L&F8L{69SlFEao=rkE7EI0$*${ zaqWSVbmwm-=udToE@y^cv_Ps3pus|{AA5y&IUWbq`BRpsz#EfxHOTbO-py70c++e~ z?sQPKigzW7GL ziD6@{iOD3E{1l@Hx8ozcF%wM)q*K61j=}XDYM6FX+p6yLeQCI0`ip!N)^R z46xYL_(Ldt<2R40;7zfs3luB!JR=9!wG4V}#qnP%EfRXv7je9P6eu1Y)xnR1XNVuG(%{m6r<@ddvIy5*GEwBbG#NA!9rv&SN zKQM24sh0=Q8Hg}66ndNtXaAKOUSBqk=h3TmW~HR>O*CIg_D1`o?)9uV8a9)J$0>{n zXh)H`Ou`OC{y8yYxWVCF8_OOZHz7)U`Lym~>_n_xyUp(8+>1oLD3`nS&imfn6I`U5 z;n>9EWyUK31+?@@0&bYB@)u@^{0vmB`eXgfZ-K!{fTLMMBu_B*JhmV~>x-k($_V=L zXljzD(I(hQLK04CYa4Q9AhbeE#2ng&T&d9W2JV`6AN#5Hh`*_^n`n}BlOz>K*>z79FPnLp|t!1SW3-z zvuqw)6=jp0Q;42#%TSCyP!7)$=QF+x{f8nTBUUC(t8fzhq4A6ARfdJ8Imq=YJyXw# z3@$RmTzQJ58gGz(z7B^;Gg?*LsDhfx5B9+3CVwIV#lh9z*gVYazXgQs&X6PNXh4`h z52|tsdzO8h?0lP0wKK!3l7;oOZf~H?=}pT%PW##IToG!D3v&PsJFc@>2xQ2Gm5JH< zvQ=^cvRp69Mc#=+%8x$2tT}tdXiRvFk7BT;zE9h!7t7Ox6tiy}J0syiOJ8R-qd=Iu z+#ZrZmq`))nF}_g6EFDtW9M(_2$9tErvj;uXelS&rhl4{I&H(do~@x?&)|ilM(dz^ zrZ1<-7WG9j=l$31$6qoXoo$9HIioND?ss6 zciLW(m6zel3>Lg+YjFywr)9h-FKu}LI6$|V>kz|4&l`LmOC82yp3X)DV+^<$R9N5< zEel#I+i53H7e`Hw>{ANRZ}Miu27?7hi_^W-t{bi|t2Q_I8m(oFk6MPQNJ_Jw;>D5~ z-8v69px=-0u#EEgIb`fUiEN`fJ#Yn75HW zyFQk72kJj3g%G zO)Kz2(GbSBlb7n9RXW>zq@?d=vao1T``2w$d9aRK_bv#{@i^_sa_)TIEu8MN`Cy$j?a^7q=Rv>Ytr?DD^0It$D4> zGpsL45(MnlM+LpHt)+z^GSxVCT-^Kme@8RGSm15dHHWdmE(7Mtd=2sVkW(ch7nj+P zZ7l%h!CR8+V^o8c_4hN!vF-WD^gZ2WWyg-NnIekVD+4|D&P9&?IsyP`d>d$v?=C-~@JIw4 zbKf_t>+N7UGrv{ojt_67u!|BDj%?C#f=KjedA_~*9%W=xn=pk;WKMgWamn{9aviT@ zTlce#o3QlbCBh{cMJhKQ0Cap{Mp;*4=K2fx;U@&(Oh-XqeK#~!1(|>Zdp=+_1iSoR zW3mh+E^bhnNIFg}#R9uzA)cE*c&#linR!zV7(Vy0I!E zfRfXehc42hvB!l8CIUp2en>cGIWi6zVP-`qF6?mmL#pTJv9yKzA}kW|>{QBI=I23U zqnXEivsetn*v#rF%xbJ|9&sc?9Y=tUw{1g~H-CZfK)!7$7H?Y+_!$v=Nm=&u{RsHC zVkJZo#BtvSWr4Tga}Yu|!UgWHn22EKq<4ka)PfY&=S9pkLM?%{v~Wf}ni8!*8X!Vu z24s;E7bQ%A(%8iP0Gj!#d&FXLgMTmtx0A4iBr13Q@RTd6n?6+SZHYp;g4F+G>Z`+| zTEB2-h5?3d5Tsjam97ElMx>>?8>ELuxu5sFM*jDx7_**+6~l^k)#1GSOLJXz&vz5ALzKlnH82cSv$M z%RY2V8p8UAmYm{MlxZFD&%`8Im(O4ziTYHx-yOrJ{2GFg1*JQXU0Rvb9?cB1tSpvY z(7)78w%tZbaVAY%vy@0n{W*(wm9p6mp0|bPsJ^C2%j6~cbg8OHCm!f?Qv3#L$@T@y zxRHE}HhzBb9?{@`b2>Rn#oxdRiT@yuambAU`@ZL3JqOch{DH7_YWRKp;Wg(j&vDFe ziRKVT0_$!O9Mu$YRKwfi!g^MY8rTW?3Tb7kBeuayv*_2J#l&csafH=sl`OBOa8u4ntlWv6VX2N^dfLGX|!iA=?)vuZ2v>fb15hc2d_yT_3w zi-w9LbioI90zTD$Gu5bCFLdCD<2`QPAhsf!)WMN}vT|Jk^c+-uw>mifWy>mAn(njt zguYIPTMJFLmGnPW^8p*#Xmo0ul_A?qb@0Q!PJnl8cVFzYHyE|x`J_Vp31%KaRk?}g zQ&V)WJtoko&g{Kn?^;(>IZVXhm`kUN5t&fDI&jUqqvewP(_HR>O4z`#fshoyas6Vt zuV8>oE>WcEJwu85IK4Y#H(e!Gay9yz5wQ9i%eCXnBmaCO z2|uMO%?!})r1+ikBf|2&3mPi@aQy`r=ptN9`o)q6JG=wg&wMZGrWC- z@;2Rs1;GFbH~)r11I4?UpY|hjuYtYrGp7ccb|MXtuYP-N94dVZ$Yp76`VY_7qilFNb<1aYwIWK#D>%mv!ZN=4-eY{B= zs=^^JoA}A8fxuzkSm(NDiI-*(bU^{t(PJU)Ac8pd;b=TR=xsh)^WMq^dN`zqx^tyQ z_e|VSv8pTL17J^BE*~LfPKiJ&OYl8qa4Bmc__XF9lHU}Ybj#}S5o+o1S<5JJPX zd!a%%=om`A35J2ju0iQAcUVeqJEfYy;~lbd@PTAGx!t! zw_i?4J#(+7BL|uXL+@Z|Rp0=A>CM+~axLlI27ekx!^h>nL2M+AUyh?S_Gh?5eJnYo%i!FZ>g*r? zr^b?jseQR>lpJaYB4ZScnLErc9c?w0iubc%aP50qG6l92JnlhkrVvxY72n1w2A8a; zse9h7LoPiPRWTgL9sbYy<#(^OpDu(AfKj*;ldZ4v95u@`aSK^INJ}?`MVZnY^Vp_dx0rG)axtCZV2U( zcXaa@Ir-5#PqbdYakL_Y5<8dfiV=a5l_CjDM+HyO>QT0IBD;t%kmSt<+VAx5(q7%q zU_2rgm3x#lh0w^Q1AlrX_cmi_TrF|JBl`>J5{48R&ue4c{dh44cHi@3O+t91Y+BQU z>o4zor^~6@V$()aoUPOgMHd*vwd9cu1d5Q3>Id-elvgFc+XpPV#0LLnt%~EE7>&BV zcjns9=Ac7iU3zlxJAP-c*>*S&JcgaYG@N`^^nwZ#S}^57g8^qFg7u||$ZQcDHK2?# z_l>5}anp3V`w03r7+_v+?)qW^1evJ?2%esh$8Q!c=zqk&e0ERubNLtkZ5uHM&_|9QV~J$~e?g%Y5@xn71|0&7 zdm(J1!h%9#z4e3SQ2-EKK|#+XoE&Cxd6>n&BJEC8?@L$iU7J-r=;O(r{VE}y`|3jk z$_STjXD|P6-xrygMq?Mx{?;yA-&H4pM6wuMIrTb9ABIOW5#TXh{2zz0dE;hDp8LX} zN+l%YtIv;w7~o@DeC}@8qkI!9X%bH{>KyrPsxWDL-T z-f!ggo&`QCOYh#c#hAQ}KixHcvtxuqB>XkKpnrcfDcH;7?jk%9LGbC(U`-32^ni-f z4KAWr29XOFi7~sZL)l`T0uuU1pBVene;&K4Ws_-hqJCqv{vcVUxo#@$)SviTa^Hyh zsr{4JCp~}}D$;fp1n?tupIANO{9{N+n-%8d(R0CFz313DfS`|ZhSLHO_&6HM`|@2e zZ*WOJ=r=(u)7ayn0}-%a>`MuGRulQM=abX>h$@cCH@G9I`q|LY^KFB-GD@6Ui8_+o zM%0*o+O|O57lgj>^Fc;{<0?fhvOF$vOX;;2r2dOb;PO$(0V6zGgbpPheTNi0I~D*% z3WFB&%#$C_bC=3n&n~eTf$pk!^078^bx+~f#DWpHwxA3&!n!D z>{C3{6GJM6K~V)#x(fEBeAdvx(RWE7p>X3AyoDUEW#KX}5%JOhv{os}C@@U|4<_ zM!EjE4mr|{y!{^Ao$*Ioq-hs*^}t{y_-oqm#bv>-(3{b@n)gs*oU=B~Zx6Cuqiit_qmujpzFUCr z*m{i9-R=Wi7_5uR{nWF-Ih**{nG;nMh_`E~5JxXU+v(;&CA(s?pvZ_$%r**kcn%D@ zhOts!J+y_*2zKL5L;y&x;9^hC)5-SIs!8wNNu8rk-aE&qEkpF!S09p-C1FCs~hV#!=tXly+6NGO9g*S zn0i}Ubwi9%bHmD$IuRXq9tO|J0(Hz;(qiZ!gu(v2d(bbsca0Ph@$}p2pC^3mc(4}O zS#6*4<-aXT+-|#`3~x!@E?t+b7pIFS9wQ7XQU@BeJti0tQJy9`!T@3g-I@U`4%BuG zgqk!OM~zW`MlW(%(C+BuXlA27vbys)$ zg!faGGYi^_iu2dySIZYaK3OBx8n=GRJ-Aka8p3S@FyphdFuh13wjHhT(lF5bXjm5_ z#&CX7xje;qL?o>Dw#0}c@Gb=U0^>{q-23S=KKcwF{ut3nh0tO)7A4VHGsdS4Jpb`S zKiKAHp(8+n3`{`Ob6!B&k5tZlk3!$c0S$b@q7~NT`-`0EMbT@(NgK36Xt1 zuoW@;d0fDq*JNR`yVSrUfg^;n*yyzoJ5m6=&FMmT#F>N`@VbA#Ck$>|Y%}@OxLs3O zSdbreD+q@UDt1TGJ_qA2mr_XE6`0G=O!&?G)heS9;UxZ-{I%ZLy+L(l3X33gd$^9H7!3u&1M09MtBl|NfRqKuehO!A^HG3*{l!v*}>|p4RWxjm=8z zdFkx;#L(QDO=P6baf$DZJ9({<8P#XC1YQ)}$a$l;L(3F_5y43<63HO8H74w&Sc<2F zhoBj2AruX!v`B>LnH(dc8||XU3_K?J&cph9YNO*zUC;VGwdGr{RPQk>hFRRt~kJ8 zuM}8R)CpU3u)$&RAa*{pxjqMVJ!8A?eEU=$TXY;{)f6nWFHtZ65)A{dMD&<2Sf7XS zS?1N@@!ob0IB)_BEc)g<R(XF5u0%@$422NyOwxrQ~A@FhlGXQr=4q};#CIc~b z7Wqcg-De7uYkp)*w^KxcqNC(Ts*Mj$1cZq*sm%mFp=EfQUtG4h^icBK?5*`cemE7n z8moEjdG#MH4>pwL(Kv+GbS<2b)~o^YLN7lbeN6J44GV1vmH1b;V;JcMxfEm#k5J3O zK!ae2l>^~}9XF}5`W6J#T{~_F1@vM;^D*Ed7J5a1s^;?+a+y>Pr1X>A+|Qqm|2=d3 z-`u>j{aU%ngNmb6V71douJ)@ELk_p900+!bWg!}?ghiOCk-X7roxAvS?wlo_w2hP= z{QP-wSMG5HtXxjwv*n5kT$K^X|1e)tHikRwiv5PDL7FaMADcyL3}IUv7%PfdVrc@A zwJ=K?TCn+7<%2gqi3pEa=|`(h%oATdI;4Kvq0!~^HB<;E?tJmEt(OrhCgniMtDSHN z6l$&Yedj6lx6za#2j@vdBtdexXBSC^F@OPkzuP%wgIW*936PE_QD2j`V?hpKkG-c< zW>LOMCv;d#e=lyx?n0TT`Kbi4Cddd{_21nS+sUa-0ByEYSIX35p>L#hCC`ZXLrzJ`Mg6n6*}MJPOA0!qO-}lpc(iO` znxn(=71XCCe-a4!5cS^+p4IB0(T>KnWXIt8CGRLmHxrDvXGO zlre(pV78y}?|(C9xIQ5IR}}hzeip6TbSQcHQ~f4_n`AlU#E|f6Nhm`o&&d<1H3c=gJLBH~8{6s;#nXMeju%w|huqSs+7S|%)X$V)YOUkbvR^Qhh=qEn z6j~JLBT`az>M4_*i5Jqng_{|*KX3xT)i=D9DG~>+BTva#_m z?O**#0;c~;EUBwV+($PSVzPu3+!@1^#uW+dS#HJc)gA(-D71feT~G2O90K?WW_j(k zuE|#g=I>%8ktoyxpQkZK*+Q-I%vs&v{B(hDo@g%F(oHHX6xu5MGE0sF7uWOST}AK` zOKlOnxvp%v)L`A1f94o&H(vcpqm8*OsV(6-J&HfiRCmxZI@0LX@N10j{-W5e)zNm} zRQy3Y>-vE<^pMMnpkK${v}|W_VR^hs4S|JCwZArU#F7thVk%0%0Ki5+|FNJADBGkUyP44;+05>o^L${TrLmjQ}3*OjE=`7i- zMKOakCFgKCWwdGdW_z{I?4$-2hUd^5BUm^{>Eg)ROLqc)*lubeEAOy$9}$g zzt_*jdJ3~i`ZeX@YdmE^7VnG|>@&&_6QGpwjK&(@EfvM0wbIx`Eu zi-&b;L7O4-+XW9J%@Oq$E)6=k`Y5g<{sj)HW;lIHCc-HoMW*o;H#--3SRxPMRk*nB zeSHb5iIbO0cJ#{6dPP5K8z?;`xd&B5=r@kGRsNxM%LYlOD=CN^x~no1f1O@Sd6;*O z4^{Hp7fM|x4&7mj#<2P%gqbEG?GE*V0z|94G^GJ*gQ;X%RY(HN5WZNJS!+}MAcNR!PMR^)TSQkB_%uYU0KU!V2u^r^KW(N zB>GPG224kb(TaPw3Ui z+fv1DcmenuYPi>{1h$zsjNnr(N{lem+Q$}tg3OLD_Gp@n7?CAAZ?*oUb7RQkIheQs zT!i<~(@qQ(1tE!S+!ium)|^`2{Iu^G7kWN8%u9U69bY;eo*M<5ARy=lr>Kue;n$&S^GCN zQHy~Hr*`p#&@K^JmDrkVk4;YwjPi8PUR|}HtODq&o+pyMG0L)R=0+~A)#B1I!{^%d zCwj|1(3KdzX$9j4UGA{(b+`6$14m5Vxlmd{?#I8H<`bczPx0CfH0tgvN5WctRqI8) zZpZl{PcgRm2~@T{Y7a5}CT(H%d#p7IpuF(6O{>zoR%+WN+jhliX=euY@NXP;Qxja`S2N^m&_DCF6>Z z0L~(3iw=3v@Oi*fFs%nSDV)X9*n&j&97(`Ejtk;=b8+|<-;F@uAU7IFZ8VY=7|-AY<+2;(Ij`(^PXlpIIt-*%ZDdl!Tt`Ga2U zZdw{Va%O0^4f-g36B3}Qt$w^d>@M)Pj__}~7JZ5S#;~;LiqAd9*-NDbr6_esjfgf( zKUIz@TMqe9XoMBmWgYXTA{ahxc8?3qIhjRhzBUzXV8G`TMliRK17(6bcdbt2F(aUd zTFw*LO0Y5CdPvVqbE7q+C(D>aSV!xMfd|+K(mEzknUAm4;%$B1a>=7Vo!=D$guw}) zGDpz%ipA_Es35D6uo+;0NX5_DBaL4pi|;JvcUAZNFf-Pf4bI9>rn-nWRJZ4fXNB`m z8-T6&b1!dBuVeSx0L7~Kwr@&kPO9Yq1Nq0KdcXI1@cD}%KJ~Oo7$^uwIn(qdbW~EG z@tLLlsIzjyiyuG)NPF<%NroRHYC%8>C@nTb7k`aW1 z!X~N&>Aq4j0*1q=1)y>MmPY0xt8WyhJ5Y`WVtH@0R+jhU;e}1l%u|1z774YRlU6wB zv0(4Pu?g#qS47eh1xavY#ig3Fo%wFd4(#5VZ~n@V2YIU|?5=+x<;{ud%TY}s)KeU~ zBJ_HqG|MMyKH@Bp{rGvj@)fyj8Z^x_iZaJtcR7=)kc!vlH23m`hpPH`a}d6QY(_zD z;n%MBh*EJhH#Ae@zN5zW!b!t=Qbo**etZ0G06fcW-;{HB^ZWOYztOCmDJ~jj5T*TJ zJh|wWki3Lzh0Yo`S2rd0jUW8>9vBa#UNf$N3#8_`HB-wuIK49kW;i2FwDd*=^@5bi z>byRWd4W;ST~AYd^4h~I7z>x*oFZ+#23&-R5btg*K&-!nwN^$FgOG?Vc7ai(l81Q( zFHUUZV60+2c04)S3EFVDxwfk>SxUoRbMNPxj@b;xobEcTeCmLb1L)r=QR;Ceo{OzF zqlj}Nwx`RXsPl&*ue@}l2eYTf{$NT^on@6Pfm&;|92`?IHqvhbTVH{(cxG`=_ zB-N4)=Q}7cE{SnZQXg&1AWx_Tx1;I-EzW{)6k}fP!PUB(4gl72SgY$vioS^7!~)A- zmu!7*2)kKw_4vXKtqoX&&a*yIE8eBrJ+ktpH%?&zyq0-sJ1}+OQ8Uly{9IbQ@a3jf z);H|M0LKgK+d+f9QjkxJ1Ww2T4{N~zorNjQ7zpu84lJua{N9PqdVIq3!%YirN{BH^;TFI` zs3zp|)awKK3uf(~cwkqT!h8>1()il}7)CX{ z{vqJ)tqJ#YH;P^%(_pRi;U9-XUkpUiaa(!#N2vf<#oa`oI0J=4ObDwKq`v{C3o8;1VXnHRULG20p__3*Evzk~R zUY|fWQ(y5Fe?O9RUy?Vg4Iq+D(!XgYax5VSW6snYBSqxm707m?<^D>m9;=TL4hvY7 zSzqk#qXfqM?w%KRV1XmgWIXg|DR*j!W$1D|&=K^QuU_+g<7U{leyWe^}Po#$=G z0<%(T>U^UMV%P=SjHh-m+O;=0bfTTrm6SfZyS0K`nMS7qJwYVujn>(|L4CacJOM^C z5ZmP-B>s{!6Vm9M`8%$PTVHuRer-48?Zc)i58_g3{UPr~MY3zHITLJ$abhc=s~)R>GZFDO zuOUi`!z&P)9*aaCrLArS^A~F%nPUwa`KKw|vP`j+WDXJg9P{%x>v-1;YB5~UH%h^X zp0RzOgJe+7O!h}9cT$F47}O5^$gPLV)|3WQi{XDgKY~H@g%U|d1gu}a`8%>t015Bkb7UBEV&oBq8b3>EzPUf47%bJ0y^Xena%agn48fK;5fd#VPH*b- zEFn4MK6ak?L%7JlP}0Aw_gB3d^RGBvTrcw64D+%6Z%CLQ5jA*!0bt5L-(k^qr5iWj zYn!d5A3qW7{OK*CQ8yQ=%s zmmva>h&~$3=XFI`efIb?4GkM4HXkGm%iiACif&a2p^9oNq(QASF&jiTJ_Bz2vBCA4 ziaQ*+`XB(n&eGA<_ z03kYarbtEr-%oMEjH-s4eE9K-bKjTe)3pBWRarMkE2YI%@kFbx#QcdME9oFtY^R=r zr7+g6n$J?-VJe6im{XNwa4sZ_(%X6^W4ug-01&ZZ0I|O4N#g~tjkbiNlS>H4a?+05 z`xyKN%j|L4qz9+Np1@IFt-E_W7=ZY!i6AaAH&=&4Lv@2JNpkf7?PZZsQ^PTeP=M_TW^*wUPnxU=obB( zIxfx+|HyJR?Mw(R+rHY=$`rD|dty3k9w#CAxSp`N_2Ky5NWM*G!?@T;B=b_mkRtYbC$$9O_Ib z*)*(>4aHKshPcYoDu)z}3*2hv*hK%&%}UeX>)%F(3a2Qik_=UtZuqHv?%NwRfKYxi?%mueB9@0%NM} z4+t1;DYHn^RTlt@e-#RPSGp2@j`sXxqqM$;D`#Ga_t0WvDe2Iq;V*j4Nfr4fw*9?) zQJT;4x&s7nq%q~4%}3$(oZ1qt?Y8VA z489p=7@b(^y>GB;vF%~}#=~?zpPlJnWJ~_Fg!L1GsFqn4_eEI*(a}q?lywyg`m4AV zvk!J}s()>1vwkMI(Jp(XyWWhia#UbQBK|VP_Qk$F}ti?C=+p$E^t6G9S`ljqKS71c%|Wd6#w%`g$r^DVkmw-GJia{>w*D? zJrcdqA=5}9KZdN-^*XW2XH03MhjuBMwl@634DHI6a; zxxHWJ4~gHTPjCL1?~A&SR1~qCG{n1h=9&OU%-HTeQu~0oRBbIp=p@YfURPOtgXp+~ z#0`x$zxtFD)y<=q0o&r$2RMbQ*#%C=$Gd_HjJOcmsgAxD)Kd35Kjc}8j#X>E5~(8j z^jW7&gYsJQuCI+3-2X8|%4qhQ!q@9UT(eF}8U@370F@*Y9hy&pNd{_z#lxXZ?wZXp@!- zb{d*3p7^@SYL;aBpIb#({WC3+O7xLO<(WR) zrkmkFE$Yjj+XU8Ub!C^Bn$%33$`?e=hZiLFllK$ysd{bsaU65!WwAbjXRixyADj}2 za(eB&-{2wwr8b_2v*ti1#Z33N>Dep5&fa&2oVI=($7y};Lr0N zlKZg;m#W&{oSBj;3N(~uf~K~il=Hhft`M}etByEiR+xw$7ZHK(~5z}E^8uz>n} zz;`+<314!0N*;by*~V1%Bu*qsP#)gjyqOT)4$o5o_4TaJFJM?>G)k#FRLrUObf4mV z5QUBU+}D+78@?X}2e=4>mc`B;tWT@^BTpR-eOC~vK7?C`!)AEnD}nBF#-e>fl3MQQ zp>rWtL*e*~jeD%?2?Bo9ZhUpG;FYS|~O?yuWi9 zH>)T$Gw_l=$_&G3Chv5Z953f%$unKRMc^wGqnsBh2Wb%6fGqY^S6IEoaPCiLk@Z36 z{Qb08_0%F}33?IlNap$2tmlhH$);DKU&zDQKUlVk&0k!t^8dP&5#fnWL-3>z)a2W* zLG7^|&7AB5lkaTCf9-vrsy>>akrmgkgrRQpb38V1)6Vl@`VCs(b)njY{m7MBn-fha zdB*Cb3~9n^jeRE`)9l0SOj!wz&S|aY$e`wpZgdb6D4>5PaAq%WyG+t~CZa8VUMj<~ zk`_tioWf+%_~5u-Ni}oJgoFqRT_?km2*>lAO^NVT5E0$x9kpqHbvT^=2}e48Yhb2bE^y_U z%x4k3^K18It7BsEikLKv%ScN|FK0+l*19k_Do?8DOnmN!hv=(c*3zxj`BK)!>~)+a zs(Itue(F1dQTlqN3lr{Gkb<@cKglEv1Fd+!~p znY6}oe)=2uGlV=uoP8AnRfeTfnN00ebHNdN9E!MHR%1 zi;Le>Z;8&Lq5czcbq!w)^8}-p10>bC7rU$o)pZu+dkW~*Ma%2T!gUeG0Fr9X(jr|% zQm%Y|@{UZ0{F(oi_ES6G!&f?E? zn`n_az%N>A%M6Fk{Fpy-exrgI1j1TplwH5PVjDl7GN*#B0I#=RWU)u2z+jj2@`?I-$~C4a0CwXSA^zI&R#M9fQp5**M`607$_+Cc&HLD#ODy^l z{Du}k8YHS;l66m3t0@S&@50iLMk#CnJ{;rCj_R^Z4yC|H&naan-XN7Vg~W)dS=3lL zuYVOENiLQ*dVP?EOebK0o5nl%NCFC+`n~CNDJsmf%d*#Yen_u+onqR4(|-g^U8Y@e zEw*z75ze8_XnruWjK%2&Zz=T79`+@VpbBmZ&dY^Vj%T=urTV-a4_E6hYN^OBHmb;L zt`34rTs!m4JdeElcs^E6{-tj(XeOXZe<@!A>$S26q`v<0a1NO2Q4kig6DqbH#wp~L zwmmk-m?eL#3k6rF9VSeLEMoIi6Gf${Xb4WIkym@ z>f8BS2}LdJ^O=<&m0&`j;A*(F;f*rq!&P3hbsx!|J;b%j*$=ET9pk_rDI&7&Exa;L z!>6=f_knIlOQPGQB8>0$B1Yc+vWC#pAO~Xz;u^=;e*woicWtL%SE>~+uP*$n|8I@( zPIj_27v3O|f($S}c8SqMt@>>sO12*0O&j6D(2lSr3R zhMLUt>FV4VZ`1P38GWv#ZTs$f9}jNz@5B(o4_V{+bjrB!V#p_A^{mmC4*#^XU2#N?R*d(Xp9`VzkW38JnsV) zc*rt6x-W__&7eYvZKqf7J1#%KG*YldiG#6s3GsCY^ny9C^@mRP8TJJ?=Vws}SXRUY z!04?qdo5%lkkWu~QtgxC}w?(Fo z7FHt?q#vKrH~dX({EyF^dY2cl#JNu=-AnFS{RhSAh~xJD*)hLKp{8*Cz`@jM!LUxg zXP42~sm*YtJXGo^o3W6Lo$XDTqEsU%F0wvcQ3WI7C5<4)3;XRXWP#y89eVh}@}06@ zc{*YWxGM&J@rW|_@u}bs85UY&S?d7_jZ1ntysIN3P-I>sGaMJ5*;&%6!^Pn40t$VW zLhj7OgeJ+DWGD*E*EC%|$xGxi?raIVJE>m$4y?2F(}`qdJ&FnLA~|@nE*uCAW}>O+5@S@)-6uQ88yWK3cRloq~SX zVHvI_8$UOlH<&)V#%Ch#LWR(dqJZCNPI%iJW2NZFqi^4wf!$N`lfnXs5$o*<@|jdaDd>~LimoVo+uu z$Hxb@MOjtJTmrAiA{D~5aAA@Z#>W~%l5_D%b)}-is#YI;*tH{<^s`zy(aLW}=6{k( z8uPK?(cwWK|LGSkk3)2-#bJbE2sj~3j5vKza871Y?Ru8Mk^G6~hrEbDD-~ zvBa~*zc(Q!-jHKC{8}E5Wo7POyRLn;9X)BM!eqWj_!9<}RuCcn@<9-)a_CF(@wqeKhQ*8Z z&Kg0P91?s`5ChSQy{u2%W*-CDXuv>CCpCG}jm=zJB!Pr4&8(y(QD*)5Xt-?J;7<5= z9Y|YG+w6x*qdHQdfqT{w^?p9IZD0ZNbVzcemG2k(ytOVbQ$cx{8th0&6lycmHF@!Sk@F~7>g zJHjM!-Z}iiCh8y)o1Phi5GSjbKjRv+T$yd^qk#>7%k9sF+ojvnf#+yk|FWMEXA~P) zQ6?+_MQwwtjPviKYigIFc)r+LIwk*UBUoIag7ahS1xUxdpG^8 z4~%b1g05go5=O#OMQJOxUYrmd1BHOuJ*?TnTVwq(!qav_1XvDb;eEY z_T>sR!$D9uoC{*Qzs&I&V=`=#?JS90G0p}TIl*i3YeVf)QSbUi_6D% z7A=K)6?@M)8Z>A#@H%(N&*^EzrgoE0geA&p6BMAVe*$@=?~QDVeiY&M*E1Mn?8DMb zih``aw?cbekrSVu>OUK$KIybPm%?mf`Z=qz#haBmw)p~R@*J~IXXZb~;nD2OimTbD z+>UC>VILdcuUu6OW~xkM&XV^&O?S2(>s*=oh#!#8K|GdWtF)q~~XN zEpVvuIEC}qhB?d4;ZH^5QtoUgk)%IccQAV#{88s52y6uXU1|4#yE%OU#k~OTnxu%R zJAc7RqUBwl@k6WE4?X9zUm77UnzaeB9Dn}3Pu-?tZcw=X& zRt-n#a}WgXU@|F7jCIsXVhl(B-_oN%sGVNUU!M=KUcLLK+=@a=ocipiQ!$x6mQwwN z8udRbL#jZjd4-n)Rh`*aK@+(8twF@;%gK4o=>Mz+1o32o@9sZ-=DL_lu?bueiA%+a zyc8U5eO#YrI)Z#|7VoDLHYSbp6Upo2YmeH8q4d6Ih?{=Ze%2nNOP%1qx* z*z)OEqQMcRCtBC!R!;gjgs0$tgbf1oLai5Bptl;rnvSwtv!olP^E9{+}{p7-%66Z?ZlK$Jj}HH2*FL>i!h8 zbl+L5i>Pi$51;!_(g{)!!Uv1mle-J^b|xxNBK_FLt12K+b5{NKi~1h}0=#k(XjAcS zX~!aUVjuA;)g2V-$Q#CMG^+S2UO@y2*dt1i*;*N2?fEa9w2DEh^i;5 znMeX7pEc=!CP2u7ESwdq9{Dq{sQ(lX?v)b#SW#88``e9DE>7@2z63BOWH;1$yLH&; zwSm0}Nub(~pU**(iVr0_cuZkjml9@Q{wF9b!kn%^?eMjZ+2Asd@}vzaP_Bnh)s2cC zrp|OF>LuZS&t!)q*!G$_#ySo;aunV?ow3wwZ|+zZk(NnCHv5o-{cF5ROmO|_hYcD7 z|Hjf-Tl*o>=b&zx&Zgf~!?X0U=XpZ^31dsy*ofkTkUhV||2J`s?1c=u*gkuFDb1c<-0lG}oy95aC*L zhmGJKJgmHxF|i-c?O%QVKZO9!>(>8!9oj80vR*53HIf{`IGd*1_op8}(gA+%737 zHP*d(eDRF$lbRZ(H6rq#{r6aWFoo(<)=8&KA`<~y4B9yf4U}=!WRk1wQ}RtzsoWKk ze{7GI4wB;Hsq3Kr{%r|y9Vo7ik{1PkpK7P(Lhu7V@gvk=?SC{I6bc(%>5RJg-ac0v z(ur6dBu7|tk)bP)jZGo#^0SXI|J(FDfQaa3Eb#1Tn|633`!OAULG5Q^q zIhVg220Hwaf&S~dgd)%1G?<|5e+ByLBdGAofXkx5=(X@lp=hZTJyR;E)zcaXeY9;} zZchGR*BCUx2oHHs!MqmM{PZN*7W&E(c(>0XQGc9ht7RE6x`EsVI2sZF*YKmWr*E;a zRNyk&QB@G#n9k_Y9-D=7aomGYSl|UW)nCcK`Up2~%XT%0z4){=ij3yA;ydjIVV&fgoBDkGLxxyp2tzG1)OM9ZV@_*X?>D}tQLm51R$_w0?wJM)L^-zRjF0WYU+exFy?M~rv$ zam(sIB?v@=aPF6DmdN`a-3IPy3xLpdCcMjOHk4X7WnaAg_k>!(hKMArx@7;N`C|U@ z`QF$7rU1x5;96E&RBHDnp63;&)4#gdQ$n2ZrpFZB$zJl!&SEpT4`g`pzb=Lrxn2$V7>|IR3pbnjnYe;m5+hV7q65Q$yeDx)h;E z@K&y{ZM$ZZ+WOY_|92%8K~C|UgX6pGkS=l0VkCGghydsQoB3gAb~1lUbA)O0zlH#0 zHJ|pKT}E2yzkVc{_}q%C1BuL!`1amaTi^evI;#8I8fzT=zekfLklgTl$!}i%jhH>h zJst3#gIrEO#ou%T>6Vo-GcB$TE99>+FcanM&vPVw_uxZ&=tcH`Ba^jJt6_;m&JDd+ zbuL;GJpaB+2u7rJpuB6gmT=i0w~&vnpWd>AOSXQlO%~#@DYa-wYrg*XfEtYkFf#>X&UYyO(oJolb+ zp7(vf&pG#b-DflGXYPs$3T%~x3IAqm4^HfI@+G}A8O*xtHuNuV(+-2tQTGis%lwN^ zQEJa6_(}N7!!Ohe^or!VQ#H=&IUX-gis!tg_xu>+Kh70VhkWdI)&?q1k*BlSYlM$P zAU)J&e^XYDUK#z!wuX2@_{14>G70sU^P@6gKgtIQCG)YGC&;a)6a9WtQKHb(9T9No zRqhQ>`R@H{HKLI!w=vIOE}V`+IgTGxj6W;>tnM=+e1seq#z2B=^H}t87oP3r;Z6Cx z@0Bz0YzCbKG+g-LvsJU`}jFyPlSWF70mXZ@_Ok(=t42E`oitT zWQrH~8N9)P6_Vn4c;bd`;C#?v@i6BseOb7$;I`|EMx`N`F1-wKsHaooKqQbr! z^|ojzX$b$p;PiFF)8Pf;d4bgoG>q$rl0JEU$Wq9o)z~`2!r}i#A|JK&{_5uIn93hA zrPDD%8qmN1-2=^8aKqRzSGSaumzgp~r7abcV0@5TbQ-ljAn;yu%}BqkMBqxe*txjx ze~mRkR8#9?Z~K%|tQxzAB0iee0&sf8wojbGfdf}fZGBEDz`552ZezZG)p7x*IIsit zIA_-B{Pt{|gKVUn9Vdij&q#0*Ekx_~i5?=#oMc!(S86PaqS=k+%$Ze=J$+iKd7Wir z>0BKy$`At9por!$G<_Q2l%i0^<=Hu2+jyl^j;pB*Kni7Rr zXt?(hBkh<*uVr7>rC4T(s%64KOn6|!^a+;{1s7*xR#9~Hn=-1(H)z;|jv!>e+5Siw zW0CLBV$!&?K!!`F))tP$WOQo1+U$Q>ZL?xx5MqR(jk_-V>fxPvxgg`=JG;?gB4_A{ zef5|mZ|7FqX{+<~j^z0vC*MI{DV0RJSf|dZQ_-%zXBpyGJb|(?n>pG**58!zRRwp zZB%it1TBPZhEU?Jk-$OZ873vRK_O-S-)8DMFTo7N;io!A<%|X7eT`PL+K<1<&3T`o zZq&;>dD>-s2{4WA=J%7!wnOpjrOI|f6R-H;^01nP%BS2?Oi6Kz#MSH%4D#ha66=^~ zF3Zq+)Px?R23c8|?{FxL?RuIE$DjtB(W`Q_F^%Y_r4iLl2W+}C=OYXE%oMw2(*}{iQzg>vucZSK z1Jp+)-S!VC_+F?hT8=CR?AfM|Qref@J}9ht{?naiqxfM6Ih{YHPdeg>if8(;2q>`E zAcr>wz)T(jL~$|`$4x68V)gH{&KV_(f#t!_iyJOP7uxC*NP@WNei=dqgAa&0NdD3H z6G%TWW|o_wwBmlTiL1$g8{FSZ1x5a^E{h!AHJt9s;C?6Bakyh4SK=1nqOl~{ent5g zDYm8gt+Qi8sOP0;fcc8Z*Hv|iuSdUi@HZ^yxe7KP)57-j*5G}C0|Z2M_QAoMI)4H! zWg{XGHekih=^}p#qjZ1jX@+y(7YwXp3=Y{gh9Db;6-cDwkT^HNTy=q3X;TUwSqAPB z`K&(cNx7U@^9dtw$@G>(!oeGwgD^>sY!xFemzqUOjI? zT!BW5bVK(26RD4du?2iE9%h6b7Jqr4#rn(IK-I_J zgapM9Je8(Jl4?XNWf7pKoB!d;-@j!pUpUK5CH!&=Tna_zoa#$@WXdbS&v_#O6Rh`z zK3_exRU1^S7pGk6+kSlHJ$O0R<=(iIJecI9s25wp-dSv^ffAh4%OQPamhoG5r%UfS=4O%v{SZ*I!z8cBeNmX*be4PTfi

G7Ec-GOLc}o z7%)$o3dZgEvHto{eA5FmN=oP^G3vO`a}+|1BgZ?iGqGptiu{`W+SO!Q<>F?y=E#WR zI0id}6|$DdWJXj6K010%|6+PqN#^bF0oJPA5B*5D5WFv{EyoaEdN8fOx}no&UUPT* zEB|D2t1p&#mPW}V!u>^B2oJPhTWE&Z;T#o^T*i=wl1>`bSgQhC$B_3(d?Jg`gLjK$3w1 zmWO@FiI-y$H<~hkI(a0e?^;X%yqzWUVI90R;d~yvcl^U6wmGK*x7xO;Vo-CLx@P_+I@IBB@_AL zy>4B)7oysSq6*JBh?La5LAq&yRBlbWKhQwOXby5OSga58P~{Q$VUsbTF7YmX<);pZ z-hJd+mG8_~dXmCAP*ULZ(@`ySUz2yyY@p^X_j=@_Wc$pYd{A>usLyH5I3sP z`EZ7b{E695<8|I%+NSSt*1TGVS+hJ6?>};oJ^1dFP}oBMXfNgBsP0h0h{q)rzgwUFZ_=*bShg+KtMQ?P$)lHygXktG)d1b!OQ+9z6V}kI@N^XmwJ^h4@07 z(C6IserK*pyA8V*!WzX+=S1J7bL_cKx1}xV650GweQQ2I!Wu}m9|%p3xIqUZm>NCxctGcXyZu7THb&sCNGaGjv{rbf<)+Oxar}u%^ zN&^55;_(-0IJnNBgC#1$d8s=EJ*nT(YGqB89E74^`8dgz>PO_kiSA?phg{hwgd)5K zE~`R%GH=UTNBrioIP}eE^df6?6|1MLVw*pst83AoSASeXXq88UWWLcKf3`-p*MrO& zk(o~`g6{4?DMfD$qaWP+zwRig3;y44iN^E+@&i-V9^Sl(4+cIu8U*z+Rbt5h0I_01 AGXMYp literal 0 HcmV?d00001 diff --git a/android/res/layout/activity_splash.xml b/android/res/layout/activity_splash.xml new file mode 100644 index 0000000000..3e66bd81f2 --- /dev/null +++ b/android/res/layout/activity_splash.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java index 8a3c915536..f23745a5c9 100644 --- a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java +++ b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java @@ -214,9 +214,9 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity @CallSuper @Override - protected void safeOnCreate(@Nullable Bundle savedInstanceState) + protected void onCreate(@Nullable Bundle savedInstanceState) { - super.safeOnCreate(savedInstanceState); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_download_resources); initViewsAndListeners(); @@ -251,8 +251,9 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity @CallSuper @Override - protected void safeOnResume() + protected void onResume() { + super.onResume(); if (!isFinishing()) LocationHelper.INSTANCE.addListener(mLocationListener, true); } @@ -488,8 +489,12 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity if (intent == null) return false; + final Intent extra = intent.getParcelableExtra(SplashActivity.EXTRA_INTENT); + if (extra == null) + return false; + for (final IntentProcessor ip : mIntentProcessors) - if (ip.isSupported(intent) && ip.process(intent)) + if (ip.isSupported(extra) && ip.process(extra)) return true; return false; diff --git a/android/src/com/mapswithme/maps/MapFragment.java b/android/src/com/mapswithme/maps/MapFragment.java index 9595021a98..8811b5c963 100644 --- a/android/src/com/mapswithme/maps/MapFragment.java +++ b/android/src/com/mapswithme/maps/MapFragment.java @@ -166,7 +166,7 @@ public class MapFragment extends BaseMwmFragment getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); final float exactDensityDpi = metrics.densityDpi; - boolean firstStart = ((MwmActivity) getMwmActivity()).isFirstStart(); + boolean firstStart = SplashActivity.isFirstStart(); if (firstStart) PushwooshHelper.nativeProcessFirstLaunch(); diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 729cbb38e8..80f56408a9 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -53,11 +53,8 @@ import com.mapswithme.maps.editor.EditorActivity; import com.mapswithme.maps.editor.EditorHostFragment; import com.mapswithme.maps.editor.FeatureCategoryActivity; import com.mapswithme.maps.editor.ReportFragment; -import com.mapswithme.maps.editor.ViralFragment; import com.mapswithme.maps.location.CompassData; import com.mapswithme.maps.location.LocationHelper; -import com.mapswithme.maps.news.FirstStartFragment; -import com.mapswithme.maps.news.NewsFragment; import com.mapswithme.maps.routing.NavigationController; import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.maps.routing.RoutingPlanController; @@ -155,6 +152,7 @@ public class MwmActivity extends BaseMwmFragmentActivity private FadeView mFadeView; + @Nullable private MyPositionButton mNavMyPosition; private TrafficButton mTraffic; @Nullable @@ -176,8 +174,6 @@ public class MwmActivity extends BaseMwmFragmentActivity private FloatingSearchToolbarController mSearchController; - // The first launch of application ever - onboarding screen will be shown. - private boolean mFirstStart; private boolean mPlacePageRestored; private boolean mLocationErrorDialogAnnoying = false; @@ -249,7 +245,7 @@ public class MwmActivity extends BaseMwmFragmentActivity int oldLeft, int oldTop, int oldRight, int oldBottom) { mScreenFullRect = new Rect(left, top, right, bottom); - if (mPlacePageVisible && (mPlacePage == null || mPlacePage.GetPreview().getVisibility() != View.VISIBLE)) + if (mPlacePageVisible && (mPlacePage == null || UiUtils.isHidden(mPlacePage.GetPreview()))) mPlacePageVisible = false; recalculateVisibleRect(mScreenFullRect); } @@ -454,7 +450,6 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override protected void safeOnCreate(@Nullable Bundle savedInstanceState) { - super.safeOnCreate(savedInstanceState); if (savedInstanceState != null) mLocationErrorDialogAnnoying = savedInstanceState.getBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING); mIsFragmentContainer = getResources().getBoolean(R.bool.tabletLayout); @@ -885,6 +880,12 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public void onDestroy() { + if (!isInitializationComplete()) + { + super.onDestroy(); + return; + } + // TODO move listeners attach-deattach to onStart-onStop since onDestroy isn't guaranteed. Framework.nativeRemoveMapObjectListener(); BottomSheetHelper.free(); @@ -917,7 +918,8 @@ public class MwmActivity extends BaseMwmFragmentActivity RoutingController.get().onSaveState(); outState.putBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING, mLocationErrorDialogAnnoying); - mNavMyPosition.onSaveState(outState); + if (mNavMyPosition != null) + mNavMyPosition.onSaveState(outState); if(mNavAnimationController != null) mNavAnimationController.onSaveState(outState); @@ -928,9 +930,9 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - protected void safeOnRestoreInstanceState(@NonNull Bundle savedInstanceState) + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - super.safeOnRestoreInstanceState(savedInstanceState); + super.onRestoreInstanceState(savedInstanceState); final State state = State.values()[savedInstanceState.getInt(STATE_PP, 0)]; if (mPlacePage != null && state != State.HIDDEN) @@ -953,7 +955,8 @@ public class MwmActivity extends BaseMwmFragmentActivity if (mNavigationController != null) mNavigationController.onRestoreState(savedInstanceState); - mNavMyPosition.onRestoreState(savedInstanceState); + if (mNavMyPosition != null) + mNavMyPosition.onRestoreState(savedInstanceState); if(mNavAnimationController != null) mNavAnimationController.onRestoreState(savedInstanceState); @@ -1018,9 +1021,11 @@ public class MwmActivity extends BaseMwmFragmentActivity runTasks(); } + @CallSuper @Override - protected void safeOnResume() + protected void onResume() { + super.onResume(); mPlacePageRestored = mPlacePage != null && mPlacePage.getState() != State.HIDDEN; mSearchController.refreshToolbar(); mMainMenu.onResume(new Runnable() @@ -1054,23 +1059,9 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - protected void safeOnResumeFragments() + protected void onResumeFragments() { - if (!RoutingController.get().isNavigating()) - { - mFirstStart = FirstStartFragment.showOn(this); - if (mFirstStart) - return; - - if (!NewsFragment.showOn(this)) - { - if (ViralFragment.shouldDisplay()) - new ViralFragment().show(getSupportFragmentManager(), ""); - else - LikesManager.INSTANCE.showDialogs(this); - } - } - + super.onResumeFragments(); RoutingController.get().restore(); if (mPlacePage != null) mPlacePage.restore(); @@ -1095,11 +1086,6 @@ public class MwmActivity extends BaseMwmFragmentActivity { super.onStart(); RoutingController.get().attach(this); - } - - @Override - protected void safeOnStart() - { if (MapFragment.nativeIsEngineCreated()) LocationHelper.INSTANCE.attach(this); if (mTrafficButtonController != null) @@ -1299,12 +1285,13 @@ public class MwmActivity extends BaseMwmFragmentActivity }); if (mNavAnimationController != null) mNavAnimationController.disappearZoomButtons(); - mNavMyPosition.hide(); + if (mNavMyPosition != null) + mNavMyPosition.hide(); mTraffic.hide(); } else { - if (mPlacePage.isHidden() && mNavAnimationController != null) + if (mPlacePage != null && mPlacePage.isHidden() && mNavAnimationController != null) mNavAnimationController.appearZoomButtons(); if (!mIsFullscreenAnimating) appearMenu(menu); @@ -1323,7 +1310,8 @@ public class MwmActivity extends BaseMwmFragmentActivity adjustRuler(0, 0); } }); - mNavMyPosition.show(); + if (mNavMyPosition != null) + mNavMyPosition.show(); mTraffic.show(); } @@ -1811,17 +1799,11 @@ public class MwmActivity extends BaseMwmFragmentActivity mSearchController.refreshToolbar(); } - boolean isFirstStart() - { - boolean res = mFirstStart; - mFirstStart = false; - return res; - } - @Override public void onMyPositionModeChanged(int newMode) { - mNavMyPosition.update(newMode); + if (mNavMyPosition != null) + mNavMyPosition.update(newMode); } @Override diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index af8293af93..5d64e6f3f3 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -10,7 +10,6 @@ import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.support.multidex.MultiDex; -import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; @@ -33,6 +32,7 @@ import com.mapswithme.maps.sound.TtsPlayer; import com.mapswithme.maps.traffic.TrafficManager; import com.mapswithme.util.Config; import com.mapswithme.util.Constants; +import com.mapswithme.util.Counters; import com.mapswithme.util.ThemeSwitcher; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; @@ -56,9 +56,7 @@ public class MwmApplication extends Application private SharedPreferences mPrefs; private AppBackgroundTracker mBackgroundTracker; - private boolean mAreCountersInitialized; private boolean mIsFrameworkInitialized; - private boolean mIsPlatformInitialized; private Handler mMainLoopHandler; private final Object mMainQueueToken = new Object(); @@ -157,9 +155,6 @@ public class MwmApplication extends Application public void initNativePlatform() { - if (mIsPlatformInitialized) - return; - final boolean isInstallationIdFound = setInstallationIdToCrashlytics(); initTracker(); @@ -185,8 +180,6 @@ public class MwmApplication extends Application mBackgroundTracker.addListener(mBackgroundListener); TrackRecorder.init(); Editor.init(); - - mIsPlatformInitialized = true; } public void initNativeCore() @@ -264,11 +257,6 @@ public class MwmApplication extends Application return mIsFrameworkInitialized; } - public boolean isPlatformInitialized() - { - return mIsPlatformInitialized; - } - public String getApkPath() { try @@ -370,19 +358,9 @@ public class MwmApplication extends Application MyTracker.initTracker(); } - public void initCounters() - { - if (!mAreCountersInitialized) - { - mAreCountersInitialized = true; - Config.updateLaunchCounter(); - PreferenceManager.setDefaultValues(this, R.xml.prefs_main, false); - } - } - public static void onUpgrade() { - Config.resetAppSessionCounters(); + Counters.resetAppSessionCounters(); } @SuppressWarnings("unused") diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java new file mode 100644 index 0000000000..2dc9545dc1 --- /dev/null +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -0,0 +1,198 @@ +package com.mapswithme.maps; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; + +import com.mapswithme.maps.ads.LikesManager; +import com.mapswithme.maps.editor.ViralFragment; +import com.mapswithme.maps.location.LocationHelper; +import com.mapswithme.maps.news.BaseNewsFragment; +import com.mapswithme.maps.news.FirstStartFragment; +import com.mapswithme.maps.news.NewsFragment; +import com.mapswithme.util.Counters; +import com.mapswithme.util.UiUtils; +import com.mapswithme.util.Utils; +import com.mapswithme.util.concurrency.UiThread; + +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.GET_ACCOUNTS; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +public class SplashActivity extends AppCompatActivity + implements BaseNewsFragment.NewsDialogListener +{ + public static final String[] PERMISSIONS = new String[] + { + WRITE_EXTERNAL_STORAGE, + ACCESS_COARSE_LOCATION, + ACCESS_FINE_LOCATION, + GET_ACCOUNTS + }; + public static final String EXTRA_INTENT = "extra_intent"; + private static final String EXTRA_ACTIVITY_TO_START = "extra_activity_to_start"; + private static final int REQUEST_PERMISSIONS = 1; + private static final long DELAY = 100; + + // The first launch of application ever - onboarding screen will be shown. + private static boolean sFirstStart; + + private boolean mPermissionsGranted; + + public static void start(@NonNull Context context, + @Nullable Class activityToStart) + { + Intent intent = new Intent(context, SplashActivity.class); + intent.putExtra(EXTRA_ACTIVITY_TO_START, activityToStart); + context.startActivity(intent); + } + + public static boolean isFirstStart() + { + boolean res = sFirstStart; + sFirstStart = false; + return res; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + Counters.initCounters(this); + initView(); + } + + @Override + protected void onResume() + { + super.onResume(); + mPermissionsGranted = Utils.checkPermissions(this, PERMISSIONS); + if (!mPermissionsGranted) + { +// TODO requestPermissions after Permissions dialog + ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSIONS); + return; + } + + UiThread.runLater(new Runnable() + { + @Override + public void run() + { + init(); + resumeDialogs(); + } + }, DELAY); + } + + private void resumeDialogs() + { + // TODO show permissions dialog if Permissions is not granted + if (!mPermissionsGranted) + return; + + sFirstStart = FirstStartFragment.showOn(this, this); + if (sFirstStart) + return; + + if (!NewsFragment.showOn(this, this)) + { + if (ViralFragment.shouldDisplay()) + { + ViralFragment dialog = new ViralFragment(); + dialog.onDismiss(new DialogInterface() + { + @Override + public void cancel() + { + processNavigation(); + } + + @Override + public void dismiss() + { + processNavigation(); + } + }); + dialog.show(getSupportFragmentManager(), ""); + } + else + { + LikesManager.INSTANCE.showDialogs(this); + processNavigation(); + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length == 0) + return; + + boolean isWriteGranted = false; + for (int i = 0; i < permissions.length; ++i) + { + int result = grantResults[i]; + String permission = permissions[i]; + if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) + isWriteGranted = true; + } + + if (isWriteGranted) + { + mPermissionsGranted = true; + init(); + resumeDialogs(); + } + else + { + finish(); + } + } + + @Override + public void onDialogDone() + { + processNavigation(); + } + + private void initView() + { + UiUtils.setupStatusBar(this); + setContentView(R.layout.activity_splash); + } + + private void init() + { + MwmApplication.get().initNativePlatform(); + MwmApplication.get().initNativeCore(); + LocationHelper.INSTANCE.init(); + } + + @SuppressWarnings("unchecked") + private void processNavigation() + { + Intent input = getIntent(); + Intent intent = new Intent(this, DownloadResourcesActivity.class); + if (input != null) + { + Class type = (Class) input.getSerializableExtra(EXTRA_ACTIVITY_TO_START); + if (type != null) + intent = new Intent(this, type); + intent.putExtra(EXTRA_INTENT, input); + } + startActivity(intent); + finish(); + } +} diff --git a/android/src/com/mapswithme/maps/ads/GooglePlusDialogFragment.java b/android/src/com/mapswithme/maps/ads/GooglePlusDialogFragment.java index f4b2207412..0cb87fce36 100644 --- a/android/src/com/mapswithme/maps/ads/GooglePlusDialogFragment.java +++ b/android/src/com/mapswithme/maps/ads/GooglePlusDialogFragment.java @@ -11,8 +11,8 @@ import android.view.LayoutInflater; import com.google.android.gms.plus.PlusOneButton; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmDialogFragment; -import com.mapswithme.util.Config; import com.mapswithme.util.Constants; +import com.mapswithme.util.Counters; import com.mapswithme.util.statistics.Statistics; public class GooglePlusDialogFragment extends BaseMwmDialogFragment @@ -31,7 +31,7 @@ public class GooglePlusDialogFragment extends BaseMwmDialogFragment @Override public void onPlusOneClick(Intent intent) { - Config.setRatingApplied(GooglePlusDialogFragment.class); + Counters.setRatingApplied(GooglePlusDialogFragment.class); dismiss(); startActivityForResult(intent, 0); } diff --git a/android/src/com/mapswithme/maps/ads/LikesManager.java b/android/src/com/mapswithme/maps/ads/LikesManager.java index ae4e068456..07f9a2d521 100644 --- a/android/src/com/mapswithme/maps/ads/LikesManager.java +++ b/android/src/com/mapswithme/maps/ads/LikesManager.java @@ -8,8 +8,8 @@ import java.lang.ref.WeakReference; import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.routing.RoutingController; -import com.mapswithme.util.Config; import com.mapswithme.util.ConnectionState; +import com.mapswithme.util.Counters; import com.mapswithme.util.concurrency.UiThread; public enum LikesManager @@ -19,7 +19,7 @@ public enum LikesManager private static final int DIALOG_DELAY_DEFAULT = 30000; private static final int DIALOG_DELAY_SHORT = 5000; - private static final int SESSION_NUM = Config.getSessionCount(); + private static final int SESSION_NUM = Counters.getSessionCount(); /* Maps type of like dialog to the dialog, performing like. @@ -72,7 +72,7 @@ public enum LikesManager sNewUsersMapping.put(55, LikeType.FACEBOOK_INVITE_NEW_USERS); } - private final boolean mIsNewUser = (Config.getFirstInstallVersion() == BuildConfig.VERSION_CODE); + private final boolean mIsNewUser = (Counters.getFirstInstallVersion() == BuildConfig.VERSION_CODE); private Runnable mLikeRunnable; private WeakReference mActivityRef; @@ -95,10 +95,10 @@ public enum LikesManager private void displayLikeDialog(final Class dialogFragmentClass, final int delayMillis) { - if (Config.isSessionRated(SESSION_NUM) || Config.isRatingApplied(dialogFragmentClass)) + if (Counters.isSessionRated(SESSION_NUM) || Counters.isRatingApplied(dialogFragmentClass)) return; - Config.setRatedSession(SESSION_NUM); + Counters.setRatedSession(SESSION_NUM); UiThread.cancelDelayedTasks(mLikeRunnable); mLikeRunnable = new Runnable() diff --git a/android/src/com/mapswithme/maps/ads/RateStoreDialogFragment.java b/android/src/com/mapswithme/maps/ads/RateStoreDialogFragment.java index 5a710216e7..d08d98b220 100644 --- a/android/src/com/mapswithme/maps/ads/RateStoreDialogFragment.java +++ b/android/src/com/mapswithme/maps/ads/RateStoreDialogFragment.java @@ -22,8 +22,8 @@ import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmDialogFragment; -import com.mapswithme.util.Config; import com.mapswithme.util.Constants; +import com.mapswithme.util.Counters; import com.mapswithme.util.Graphics; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; @@ -64,7 +64,7 @@ public class RateStoreDialogFragment extends BaseMwmDialogFragment implements Vi mRating = rating; if (rating >= BuildConfig.RATING_THRESHOLD) { - Config.setRatingApplied(RateStoreDialogFragment.class); + Counters.setRatingApplied(RateStoreDialogFragment.class); dismiss(); Utils.openAppInMarket(getActivity(), BuildConfig.REVIEW_URL); } @@ -109,7 +109,7 @@ public class RateStoreDialogFragment extends BaseMwmDialogFragment implements Vi switch (v.getId()) { case R.id.btn__explain_bad_rating: - Config.setRatingApplied(GooglePlusDialogFragment.class); + Counters.setRatingApplied(GooglePlusDialogFragment.class); dismiss(); final Intent intent = new Intent(Intent.ACTION_SENDTO); final PackageInfo info; diff --git a/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java b/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java index 740eef4908..13017f93c7 100644 --- a/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java +++ b/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java @@ -18,7 +18,8 @@ public class ConnectivityChangedReceiver extends BroadcastReceiver @Override public void onReceive(Context context, Intent intent) { - if (!ConnectionState.isWifiConnected() || MapManager.nativeNeedMigrate()) + if (!MwmApplication.get().isFrameworkInitialized() || !ConnectionState.isWifiConnected() + || MapManager.nativeNeedMigrate()) return; final long lastEventTimestamp = prefs().getLong(DOWNLOAD_UPDATE_TIMESTAMP, 0); diff --git a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java index 2d69367e0d..3431c4e3f8 100644 --- a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java +++ b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java @@ -1,23 +1,15 @@ package com.mapswithme.maps.base; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.mapswithme.maps.MwmApplication; -import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.Config; import com.mapswithme.util.UiUtils; -import com.mapswithme.util.Utils; import com.mapswithme.util.ViewServer; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.statistics.Statistics; import com.my.tracker.MyTracker; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; - public class BaseActivityDelegate { @NonNull @@ -33,7 +25,8 @@ public class BaseActivityDelegate public void onCreate() { mThemeName = Config.getCurrentUiTheme(); - mActivity.get().setTheme(mActivity.getThemeResourceId(mThemeName)); + if (mThemeName != null) + mActivity.get().setTheme(mActivity.getThemeResourceId(mThemeName)); } public void onDestroy() @@ -48,24 +41,18 @@ public class BaseActivityDelegate public void onStart() { - if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) - return; Statistics.INSTANCE.startActivity(mActivity.get()); MyTracker.onStartActivity(mActivity.get()); } public void onStop() { - if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) - return; Statistics.INSTANCE.stopActivity(mActivity.get()); MyTracker.onStopActivity(mActivity.get()); } public void onResume() { - if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) - return; org.alohalytics.Statistics.logEvent("$onResume", mActivity.getClass().getSimpleName() + ":" + UiUtils.deviceOrientationAsString(mActivity.get())); ViewServer.get(mActivity.get()).setFocusedWindow(mActivity.get()); @@ -73,14 +60,12 @@ public class BaseActivityDelegate public void onPause() { - if (!MwmApplication.get().isPlatformInitialized() || !Utils.isWriteExternalGranted(mActivity.get())) - return; org.alohalytics.Statistics.logEvent("$onPause", mActivity.getClass().getSimpleName()); } public void onPostResume() { - if (mThemeName == null || mThemeName.equals(Config.getCurrentUiTheme()) || !Utils.isWriteExternalGranted(mActivity.get())) + if (mThemeName != null && mThemeName.equals(Config.getCurrentUiTheme())) return; // Workaround described in https://code.google.com/p/android/issues/detail?id=93731 diff --git a/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java index be559b7756..d8833f9694 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmDialogFragment.java @@ -1,28 +1,15 @@ package com.mapswithme.maps.base; import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; import android.support.v4.app.DialogFragment; -import android.view.View; -import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.util.ThemeUtils; -import com.mapswithme.util.Utils; - -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class BaseMwmDialogFragment extends DialogFragment { - @Nullable - private View mView; - @Nullable - private Bundle mSavedInstanceState; - protected final @StyleRes int getFullscreenTheme() { return (ThemeUtils.isNightTheme() ? R.style.MwmTheme_DialogFragment_Fullscreen_Night @@ -43,20 +30,12 @@ public class BaseMwmDialogFragment extends DialogFragment public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSavedInstanceState = savedInstanceState; int style = getStyle(); int theme = getCustomTheme(); if (style != STYLE_NORMAL || theme != 0) //noinspection WrongConstant setStyle(style, theme); - - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnCreate(savedInstanceState); - } - - protected void safeOnCreate(@Nullable Bundle savedInstanceState) - { } @Override @@ -65,13 +44,6 @@ public class BaseMwmDialogFragment extends DialogFragment super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", getClass().getSimpleName() + ":" + com.mapswithme.util.UiUtils.deviceOrientationAsString(getActivity())); - - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnResume(); - } - - protected void safeOnResume() - { } @Override @@ -80,58 +52,4 @@ public class BaseMwmDialogFragment extends DialogFragment super.onPause(); org.alohalytics.Statistics.logEvent("$onPause", getClass().getSimpleName()); } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) - { - super.onViewCreated(view, savedInstanceState); - mView = view; - mSavedInstanceState = savedInstanceState; - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnViewCreated(view, savedInstanceState); - } - - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) - { - } - - @CallSuper - @Override - public void onDestroyView() - { - mView = null; - mSavedInstanceState = null; - super.onDestroyView(); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (grantResults.length == 0) - return; - - boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; - } - - if (isWriteGranted) - safeOnCreate(mSavedInstanceState); - - if (isWriteGranted && mView != null) - { - safeOnViewCreated(mView, mSavedInstanceState); - safeOnResume(); - } - } - - public BaseMwmFragmentActivity getMwmActivity() - { - return (BaseMwmFragmentActivity) getActivity(); - } } diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java index 5a01b91df7..c86c4bc85f 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java @@ -1,24 +1,26 @@ package com.mapswithme.maps.base; -import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.content.Context; import android.support.v4.app.Fragment; -import android.view.View; +import android.support.v7.app.AppCompatActivity; import com.mapswithme.maps.MwmApplication; -import com.mapswithme.util.Utils; - -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class BaseMwmFragment extends Fragment { - @Nullable - private View mView; - @Nullable - private Bundle mSavedInstanceState; + + @Override + public void onAttach(Context context) + { + super.onAttach(context); + if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + { + ((AppCompatActivity)context).getSupportFragmentManager() + .beginTransaction() + .detach(this) + .commit(); + } + } @Override public void onResume() @@ -26,13 +28,6 @@ public class BaseMwmFragment extends Fragment super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", this.getClass().getSimpleName() + ":" + com.mapswithme.util.UiUtils.deviceOrientationAsString(getActivity())); - - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnResume(); - } - - protected void safeOnResume() - { } @Override @@ -42,53 +37,6 @@ public class BaseMwmFragment extends Fragment org.alohalytics.Statistics.logEvent("$onPause", this.getClass().getSimpleName()); } - @CallSuper - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) - { - super.onViewCreated(view, savedInstanceState); - mView = view; - mSavedInstanceState = savedInstanceState; - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnViewCreated(view, savedInstanceState); - } - - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) - { - } - - @CallSuper - @Override - public void onDestroyView() - { - mView = null; - mSavedInstanceState = null; - super.onDestroyView(); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (grantResults.length == 0) - return; - - boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; - } - - if (isWriteGranted && mView != null) - { - safeOnViewCreated(mView, mSavedInstanceState); - safeOnResume(); - } - } - public BaseMwmFragmentActivity getMwmActivity() { return (BaseMwmFragmentActivity) getActivity(); diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java index e73b965541..506cdffbf4 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java @@ -3,8 +3,8 @@ package com.mapswithme.maps.base; import android.app.Activity; import android.media.AudioManager; import android.os.Bundle; -import android.support.annotation.ColorRes; import android.support.annotation.CallSuper; +import android.support.annotation.ColorRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; @@ -13,41 +13,23 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.MenuItem; +import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; +import com.mapswithme.maps.SplashActivity; import com.mapswithme.util.Config; -import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static android.Manifest.permission.GET_ACCOUNTS; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - public class BaseMwmFragmentActivity extends AppCompatActivity implements BaseActivity { - private static final int REQUEST_PERMISSIONS_START = 1; - private static final int REQUEST_PERMISSIONS_RESUME = 2; - - private static final String[] START_PERMISSIONS = new String[] - { - WRITE_EXTERNAL_STORAGE, - ACCESS_COARSE_LOCATION, - ACCESS_FINE_LOCATION, - GET_ACCOUNTS - }; - private static final String[] RESUME_PERMISSIONS = new String[] {WRITE_EXTERNAL_STORAGE}; - private final BaseActivityDelegate mBaseDelegate = new BaseActivityDelegate(this); @Nullable - private Bundle mSavedInstanceState; - private boolean mRequestedFromOnCreate = false; - private boolean mFromInstanceState = false; + private Bundle mSavedState; + private boolean mInitializationComplete = false; @Override public Activity get() @@ -68,29 +50,23 @@ public class BaseMwmFragmentActivity extends AppCompatActivity throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme); } + @CallSuper @Override protected void onCreate(@Nullable Bundle savedInstanceState) { - mFromInstanceState = savedInstanceState != null; - if (!Utils.checkPermissions(this, MwmApplication.get().isPlatformInitialized() - ? RESUME_PERMISSIONS : START_PERMISSIONS, - REQUEST_PERMISSIONS_START)) + mSavedState = savedInstanceState; + if (!MwmApplication.get().isFrameworkInitialized() + || !Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) { - mRequestedFromOnCreate = true; super.onCreate(savedInstanceState); + goToSplashScreen(); return; } + mInitializationComplete = true; - MwmApplication.get().initNativePlatform(); mBaseDelegate.onCreate(); - super.onCreate(savedInstanceState); - safeOnCreate(savedInstanceState); - } - @CallSuper - protected void safeOnCreate(@Nullable Bundle savedInstanceState) - { setVolumeControlStream(AudioManager.STREAM_MUSIC); final int layoutId = getContentLayoutResId(); if (layoutId != 0) @@ -108,12 +84,18 @@ public class BaseMwmFragmentActivity extends AppCompatActivity getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } - MwmApplication.get().initNativeCore(); - MwmApplication.get().initCounters(); - - LocationHelper.INSTANCE.init(); - attachDefaultFragment(); + + safeOnCreate(savedInstanceState); + } + + protected void safeOnCreate(@Nullable Bundle savedInstanceState) + { + } + + protected boolean isInitializationComplete() + { + return mInitializationComplete; } @ColorRes @@ -158,12 +140,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity { super.onStart(); mBaseDelegate.onStart(); - if (MwmApplication.get().isPlatformInitialized()) - safeOnStart(); - } - - protected void safeOnStart() - { } @Override @@ -173,25 +149,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity mBaseDelegate.onStop(); } - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) - { - super.onRestoreInstanceState(savedInstanceState); - mSavedInstanceState = savedInstanceState; - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) - safeOnRestoreInstanceState(savedInstanceState); - } - - protected void safeOnRestoreInstanceState(@NonNull Bundle savedInstanceState) - { - } - - @Nullable - public Bundle getSavedInstanceState() - { - return mSavedInstanceState; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -203,23 +160,18 @@ public class BaseMwmFragmentActivity extends AppCompatActivity return super.onOptionsItemSelected(item); } + @CallSuper @Override protected void onResume() { super.onResume(); - if (!mRequestedFromOnCreate && !Utils.checkPermissions(this, RESUME_PERMISSIONS, - REQUEST_PERMISSIONS_RESUME)) + if (!Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) { + goToSplashScreen(); return; } mBaseDelegate.onResume(); - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) - safeOnResume(); - } - - protected void safeOnResume() - { } @Override @@ -229,19 +181,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity mBaseDelegate.onPostResume(); } - @CallSuper - @Override - protected void onResumeFragments() - { - super.onResumeFragments(); - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(this)) - safeOnResumeFragments(); - } - - protected void safeOnResumeFragments() - { - } - @Override protected void onPause() { @@ -249,48 +188,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity mBaseDelegate.onPause(); } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (grantResults.length == 0) - return; - - boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; - } - if (isWriteGranted) - { - if (requestCode == REQUEST_PERMISSIONS_START) - { - MwmApplication.get().initNativePlatform(); - mBaseDelegate.onCreate(); - safeOnCreate(mSavedInstanceState); - mBaseDelegate.onStart(); - if (mSavedInstanceState != null) - safeOnRestoreInstanceState(mSavedInstanceState); - mBaseDelegate.onResume(); - safeOnResume(); - if (!mFromInstanceState) - safeOnResumeFragments(); - } - else if (requestCode == REQUEST_PERMISSIONS_RESUME) - { - safeOnResume(); - safeOnResumeFragments(); - } - } - else - { - finish(); - } - } - protected Toolbar getToolbar() { return (Toolbar) findViewById(R.id.toolbar); @@ -355,4 +252,19 @@ public class BaseMwmFragmentActivity extends AppCompatActivity { return android.R.id.content; } + + private void goToSplashScreen() + { + Class type = null; + if (!(this instanceof MwmActivity)) + type = getClass(); + SplashActivity.start(this, type); + finish(); + } + + @Nullable + public Bundle getSavedInstanceState() + { + return mSavedState; + } } diff --git a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java index 172571a922..86ed6705bf 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java @@ -1,10 +1,10 @@ package com.mapswithme.maps.base; +import android.content.Context; import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; @@ -13,28 +13,22 @@ import com.mapswithme.maps.R; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - @Deprecated public abstract class BaseMwmListFragment extends ListFragment { private Toolbar mToolbar; - @Nullable - private View mView; - @Nullable - private Bundle mSavedInstanceState; @Override - public void onCreate(@Nullable Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnCreate(savedInstanceState); - } - - protected void safeOnCreate(@Nullable Bundle savedInstanceState) + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + { + ((AppCompatActivity)context).getSupportFragmentManager() + .beginTransaction() + .detach(this) + .commit(); + } } @Override @@ -42,15 +36,6 @@ public abstract class BaseMwmListFragment extends ListFragment { super.onViewCreated(view, savedInstanceState); - mView = view; - mSavedInstanceState = savedInstanceState; - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnViewCreated(view, savedInstanceState); - } - - @CallSuper - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) - { mToolbar = (Toolbar) view.findViewById(R.id.toolbar); if (mToolbar != null) { @@ -66,33 +51,6 @@ public abstract class BaseMwmListFragment extends ListFragment } } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (grantResults.length == 0) - return; - - boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; - } - - if (isWriteGranted) - { - safeOnCreate(mSavedInstanceState); - if (mView != null) - { - safeOnViewCreated(mView, mSavedInstanceState); - safeOnResume(); - } - } - } - public Toolbar getToolbar() { return mToolbar; @@ -104,13 +62,6 @@ public abstract class BaseMwmListFragment extends ListFragment super.onResume(); org.alohalytics.Statistics.logEvent("$onResume", getClass().getSimpleName() + ":" + UiUtils.deviceOrientationAsString(getActivity())); - - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnResume(); - } - - protected void safeOnResume() - { } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index 0438a2bb0a..5615944bed 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -1,11 +1,13 @@ package com.mapswithme.maps.base; +import android.content.Context; import android.os.Bundle; import android.support.annotation.CallSuper; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; @@ -19,9 +21,6 @@ import com.mapswithme.maps.widget.PlaceholderView; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - public abstract class BaseMwmRecyclerFragment extends Fragment { private Toolbar mToolbar; @@ -29,11 +28,6 @@ public abstract class BaseMwmRecyclerFragment extends Fragment private RecyclerView mRecycler; private PlaceholderView mPlaceholder; - @Nullable - private View mView; - @Nullable - private Bundle mSavedInstanceState; - protected abstract RecyclerView.Adapter createAdapter(); protected @LayoutRes int getLayoutRes() @@ -47,26 +41,31 @@ public abstract class BaseMwmRecyclerFragment extends Fragment return mRecycler != null ? mRecycler.getAdapter() : null; } + @Override + public void onAttach(Context context) + { + super.onAttach(context); + if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + { + ((AppCompatActivity)context).getSupportFragmentManager() + .beginTransaction() + .detach(this) + .commit(); + } + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(getLayoutRes(), container, false); } + @CallSuper @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mView = view; - mSavedInstanceState = savedInstanceState; - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnViewCreated(view, savedInstanceState); - } - - @CallSuper - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) - { mToolbar = (Toolbar) view.findViewById(R.id.toolbar); if (mToolbar != null) { @@ -94,50 +93,6 @@ public abstract class BaseMwmRecyclerFragment extends Fragment setupPlaceholder(mPlaceholder); } - @CallSuper - @Override - public void onDestroyView() - { - mView = null; - mSavedInstanceState = null; - super.onDestroyView(); - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) - { - super.onActivityCreated(savedInstanceState); - if (MwmApplication.get().isPlatformInitialized() && Utils.isWriteExternalGranted(getActivity())) - safeOnActivityCreated(savedInstanceState); - } - - protected void safeOnActivityCreated(@Nullable Bundle savedInstanceState) - { - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (grantResults.length == 0) - return; - - boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; - } - - if (isWriteGranted && mView != null) - { - safeOnViewCreated(mView, mSavedInstanceState); - safeOnActivityCreated(mSavedInstanceState); - } - } - public Toolbar getToolbar() { return mToolbar; diff --git a/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java index a2548098c8..72f27853b1 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmToolbarFragment.java @@ -1,7 +1,6 @@ package com.mapswithme.maps.base; import android.os.Bundle; -import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; @@ -12,9 +11,8 @@ public class BaseMwmToolbarFragment extends BaseMwmFragment { protected ToolbarController mToolbarController; - @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mToolbarController = onCreateToolbarController(view); } diff --git a/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java b/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java index c295c161ea..58157b8e8c 100644 --- a/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseToolbarActivity.java @@ -14,20 +14,23 @@ public abstract class BaseToolbarActivity extends BaseMwmFragmentActivity { @CallSuper @Override - protected void safeOnCreate(@Nullable Bundle savedInstanceState) + protected void onCreate(@Nullable Bundle savedInstanceState) { - super.safeOnCreate(savedInstanceState); + super.onCreate(savedInstanceState); Toolbar toolbar = getToolbar(); - UiUtils.extendViewWithStatusBar(toolbar); - int title = getToolbarTitle(); - if (title == 0) - toolbar.setTitle(getTitle()); - else - toolbar.setTitle(title); + if (toolbar != null) + { + UiUtils.extendViewWithStatusBar(toolbar); + int title = getToolbarTitle(); + if (title == 0) + toolbar.setTitle(getTitle()); + else + toolbar.setTitle(title); - UiUtils.showHomeUpButton(toolbar); - displayToolbarAsActionBar(); + UiUtils.showHomeUpButton(toolbar); + displayToolbarAsActionBar(); + } } @StringRes diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java index b5931fa4cb..7aec8a9803 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java @@ -51,9 +51,9 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); if (getAdapter() != null) { diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java index b117183aa4..fd1cdd5f8f 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarksListFragment.java @@ -43,8 +43,9 @@ public class BookmarksListFragment extends BaseMwmListFragment @CallSuper @Override - protected void safeOnCreate(@Nullable Bundle savedInstanceState) + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); mCategoryIndex = getArguments().getInt(ChooseBookmarkCategoryFragment.CATEGORY_ID, -1); mCategory = BookmarkManager.INSTANCE.getCategory(mCategoryIndex); } @@ -57,9 +58,9 @@ public class BookmarksListFragment extends BaseMwmListFragment @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); initList(); setHasOptionsMenu(true); ActionBar bar = ((AppCompatActivity) getActivity()).getSupportActionBar(); @@ -68,8 +69,9 @@ public class BookmarksListFragment extends BaseMwmListFragment } @Override - protected void safeOnResume() + public void onResume() { + super.onResume(); if (mAdapter == null) return; diff --git a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java index 12ff82a0d8..90a1f71792 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java +++ b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java @@ -116,9 +116,9 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); } - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); mSubscriberSlot = MapManager.nativeSubscribe(new MapManager.StorageCallback() { @Override diff --git a/android/src/com/mapswithme/maps/editor/EditorFragment.java b/android/src/com/mapswithme/maps/editor/EditorFragment.java index f1d6aacd73..3e9d737b0c 100644 --- a/android/src/com/mapswithme/maps/editor/EditorFragment.java +++ b/android/src/com/mapswithme/maps/editor/EditorFragment.java @@ -126,7 +126,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mIsViewCreated = true; diff --git a/android/src/com/mapswithme/maps/editor/EditorHostFragment.java b/android/src/com/mapswithme/maps/editor/EditorHostFragment.java index 11fae484d3..43c97abca0 100644 --- a/android/src/com/mapswithme/maps/editor/EditorHostFragment.java +++ b/android/src/com/mapswithme/maps/editor/EditorHostFragment.java @@ -121,9 +121,9 @@ public class EditorHostFragment extends BaseMwmToolbarFragment @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); mToolbarController.findViewById(R.id.save).setOnClickListener(this); mToolbarController.getToolbar().setNavigationOnClickListener(new View.OnClickListener() diff --git a/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java b/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java index 7153d2e801..e1ee89b7ab 100644 --- a/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java +++ b/android/src/com/mapswithme/maps/editor/FeatureCategoryFragment.java @@ -34,9 +34,9 @@ public class FeatureCategoryFragment extends BaseMwmRecyclerFragment @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); if (getArguments() != null && getArguments().containsKey(FeatureCategoryActivity.EXTRA_FEATURE_CATEGORY)) mSelectedCategory = getArguments().getParcelable(FeatureCategoryActivity.EXTRA_FEATURE_CATEGORY); diff --git a/android/src/com/mapswithme/maps/editor/StreetFragment.java b/android/src/com/mapswithme/maps/editor/StreetFragment.java index c4a5addd03..7f233ebb8f 100644 --- a/android/src/com/mapswithme/maps/editor/StreetFragment.java +++ b/android/src/com/mapswithme/maps/editor/StreetFragment.java @@ -25,10 +25,10 @@ public class StreetFragment extends BaseMwmRecyclerFragment implements EditTextD @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mSelectedString = Editor.nativeGetStreet(); - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); } @Override diff --git a/android/src/com/mapswithme/maps/news/BaseNewsFragment.java b/android/src/com/mapswithme/maps/news/BaseNewsFragment.java index 7b155fad2b..ff48ae8d4e 100644 --- a/android/src/com/mapswithme/maps/news/BaseNewsFragment.java +++ b/android/src/com/mapswithme/maps/news/BaseNewsFragment.java @@ -5,7 +5,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Bundle; import android.support.annotation.ArrayRes; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; @@ -39,6 +41,9 @@ public abstract class BaseNewsFragment extends BaseMwmDialogFragment private int mPageCount; + @Nullable + private NewsDialogListener mListener; + abstract class Adapter extends PagerAdapter { private final int[] mImages; @@ -285,18 +290,23 @@ public abstract class BaseNewsFragment extends BaseMwmDialogFragment return res; } + @CallSuper protected void onDoneClick() { dismissAllowingStateLoss(); + if (mListener != null) + mListener.onDialogDone(); } @SuppressWarnings("TryWithIdenticalCatches") static void create(@NonNull FragmentActivity activity, - @NonNull Class clazz) + @NonNull Class clazz, + @Nullable NewsDialogListener listener) { try { final BaseNewsFragment fragment = clazz.newInstance(); + fragment.mListener = listener; activity.getSupportFragmentManager() .beginTransaction() .add(fragment, clazz.getName()) @@ -321,4 +331,9 @@ public abstract class BaseNewsFragment extends BaseMwmDialogFragment fm.executePendingTransactions(); return true; } + + public interface NewsDialogListener + { + void onDialogDone(); + } } diff --git a/android/src/com/mapswithme/maps/news/FirstStartFragment.java b/android/src/com/mapswithme/maps/news/FirstStartFragment.java index 0de3368533..26c29cc8a4 100644 --- a/android/src/com/mapswithme/maps/news/FirstStartFragment.java +++ b/android/src/com/mapswithme/maps/news/FirstStartFragment.java @@ -3,13 +3,14 @@ package com.mapswithme.maps.news; import android.app.Dialog; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.R; import com.mapswithme.maps.location.LocationHelper; -import com.mapswithme.util.Config; +import com.mapswithme.util.Counters; public class FirstStartFragment extends BaseNewsFragment { @@ -73,22 +74,23 @@ public class FirstStartFragment extends BaseNewsFragment LocationHelper.INSTANCE.onExitFromFirstRun(); } - public static boolean showOn(@NonNull FragmentActivity activity) + public static boolean showOn(@NonNull FragmentActivity activity, + @Nullable NewsDialogListener listener) { - if (Config.getFirstInstallVersion() < BuildConfig.VERSION_CODE) + if (Counters.getFirstInstallVersion() < BuildConfig.VERSION_CODE) return false; FragmentManager fm = activity.getSupportFragmentManager(); if (fm.isDestroyed()) return false; - if (Config.isFirstStartDialogSeen() && + if (Counters.isFirstStartDialogSeen() && !recreate(activity, FirstStartFragment.class)) return false; - create(activity, FirstStartFragment.class); + create(activity, FirstStartFragment.class, listener); - Config.setFirstStartDialogSeen(); + Counters.setFirstStartDialogSeen(); return true; } } diff --git a/android/src/com/mapswithme/maps/news/NewsFragment.java b/android/src/com/mapswithme/maps/news/NewsFragment.java index 42c9d6a74b..9c33ee7281 100644 --- a/android/src/com/mapswithme/maps/news/NewsFragment.java +++ b/android/src/com/mapswithme/maps/news/NewsFragment.java @@ -1,6 +1,7 @@ package com.mapswithme.maps.news; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; @@ -8,8 +9,8 @@ import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.downloader.UpdaterDialogFragment; -import com.mapswithme.util.Config; import com.mapswithme.util.concurrency.UiThread; +import com.mapswithme.util.Counters; public class NewsFragment extends BaseNewsFragment { @@ -91,22 +92,23 @@ public class NewsFragment extends BaseNewsFragment * Displays "What's new" dialog on given {@code activity}. Or not. * @return whether "What's new" dialog should be shown. */ - public static boolean showOn(@NonNull FragmentActivity activity) + public static boolean showOn(@NonNull FragmentActivity activity, + @Nullable NewsDialogListener listener) { - if (Config.getFirstInstallVersion() >= BuildConfig.VERSION_CODE) + if (Counters.getFirstInstallVersion() >= BuildConfig.VERSION_CODE) return false; FragmentManager fm = activity.getSupportFragmentManager(); if (fm.isDestroyed()) return false; - if (Config.getLastWhatsNewVersion() / 10 >= BuildConfig.VERSION_CODE / 10 && + if (Counters.getLastWhatsNewVersion() / 10 >= BuildConfig.VERSION_CODE / 10 && !recreate(activity, NewsFragment.class)) return false; - create(activity, NewsFragment.class); + create(activity, NewsFragment.class, listener); - Config.setWhatsNewShown(); + Counters.setWhatsNewShown(); return true; } } diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 627ab2bd6f..79e62919cc 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -288,9 +288,9 @@ public class SearchFragment extends BaseMwmFragment @CallSuper @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); readArguments(); ViewGroup root = (ViewGroup) view; @@ -390,8 +390,9 @@ public class SearchFragment extends BaseMwmFragment } - protected void safeOnResume() + public void onResume() { + super.onResume(); LocationHelper.INSTANCE.addListener(mLocationListener, true); mAppBarLayout.addOnOffsetChangedListener(mOffsetListener); } diff --git a/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java b/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java index e9ae159e44..4f22c99985 100644 --- a/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java @@ -36,9 +36,9 @@ public class SearchHistoryFragment extends BaseMwmRecyclerFragment } @Override - protected void safeOnViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.safeOnViewCreated(view, savedInstanceState); + super.onViewCreated(view, savedInstanceState); getRecyclerView().setLayoutManager(new LinearLayoutManager(view.getContext())); mPlaceHolder = (PlaceholderView) view.findViewById(R.id.placeholder); mPlaceHolder.setContent(R.drawable.img_search_empty_history_light, @@ -60,8 +60,9 @@ public class SearchHistoryFragment extends BaseMwmRecyclerFragment @CallSuper @Override - protected void safeOnActivityCreated(@Nullable Bundle savedInstanceState) + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); ((SearchFragment) getParentFragment()).setRecyclerScrollListener(getRecyclerView()); } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java b/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java index 4357cf1f12..9972ebb66c 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java +++ b/android/src/com/mapswithme/maps/widget/placepage/EditBookmarkFragment.java @@ -78,7 +78,7 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View. } @Override - protected void safeOnViewCreated(@NonNull View view, Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { final Bundle args = getArguments(); mCategoryId = args.getInt(EXTRA_CATEGORY_ID); diff --git a/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java b/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java index ab2e584d85..eda3ad6efb 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java +++ b/android/src/com/mapswithme/maps/widget/placepage/EditDescriptionFragment.java @@ -50,7 +50,7 @@ public class EditDescriptionFragment extends BaseMwmDialogFragment } @Override - protected void safeOnViewCreated(@NonNull View view, Bundle savedInstanceState) + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { mBookmark = getArguments().getParcelable(EXTRA_BOOKMARK); String description = null; diff --git a/android/src/com/mapswithme/util/Config.java b/android/src/com/mapswithme/util/Config.java index 03f511bc8a..2b6092ddee 100644 --- a/android/src/com/mapswithme/util/Config.java +++ b/android/src/com/mapswithme/util/Config.java @@ -1,19 +1,11 @@ package com.mapswithme.util; import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.text.TextUtils; -import android.text.format.DateUtils; -import com.mapswithme.maps.BuildConfig; +import com.mapswithme.maps.MwmApplication; public final class Config { - private static final String KEY_APP_FIRST_INSTALL_VERSION = "FirstInstallVersion"; - private static final String KEY_APP_LAUNCH_NUMBER = "LaunchNumber"; - private static final String KEY_APP_SESSION_NUMBER = "SessionNumber"; - private static final String KEY_APP_LAST_SESSION_TIMESTAMP = "LastSessionTimestamp"; - private static final String KEY_APP_FIRST_INSTALL_FLAVOR = "FirstInstallFlavor"; private static final String KEY_APP_STORAGE = "StoragePath"; private static final String KEY_TTS_ENABLED = "TtsEnabled"; @@ -25,13 +17,8 @@ public final class Config private static final String KEY_PREF_STATISTICS = "StatisticsEnabled"; private static final String KEY_PREF_USE_GS = "UseGoogleServices"; - private static final String KEY_LIKES_RATED_DIALOG = "RatedDialog"; - private static final String KEY_LIKES_LAST_RATED_SESSION = "LastRatedSession"; - private static final String KEY_MISC_DISCLAIMER_ACCEPTED = "IsDisclaimerApproved"; private static final String KEY_MISC_KITKAT_MIGRATED = "KitKatMigrationCompleted"; - private static final String KEY_MISC_NEWS_LAST_VERSION = "WhatsNewShownVersion"; - private static final String KEY_MISC_FIRST_START_DIALOG_SEEN = "FirstStartDialogSeen"; private static final String KEY_MISC_UI_THEME = "UiTheme"; private static final String KEY_MISC_UI_THEME_SETTINGS = "UiThemeSettings"; private static final String KEY_MISC_USE_MOBILE_DATA = "UseMobileData"; @@ -106,83 +93,6 @@ public final class Config nativeSetBoolean(key, value); } - /** - * Increments integer value. - * @return Previous value before increment. - */ - private static int increment(String key) - { - int res = getInt(key); - setInt(key, res + 1); - return res; - } - - public static int getFirstInstallVersion() - { - return getInt(KEY_APP_FIRST_INSTALL_VERSION); - } - - /** - * Increments counter of app starts. - * @return Previous value before increment. - */ - private static int incrementLaunchNumber() - { - return increment(KEY_APP_LAUNCH_NUMBER); - } - - /** - * Session = single day, when app was started any number of times. - */ - public static int getSessionCount() - { - return getInt(KEY_APP_SESSION_NUMBER); - } - - private static void incrementSessionNumber() - { - long lastSessionTimestamp = getLong(KEY_APP_LAST_SESSION_TIMESTAMP); - if (DateUtils.isToday(lastSessionTimestamp)) - return; - - setLong(KEY_APP_LAST_SESSION_TIMESTAMP, System.currentTimeMillis()); - increment(KEY_APP_SESSION_NUMBER); - } - - public static void resetAppSessionCounters() - { - setInt(KEY_APP_LAUNCH_NUMBER, 0); - setInt(KEY_APP_SESSION_NUMBER, 0); - setLong(KEY_APP_LAST_SESSION_TIMESTAMP, 0L); - setInt(KEY_LIKES_LAST_RATED_SESSION, 0); - incrementSessionNumber(); - } - - public static String getInstallFlavor() - { - return getString(KEY_APP_FIRST_INSTALL_FLAVOR); - } - - private static void updateInstallFlavor() - { - String installedFlavor = getInstallFlavor(); - if (TextUtils.isEmpty(installedFlavor)) - setString(KEY_APP_FIRST_INSTALL_FLAVOR, BuildConfig.FLAVOR); - } - - public static void updateLaunchCounter() - { - if (incrementLaunchNumber() == 0) - { - if (getFirstInstallVersion() == 0) - setInt(KEY_APP_FIRST_INSTALL_VERSION, BuildConfig.VERSION_CODE); - - updateInstallFlavor(); - } - - incrementSessionNumber(); - } - public static String getStoragePath() { return getString(KEY_APP_STORAGE); @@ -235,12 +145,12 @@ public final class Config public static boolean isStatisticsEnabled() { - return getBool(KEY_PREF_STATISTICS, true); + return MwmApplication.prefs().getBoolean(KEY_PREF_STATISTICS, true); } public static void setStatisticsEnabled(boolean enabled) { - setBool(KEY_PREF_STATISTICS, enabled); + MwmApplication.prefs().edit().putBoolean(KEY_PREF_STATISTICS, enabled).apply(); } public static boolean useGoogleServices() @@ -253,26 +163,6 @@ public final class Config setBool(KEY_PREF_USE_GS, use); } - public static boolean isRatingApplied(Class dialogFragmentClass) - { - return getBool(KEY_LIKES_RATED_DIALOG + dialogFragmentClass.getSimpleName()); - } - - public static void setRatingApplied(Class dialogFragmentClass) - { - setBool(KEY_LIKES_RATED_DIALOG + dialogFragmentClass.getSimpleName()); - } - - public static boolean isSessionRated(int session) - { - return (getInt(KEY_LIKES_LAST_RATED_SESSION) >= session); - } - - public static void setRatedSession(int session) - { - setInt(KEY_LIKES_LAST_RATED_SESSION, session); - } - public static boolean isRoutingDisclaimerAccepted() { return getBool(KEY_MISC_DISCLAIMER_ACCEPTED); @@ -293,26 +183,6 @@ public final class Config setBool(KEY_MISC_KITKAT_MIGRATED); } - public static int getLastWhatsNewVersion() - { - return getInt(KEY_MISC_NEWS_LAST_VERSION); - } - - public static void setWhatsNewShown() - { - setInt(KEY_MISC_NEWS_LAST_VERSION, BuildConfig.VERSION_CODE); - } - - public static boolean isFirstStartDialogSeen() - { - return getBool(KEY_MISC_FIRST_START_DIALOG_SEEN); - } - - public static void setFirstStartDialogSeen() - { - setBool(KEY_MISC_FIRST_START_DIALOG_SEEN); - } - @NonNull public static String getCurrentUiTheme() { diff --git a/android/src/com/mapswithme/util/Counters.java b/android/src/com/mapswithme/util/Counters.java new file mode 100644 index 0000000000..91f8bfee34 --- /dev/null +++ b/android/src/com/mapswithme/util/Counters.java @@ -0,0 +1,176 @@ +package com.mapswithme.util; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.preference.PreferenceManager; +import android.text.TextUtils; +import android.text.format.DateUtils; + +import com.mapswithme.maps.BuildConfig; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.R; + +public final class Counters +{ + private static final String KEY_APP_LAUNCH_NUMBER = "LaunchNumber"; + private static final String KEY_APP_FIRST_INSTALL_VERSION = "FirstInstallVersion"; + private static final String KEY_APP_FIRST_INSTALL_FLAVOR = "FirstInstallFlavor"; + private static final String KEY_APP_LAST_SESSION_TIMESTAMP = "LastSessionTimestamp"; + private static final String KEY_APP_SESSION_NUMBER = "SessionNumber"; + private static final String KEY_MISC_FIRST_START_DIALOG_SEEN = "FirstStartDialogSeen"; + private static final String KEY_MISC_NEWS_LAST_VERSION = "WhatsNewShownVersion"; + private static final String KEY_LIKES_LAST_RATED_SESSION = "LastRatedSession"; + + private static final String KEY_LIKES_RATED_DIALOG = "RatedDialog"; + + private Counters() {} + + public static void initCounters(@NonNull Context context) + { + PreferenceManager.setDefaultValues(context, R.xml.prefs_main, false); + updateLaunchCounter(); + } + + public static int getFirstInstallVersion() + { + return MwmApplication.prefs().getInt(KEY_APP_FIRST_INSTALL_VERSION, 0); + } + + public static boolean isFirstStartDialogSeen() + { + return MwmApplication.prefs().getBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, false); + } + + public static void setFirstStartDialogSeen() + { + MwmApplication.prefs() + .edit() + .putBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, true) + .apply(); + } + + public static int getLastWhatsNewVersion() + { + return MwmApplication.prefs().getInt(KEY_MISC_NEWS_LAST_VERSION, 0); + } + + public static void setWhatsNewShown() + { + MwmApplication.prefs() + .edit() + .putInt(KEY_MISC_NEWS_LAST_VERSION, BuildConfig.VERSION_CODE) + .apply(); + } + + public static void resetAppSessionCounters() + { + MwmApplication.prefs() + .edit() + .putInt(KEY_APP_LAUNCH_NUMBER, 0) + .putInt(KEY_APP_SESSION_NUMBER, 0) + .putLong(KEY_APP_LAST_SESSION_TIMESTAMP, 0L) + .putInt(KEY_LIKES_LAST_RATED_SESSION, 0) + .apply(); + incrementSessionNumber(); + } + + public static boolean isSessionRated(int session) + { + return (MwmApplication.prefs().getInt(KEY_LIKES_LAST_RATED_SESSION, 0) >= session); + } + + public static void setRatedSession(int session) + { + MwmApplication.prefs() + .edit() + .putInt(KEY_LIKES_LAST_RATED_SESSION, session) + .apply(); + } + + /** + * Session = single day, when app was started any number of times. + */ + public static int getSessionCount() + { + return MwmApplication.prefs().getInt(KEY_APP_SESSION_NUMBER, 0); + } + + public static boolean isRatingApplied(Class dialogFragmentClass) + { + return MwmApplication.prefs() + .getBoolean(KEY_LIKES_RATED_DIALOG + dialogFragmentClass.getSimpleName(), + false); + } + + public static void setRatingApplied(Class dialogFragmentClass) + { + MwmApplication.prefs() + .edit() + .putBoolean(KEY_LIKES_RATED_DIALOG + dialogFragmentClass.getSimpleName(), true) + .apply(); + } + + public static String getInstallFlavor() + { + return MwmApplication.prefs().getString(KEY_APP_FIRST_INSTALL_FLAVOR, ""); + } + + private static void updateLaunchCounter() + { + if (incrementLaunchNumber() == 0) + { + if (getFirstInstallVersion() == 0) + { + MwmApplication.prefs() + .edit() + .putInt(KEY_APP_FIRST_INSTALL_VERSION, BuildConfig.VERSION_CODE) + .apply(); + } + + updateInstallFlavor(); + } + + incrementSessionNumber(); + } + + private static int incrementLaunchNumber() + { + return increment(KEY_APP_LAUNCH_NUMBER); + } + + private static void updateInstallFlavor() + { + String installedFlavor = getInstallFlavor(); + if (TextUtils.isEmpty(installedFlavor)) + { + MwmApplication.prefs() + .edit() + .putString(KEY_APP_FIRST_INSTALL_FLAVOR, BuildConfig.FLAVOR) + .apply(); + } + } + + private static void incrementSessionNumber() + { + long lastSessionTimestamp = MwmApplication.prefs().getLong(KEY_APP_LAST_SESSION_TIMESTAMP, 0); + if (DateUtils.isToday(lastSessionTimestamp)) + return; + + MwmApplication.prefs() + .edit() + .putLong(KEY_APP_LAST_SESSION_TIMESTAMP, System.currentTimeMillis()) + .apply(); + increment(KEY_APP_SESSION_NUMBER); + } + + private static int increment(@NonNull String key) + { + int value = MwmApplication.prefs().getInt(key, 0); + MwmApplication.prefs() + .edit() + .putInt(key, ++value) + .apply(); + return value; + } +} diff --git a/android/src/com/mapswithme/util/MultipleTrackerReferrerReceiver.java b/android/src/com/mapswithme/util/MultipleTrackerReferrerReceiver.java index b2ef57e552..17c62ecf33 100644 --- a/android/src/com/mapswithme/util/MultipleTrackerReferrerReceiver.java +++ b/android/src/com/mapswithme/util/MultipleTrackerReferrerReceiver.java @@ -16,7 +16,7 @@ public class MultipleTrackerReferrerReceiver extends BroadcastReceiver @Override public void onReceive(Context context, Intent intent) { - MwmApplication.get().initCounters(); + Counters.initCounters(context); // parse & send referrer to Aloha try { diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index e06afe70da..737761dbc4 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -413,8 +413,7 @@ public class Utils } } - public static boolean checkPermissions(@NonNull Activity activity, @NonNull String[] permissions, - int requestCode) + public static boolean checkPermissions(@NonNull Activity activity, @NonNull String[] permissions) { if (Build.VERSION.SDK_INT >= 23) { @@ -425,15 +424,7 @@ public class Utils if (!isGranted) break; } - if (isGranted) - { - return true; - } - else - { - ActivityCompat.requestPermissions(activity, permissions, requestCode); - return false; - } + return isGranted; } //permission is automatically granted on sdk<23 upon installation diff --git a/android/src/com/mapswithme/util/statistics/Statistics.java b/android/src/com/mapswithme/util/statistics/Statistics.java index e2e7bfb70d..c9448c9ad2 100644 --- a/android/src/com/mapswithme/util/statistics/Statistics.java +++ b/android/src/com/mapswithme/util/statistics/Statistics.java @@ -28,6 +28,7 @@ import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.maps.widget.placepage.Sponsored; import com.mapswithme.util.Config; import com.mapswithme.util.ConnectionState; +import com.mapswithme.util.Counters; import java.util.ArrayList; import java.util.HashMap; @@ -381,7 +382,7 @@ public enum Statistics Config.setStatisticsEnabled(isEnabled); // We track if user turned on/off statistics to understand data better. - trackEvent(EventName.STATISTICS_STATUS_CHANGED + " " + Config.getInstallFlavor(), + trackEvent(EventName.STATISTICS_STATUS_CHANGED + " " + Counters.getInstallFlavor(), params().add(EventParam.ENABLED, String.valueOf(isEnabled))); } From 382550e40b61984d1bf477f00a53a5d0b0228034 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 31 Mar 2017 11:26:13 +0400 Subject: [PATCH 04/16] =?UTF-8?q?[android]=20fix=20=E2=80=9CCan=20not=20pe?= =?UTF-8?q?rform=20this=20action=20after=20onSaveInstanceState=E2=80=9D=20?= =?UTF-8?q?exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/AndroidManifest.xml | 1 - android/res/layout/activity_splash.xml | 1 + .../src/com/mapswithme/maps/MwmActivity.java | 1 + .../com/mapswithme/maps/MwmApplication.java | 9 ++++++++ .../com/mapswithme/maps/SplashActivity.java | 21 +++++++++++++++---- .../ConnectivityChangedReceiver.java | 3 ++- .../maps/background/WorkerService.java | 14 ++++++++++++- .../mapswithme/maps/base/BaseMwmFragment.java | 2 +- .../maps/base/BaseMwmFragmentActivity.java | 14 ++++++------- .../maps/base/BaseMwmListFragment.java | 2 +- .../maps/base/BaseMwmRecyclerFragment.java | 2 +- .../maps/news/BaseNewsFragment.java | 6 +++--- 12 files changed, 56 insertions(+), 20 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index a5611e315b..55aeaf5d9d 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -83,7 +83,6 @@ diff --git a/android/res/layout/activity_splash.xml b/android/res/layout/activity_splash.xml index 3e66bd81f2..076596ab23 100644 --- a/android/res/layout/activity_splash.xml +++ b/android/res/layout/activity_splash.xml @@ -7,6 +7,7 @@ android:gravity="center"> diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 80f56408a9..470fb31fbb 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -450,6 +450,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override protected void safeOnCreate(@Nullable Bundle savedInstanceState) { + super.safeOnCreate(savedInstanceState); if (savedInstanceState != null) mLocationErrorDialogAnnoying = savedInstanceState.getBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING); mIsFragmentContainer = getResources().getBoolean(R.bool.tabletLayout); diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index 5d64e6f3f3..21a8c0d89a 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -57,6 +57,7 @@ public class MwmApplication extends Application private AppBackgroundTracker mBackgroundTracker; private boolean mIsFrameworkInitialized; + private boolean mIsPlatformInitialized; private Handler mMainLoopHandler; private final Object mMainQueueToken = new Object(); @@ -155,6 +156,9 @@ public class MwmApplication extends Application public void initNativePlatform() { + if (mIsPlatformInitialized) + return; + final boolean isInstallationIdFound = setInstallationIdToCrashlytics(); initTracker(); @@ -180,6 +184,7 @@ public class MwmApplication extends Application mBackgroundTracker.addListener(mBackgroundListener); TrackRecorder.init(); Editor.init(); + mIsPlatformInitialized = true; } public void initNativeCore() @@ -256,6 +261,10 @@ public class MwmApplication extends Application { return mIsFrameworkInitialized; } + public boolean isPlatformInitialized() + { + return mIsPlatformInitialized; + } public String getApkPath() { diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index 2dc9545dc1..2c3121376a 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -9,6 +9,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; +import android.view.View; import com.mapswithme.maps.ads.LikesManager; import com.mapswithme.maps.editor.ViralFragment; @@ -45,6 +46,8 @@ public class SplashActivity extends AppCompatActivity // The first launch of application ever - onboarding screen will be shown. private static boolean sFirstStart; + private View mIvLogo; + private boolean mPermissionsGranted; public static void start(@NonNull Context context, @@ -101,25 +104,30 @@ public class SplashActivity extends AppCompatActivity sFirstStart = FirstStartFragment.showOn(this, this); if (sFirstStart) + { + UiUtils.hide(mIvLogo); return; + } - if (!NewsFragment.showOn(this, this)) + boolean showNews = NewsFragment.showOn(this, this); + if (!showNews) { if (ViralFragment.shouldDisplay()) { + UiUtils.hide(mIvLogo); ViralFragment dialog = new ViralFragment(); dialog.onDismiss(new DialogInterface() { @Override public void cancel() { - processNavigation(); + onDialogDone(); } @Override public void dismiss() { - processNavigation(); + onDialogDone(); } }); dialog.show(getSupportFragmentManager(), ""); @@ -130,6 +138,10 @@ public class SplashActivity extends AppCompatActivity processNavigation(); } } + else + { + UiUtils.hide(mIvLogo); + } } @Override @@ -141,7 +153,7 @@ public class SplashActivity extends AppCompatActivity return; boolean isWriteGranted = false; - for (int i = 0; i < permissions.length; ++i) + for (int i = 0; i < permissions.length; i++) { int result = grantResults[i]; String permission = permissions[i]; @@ -171,6 +183,7 @@ public class SplashActivity extends AppCompatActivity { UiUtils.setupStatusBar(this); setContentView(R.layout.activity_splash); + mIvLogo = findViewById(R.id.iv__logo); } private void init() diff --git a/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java b/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java index 13017f93c7..c573d68161 100644 --- a/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java +++ b/android/src/com/mapswithme/maps/background/ConnectivityChangedReceiver.java @@ -18,7 +18,8 @@ public class ConnectivityChangedReceiver extends BroadcastReceiver @Override public void onReceive(Context context, Intent intent) { - if (!MwmApplication.get().isFrameworkInitialized() || !ConnectionState.isWifiConnected() + MwmApplication.get().initNativePlatform(); + if (!ConnectionState.isWifiConnected() || MapManager.nativeNeedMigrate()) return; diff --git a/android/src/com/mapswithme/maps/background/WorkerService.java b/android/src/com/mapswithme/maps/background/WorkerService.java index 465c3102cc..d87b7c5ec9 100644 --- a/android/src/com/mapswithme/maps/background/WorkerService.java +++ b/android/src/com/mapswithme/maps/background/WorkerService.java @@ -125,10 +125,22 @@ public class WorkerService extends IntentService private static boolean processLocation() { final LocationManager manager = (LocationManager) MwmApplication.get().getSystemService(Context.LOCATION_SERVICE); - final Location l = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); + Location l = null; + try + { + l = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); + } + catch (SecurityException e) + { + e.printStackTrace(); + } + if (l == null || LocationUtils.isExpired(l, l.getTime(), LocationUtils.LOCATION_EXPIRATION_TIME_MILLIS_LONG)) return false; + MwmApplication.get().initNativePlatform(); + MwmApplication.get().initNativeCore(); + String country = MapManager.nativeFindCountry(l.getLatitude(), l.getLongitude()); if (TextUtils.isEmpty(country)) return false; diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java index c86c4bc85f..60d6ba8952 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java @@ -13,7 +13,7 @@ public class BaseMwmFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) { ((AppCompatActivity)context).getSupportFragmentManager() .beginTransaction() diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java index 506cdffbf4..c66ecd68f5 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java @@ -55,7 +55,7 @@ public class BaseMwmFragmentActivity extends AppCompatActivity protected void onCreate(@Nullable Bundle savedInstanceState) { mSavedState = savedInstanceState; - if (!MwmApplication.get().isFrameworkInitialized() + if (!MwmApplication.get().isPlatformInitialized() || !Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) { super.onCreate(savedInstanceState); @@ -67,6 +67,12 @@ public class BaseMwmFragmentActivity extends AppCompatActivity mBaseDelegate.onCreate(); super.onCreate(savedInstanceState); + safeOnCreate(savedInstanceState); + } + + @CallSuper + protected void safeOnCreate(@Nullable Bundle savedInstanceState) + { setVolumeControlStream(AudioManager.STREAM_MUSIC); final int layoutId = getContentLayoutResId(); if (layoutId != 0) @@ -85,12 +91,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity } attachDefaultFragment(); - - safeOnCreate(savedInstanceState); - } - - protected void safeOnCreate(@Nullable Bundle savedInstanceState) - { } protected boolean isInitializationComplete() diff --git a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java index 86ed6705bf..92fd81bfda 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java @@ -22,7 +22,7 @@ public abstract class BaseMwmListFragment extends ListFragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) { ((AppCompatActivity)context).getSupportFragmentManager() .beginTransaction() diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index 5615944bed..818255c3e2 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -45,7 +45,7 @@ public abstract class BaseMwmRecyclerFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isFrameworkInitialized()) + if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) { ((AppCompatActivity)context).getSupportFragmentManager() .beginTransaction() diff --git a/android/src/com/mapswithme/maps/news/BaseNewsFragment.java b/android/src/com/mapswithme/maps/news/BaseNewsFragment.java index ff48ae8d4e..5d652db75d 100644 --- a/android/src/com/mapswithme/maps/news/BaseNewsFragment.java +++ b/android/src/com/mapswithme/maps/news/BaseNewsFragment.java @@ -308,9 +308,9 @@ public abstract class BaseNewsFragment extends BaseMwmDialogFragment final BaseNewsFragment fragment = clazz.newInstance(); fragment.mListener = listener; activity.getSupportFragmentManager() - .beginTransaction() - .add(fragment, clazz.getName()) - .commitAllowingStateLoss(); + .beginTransaction() + .add(fragment, clazz.getName()) + .commitAllowingStateLoss(); } catch (java.lang.InstantiationException ignored) {} catch (IllegalAccessException ignored) From 5a234b8cb5e87a77378a6ed374c633596994f70a Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 31 Mar 2017 13:50:24 +0400 Subject: [PATCH 05/16] [android] fixed: SplashActivity.resumeDialogs() called before onPause() --- .../com/mapswithme/maps/SplashActivity.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index 2c3121376a..e0026d36d5 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -49,6 +49,27 @@ public class SplashActivity extends AppCompatActivity private View mIvLogo; private boolean mPermissionsGranted; + private boolean mCanceled; + + @NonNull + private final Runnable mDelayedTask = new Runnable() + { + @Override + public void run() + { + init(); + UiThread.runLater(mFinalTask); + } + }; + @NonNull + private final Runnable mFinalTask = new Runnable() + { + @Override + public void run() + { + resumeDialogs(); + } + }; public static void start(@NonNull Context context, @Nullable Class activityToStart) @@ -69,6 +90,8 @@ public class SplashActivity extends AppCompatActivity protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + UiThread.cancelDelayedTasks(mDelayedTask); + UiThread.cancelDelayedTasks(mFinalTask); Counters.initCounters(this); initView(); } @@ -77,6 +100,7 @@ public class SplashActivity extends AppCompatActivity protected void onResume() { super.onResume(); + mCanceled = false; mPermissionsGranted = Utils.checkPermissions(this, PERMISSIONS); if (!mPermissionsGranted) { @@ -85,21 +109,22 @@ public class SplashActivity extends AppCompatActivity return; } - UiThread.runLater(new Runnable() - { - @Override - public void run() - { - init(); - resumeDialogs(); - } - }, DELAY); + UiThread.runLater(mDelayedTask, DELAY); + } + + @Override + protected void onPause() + { + mCanceled = true; + UiThread.cancelDelayedTasks(mDelayedTask); + UiThread.cancelDelayedTasks(mFinalTask); + super.onPause(); } private void resumeDialogs() { // TODO show permissions dialog if Permissions is not granted - if (!mPermissionsGranted) + if (!mPermissionsGranted || mCanceled) return; sFirstStart = FirstStartFragment.showOn(this, this); From dc7d46fc111a44364932f67275619fc27c1258e1 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 5 Apr 2017 11:54:39 +0400 Subject: [PATCH 06/16] [android] Review fixes. --- .../src/com/mapswithme/maps/MwmActivity.java | 7 +++++++ .../com/mapswithme/maps/SplashActivity.java | 10 +++++----- .../maps/background/WorkerService.java | 20 +++++-------------- .../maps/base/BaseActivityDelegate.java | 19 +++++++++--------- .../mapswithme/maps/base/BaseMwmFragment.java | 9 ++------- .../maps/base/BaseMwmFragmentActivity.java | 9 --------- .../maps/base/BaseMwmListFragment.java | 8 +------- .../maps/base/BaseMwmRecyclerFragment.java | 8 +------- .../bookmarks/BookmarkCategoriesFragment.java | 2 +- .../maps/downloader/OnmapDownloader.java | 1 - .../maps/editor/EditorFragment.java | 7 +------ .../maps/location/AndroidNativeProvider.java | 14 ++++++++++--- .../maps/location/LocationHelper.java | 6 ------ .../maps/routing/RoutingPlanFragment.java | 4 ---- android/src/com/mapswithme/util/Utils.java | 14 +++++++++++++ 15 files changed, 58 insertions(+), 80 deletions(-) diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 470fb31fbb..0e2dd7eef4 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -950,6 +950,13 @@ public class MwmActivity extends BaseMwmFragmentActivity }); } + if (mIsFragmentContainer) + { + RoutingPlanFragment fragment = (RoutingPlanFragment) getFragment(RoutingPlanFragment.class); + if (fragment != null) + fragment.restoreRoutingPanelState(savedInstanceState); + } + if (!mIsFragmentContainer && RoutingController.get().isPlanning()) mRoutingPlanInplaceController.restoreState(savedInstanceState); diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index e0026d36d5..2354d92eea 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -177,18 +177,19 @@ public class SplashActivity extends AppCompatActivity if (grantResults.length == 0) return; - boolean isWriteGranted = false; for (int i = 0; i < permissions.length; i++) { int result = grantResults[i]; String permission = permissions[i]; if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - isWriteGranted = true; + { + mPermissionsGranted = true; + break; + } } - if (isWriteGranted) + if (mPermissionsGranted) { - mPermissionsGranted = true; init(); resumeDialogs(); } @@ -215,7 +216,6 @@ public class SplashActivity extends AppCompatActivity { MwmApplication.get().initNativePlatform(); MwmApplication.get().initNativeCore(); - LocationHelper.INSTANCE.init(); } @SuppressWarnings("unchecked") diff --git a/android/src/com/mapswithme/maps/background/WorkerService.java b/android/src/com/mapswithme/maps/background/WorkerService.java index d87b7c5ec9..a337dec39c 100644 --- a/android/src/com/mapswithme/maps/background/WorkerService.java +++ b/android/src/com/mapswithme/maps/background/WorkerService.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.location.Location; -import android.location.LocationManager; import android.text.TextUtils; import java.util.concurrent.CountDownLatch; @@ -15,6 +14,7 @@ import com.mapswithme.maps.R; import com.mapswithme.maps.downloader.CountryItem; import com.mapswithme.maps.downloader.MapManager; import com.mapswithme.maps.editor.Editor; +import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.LocationUtils; import com.mapswithme.util.concurrency.UiThread; @@ -124,23 +124,13 @@ public class WorkerService extends IntentService @android.support.annotation.UiThread private static boolean processLocation() { - final LocationManager manager = (LocationManager) MwmApplication.get().getSystemService(Context.LOCATION_SERVICE); - Location l = null; - try - { - l = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); - } - catch (SecurityException e) - { - e.printStackTrace(); - } - - if (l == null || LocationUtils.isExpired(l, l.getTime(), LocationUtils.LOCATION_EXPIRATION_TIME_MILLIS_LONG)) - return false; - MwmApplication.get().initNativePlatform(); MwmApplication.get().initNativeCore(); + Location l = LocationHelper.INSTANCE.getLastKnownLocation(); + if (l == null) + return false; + String country = MapManager.nativeFindCountry(l.getLatitude(), l.getLongitude()); if (TextUtils.isEmpty(country)) return false; diff --git a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java index 3431c4e3f8..6a7592f56a 100644 --- a/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java +++ b/android/src/com/mapswithme/maps/base/BaseActivityDelegate.java @@ -2,6 +2,7 @@ package com.mapswithme.maps.base; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import com.mapswithme.util.Config; import com.mapswithme.util.UiUtils; @@ -10,14 +11,14 @@ import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.statistics.Statistics; import com.my.tracker.MyTracker; -public class BaseActivityDelegate +class BaseActivityDelegate { @NonNull private final BaseActivity mActivity; @Nullable private String mThemeName; - public BaseActivityDelegate(@NonNull BaseActivity activity) + BaseActivityDelegate(@NonNull BaseActivity activity) { mActivity = activity; } @@ -25,27 +26,27 @@ public class BaseActivityDelegate public void onCreate() { mThemeName = Config.getCurrentUiTheme(); - if (mThemeName != null) + if (!TextUtils.isEmpty(mThemeName)) mActivity.get().setTheme(mActivity.getThemeResourceId(mThemeName)); } - public void onDestroy() + void onDestroy() { ViewServer.get(mActivity.get()).removeWindow(mActivity.get()); } - public void onPostCreate() + void onPostCreate() { ViewServer.get(mActivity.get()).addWindow(mActivity.get()); } - public void onStart() + void onStart() { Statistics.INSTANCE.startActivity(mActivity.get()); MyTracker.onStartActivity(mActivity.get()); } - public void onStop() + void onStop() { Statistics.INSTANCE.stopActivity(mActivity.get()); MyTracker.onStopActivity(mActivity.get()); @@ -63,9 +64,9 @@ public class BaseActivityDelegate org.alohalytics.Statistics.logEvent("$onPause", mActivity.getClass().getSimpleName()); } - public void onPostResume() + void onPostResume() { - if (mThemeName != null && mThemeName.equals(Config.getCurrentUiTheme())) + if (!TextUtils.isEmpty(mThemeName) && mThemeName.equals(Config.getCurrentUiTheme())) return; // Workaround described in https://code.google.com/p/android/issues/detail?id=93731 diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java index 60d6ba8952..e06cfc6b35 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java @@ -5,6 +5,7 @@ import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import com.mapswithme.maps.MwmApplication; +import com.mapswithme.util.Utils; public class BaseMwmFragment extends Fragment { @@ -13,13 +14,7 @@ public class BaseMwmFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) - { - ((AppCompatActivity)context).getSupportFragmentManager() - .beginTransaction() - .detach(this) - .commit(); - } + Utils.detachFragmentIfInitializing(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java index c66ecd68f5..4ce718c2ec 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java @@ -27,8 +27,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity { private final BaseActivityDelegate mBaseDelegate = new BaseActivityDelegate(this); - @Nullable - private Bundle mSavedState; private boolean mInitializationComplete = false; @Override @@ -54,7 +52,6 @@ public class BaseMwmFragmentActivity extends AppCompatActivity @Override protected void onCreate(@Nullable Bundle savedInstanceState) { - mSavedState = savedInstanceState; if (!MwmApplication.get().isPlatformInitialized() || !Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) { @@ -261,10 +258,4 @@ public class BaseMwmFragmentActivity extends AppCompatActivity SplashActivity.start(this, type); finish(); } - - @Nullable - public Bundle getSavedInstanceState() - { - return mSavedState; - } } diff --git a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java index 92fd81bfda..91fcf4f00b 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java @@ -22,13 +22,7 @@ public abstract class BaseMwmListFragment extends ListFragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) - { - ((AppCompatActivity)context).getSupportFragmentManager() - .beginTransaction() - .detach(this) - .commit(); - } + Utils.detachFragmentIfInitializing(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index 818255c3e2..b32a80a710 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -45,13 +45,7 @@ public abstract class BaseMwmRecyclerFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) - { - ((AppCompatActivity)context).getSupportFragmentManager() - .beginTransaction() - .detach(this) - .commit(); - } + Utils.detachFragmentIfInitializing(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java index 7aec8a9803..f8a861150b 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java @@ -46,7 +46,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment protected BookmarkCategoriesAdapter getAdapter() { RecyclerView.Adapter adapter = super.getAdapter(); - return adapter != null ? (BookmarkCategoriesAdapter)adapter : null; + return adapter != null ? (BookmarkCategoriesAdapter) adapter : null; } @CallSuper diff --git a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java index 4cf0617bc4..952bc40b39 100644 --- a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java +++ b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java @@ -253,7 +253,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener { MapManager.nativeUnsubscribe(mStorageSubscriptionSlot); mStorageSubscriptionSlot = 0; - MapManager.nativeUnsubscribeOnCountryChanged(); } } diff --git a/android/src/com/mapswithme/maps/editor/EditorFragment.java b/android/src/com/mapswithme/maps/editor/EditorFragment.java index 3e9d737b0c..c30dc341ab 100644 --- a/android/src/com/mapswithme/maps/editor/EditorFragment.java +++ b/android/src/com/mapswithme/maps/editor/EditorFragment.java @@ -115,8 +115,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe private EditorHostFragment mParent; - private boolean mIsViewCreated = false; - @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) @@ -128,8 +126,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - mIsViewCreated = true; - mParent = (EditorHostFragment) getParentFragment(); initViews(view); @@ -210,8 +206,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (mIsViewCreated) - setEdits(); + setEdits(); } boolean setEdits() diff --git a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java index 495f831994..e668c89ec8 100644 --- a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java +++ b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java @@ -9,6 +9,8 @@ import android.support.annotation.Nullable; import com.mapswithme.maps.MwmApplication; import com.mapswithme.util.LocationUtils; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -19,6 +21,8 @@ class AndroidNativeProvider extends BaseLocationProvider private final static String TAG = AndroidNativeProvider.class.getSimpleName(); private final static String[] TRUSTED_PROVIDERS = { LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER }; + private final static Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.LOCATION); + @NonNull private final LocationManager mLocationManager; @NonNull @@ -51,15 +55,18 @@ class AndroidNativeProvider extends BaseLocationProvider long interval = LocationHelper.INSTANCE.getInterval(); LOGGER.d(TAG, "Request Android native provider '" + provider + "' to get locations at this interval = " + interval + " ms"); + mListeners.add(listener); try { mLocationManager.requestLocationUpdates(provider, interval, 0, listener); } catch (SecurityException e) { - e.printStackTrace(); + LOGGER.e(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", + e); + setActive(false); + return; } - mListeners.add(listener); } LocationHelper.INSTANCE.startSensors(); @@ -131,7 +138,8 @@ class AndroidNativeProvider extends BaseLocationProvider } catch (SecurityException e) { - e.printStackTrace(); + LOGGER.e(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", + e); } return res; } diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index 63c676823a..9490d433d1 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -158,12 +158,6 @@ public enum LocationHelper } }; - - public void init() - { - mLogger.d(LocationHelper.class.getSimpleName(), "ctor()"); - } - @UiThread public void initialize() { diff --git a/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java b/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java index 841b6e7be0..d04188f548 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java +++ b/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java @@ -28,10 +28,6 @@ public class RoutingPlanFragment extends BaseMwmFragment mPlanController = new RoutingPlanController(res, getActivity()); updatePoints(); - Bundle activityState = getMwmActivity().getSavedInstanceState(); - if (activityState != null) - restoreRoutingPanelState(activityState); - return res; } diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index 737761dbc4..c1b908355a 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -15,8 +15,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; import android.support.v4.app.NavUtils; import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; @@ -438,4 +440,16 @@ public class Utils return true; } + + public static void detachFragmentIfInitializing(@NonNull Context context, + @NonNull Fragment fragment) + { + if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) + { + ((AppCompatActivity)context).getSupportFragmentManager() + .beginTransaction() + .detach(fragment) + .commit(); + } + } } From 93f2195b68ee2c49c74ceb176b0699bf3cc04ed2 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 5 Apr 2017 13:07:16 +0400 Subject: [PATCH 07/16] [android] added comment --- android/src/com/mapswithme/maps/SplashActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index 2354d92eea..f55f0bf35d 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -58,6 +58,7 @@ public class SplashActivity extends AppCompatActivity public void run() { init(); +// Run delayed task because resumeDialogs() must be called after onPause() UiThread.runLater(mFinalTask); } }; From c8e5145f5812232c85ede11f36bf750d96a33750 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 5 Apr 2017 14:25:56 +0400 Subject: [PATCH 08/16] [android] move PushwooshHelper.nativeProcessFirstLaunch() to the SplashActivity. --- android/src/com/mapswithme/maps/MapFragment.java | 6 +----- android/src/com/mapswithme/maps/SplashActivity.java | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/android/src/com/mapswithme/maps/MapFragment.java b/android/src/com/mapswithme/maps/MapFragment.java index 8811b5c963..5222840e11 100644 --- a/android/src/com/mapswithme/maps/MapFragment.java +++ b/android/src/com/mapswithme/maps/MapFragment.java @@ -166,11 +166,7 @@ public class MapFragment extends BaseMwmFragment getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); final float exactDensityDpi = metrics.densityDpi; - boolean firstStart = SplashActivity.isFirstStart(); - if (firstStart) - PushwooshHelper.nativeProcessFirstLaunch(); - - if (!nativeCreateEngine(surface, (int) exactDensityDpi, firstStart)) + if (!nativeCreateEngine(surface, (int) exactDensityDpi, SplashActivity.isFirstStart())) { reportUnsupported(); return; diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index f55f0bf35d..539fd857fd 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -21,6 +21,7 @@ import com.mapswithme.util.Counters; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; import com.mapswithme.util.concurrency.UiThread; +import com.mapswithme.util.statistics.PushwooshHelper; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; @@ -131,6 +132,7 @@ public class SplashActivity extends AppCompatActivity sFirstStart = FirstStartFragment.showOn(this, this); if (sFirstStart) { + PushwooshHelper.nativeProcessFirstLaunch(); UiUtils.hide(mIvLogo); return; } From bfa6a5ae8b7e6ad1b1c908a4545f50d8b7f37605 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Mon, 10 Apr 2017 09:38:45 +0400 Subject: [PATCH 09/16] [android] Review fixes --- android/build.gradle | 2 +- .../com/mapswithme/maps/SplashActivity.java | 37 ++------ .../mapswithme/maps/base/BaseMwmFragment.java | 4 +- .../maps/base/BaseMwmFragmentActivity.java | 5 +- .../maps/base/BaseMwmListFragment.java | 5 +- .../maps/base/BaseMwmRecyclerFragment.java | 4 +- .../maps/location/AndroidNativeProvider.java | 39 +++----- .../location/GoogleFusedLocationProvider.java | 21 ++--- .../maps/location/LocationHelper.java | 8 ++ .../maps/settings/AboutFragment.java | 8 -- .../maps/settings/SettingsActivity.java | 14 ++- .../com/mapswithme/util/PermissionsUtils.java | 94 +++++++++++++++++++ android/src/com/mapswithme/util/Utils.java | 33 +------ .../util/permissions/PermissionsResult.java | 31 ++++++ 14 files changed, 179 insertions(+), 126 deletions(-) create mode 100644 android/src/com/mapswithme/util/PermissionsUtils.java create mode 100644 android/src/com/mapswithme/util/permissions/PermissionsResult.java diff --git a/android/build.gradle b/android/build.gradle index 107dfb4fbf..2fd871783f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -252,7 +252,7 @@ android { android.applicationVariants.all { variant -> def task = variant.name.capitalize() project.task(type: Exec, "run${task}", dependsOn: "install${task}") { - commandLine android.getAdbExe(), 'shell', 'am', 'start', '-n', "${applicationId}/com.mapswithme.maps.DownloadResourcesActivity" + commandLine android.getAdbExe(), 'shell', 'am', 'start', '-n', "${applicationId}/com.mapswithme.maps.SplashActivity" } variant.outputs.each { output -> diff --git a/android/src/com/mapswithme/maps/SplashActivity.java b/android/src/com/mapswithme/maps/SplashActivity.java index 539fd857fd..35268f8ec7 100644 --- a/android/src/com/mapswithme/maps/SplashActivity.java +++ b/android/src/com/mapswithme/maps/SplashActivity.java @@ -7,38 +7,23 @@ import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; import com.mapswithme.maps.ads.LikesManager; import com.mapswithme.maps.editor.ViralFragment; -import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.maps.news.BaseNewsFragment; import com.mapswithme.maps.news.FirstStartFragment; import com.mapswithme.maps.news.NewsFragment; import com.mapswithme.util.Counters; +import com.mapswithme.util.PermissionsUtils; import com.mapswithme.util.UiUtils; -import com.mapswithme.util.Utils; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.statistics.PushwooshHelper; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static android.Manifest.permission.GET_ACCOUNTS; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - public class SplashActivity extends AppCompatActivity implements BaseNewsFragment.NewsDialogListener { - public static final String[] PERMISSIONS = new String[] - { - WRITE_EXTERNAL_STORAGE, - ACCESS_COARSE_LOCATION, - ACCESS_FINE_LOCATION, - GET_ACCOUNTS - }; public static final String EXTRA_INTENT = "extra_intent"; private static final String EXTRA_ACTIVITY_TO_START = "extra_activity_to_start"; private static final int REQUEST_PERMISSIONS = 1; @@ -59,7 +44,9 @@ public class SplashActivity extends AppCompatActivity public void run() { init(); -// Run delayed task because resumeDialogs() must be called after onPause() +// Run delayed task because resumeDialogs() must see the actual value of mCanceled flag, +// since onPause() callback can be blocked because of UI thread is busy with framework +// initialization. UiThread.runLater(mFinalTask); } }; @@ -103,11 +90,11 @@ public class SplashActivity extends AppCompatActivity { super.onResume(); mCanceled = false; - mPermissionsGranted = Utils.checkPermissions(this, PERMISSIONS); + mPermissionsGranted = PermissionsUtils.isExternalStorageGranted(); if (!mPermissionsGranted) { // TODO requestPermissions after Permissions dialog - ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSIONS); + PermissionsUtils.requestPermissions(this, REQUEST_PERMISSIONS); return; } @@ -180,16 +167,8 @@ public class SplashActivity extends AppCompatActivity if (grantResults.length == 0) return; - for (int i = 0; i < permissions.length; i++) - { - int result = grantResults[i]; - String permission = permissions[i]; - if (permission.equals(WRITE_EXTERNAL_STORAGE) && result == PERMISSION_GRANTED) - { - mPermissionsGranted = true; - break; - } - } + mPermissionsGranted = PermissionsUtils.computePermissionsResult(permissions, grantResults) + .isExternalStorageGranted(); if (mPermissionsGranted) { diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java index e06cfc6b35..4f20e3cec2 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragment.java @@ -2,9 +2,7 @@ package com.mapswithme.maps.base; import android.content.Context; import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; -import com.mapswithme.maps.MwmApplication; import com.mapswithme.util.Utils; public class BaseMwmFragment extends Fragment @@ -14,7 +12,7 @@ public class BaseMwmFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - Utils.detachFragmentIfInitializing(context, this); + Utils.detachFragmentIfCoreNotInitialized(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java index 4ce718c2ec..f7b5b456e4 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmFragmentActivity.java @@ -18,6 +18,7 @@ import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.SplashActivity; import com.mapswithme.util.Config; +import com.mapswithme.util.PermissionsUtils; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; @@ -53,7 +54,7 @@ public class BaseMwmFragmentActivity extends AppCompatActivity protected void onCreate(@Nullable Bundle savedInstanceState) { if (!MwmApplication.get().isPlatformInitialized() - || !Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) + || !PermissionsUtils.isExternalStorageGranted()) { super.onCreate(savedInstanceState); goToSplashScreen(); @@ -162,7 +163,7 @@ public class BaseMwmFragmentActivity extends AppCompatActivity protected void onResume() { super.onResume(); - if (!Utils.checkPermissions(this, SplashActivity.PERMISSIONS)) + if (!PermissionsUtils.isExternalStorageGranted()) { goToSplashScreen(); return; diff --git a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java index 91fcf4f00b..2afc9b5607 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmListFragment.java @@ -2,13 +2,10 @@ package com.mapswithme.maps.base; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; -import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; @@ -22,7 +19,7 @@ public abstract class BaseMwmListFragment extends ListFragment public void onAttach(Context context) { super.onAttach(context); - Utils.detachFragmentIfInitializing(context, this); + Utils.detachFragmentIfCoreNotInitialized(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index b32a80a710..d4f7494d69 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -7,7 +7,6 @@ import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; @@ -15,7 +14,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.widget.PlaceholderView; import com.mapswithme.util.UiUtils; @@ -45,7 +43,7 @@ public abstract class BaseMwmRecyclerFragment extends Fragment public void onAttach(Context context) { super.onAttach(context); - Utils.detachFragmentIfInitializing(context, this); + Utils.detachFragmentIfCoreNotInitialized(context, this); } @Override diff --git a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java index e668c89ec8..6859d592dd 100644 --- a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java +++ b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java @@ -9,8 +9,6 @@ import android.support.annotation.Nullable; import com.mapswithme.maps.MwmApplication; import com.mapswithme.util.LocationUtils; -import com.mapswithme.util.log.Logger; -import com.mapswithme.util.log.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -21,7 +19,6 @@ class AndroidNativeProvider extends BaseLocationProvider private final static String TAG = AndroidNativeProvider.class.getSimpleName(); private final static String[] TRUSTED_PROVIDERS = { LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER }; - private final static Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.LOCATION); @NonNull private final LocationManager mLocationManager; @@ -34,6 +31,8 @@ class AndroidNativeProvider extends BaseLocationProvider mLocationManager = (LocationManager) MwmApplication.get().getSystemService(Context.LOCATION_SERVICE); } + @SuppressWarnings("MissingPermission") + // A permission is checked externally @Override protected void start() { @@ -55,18 +54,8 @@ class AndroidNativeProvider extends BaseLocationProvider long interval = LocationHelper.INSTANCE.getInterval(); LOGGER.d(TAG, "Request Android native provider '" + provider + "' to get locations at this interval = " + interval + " ms"); + mLocationManager.requestLocationUpdates(provider, interval, 0, listener); mListeners.add(listener); - try - { - mLocationManager.requestLocationUpdates(provider, interval, 0, listener); - } - catch (SecurityException e) - { - LOGGER.e(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", - e); - setActive(false); - return; - } } LocationHelper.INSTANCE.startSensors(); @@ -120,26 +109,20 @@ class AndroidNativeProvider extends BaseLocationProvider expirationMillis); } + @SuppressWarnings("MissingPermission") + // A permission is checked externally @Nullable private static Location findBestNotExpiredLocation(LocationManager manager, List providers, long expirationMillis) { Location res = null; - try + for (final String pr : providers) { - for (final String pr : providers) - { - final Location last = manager.getLastKnownLocation(pr); - if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) - continue; + final Location last = manager.getLastKnownLocation(pr); + if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) + continue; - if (res == null || res.getAccuracy() > last.getAccuracy()) - res = last; - } - } - catch (SecurityException e) - { - LOGGER.e(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", - e); + if (res == null || res.getAccuracy() > last.getAccuracy()) + res = last; } return res; } diff --git a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java index 768f250d0a..ee8a5cad3e 100644 --- a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java +++ b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java @@ -123,24 +123,19 @@ class GoogleFusedLocationProvider extends BaseLocationProvider LocationHelper.INSTANCE.start(); } + @SuppressWarnings("MissingPermission") + // A permission is checked externally private void requestLocationUpdates() { if (!mGoogleApiClient.isConnected()) return; - try - { - LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mListener); - LocationHelper.INSTANCE.startSensors(); - Location last = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); - if (last != null) - mListener.onLocationChanged(last); - } - catch (SecurityException e) - { - e.printStackTrace(); - stop(); - } + + LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mListener); + LocationHelper.INSTANCE.startSensors(); + Location last = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); + if (last != null) + mListener.onLocationChanged(last); } @Override diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index 9490d433d1..a8cb82556b 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -16,9 +16,11 @@ import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.util.Config; import com.mapswithme.util.Listeners; import com.mapswithme.util.LocationUtils; +import com.mapswithme.util.PermissionsUtils; import com.mapswithme.util.Utils; import com.mapswithme.util.log.Logger; import com.mapswithme.util.log.LoggerFactory; +import com.mapswithme.util.permissions.PermissionsResult; public enum LocationHelper { @@ -476,6 +478,12 @@ public enum LocationHelper mLogger.d(TAG, "startInternal(), current provider is '" + mLocationProvider + "' , my position mode = " + LocationState.nameOf(getMyPositionMode()) + ", mInFirstRun = " + mInFirstRun); + if (!PermissionsUtils.isLocationGranted()) + { + mLogger.w(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", + new Throwable()); + return; + } checkProviderInitialization(); //noinspection ConstantConditions mLocationProvider.start(); diff --git a/android/src/com/mapswithme/maps/settings/AboutFragment.java b/android/src/com/mapswithme/maps/settings/AboutFragment.java index 0e4d397d93..ce7390bdb1 100644 --- a/android/src/com/mapswithme/maps/settings/AboutFragment.java +++ b/android/src/com/mapswithme/maps/settings/AboutFragment.java @@ -42,13 +42,6 @@ public class AboutFragment extends BaseSettingsFragment return R.layout.about; } -// @Override -// protected BaseShadowController createShadowController() -// { -// clearPaddings(); -// return new ScrollViewShadowController((ObservableScrollView) mFrame.findViewById(R.id.content_frame)); -// } - @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -127,7 +120,6 @@ public class AboutFragment extends BaseSettingsFragment case R.id.copyright: Statistics.INSTANCE.trackEvent(Statistics.EventName.Settings.COPYRIGHT); AlohaHelper.logClick(AlohaHelper.Settings.COPYRIGHT); -// getSettingsActivity().switchToFragment(CopyrightFragment.class, R.string.copyright); getSettingsActivity().replaceFragment(CopyrightFragment.class, getString(R.string.copyright), null); break; diff --git a/android/src/com/mapswithme/maps/settings/SettingsActivity.java b/android/src/com/mapswithme/maps/settings/SettingsActivity.java index bd55a918b6..5506031c87 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsActivity.java +++ b/android/src/com/mapswithme/maps/settings/SettingsActivity.java @@ -7,6 +7,7 @@ import android.support.v4.app.Fragment; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; import android.support.v7.preference.PreferenceScreen; +import android.support.v7.widget.Toolbar; import android.text.TextUtils; import com.mapswithme.maps.R; @@ -61,7 +62,8 @@ public class SettingsActivity extends BaseToolbarActivity { final int resId = getFragmentContentResId(); if (resId <= 0 || findViewById(resId) == null) - throw new IllegalStateException("Fragment can't be added, since getFragmentContentResId() isn't implemented or returns wrong resourceId."); + throw new IllegalStateException("Fragment can't be added, since getFragmentContentResId() " + + "isn't implemented or returns wrong resourceId."); String name = fragmentClass.getName(); final Fragment fragment = Fragment.instantiate(this, name, args); @@ -71,10 +73,14 @@ public class SettingsActivity extends BaseToolbarActivity .commitAllowingStateLoss(); getSupportFragmentManager().executePendingTransactions(); - if(title != null) + if (title != null) { - mLastTitle = getToolbar().getTitle().toString(); - getToolbar().setTitle(title); + Toolbar toolbar = getToolbar(); + if (toolbar != null && toolbar.getTitle() != null) + { + mLastTitle = toolbar.getTitle().toString(); + toolbar.setTitle(title); + } } } diff --git a/android/src/com/mapswithme/util/PermissionsUtils.java b/android/src/com/mapswithme/util/PermissionsUtils.java new file mode 100644 index 0000000000..220da3cee7 --- /dev/null +++ b/android/src/com/mapswithme/util/PermissionsUtils.java @@ -0,0 +1,94 @@ +package com.mapswithme.util; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; + +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.util.permissions.PermissionsResult; + +import java.util.HashMap; +import java.util.Map; + +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.GET_ACCOUNTS; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; + +public final class PermissionsUtils +{ + private static final String[] PERMISSIONS = new String[] + { + WRITE_EXTERNAL_STORAGE, + ACCESS_COARSE_LOCATION, + ACCESS_FINE_LOCATION, + GET_ACCOUNTS + }; + + private PermissionsUtils() {} + + @NonNull + public static PermissionsResult computePermissionsResult(@NonNull String[] permissions, + @NonNull int[] grantResults) + { + Map result = new HashMap<>(); + for (int i = 0; i < permissions.length; i++) + { + result.put(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED); + } + + return getPermissionsResult(result); + } + + public static boolean isLocationGranted() + { + return checkPermissions().isLocationGranted(); + } + + public static boolean isExternalStorageGranted() + { + return checkPermissions().isExternalStorageGranted(); + } + + public static boolean isGetAccountsGranted() + { + return checkPermissions().isGetAccountsGranted(); + } + + @NonNull + private static PermissionsResult checkPermissions() + { + Context context = MwmApplication.get().getApplicationContext(); + Map result = new HashMap<>(); + for (String permission: PERMISSIONS) + { + result.put(permission, + Build.VERSION.SDK_INT < Build.VERSION_CODES.M + || context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED); + } + + return getPermissionsResult(result); + } + + @NonNull + private static PermissionsResult getPermissionsResult(@NonNull Map result) + { + boolean externalStorageGranted = result.containsKey(WRITE_EXTERNAL_STORAGE) + ? result.get(WRITE_EXTERNAL_STORAGE) : false; + boolean locationGranted = (result.containsKey(ACCESS_COARSE_LOCATION) + ? result.get(ACCESS_COARSE_LOCATION) : false) + || (result.containsKey(ACCESS_FINE_LOCATION) + ? result.get(ACCESS_FINE_LOCATION) : false); + boolean getAccountsGranted = result.containsKey(GET_ACCOUNTS) + ? result.get(GET_ACCOUNTS) : false; + return new PermissionsResult(externalStorageGranted, locationGranted, getAccountsGranted); + } + + public static void requestPermissions(@NonNull Activity activity, int code) + { + ActivityCompat.requestPermissions(activity, PERMISSIONS, code); + } +} diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index c1b908355a..8ad2d60fcb 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -14,7 +14,6 @@ import android.support.annotation.DimenRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; -import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.app.NavUtils; import android.support.v7.app.AlertDialog; @@ -43,8 +42,6 @@ import java.util.Currency; import java.util.Locale; import java.util.Map; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; - public class Utils { private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); @@ -415,34 +412,8 @@ public class Utils } } - public static boolean checkPermissions(@NonNull Activity activity, @NonNull String[] permissions) - { - if (Build.VERSION.SDK_INT >= 23) - { - boolean isGranted = false; - for (String permission: permissions) - { - isGranted = activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; - if (!isGranted) - break; - } - return isGranted; - } - - //permission is automatically granted on sdk<23 upon installation - return true; - } - - public static boolean isWriteExternalGranted(@NonNull Activity activity) - { - if (Build.VERSION.SDK_INT >= 23) - return activity.checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; - - return true; - } - - public static void detachFragmentIfInitializing(@NonNull Context context, - @NonNull Fragment fragment) + public static void detachFragmentIfCoreNotInitialized(@NonNull Context context, + @NonNull Fragment fragment) { if (context instanceof AppCompatActivity && !MwmApplication.get().isPlatformInitialized()) { diff --git a/android/src/com/mapswithme/util/permissions/PermissionsResult.java b/android/src/com/mapswithme/util/permissions/PermissionsResult.java new file mode 100644 index 0000000000..8f214405b6 --- /dev/null +++ b/android/src/com/mapswithme/util/permissions/PermissionsResult.java @@ -0,0 +1,31 @@ +package com.mapswithme.util.permissions; + +public final class PermissionsResult +{ + private final boolean mExternalStorageGranted; + private final boolean mLocationGranted; + private final boolean mGetAccountsGranted; + + public PermissionsResult(boolean externalStorageGranted, boolean locationGranted, + boolean getAccountsGranted) + { + mExternalStorageGranted = externalStorageGranted; + mLocationGranted = locationGranted; + mGetAccountsGranted = getAccountsGranted; + } + + public boolean isExternalStorageGranted() + { + return mExternalStorageGranted; + } + + public boolean isLocationGranted() + { + return mLocationGranted; + } + + public boolean isGetAccountsGranted() + { + return mGetAccountsGranted; + } +} From 5dc2affbc46174b404e035e8da309b69f9aee8db Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 13 Apr 2017 14:42:18 +0400 Subject: [PATCH 10/16] [android] fix SecurityException --- .../maps/location/AndroidNativeProvider.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java index 6859d592dd..07ad08f83f 100644 --- a/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java +++ b/android/src/com/mapswithme/maps/location/AndroidNativeProvider.java @@ -109,20 +109,26 @@ class AndroidNativeProvider extends BaseLocationProvider expirationMillis); } - @SuppressWarnings("MissingPermission") - // A permission is checked externally @Nullable private static Location findBestNotExpiredLocation(LocationManager manager, List providers, long expirationMillis) { Location res = null; - for (final String pr : providers) + try { - final Location last = manager.getLastKnownLocation(pr); - if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) - continue; + for (final String pr : providers) + { + final Location last = manager.getLastKnownLocation(pr); + if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis)) + continue; - if (res == null || res.getAccuracy() > last.getAccuracy()) - res = last; + if (res == null || res.getAccuracy() > last.getAccuracy()) + res = last; + } + } + catch (SecurityException e) + { + LOGGER.e(TAG, "Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted", + e); } return res; } From e14e7ccae0d07f10070e004573e1e0f8e6b8f279 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 13 Apr 2017 15:17:08 +0400 Subject: [PATCH 11/16] [android] fix my position button mode when location permission is not granted --- .../src/com/mapswithme/maps/location/LocationHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index a8cb82556b..7f5a4715c3 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -132,6 +132,12 @@ public enum LocationHelper @Override public void onMyPositionModeChanged(int newMode) { + if (!PermissionsUtils.isLocationGranted()) + { + notifyMyPositionModeChanged(LocationState.NOT_FOLLOW_NO_POSITION); + return; + } + notifyMyPositionModeChanged(newMode); mLogger.d(TAG, "onMyPositionModeChanged mode = " + LocationState.nameOf(newMode)); From 98030df14e012a0c90cee591960899ac500142d3 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 19 Apr 2017 16:05:16 +0400 Subject: [PATCH 12/16] [android] Revert 8c2d75f commit --- .../src/com/mapswithme/maps/location/LocationHelper.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index 7f5a4715c3..a8cb82556b 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -132,12 +132,6 @@ public enum LocationHelper @Override public void onMyPositionModeChanged(int newMode) { - if (!PermissionsUtils.isLocationGranted()) - { - notifyMyPositionModeChanged(LocationState.NOT_FOLLOW_NO_POSITION); - return; - } - notifyMyPositionModeChanged(newMode); mLogger.d(TAG, "onMyPositionModeChanged mode = " + LocationState.nameOf(newMode)); From 7eac281c587bd18e4a56fddb68f590ca54286e26 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 19 Apr 2017 09:56:11 +0400 Subject: [PATCH 13/16] [strings] Added tts link string --- strings.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/strings.txt b/strings.txt index 2591974923..1ac9497118 100644 --- a/strings.txt +++ b/strings.txt @@ -5122,6 +5122,10 @@ en = For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. ru = For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. + [prefs_languages_information_off_link] + en = For more information please check the guide. + ru = Для получения дополнительной информации, пожалуйста, ознакомьтесь с руководством. + [prefs_group_map] comment = Settings screen: "Map" category title tags = android From a6639670e504d92a24f84c366aff1ffbedb77c67 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 19 Apr 2017 09:58:19 +0400 Subject: [PATCH 14/16] [strings] Generated strings --- android/res/values-ru/strings.xml | 1 + android/res/values/strings.xml | 1 + .../ar.lproj/Localizable.strings | 2 ++ .../cs.lproj/Localizable.strings | 2 ++ .../da.lproj/Localizable.strings | 2 ++ .../de.lproj/Localizable.strings | 2 ++ .../el.lproj/Localizable.strings | 18 ++++++++++++++++++ .../en-GB.lproj/Localizable.strings | 2 ++ .../en.lproj/Localizable.strings | 2 ++ .../es.lproj/Localizable.strings | 2 ++ .../fi.lproj/Localizable.strings | 2 ++ .../fr.lproj/Localizable.strings | 2 ++ .../hu.lproj/Localizable.strings | 2 ++ .../id.lproj/Localizable.strings | 2 ++ .../it.lproj/Localizable.strings | 2 ++ .../ja.lproj/Localizable.strings | 2 ++ .../ko.lproj/Localizable.strings | 2 ++ .../nb.lproj/Localizable.strings | 2 ++ .../nl.lproj/Localizable.strings | 2 ++ .../pl.lproj/Localizable.strings | 2 ++ .../pt.lproj/Localizable.strings | 2 ++ .../ro.lproj/Localizable.strings | 2 ++ .../ru.lproj/Localizable.strings | 2 ++ .../sk.lproj/Localizable.strings | 2 ++ .../sv.lproj/Localizable.strings | 2 ++ .../th.lproj/Localizable.strings | 2 ++ .../tr.lproj/Localizable.strings | 2 ++ .../uk.lproj/Localizable.strings | 2 ++ .../vi.lproj/Localizable.strings | 2 ++ .../zh-Hans.lproj/Localizable.strings | 2 ++ .../zh-Hant.lproj/Localizable.strings | 2 ++ 31 files changed, 76 insertions(+) diff --git a/android/res/values-ru/strings.xml b/android/res/values-ru/strings.xml index 28ca8dd1d7..91ad56aee5 100644 --- a/android/res/values-ru/strings.xml +++ b/android/res/values-ru/strings.xml @@ -308,6 +308,7 @@ Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device\'s settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. + Для получения дополнительной информации, пожалуйста, ознакомьтесь с руководством. Карта diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 00d393313b..0aeecb9ee6 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -310,6 +310,7 @@ Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.). For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device\'s settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine. + For more information please check the guide. Map diff --git a/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings index 3a1ef44bf6..583a2e4e23 100644 --- a/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ar.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "الخريطة"; diff --git a/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings index 014019656a..d9b324eea0 100644 --- a/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/cs.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; diff --git a/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings index a91c7f5036..a5df4882e7 100644 --- a/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/da.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kort"; diff --git a/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings index a127f751cf..3b0f1772ac 100644 --- a/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/de.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Karte"; diff --git a/iphone/Maps/LocalizedStrings/el.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/el.lproj/Localizable.strings index e449096763..c8e10be72e 100644 --- a/iphone/Maps/LocalizedStrings/el.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/el.lproj/Localizable.strings @@ -467,6 +467,20 @@ "menu_search" = "Search"; +/* Settings general group in settings screen */ +"prefs_group_general" = "General settings"; + +/* Settings information group in settings screen */ +"prefs_group_information" = "Information"; + +/* Settings languages information in voice instructions screen */ +"prefs_languages_information" = "Написать о том когда вообще ничего не установлено\n\nНа Android голосовые подсказки доступны на 24 языках: русский, английский, арабский, венгерский, голландский, греческий, датский, индонезийский, испанский, итальянский, китайский (традиционный и упрощенный), корейский, немецкий, польский, португальский, румынский, тайский, турецкий, финский, французский, хинди, чешский, японский.\n\nВозможно, для некоторых языков вам необходимо будет установить сторонний синтезатор речи и дополнительный языковой пакет из магазина приложений (Google Play Маркет, Samsung Apps и др.)."; + +/* Settings languages off information in voice instructions screen */ +"prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; + +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Χάρτης"; @@ -1870,3 +1884,7 @@ "search_hotel_filter_resort" = "Θέρετρο"; "search_hotel_filter_motel" = "Motel"; + +"on" = "On"; + +"off" = "Off"; diff --git a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings index 755b96c205..a7697d3fdb 100644 --- a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Map"; diff --git a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings index d12acbe404..29b9bdd50c 100644 --- a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Map"; diff --git a/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings index a0e5081a9a..c0bc9f1fcd 100644 --- a/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/es.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; diff --git a/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings index 8c6a43368a..531fbdcb0c 100644 --- a/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/fi.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kartta"; diff --git a/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings index a9e081311a..4c298b9f08 100644 --- a/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/fr.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Carte"; diff --git a/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings index 8d2c8f58d5..159cd58625 100644 --- a/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/hu.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Térkép"; diff --git a/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings index f13a26539d..a6cf572cfd 100644 --- a/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/id.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Peta"; diff --git a/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings index b070b8851d..3aee7c3edf 100644 --- a/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/it.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mappa"; diff --git a/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings index 568735b60b..9ad0738472 100644 --- a/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ja.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地図"; diff --git a/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings index 117c84dcaf..a5a9ff2e28 100644 --- a/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ko.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "지도"; diff --git a/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings index bde13cb3e1..9af20c5592 100644 --- a/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/nb.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kart"; diff --git a/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings index 963a26c42b..a8ccb65556 100644 --- a/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/nl.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Kaart"; diff --git a/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings index 5c8aa19950..51b73087b0 100644 --- a/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/pl.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; diff --git a/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings index 3e24d2483c..326235e93b 100644 --- a/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/pt.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; diff --git a/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings index 11a461f482..3defb9a6db 100644 --- a/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ro.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Hartă"; diff --git a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings index dac5301ed3..29468141db 100644 --- a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "Для получения дополнительной информации, пожалуйста, ознакомьтесь с руководством."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Карта"; diff --git a/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings index 138799e784..e7e88e3d5a 100644 --- a/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/sk.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Mapa"; diff --git a/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings index 8f9f42f50e..05950ed02d 100644 --- a/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/sv.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Karta"; diff --git a/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings index d120d388ca..68a6375ba3 100644 --- a/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/th.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "แผนที่"; diff --git a/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings index 15cd2d50fb..99cafe2521 100644 --- a/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/tr.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Harita"; diff --git a/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings index 7292a3f6a4..4d8258a1e9 100644 --- a/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/uk.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Карта"; diff --git a/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings index b2c263a551..17819c9560 100644 --- a/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/vi.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "Bản đồ"; diff --git a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings index bffb9135a1..9b9ec4baea 100644 --- a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地图"; diff --git a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings index ce1a3aadff..e2fe2b32be 100644 --- a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/Localizable.strings @@ -479,6 +479,8 @@ /* Settings languages off information in voice instructions screen */ "prefs_languages_information_off" = "For some languages, you will need to install a other speech synthesizer or an additional language pack from the app store (Google Play Market, Samsung Apps).\n\nOpen your device's settings –> Language and input –> Speech –> Text to speech output.*\n\nHere you can manage settings for speech synthesis (for example, download language pack for offline use) and select another text-to-speech engine."; +"prefs_languages_information_off_link" = "For more information please check the guide."; + /* Settings screen: "Map" category title */ "prefs_group_map" = "地圖"; From 4c11e311481c9318d5a45d65682bf5f97145369b Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 19 Apr 2017 11:26:05 +0400 Subject: [PATCH 15/16] [android] Added tts instruction link --- android/res/values/donottranslate.xml | 2 + android/res/xml/prefs_main.xml | 7 +++ .../maps/settings/SettingsPrefsFragment.java | 53 ++++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml index a41dcd4d10..27348cf921 100644 --- a/android/res/values/donottranslate.xml +++ b/android/res/values/donottranslate.xml @@ -44,6 +44,8 @@ TtsEnabled TtsLanguage TtsInfo + TtsInfoLink + https://support.maps.me/hc/en-us/articles/208628985-How-can-I-check-TTS-settings-on-my-Android-device- AutoDownloadMap 3D 3DBuildings diff --git a/android/res/xml/prefs_main.xml b/android/res/xml/prefs_main.xml index 2c7cfbb4f3..3fa0b16d07 100644 --- a/android/res/xml/prefs_main.xml +++ b/android/res/xml/prefs_main.xml @@ -137,6 +137,13 @@ android:enabled="false" android:summary="@string/prefs_languages_information" android:order="3"/> + diff --git a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java index bdb8bcf264..c2a966cfc6 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java +++ b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java @@ -3,15 +3,21 @@ package com.mapswithme.maps.settings; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.speech.tts.TextToSpeech; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.TwoStatePreference; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; @@ -30,6 +36,7 @@ import com.mapswithme.maps.sound.TtsPlayer; import com.mapswithme.util.Config; import com.mapswithme.util.NetworkPolicy; import com.mapswithme.util.ThemeSwitcher; +import com.mapswithme.util.UiUtils; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.log.LoggerFactory; import com.mapswithme.util.statistics.AlohaHelper; @@ -43,6 +50,10 @@ import java.util.Map; public class SettingsPrefsFragment extends BaseXmlSettingsFragment { private static final int REQUEST_INSTALL_DATA = 1; + private static final String TTS_SCREEN_KEY = MwmApplication.get() + .getString(R.string.pref_tts_screen); + private static final String TTS_INFO_LINK = MwmApplication.get() + .getString(R.string.tts_info_link); @NonNull private final StoragePathManager mPathManager = new StoragePathManager(); @@ -55,6 +66,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private ListPreference mPrefLanguages; @Nullable private Preference mLangInfo; + @Nullable + private Preference mLangInfoLink; + private PreferenceScreen mPreferenceScreen; @NonNull private final Map mLanguages = new HashMap<>(); @@ -97,6 +111,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment mPrefLanguages.setEnabled(false); if (mLangInfo != null) mLangInfo.setSummary(R.string.prefs_languages_information_off); + if (mLangInfoLink != null && isOnTtsScreen()) + getPreferenceScreen().addPreference(mLangInfoLink); if (root != null) root.setSummary(R.string.off); @@ -112,6 +128,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment root.setSummary(R.string.on); if (mPrefEnabled != null) mPrefEnabled.setTitle(R.string.on); + if (mLangInfoLink != null) + getPreferenceScreen().removePreference(mLangInfoLink); if (mCurrentLanguage != null && mCurrentLanguage.downloaded) { @@ -166,7 +184,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private void updateTts() { - if (mPrefEnabled == null || mPrefLanguages == null || mLangInfo == null) + if (mPrefEnabled == null || mPrefLanguages == null || mLangInfo == null || mLangInfoLink == null) return; enableListeners(false); @@ -186,6 +204,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment mPrefLanguages.setEnabled(false); mPrefLanguages.setSummary(null); mLangInfo.setSummary(R.string.prefs_languages_information_off); + if (isOnTtsScreen()) + getPreferenceScreen().addPreference(mLangInfoLink); if (root != null) root.setSummary(R.string.off); @@ -199,6 +219,10 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment mPrefEnabled.setTitle(enabled ? R.string.on : R.string.off); mLangInfo.setSummary(enabled ? R.string.prefs_languages_information : R.string.prefs_languages_information_off); + if (enabled) + getPreferenceScreen().removePreference(mLangInfoLink); + else if (isOnTtsScreen()) + getPreferenceScreen().addPreference(mLangInfoLink); if (root != null) root.setSummary(enabled ? R.string.on : R.string.off); @@ -227,6 +251,11 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment enableListeners(true); } + private boolean isOnTtsScreen() + { + return mPreferenceScreen.getKey() != null && mPreferenceScreen.getKey().equals(TTS_SCREEN_KEY); + } + @Override public Fragment getCallbackFragment() { @@ -244,10 +273,32 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment { super.onCreate(savedInstanceState); + mPreferenceScreen = getPreferenceScreen(); mStoragePref = findPreference(getString(R.string.pref_storage)); mPrefEnabled = (TwoStatePreference) findPreference(getString(R.string.pref_tts_enabled)); mPrefLanguages = (ListPreference) findPreference(getString(R.string.pref_tts_language)); mLangInfo = findPreference(getString(R.string.pref_tts_info)); + mLangInfoLink = findPreference(getString(R.string.pref_tts_info_link)); + if (mLangInfoLink != null) + { + Spannable link = new SpannableString(getString(R.string.prefs_languages_information_off_link)); + link.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), + UiUtils.getStyledResourceId(getContext(), R.attr.colorAccent))), + 0, link.length(), 0); + mLangInfoLink.setSummary(link); + mLangInfoLink.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() + { + @Override + public boolean onPreferenceClick(Preference preference) + { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(TTS_INFO_LINK)); + getContext().startActivity(intent); + return false; + } + }); + mPreferenceScreen.removePreference(mLangInfoLink); + } updateStoragePrefs(); initStoragePrefCallbacks(); initMeasureUnitsPrefsCallbacks(); From 052e3fa8e4fe536dbe7b1a25ac67c9a17ff731e0 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 19 Apr 2017 16:13:25 +0400 Subject: [PATCH 16/16] [android] Review fixes --- .../maps/settings/SettingsPrefsFragment.java | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java index c2a966cfc6..0b67d7d03d 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java +++ b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java @@ -279,26 +279,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment mPrefLanguages = (ListPreference) findPreference(getString(R.string.pref_tts_language)); mLangInfo = findPreference(getString(R.string.pref_tts_info)); mLangInfoLink = findPreference(getString(R.string.pref_tts_info_link)); - if (mLangInfoLink != null) - { - Spannable link = new SpannableString(getString(R.string.prefs_languages_information_off_link)); - link.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), - UiUtils.getStyledResourceId(getContext(), R.attr.colorAccent))), - 0, link.length(), 0); - mLangInfoLink.setSummary(link); - mLangInfoLink.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() - { - @Override - public boolean onPreferenceClick(Preference preference) - { - final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(TTS_INFO_LINK)); - getContext().startActivity(intent); - return false; - } - }); - mPreferenceScreen.removePreference(mLangInfoLink); - } + initLangInfoLink(); updateStoragePrefs(); initStoragePrefCallbacks(); initMeasureUnitsPrefsCallbacks(); @@ -369,6 +350,30 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment return super.onPreferenceTreeClick(preference); } + private void initLangInfoLink() + { + if (mLangInfoLink != null) + { + Spannable link = new SpannableString(getString(R.string.prefs_languages_information_off_link)); + link.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), + UiUtils.getStyledResourceId(getContext(), R.attr.colorAccent))), + 0, link.length(), 0); + mLangInfoLink.setSummary(link); + mLangInfoLink.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() + { + @Override + public boolean onPreferenceClick(Preference preference) + { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(TTS_INFO_LINK)); + getContext().startActivity(intent); + return false; + } + }); + mPreferenceScreen.removePreference(mLangInfoLink); + } + } + private void initSimplifiedTrafficColorsPrefsCallbacks() { final TwoStatePreference prefSimplifiedColors = (TwoStatePreference)findPreference(