diff --git a/android/jni/com/mapswithme/maps/editor/OsmOAuth.cpp b/android/jni/com/mapswithme/maps/editor/OsmOAuth.cpp index a5e41087f9..e856573940 100644 --- a/android/jni/com/mapswithme/maps/editor/OsmOAuth.cpp +++ b/android/jni/com/mapswithme/maps/editor/OsmOAuth.cpp @@ -9,7 +9,6 @@ #include "editor/osm_auth.hpp" #include "editor/server_api.hpp" -#include "editor/user_stats.hpp" namespace { @@ -33,6 +32,21 @@ jobjectArray ToStringArray(JNIEnv * env, OsmOAuth::UrlRequestToken const & uks) env->SetObjectArrayElement(resultArray, 2, ToJavaString(env, uks.second.second)); return resultArray; } + +bool LoadOsmUserPreferences(KeySecret const & keySecret, UserPreferences & outPrefs) +{ + try + { + ServerApi06 const api(OsmOAuth::ServerAuth(keySecret)); + outPrefs = api.GetUserPreferences(); + return true; + } + catch (std::exception const & ex) + { + LOG(LWARNING, ("Can't load user preferences from server: ", ex.what())); + } + return false; +} } // namespace extern "C" @@ -90,47 +104,22 @@ Java_com_mapswithme_maps_editor_OsmOAuth_nativeGetGoogleAuthUrl(JNIEnv * env, jc } JNIEXPORT jstring JNICALL -Java_com_mapswithme_maps_editor_OsmOAuth_nativeGetOsmUsername(JNIEnv * env, jclass clazz, jstring token, jstring secret) +Java_com_mapswithme_maps_editor_OsmOAuth_nativeGetOsmUsername(JNIEnv * env, jclass, jstring token, jstring secret) { - try - { - KeySecret keySecret(jni::ToNativeString(env, token), jni::ToNativeString(env, secret)); - ServerApi06 const api(OsmOAuth::ServerAuth(keySecret)); - return jni::ToJavaString(env, api.GetUserPreferences().m_displayName); - } - catch (std::exception const & ex) - { - LOG(LWARNING, ("Can't load user preferences from server: ", ex.what())); - return nullptr; - } + const KeySecret keySecret(jni::ToNativeString(env, token), jni::ToNativeString(env, secret)); + UserPreferences prefs; + if (LoadOsmUserPreferences(keySecret, prefs)) + return jni::ToJavaString(env, prefs.m_displayName); + return nullptr; } -JNIEXPORT void JNICALL -Java_com_mapswithme_maps_editor_OsmOAuth_nativeUpdateOsmUserStats(JNIEnv * env, jclass clazz, jstring jUsername, jboolean forceUpdate) +JNIEXPORT jint JNICALL +Java_com_mapswithme_maps_editor_OsmOAuth_nativeGetOsmChangesetsCount(JNIEnv * env, jclass, jstring token, jstring secret) { - static jclass const statsClazz = jni::GetGlobalClassRef(env, "com/mapswithme/maps/editor/data/UserStats"); - static jmethodID const statsCtor = jni::GetConstructorID(env, statsClazz, "(IILjava/lang/String;J)V"); - static jclass const osmAuthClazz = static_cast(env->NewGlobalRef(clazz)); - // static void onUserStatsUpdated(UserStats stats) - static jmethodID const listenerId = jni::GetStaticMethodID(env, osmAuthClazz, "onUserStatsUpdated", "(Lcom/mapswithme/maps/editor/data/UserStats;)V"); - - std::string const username = jni::ToNativeString(env, jUsername); - auto const policy = forceUpdate ? editor::UserStatsLoader::UpdatePolicy::Force - : editor::UserStatsLoader::UpdatePolicy::Lazy; - g_framework->NativeFramework()->UpdateUserStats(username, policy, [username]() - { - editor::UserStats const & userStats = g_framework->NativeFramework()->GetUserStats(username); - if (!userStats.IsValid()) - return; - int32_t count, rank; - std::string levelUp; - userStats.GetChangesCount(count); - userStats.GetRank(rank); - userStats.GetLevelUpRequiredFeat(levelUp); - JNIEnv * env = jni::GetEnv(); - env->CallStaticVoidMethod(osmAuthClazz, listenerId, - env->NewObject(statsClazz, statsCtor, count, rank, jni::ToJavaString(env, levelUp), - base::TimeTToSecondsSinceEpoch(userStats.GetLastUpdate()))); - }); + const KeySecret keySecret(jni::ToNativeString(env, token), jni::ToNativeString(env, secret)); + UserPreferences prefs; + if (LoadOsmUserPreferences(keySecret, prefs)) + return prefs.m_changesets; + return -1; } } // extern "C" diff --git a/android/res/layout/fragment_auth_editor.xml b/android/res/layout/fragment_auth_editor.xml index 31c08db33e..4c9551e8b9 100644 --- a/android/res/layout/fragment_auth_editor.xml +++ b/android/res/layout/fragment_auth_editor.xml @@ -70,15 +70,6 @@ android:textColor="@color/white_primary" android:textStyle="bold"/> - @@ -147,61 +138,19 @@ android:layout_weight="1" android:padding="@dimen/margin_base"> - - - - - - - - - - - - - - + android:layout_alignParentTop="true" + android:background="?clickableBackground" + android:gravity="center" + android:paddingBottom="@dimen/margin_half" + android:paddingTop="@dimen/margin_half" + android:text="@string/history" + android:textAppearance="@style/MwmTextAppearance.Body4" + android:textColor="?colorAccent" + android:textSize="@dimen/text_size_body_1"/> sListener; - - public static void setUserStatsListener(OnUserStatsChanged listener) - { - sListener = new WeakReference<>(listener); - } - - // Called from native OsmOAuth.cpp. - @SuppressWarnings("unused") - public static void onUserStatsUpdated(UserStats stats) - { - if (sListener == null) - return; - - OnUserStatsChanged listener = sListener.get(); - if (listener != null) - listener.onStatsChange(stats); - } + private static final String PREF_OSM_CHANGESETS_COUNT = "OsmChangesetsCount"; public static final String URL_PARAM_VERIFIER = "oauth_verifier"; @@ -149,5 +126,22 @@ public final class OsmOAuth @Nullable public static native String nativeGetOsmUsername(String token, String secret); - public static native void nativeUpdateOsmUserStats(String username, boolean forceUpdate); + /** + * @return < 0 if failed to get changesets count. + */ + @WorkerThread + private static native int nativeGetOsmChangesetsCount(String token, String secret); + + @WorkerThread + public static int getOsmChangesetsCount(@NonNull Context context) { + final String token = getAuthToken(context); + final String secret = getAuthSecret(context); + final int editsCount = OsmOAuth.nativeGetOsmChangesetsCount(token, secret); + final SharedPreferences prefs = MwmApplication.prefs(context); + if (editsCount < 0) + return prefs.getInt(PREF_OSM_CHANGESETS_COUNT, 0); + + prefs.edit().putInt(PREF_OSM_CHANGESETS_COUNT, editsCount).apply(); + return editsCount; + } } diff --git a/android/src/com/mapswithme/maps/editor/ProfileFragment.java b/android/src/com/mapswithme/maps/editor/ProfileFragment.java index 254613d297..2e3c578039 100644 --- a/android/src/com/mapswithme/maps/editor/ProfileFragment.java +++ b/android/src/com/mapswithme/maps/editor/ProfileFragment.java @@ -3,8 +3,8 @@ package com.mapswithme.maps.editor; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; -import android.text.format.DateUtils; import android.view.View; import android.widget.TextView; @@ -15,25 +15,24 @@ import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import com.cocosw.bottomsheet.BottomSheet; +import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.R; -import com.mapswithme.maps.editor.data.UserStats; import com.mapswithme.util.BottomSheetHelper; import com.mapswithme.util.Constants; import com.mapswithme.util.UiUtils; +import com.mapswithme.util.concurrency.ThreadPool; +import com.mapswithme.util.concurrency.UiThread; import java.util.ArrayList; import java.util.List; -public class ProfileFragment extends AuthFragment implements View.OnClickListener, OsmOAuth.OnUserStatsChanged +public class ProfileFragment extends AuthFragment implements View.OnClickListener { private View mSentBlock; private TextView mEditsSent; - private TextView mEditsSentDate; private View mMore; private View mAuthBlock; private View mRatingBlock; - private TextView mEditorRank; - private TextView mEditorLevelUp; private enum MenuItem { @@ -57,15 +56,6 @@ public class ProfileFragment extends AuthFragment implements View.OnClickListene .create() .show(); } - }, - - REFRESH(R.drawable.ic_update, R.string.refresh) - { - @Override - void invoke(ProfileFragment fragment) - { - OsmOAuth.nativeUpdateOsmUserStats(OsmOAuth.getUsername(fragment.requireContext()), true /* forceUpdate */); - } }; final @DrawableRes int icon; @@ -87,8 +77,6 @@ public class ProfileFragment extends AuthFragment implements View.OnClickListene getToolbarController().setTitle(R.string.profile); initViews(view); refreshViews(); - OsmOAuth.setUserStatsListener(this); - OsmOAuth.nativeUpdateOsmUserStats(OsmOAuth.getUsername(requireContext()), false /* forceUpdate */); } private void initViews(View view) @@ -99,13 +87,10 @@ public class ProfileFragment extends AuthFragment implements View.OnClickListene UiUtils.show(editsBlock); mSentBlock = editsBlock.findViewById(R.id.sent_edits); mEditsSent = mSentBlock.findViewById(R.id.edits_count); - mEditsSentDate = mSentBlock.findViewById(R.id.date_sent); mAuthBlock = view.findViewById(R.id.block_auth); mRatingBlock = view.findViewById(R.id.block_rating); - mEditorRank = mRatingBlock.findViewById(R.id.rating); - // FIXME show when it will be implemented on server -// mEditorLevelUp = mRatingBlock.findViewById(R.id.level_up_feat); view.findViewById(R.id.about_osm).setOnClickListener(this); + view.findViewById(R.id.osm_history).setOnClickListener(this); } private void refreshViews() @@ -114,34 +99,17 @@ public class ProfileFragment extends AuthFragment implements View.OnClickListene { UiUtils.show(mMore, mRatingBlock, mSentBlock); UiUtils.hide(mAuthBlock); + // Update the number of uploaded changesets from OSM. + ThreadPool.getWorker().execute(() -> { + final int count = OsmOAuth.getOsmChangesetsCount(requireContext()); + UiThread.run(() -> mEditsSent.setText(String.valueOf(count))); + }); } else { UiUtils.show(mAuthBlock); UiUtils.hide(mMore, mRatingBlock, mSentBlock); } - - refreshRatings(0, 0, 0, ""); - } - - private void refreshRatings(long uploadedCount, long uploadSeconds, long rank, String levelFeat) - { - String edits, editsDate; - - if (uploadedCount == 0) - { - edits = editsDate = "---"; - } - else - { - edits = String.valueOf(uploadedCount); - editsDate = DateUtils.formatDateTime(getActivity(), uploadSeconds * 1000, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); - } - mEditsSent.setText(edits); - mEditsSentDate.setText(getString(R.string.last_update, editsDate)); - mEditorRank.setText(String.valueOf(rank)); - // FIXME show when it will be implemented on server -// mEditorLevelUp.setText(levelFeat); } @Override @@ -155,25 +123,20 @@ public class ProfileFragment extends AuthFragment implements View.OnClickListene case R.id.about_osm: startActivity(new Intent((Intent.ACTION_VIEW), Uri.parse(Constants.Url.OSM_ABOUT))); break; + case R.id.osm_history: + // Debug builds use dev OSM playground for APIs. + final String url = BuildConfig.DEBUG + ? "https://master.apis.dev.openstreetmap.org/user/%s/history" + : "https://www.openstreetmap.org/user/%s/history"; + startActivity(new Intent((Intent.ACTION_VIEW), + Uri.parse(String.format(url, OsmOAuth.getUsername(requireContext()))))); + break; } } - @Override - public void onStatsChange(final UserStats stats) - { - if (!isAdded()) - return; - - if (stats == null) - refreshRatings(0, 0, 0, ""); - else - refreshRatings(stats.editsCount, stats.uploadTimestampSeconds, stats.editorRank, stats.levelUp); - } - private void showBottomSheet() { List items = new ArrayList<>(); - items.add(MenuItem.REFRESH); items.add(MenuItem.LOGOUT); BottomSheetHelper.Builder bs = BottomSheetHelper.create(getActivity()); diff --git a/android/src/com/mapswithme/maps/editor/data/UserStats.java b/android/src/com/mapswithme/maps/editor/data/UserStats.java deleted file mode 100644 index f91b92cf36..0000000000 --- a/android/src/com/mapswithme/maps/editor/data/UserStats.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.mapswithme.maps.editor.data; - -public class UserStats -{ - public final int editsCount; - public final int editorRank; - public final String levelUp; - public final long uploadTimestampSeconds; - - public UserStats(int editsCount, int editorRank, String levelUp, long seconds) - { - this.editsCount = editsCount; - this.editorRank = editorRank; - this.levelUp = levelUp; - this.uploadTimestampSeconds = seconds; - } -} diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 3d502a887b..dbda6974da 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -31,8 +31,6 @@ set( server_api.hpp ui2oh.cpp ui2oh.hpp - user_stats.cpp - user_stats.hpp xml_feature.cpp xml_feature.hpp yes_no_unknown.hpp diff --git a/editor/editor_tests/CMakeLists.txt b/editor/editor_tests/CMakeLists.txt index 02425e94ca..7590f35db6 100644 --- a/editor/editor_tests/CMakeLists.txt +++ b/editor/editor_tests/CMakeLists.txt @@ -12,7 +12,6 @@ set( osm_editor_test.cpp osm_editor_test.hpp ui2oh_test.cpp - user_stats_test.cpp xml_feature_test.cpp ) diff --git a/editor/editor_tests/user_stats_test.cpp b/editor/editor_tests/user_stats_test.cpp deleted file mode 100644 index 7150ea3bde..0000000000 --- a/editor/editor_tests/user_stats_test.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "testing/testing.hpp" - -#include "editor/user_stats.hpp" - -#include "platform/platform_tests_support/writable_dir_changer.hpp" - -namespace editor -{ -namespace -{ -auto constexpr kEditorTestDir = "editor-tests"; -// This user has made only 9 changes and then renamed himself, so there will be no further edits from him. -auto constexpr kUserName = "Nikita Bokiy"; - -UNIT_TEST(UserStatsLoader_Smoke) -{ - WritableDirChanger wdc(kEditorTestDir, WritableDirChanger::SettingsDirPolicy::UseWritableDir); - - { - UserStatsLoader statsLoader; - TEST(!statsLoader.GetStats(kUserName), ()); - } - - { - UserStatsLoader statsLoader; - - statsLoader.Update(kUserName); - auto const userStats = statsLoader.GetStats(kUserName); - - TEST(userStats, ()); - int32_t rank, changesCount; - TEST(userStats.GetRank(rank), ()); - TEST(userStats.GetChangesCount(changesCount), ()); - - TEST_GREATER_OR_EQUAL(rank, 2100, ()); - TEST_EQUAL(changesCount, 9, ()); - } - - // This test checks if user stats info was stored in setting. - // NOTE: there Update function is not called. - { - UserStatsLoader statsLoader; - - TEST_EQUAL(statsLoader.GetUserName(), kUserName, ()); - auto const userStats = statsLoader.GetStats(kUserName); - - TEST(userStats, ()); - int32_t rank, changesCount; - TEST(userStats.GetRank(rank), ()); - TEST(userStats.GetChangesCount(changesCount), ()); - - TEST_GREATER_OR_EQUAL(rank, 2100, ()); - TEST_EQUAL(changesCount, 9, ()); - } -} -} // namespace -} // namespace editor diff --git a/editor/user_stats.cpp b/editor/user_stats.cpp deleted file mode 100644 index c2747fa431..0000000000 --- a/editor/user_stats.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "editor/user_stats.hpp" - -#include "platform/http_client.hpp" -#include "platform/platform.hpp" -#include "platform/settings.hpp" - -#include "coding/url.hpp" - -#include "base/logging.hpp" -#include "base/thread.hpp" -#include "base/timer.hpp" - -#include "3party/pugixml/src/pugixml.hpp" - -namespace -{ -std::string const kUserStatsUrl = "https://editor-api.maps.me/user?format=xml"; -int32_t constexpr kUninitialized = -1; - -auto constexpr kSettingsUserName = "LastLoggedUser"; -auto constexpr kSettingsRating = "UserEditorRating"; -auto constexpr kSettingsChangesCount = "UserEditorChangesCount"; -auto constexpr kSettingsLastUpdate = "UserEditorLastUpdate"; - -auto constexpr kSecondsInHour = 60 * 60; -} // namespace - -namespace editor -{ -// UserStat ---------------------------------------------------------------------------------------- - -UserStats::UserStats() - : m_changesCount(kUninitialized), m_rank(kUninitialized) - , m_updateTime(base::SecondsSinceEpochToTimeT(0)), m_valid(false) -{ -} - -UserStats::UserStats(time_t const updateTime, uint32_t const rating, uint32_t const changesCount, - std::string const & levelUpFeat) - : m_changesCount(changesCount) - , m_rank(rating) - , m_updateTime(updateTime) - , m_levelUpRequiredFeat(levelUpFeat) - , m_valid(true) -{ -} - -bool UserStats::GetChangesCount(int32_t & changesCount) const -{ - if (m_changesCount == kUninitialized) - return false; - changesCount = m_changesCount; - return true; -} - -bool UserStats::GetRank(int32_t & rank) const -{ - if (m_rank == kUninitialized) - return false; - rank = m_rank; - return true; -} - -bool UserStats::GetLevelUpRequiredFeat(std::string & levelUpFeat) const -{ - if (m_levelUpRequiredFeat.empty()) - return false; - levelUpFeat = m_levelUpRequiredFeat; - return true; -} - -// UserStatsLoader --------------------------------------------------------------------------------- - -UserStatsLoader::UserStatsLoader() - : m_lastUpdate(base::SecondsSinceEpochToTimeT(0)) -{ - if (!LoadFromSettings()) - LOG(LINFO, ("There is no cached user stats info in settings")); - else - LOG(LINFO, ("User stats info was loaded successfully")); -} - -bool UserStatsLoader::Update(std::string const & userName) -{ - if (userName.empty()) - return false; - - { - std::lock_guard g(m_mutex); - m_userName = userName; - } - - auto const url = kUserStatsUrl + "&name=" + url::UrlEncode(userName); - platform::HttpClient request(url); - request.SetRawHeader("User-Agent", GetPlatform().GetAppUserAgent()); - - if (!request.RunHttpRequest()) - { - LOG(LWARNING, ("Network error while connecting to", url)); - return false; - } - - if (request.ErrorCode() != 200) - { - LOG(LWARNING, ("Server returned", request.ErrorCode(), "for url", url)); - return false; - } - - auto const response = request.ServerResponse(); - - pugi::xml_document document; - if (!document.load_buffer(response.data(), response.size())) - { - LOG(LWARNING, ("Cannot parse server response:", response)); - return false; - } - - auto changesCount = document.select_node("mmwatch/edits/@value").attribute().as_int(-1); - auto rank = document.select_node("mmwatch/rank/@value").attribute().as_int(-1); - auto levelUpFeat = document.select_node("mmwatch/levelUpFeat/@value").attribute().as_string(); - - std::lock_guard g(m_mutex); - if (m_userName != userName) - return false; - - m_lastUpdate = time(nullptr); - m_userStats = UserStats(m_lastUpdate, rank, changesCount, levelUpFeat); - SaveToSettings(); - - return true; -} - -void UserStatsLoader::Update(std::string const & userName, UpdatePolicy const policy, - OnUpdateCallback fn) -{ - auto nothingToUpdate = false; - if (policy == UpdatePolicy::Lazy) - { - std::lock_guard g(m_mutex); - nothingToUpdate = m_userStats && m_userName == userName && - difftime(time(nullptr), m_lastUpdate) < kSecondsInHour; - } - - if (nothingToUpdate) - { - GetPlatform().RunTask(Platform::Thread::Gui, fn); - return; - } - - GetPlatform().RunTask(Platform::Thread::Network, [this, userName, fn] - { - if (Update(userName)) - GetPlatform().RunTask(Platform::Thread::Gui, fn); - }); -} - -void UserStatsLoader::Update(std::string const & userName, OnUpdateCallback fn) -{ - Update(userName, UpdatePolicy::Lazy, fn); -} - -void UserStatsLoader::DropStats(std::string const & userName) -{ - std::lock_guard g(m_mutex); - if (m_userName != userName) - return; - m_userStats = {}; - DropSettings(); -} - -UserStats UserStatsLoader::GetStats(std::string const & userName) const -{ - std::lock_guard g(m_mutex); - if (m_userName == userName) - return m_userStats; - return {}; -} - -std::string UserStatsLoader::GetUserName() const -{ - std::lock_guard g(m_mutex); - return m_userName; -} - -bool UserStatsLoader::LoadFromSettings() -{ - uint32_t rating = 0; - uint32_t changesCount = 0; - uint64_t lastUpdate = 0; - - if (!settings::Get(kSettingsUserName, m_userName) || - !settings::Get(kSettingsChangesCount, changesCount) || - !settings::Get(kSettingsRating, rating) || - !settings::Get(kSettingsLastUpdate, lastUpdate)) - { - return false; - } - - m_lastUpdate = base::SecondsSinceEpochToTimeT(lastUpdate); - m_userStats = UserStats(m_lastUpdate, rating, changesCount, ""); - return true; -} - -void UserStatsLoader::SaveToSettings() -{ - if (!m_userStats) - return; - - settings::Set(kSettingsUserName, m_userName); - int32_t rank; - if (m_userStats.GetRank(rank)) - settings::Set(kSettingsRating, rank); - int32_t changesCount; - if (m_userStats.GetChangesCount(changesCount)) - settings::Set(kSettingsChangesCount, changesCount); - settings::Set(kSettingsLastUpdate, base::TimeTToSecondsSinceEpoch(m_lastUpdate)); - // Do not save m_requiredLevelUpFeat for it becomes obsolete very fast. -} - -void UserStatsLoader::DropSettings() -{ - settings::Delete(kSettingsUserName); - settings::Delete(kSettingsRating); - settings::Delete(kSettingsChangesCount); - settings::Delete(kSettingsLastUpdate); -} -} // namespace editor diff --git a/editor/user_stats.hpp b/editor/user_stats.hpp deleted file mode 100644 index 20b52df0cb..0000000000 --- a/editor/user_stats.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace editor -{ -class UserStats -{ -public: - UserStats(); - UserStats(time_t const updateTime, uint32_t const rating, uint32_t const changesCount, - std::string const & levelUpFeat); - - bool IsValid() const { return m_valid; } - - operator bool() const { return IsValid(); } - - bool GetChangesCount(int32_t & changesCount) const; - bool GetRank(int32_t & rank) const; - bool GetLevelUpRequiredFeat(std::string & levelUpFeat) const; - - time_t GetLastUpdate() const { return m_updateTime; } - -private: - int32_t m_changesCount; - int32_t m_rank; - time_t m_updateTime; - /// A very doubtful field representing what a user must commit to have a better rank. - std::string m_levelUpRequiredFeat; - bool m_valid; -}; - -class UserStatsLoader -{ -public: - using OnUpdateCallback = std::function; - - enum class UpdatePolicy { Lazy, Force }; - - UserStatsLoader(); - - /// Synchronously sends request to the server. Updates stats and returns true on success. - bool Update(std::string const & userName); - - /// Launches the update process if stats are too old or if policy is UpdatePolicy::Force. - /// The process posts fn to a gui thread on success. - void Update(std::string const & userName, UpdatePolicy policy, OnUpdateCallback fn); - /// Calls Update with UpdatePolicy::Lazy. - void Update(std::string const & userName, OnUpdateCallback fn); - - /// Resets internal state and removes records from settings. - void DropStats(std::string const & userName); - - /// Atomically returns stats if userName is still actual. - UserStats GetStats(std::string const & userName) const; - - /// Debug only. - std::string GetUserName() const; - -private: - /// Not thread-safe, but called only in constructor. - bool LoadFromSettings(); - /// Not thread-safe, use synchonization. - void SaveToSettings(); - void DropSettings(); - - std::string m_userName; - - time_t m_lastUpdate; - mutable std::mutex m_mutex; - - UserStats m_userStats; -}; -} // namespace editor diff --git a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h index 415132770b..00e00642c7 100644 --- a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h +++ b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h @@ -19,9 +19,6 @@ NS_SWIFT_NAME(FrameworkHelper) + (void)setTheme:(MWMTheme)theme; + (MWMDayTime)daytimeAtLocation:(nullable CLLocation *)location; + (void)createFramework; -+ (BOOL)canUseNetwork; -+ (BOOL)isNetworkConnected; -+ (BOOL)isWiFiConnected; + (MWMMarkID)invalidBookmarkId; + (MWMMarkGroupID)invalidCategoryId; + (void)zoomMap:(MWMZoomMode)mode; diff --git a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm index 5d8907abf4..201936d291 100644 --- a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm +++ b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm @@ -69,18 +69,6 @@ UNUSED_VALUE(GetFramework()); } -+ (BOOL)canUseNetwork { - return network_policy::CanUseNetwork(); -} - -+ (BOOL)isNetworkConnected { - return GetPlatform().ConnectionStatus() != Platform::EConnectionType::CONNECTION_NONE; -} - -+ (BOOL)isWiFiConnected { - return GetPlatform().ConnectionStatus() == Platform::EConnectionType::CONNECTION_WIFI; -} - + (MWMMarkID)invalidBookmarkId { return kml::kInvalidMarkId; } diff --git a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.h b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.h index d51989098e..45a47a3e36 100644 --- a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.h +++ b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.h @@ -13,11 +13,15 @@ enum class AuthorizationButtonType // Deletes any stored credentials if called with empty key or secret. void AuthorizationStoreCredentials(osm::KeySecret const & keySecret); BOOL AuthorizationHaveCredentials(); +void AuthorizationClearCredentials(); // Returns empty key and secret if user has not beed authorized. osm::KeySecret AuthorizationGetCredentials(); void AuthorizationSetNeedCheck(BOOL needCheck); BOOL AuthorizationIsNeedCheck(); +/// Returns nil if not logged in. NSString * OSMUserName(); +/// Returns 0 if not logged in. +NSInteger OSMUserChangesetsCount(); } // namespace osm_auth_ios diff --git a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.mm b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.mm index 60e2a12809..33621903f6 100644 --- a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.mm +++ b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationCommon.mm @@ -1,4 +1,5 @@ #import "MWMAuthorizationCommon.h" +#import "MWMNetworkPolicy+UI.h" #import "UIButton+RuntimeAttributes.h" #include "base/logging.hpp" @@ -11,47 +12,37 @@ NSString * const kOSMRequestToken = @"OSMRequestToken"; NSString * const kOSMRequestSecret = @"OSMRequestSecret"; NSString * const kAuthNeedCheck = @"AuthNeedCheck"; NSString * const kOSMUserName = @"UDOsmUserName"; +NSString * const kOSMChangesetsCount = @"OSMUserChangesetsCount"; -void SetOSMUserNameWithCredentials(osm::KeySecret const & keySecret) +BOOL LoadOsmUserPreferences(osm::UserPreferences & prefs) { - using namespace osm; + __block BOOL success = false; dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { - ServerApi06 const api {OsmOAuth::ServerAuth(keySecret)}; try { - NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; - [ud setObject:@(api.GetUserPreferences().m_displayName.c_str()) forKey:kOSMUserName]; - [ud synchronize]; + osm::ServerApi06 const api {osm::OsmOAuth::ServerAuth(AuthorizationGetCredentials())}; + prefs = api.GetUserPreferences(); + success = true; } catch (std::exception const & ex) { - LOG(LWARNING, ("Can't load user preferences from OSM server:", ex.what())); + LOG(LWARNING, ("Can't load user preferences from OSM server:", ex.what())); } }); -} // namespace osm_auth_ios - -void SetEmptyOSMUserName() -{ - NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; - [ud setObject:nil forKey:kOSMUserName]; - [ud synchronize]; + return success; } void AuthorizationStoreCredentials(osm::KeySecret const & keySecret) { NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; - if (keySecret.first.empty() || keySecret.second.empty()) - { - [ud removeObjectForKey:kOSMRequestToken]; - [ud removeObjectForKey:kOSMRequestSecret]; - SetEmptyOSMUserName(); - } - else - { - [ud setObject:@(keySecret.first.c_str()) forKey:kOSMRequestToken]; - [ud setObject:@(keySecret.second.c_str()) forKey:kOSMRequestSecret]; - SetOSMUserNameWithCredentials(keySecret); + [ud setObject:@(keySecret.first.c_str()) forKey:kOSMRequestToken]; + [ud setObject:@(keySecret.second.c_str()) forKey:kOSMRequestSecret]; + osm::UserPreferences prefs; + if (LoadOsmUserPreferences(prefs)) { + [ud setObject:@(prefs.m_displayName.c_str()) forKey:kOSMUserName]; + // To also see # of edits when offline. + [ud setInteger:prefs.m_changesets forKey:kOSMChangesetsCount]; } [ud synchronize]; } @@ -61,7 +52,17 @@ BOOL AuthorizationHaveCredentials() NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; NSString * requestToken = [ud stringForKey:kOSMRequestToken]; NSString * requestSecret = [ud stringForKey:kOSMRequestSecret]; - return requestToken && requestSecret; + return requestToken.length && requestSecret.length; +} + +void AuthorizationClearCredentials() +{ + NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; + [ud removeObjectForKey:kOSMRequestToken]; + [ud removeObjectForKey:kOSMRequestSecret]; + [ud removeObjectForKey:kOSMUserName]; + [ud removeObjectForKey:kOSMChangesetsCount]; + [ud synchronize]; } osm::KeySecret AuthorizationGetCredentials() @@ -91,4 +92,23 @@ NSString * OSMUserName() return [NSUserDefaults.standardUserDefaults stringForKey:kOSMUserName]; } +NSInteger OSMUserChangesetsCount() +{ + __block NSInteger count = -1; + [[MWMNetworkPolicy sharedPolicy] callOnlineApi:^(BOOL permitted) { + if (permitted) + if (osm::UserPreferences prefs; YES == LoadOsmUserPreferences(prefs)) + count = prefs.m_changesets; + }]; + + NSUserDefaults * ud = NSUserDefaults.standardUserDefaults; + if (count >= 0) + { + [ud setInteger:count forKey:kOSMChangesetsCount]; + [ud synchronize]; + return count; + } + return [ud integerForKey:kOSMChangesetsCount]; +} + } // namespace osm_auth_ios diff --git a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationLoginViewController.mm b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationLoginViewController.mm index 6c472229b9..66a5f65f17 100644 --- a/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationLoginViewController.mm +++ b/iphone/Maps/Classes/CustomViews/Login/MWMAuthorizationLoginViewController.mm @@ -23,16 +23,11 @@ using namespace osm_auth_ios; @property (weak, nonatomic) IBOutlet UIView * authView; @property (weak, nonatomic) IBOutlet UIView * accountView; -@property (weak, nonatomic) IBOutlet UIButton * loginGoogleButton; -@property (weak, nonatomic) IBOutlet UIButton * loginFacebookButton; @property (weak, nonatomic) IBOutlet UIButton * loginOSMButton; @property (weak, nonatomic) IBOutlet UIButton * signupButton; @property (weak, nonatomic) IBOutlet UILabel * changesCountLabel; @property (weak, nonatomic) IBOutlet UILabel * lastUpdateLabel; -@property (weak, nonatomic) IBOutlet UILabel * rankLabel; -@property (weak, nonatomic) IBOutlet UILabel * changesToNextPlaceLabel; -@property (weak, nonatomic) IBOutlet NSLayoutConstraint * yourPlaceLabelCenterYAlignment; @end @@ -57,16 +52,7 @@ using namespace osm_auth_ios; - (void)checkConnection { - BOOL const isConnected = Platform::IsConnected(); - self.loginGoogleButton.enabled = isConnected; - self.loginFacebookButton.enabled = isConnected; - self.signupButton.enabled = isConnected; - - if (!isConnected) - { - self.loginGoogleButton.layer.borderColor = UIColor.clearColor.CGColor; - self.loginFacebookButton.layer.borderColor = UIColor.clearColor.CGColor; - } + self.signupButton.enabled = Platform::IsConnected(); } - (void)configHaveAuth @@ -97,22 +83,6 @@ using namespace osm_auth_ios; [self.alertController presentNoConnectionAlert]; } -- (IBAction)loginGoogle -{ - [self performOnlineAction:^ - { - [self performSegueWithIdentifier:kWebViewAuthSegue sender:self.loginGoogleButton]; - }]; -} - -- (IBAction)loginFacebook -{ - [self performOnlineAction:^ - { - [self performSegueWithIdentifier:kWebViewAuthSegue sender:self.loginFacebookButton]; - }]; -} - - (IBAction)loginOSM { [self performOnlineAction:^ @@ -134,61 +104,26 @@ using namespace osm_auth_ios; [self openUrl:[NSURL URLWithString:@"https://wiki.openstreetmap.org/wiki/Main_Page"]]; } +- (IBAction)historyTap +{ + [self openUrl:[NSURL URLWithString:[NSString stringWithFormat: +#ifdef DEBUG + @"https://master.apis.dev.openstreetmap.org/user/%@/history" +#else + @"https://www.openstreetmap.org/user/%@/history" +#endif + , OSMUserName()]]]; +} + - (void)logout { - NSString * osmUserName = OSMUserName(); - if (osmUserName.length > 0) - GetFramework().DropUserStats(osmUserName.UTF8String); - AuthorizationStoreCredentials({}); + AuthorizationClearCredentials(); [self.navigationController popViewControllerAnimated:YES]; } - (void)refresh:(BOOL)force { - [self updateUI]; - __weak auto weakSelf = self; - auto const policy = force ? editor::UserStatsLoader::UpdatePolicy::Force - : editor::UserStatsLoader::UpdatePolicy::Lazy; - NSString * osmUserName = OSMUserName(); - if (osmUserName.length > 0) - GetFramework().UpdateUserStats(osmUserName.UTF8String, policy, ^{ [weakSelf updateUI]; }); -} - -- (void)updateUI -{ - NSString * osmUserName = OSMUserName(); - if (osmUserName.length == 0) - return; - editor::UserStats stats = GetFramework().GetUserStats(osmUserName.UTF8String); - if (!stats) - return; - int32_t changesCount; - if (stats.GetChangesCount(changesCount)) - self.changesCountLabel.text = @(changesCount).stringValue; - int32_t rank; - if (stats.GetRank(rank)) - self.rankLabel.text = @(rank).stringValue; - std::string levelUpFeat; - if (stats.GetLevelUpRequiredFeat(levelUpFeat)) - { - self.yourPlaceLabelCenterYAlignment.priority = UILayoutPriorityDefaultLow; - self.changesToNextPlaceLabel.hidden = NO; - self.changesToNextPlaceLabel.text = - [NSString stringWithFormat:@"%@ %@", L(@"editor_profile_changes_for_next_place"), - @(levelUpFeat.c_str())]; - } - else - { - self.yourPlaceLabelCenterYAlignment.priority = UILayoutPriorityDefaultHigh; - self.changesToNextPlaceLabel.hidden = YES; - } - - NSString * lastUploadDate = [NSDateFormatter - localizedStringFromDate:[NSDate dateWithTimeIntervalSince1970:stats.GetLastUpdate()] - dateStyle:NSDateFormatterShortStyle - timeStyle:NSDateFormatterShortStyle]; - self.lastUpdateLabel.text = - [NSString stringWithFormat:L(@"last_update"), lastUploadDate.UTF8String]; + self.changesCountLabel.text = @(OSMUserChangesetsCount()).stringValue; } #pragma mark - ActionSheet @@ -217,15 +152,4 @@ using namespace osm_auth_ios; [self presentViewController:alertController animated:YES completion:nil]; } -#pragma mark - Segue - -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender -{ - MWMAuthorizationWebViewLoginViewController * dvc = segue.destinationViewController; - if ([self.loginGoogleButton isEqual:sender]) - dvc.authType = MWMWebViewAuthorizationTypeGoogle; - else if ([self.loginFacebookButton isEqual:sender]) - dvc.authType = MWMWebViewAuthorizationTypeFacebook; -} - @end diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 119a5a98a9..2ccadb0448 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -179,9 +179,7 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; - (void)onMapObjectSelected { [self hidePlacePage]; - [[MWMNetworkPolicy sharedPolicy] callOnlineApi:^(BOOL) { - [self showPlacePage]; - }]; + [self showPlacePage]; } - (void)onMapObjectUpdated { diff --git a/iphone/Maps/UI/Storyboard/Authorization.storyboard b/iphone/Maps/UI/Storyboard/Authorization.storyboard index c5da1c702b..93adb32c4e 100644 --- a/iphone/Maps/UI/Storyboard/Authorization.storyboard +++ b/iphone/Maps/UI/Storyboard/Authorization.storyboard @@ -1,7 +1,9 @@ - + + - + + @@ -11,14 +13,14 @@ - + - + - + @@ -28,7 +30,7 @@ - + @@ -41,7 +43,7 @@ - + @@ -51,7 +53,7 @@ - + @@ -64,7 +66,7 @@ - + @@ -100,7 +102,7 @@ + @@ -151,7 +154,6 @@ - @@ -180,14 +182,14 @@ - + - + - - + + + - - - - - + @@ -503,6 +371,7 @@ + @@ -516,7 +385,6 @@ - @@ -525,39 +393,34 @@ - - - - - - + - + - + - + @@ -565,6 +428,7 @@ + @@ -574,7 +438,6 @@ - @@ -588,7 +451,5 @@ - - diff --git a/map/framework.hpp b/map/framework.hpp index 3d52081994..a2c7ece549 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -30,7 +30,6 @@ #include "kml/type_utils.hpp" #include "editor/new_feature_categories.hpp" -#include "editor/user_stats.hpp" #include "indexer/caching_rank_table_loader.hpp" #include "indexer/data_header.hpp" @@ -737,25 +736,6 @@ public: void CreateNote(osm::MapObject const & mapObject, osm::Editor::NoteProblemType const type, std::string const & note); -public: - // User statistics. - editor::UserStats GetUserStats(std::string const & userName) const - { - return m_userStatsLoader.GetStats(userName); - } - - // Reads user stats from server or gets it from cache calls |fn| on success. - void UpdateUserStats(std::string const & userName, editor::UserStatsLoader::UpdatePolicy policy, - editor::UserStatsLoader::OnUpdateCallback fn) - { - m_userStatsLoader.Update(userName, policy, fn); - } - - void DropUserStats(std::string const & userName) { m_userStatsLoader.DropStats(userName); } - -private: - editor::UserStatsLoader m_userStatsLoader; - public: storage::CountriesVec GetTopmostCountries(ms::LatLon const & latlon) const; diff --git a/xcode/editor/editor.xcodeproj/project.pbxproj b/xcode/editor/editor.xcodeproj/project.pbxproj index 2a2f1dc81d..eff5f43b5d 100644 --- a/xcode/editor/editor.xcodeproj/project.pbxproj +++ b/xcode/editor/editor.xcodeproj/project.pbxproj @@ -15,8 +15,6 @@ 341138791C15AE42002E3B3E /* opening_hours_ui.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 341138751C15AE42002E3B3E /* opening_hours_ui.hpp */; }; 3411387A1C15AE42002E3B3E /* ui2oh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 341138761C15AE42002E3B3E /* ui2oh.cpp */; }; 3411387B1C15AE42002E3B3E /* ui2oh.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 341138771C15AE42002E3B3E /* ui2oh.hpp */; }; - 3441CE481CFC1D3C00CF30D4 /* user_stats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3441CE461CFC1D3C00CF30D4 /* user_stats.cpp */; }; - 3441CE491CFC1D3C00CF30D4 /* user_stats.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3441CE471CFC1D3C00CF30D4 /* user_stats.hpp */; }; 34527C511C89B1770015050E /* editor_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34527C4F1C89B1770015050E /* editor_config.cpp */; }; 34527C521C89B1770015050E /* editor_config.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 34527C501C89B1770015050E /* editor_config.hpp */; }; 34583BC01C8854C100F94664 /* yes_no_unknown.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 34583BBE1C8854C100F94664 /* yes_no_unknown.hpp */; }; @@ -27,7 +25,6 @@ 3496ABE31DC2035800C5DDBA /* editor_notes_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3496ABD31DC2034900C5DDBA /* editor_notes_test.cpp */; }; 3496ABE41DC2035800C5DDBA /* opening_hours_ui_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3496ABD41DC2034900C5DDBA /* opening_hours_ui_test.cpp */; }; 3496ABE61DC2035800C5DDBA /* ui2oh_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3496ABD61DC2034900C5DDBA /* ui2oh_test.cpp */; }; - 3496ABE71DC2035800C5DDBA /* user_stats_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3496ABD71DC2034900C5DDBA /* user_stats_test.cpp */; }; 3496ABE81DC2035800C5DDBA /* xml_feature_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3496ABD81DC2034900C5DDBA /* xml_feature_test.cpp */; }; 3496ABE91DC2041800C5DDBA /* libeditor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3411386B1C15AD97002E3B3E /* libeditor.a */; }; 3496ABEF1DC2041800C5DDBA /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3496ABEC1DC2041800C5DDBA /* libopening_hours.a */; }; @@ -89,8 +86,6 @@ 341138751C15AE42002E3B3E /* opening_hours_ui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opening_hours_ui.hpp; sourceTree = ""; }; 341138761C15AE42002E3B3E /* ui2oh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui2oh.cpp; sourceTree = ""; }; 341138771C15AE42002E3B3E /* ui2oh.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ui2oh.hpp; sourceTree = ""; }; - 3441CE461CFC1D3C00CF30D4 /* user_stats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_stats.cpp; sourceTree = ""; }; - 3441CE471CFC1D3C00CF30D4 /* user_stats.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = user_stats.hpp; sourceTree = ""; }; 34527C4F1C89B1770015050E /* editor_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = editor_config.cpp; sourceTree = ""; }; 34527C501C89B1770015050E /* editor_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = editor_config.hpp; sourceTree = ""; }; 34583BBE1C8854C100F94664 /* yes_no_unknown.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = yes_no_unknown.hpp; sourceTree = ""; }; @@ -102,7 +97,6 @@ 3496ABD31DC2034900C5DDBA /* editor_notes_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = editor_notes_test.cpp; sourceTree = ""; }; 3496ABD41DC2034900C5DDBA /* opening_hours_ui_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opening_hours_ui_test.cpp; sourceTree = ""; }; 3496ABD61DC2034900C5DDBA /* ui2oh_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui2oh_test.cpp; sourceTree = ""; }; - 3496ABD71DC2034900C5DDBA /* user_stats_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_stats_test.cpp; sourceTree = ""; }; 3496ABD81DC2034900C5DDBA /* xml_feature_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xml_feature_test.cpp; sourceTree = ""; }; 3496ABEC1DC2041800C5DDBA /* libopening_hours.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopening_hours.a; path = "../../../omim-build/xcode/Debug/libopening_hours.a"; sourceTree = ""; }; 3496ABED1DC2041800C5DDBA /* libpugixml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpugixml.a; path = "../../../omim-build/xcode/Debug/libpugixml.a"; sourceTree = ""; }; @@ -264,8 +258,6 @@ 34FFB34B1C316A7600BFF6C3 /* server_api.hpp */, 341138761C15AE42002E3B3E /* ui2oh.cpp */, 341138771C15AE42002E3B3E /* ui2oh.hpp */, - 3441CE461CFC1D3C00CF30D4 /* user_stats.cpp */, - 3441CE471CFC1D3C00CF30D4 /* user_stats.hpp */, 347C71261C295B1100BE9208 /* xml_feature.cpp */, 347C71271C295B1100BE9208 /* xml_feature.hpp */, 34583BBE1C8854C100F94664 /* yes_no_unknown.hpp */, @@ -287,7 +279,6 @@ 671555E720BDC5D3002BA3B4 /* osm_editor_test.hpp */, 3496AC011DC2047D00C5DDBA /* testingmain.cpp */, 3496ABD61DC2034900C5DDBA /* ui2oh_test.cpp */, - 3496ABD71DC2034900C5DDBA /* user_stats_test.cpp */, 3496ABD81DC2034900C5DDBA /* xml_feature_test.cpp */, 6715560920BEF0A4002BA3B4 /* new_feature_categories_test.cpp */, ); @@ -357,7 +348,6 @@ 6715560820BEC332002BA3B4 /* edits_migration.hpp in Headers */, 3D052487200F62EE00F24998 /* feature_matcher.hpp in Headers */, 3411387B1C15AE42002E3B3E /* ui2oh.hpp in Headers */, - 3441CE491CFC1D3C00CF30D4 /* user_stats.hpp in Headers */, 341138791C15AE42002E3B3E /* opening_hours_ui.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -489,7 +479,6 @@ 3411387A1C15AE42002E3B3E /* ui2oh.cpp in Sources */, 340DC8291C4E71E500EAA2CC /* changeset_wrapper.cpp in Sources */, 3D3058741D707DBE004AC712 /* config_loader.cpp in Sources */, - 3441CE481CFC1D3C00CF30D4 /* user_stats.cpp in Sources */, 6715560620BEC332002BA3B4 /* edits_migration.cpp in Sources */, 34527C511C89B1770015050E /* editor_config.cpp in Sources */, 34FFB34C1C316A7600BFF6C3 /* server_api.cpp in Sources */, @@ -512,7 +501,6 @@ 3496ABE41DC2035800C5DDBA /* opening_hours_ui_test.cpp in Sources */, 3496AC031DC2048E00C5DDBA /* testingmain.cpp in Sources */, 3496ABE61DC2035800C5DDBA /* ui2oh_test.cpp in Sources */, - 3496ABE71DC2035800C5DDBA /* user_stats_test.cpp in Sources */, 3496ABE81DC2035800C5DDBA /* xml_feature_test.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0;