[android] Added screen edit category settings, added notification mechanism when upload completed

This commit is contained in:
Dmitry Donskoy 2018-10-25 13:19:30 +03:00 committed by Aleksandr Zatsepin
parent 91efd1d8c0
commit 7984be229f
17 changed files with 525 additions and 29 deletions

View file

@ -447,6 +447,7 @@
android:label="@string/ugc_route_tags_screen_label">
</activity>
<activity android:name="com.mapswithme.maps.ugc.routes.UgcRouteSharingOptionsActivity"/>
<activity android:name="com.mapswithme.maps.ugc.routes.UgcRouteEditSettingsActivity"/>
<activity android:name=".purchase.BookmarkPaymentActivity"/>
<activity android:name="com.mapswithme.maps.ugc.route.SendLinkPlaceholderActivity"/>
<service

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="?cardBackground"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/MwmWidget.ToolbarStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/MwmWidget.ToolbarTheme"/>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:scrollbars="none"
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:paddingStart="@dimen/margin_base"
android:paddingEnd="@dimen/margin_base"
android:paddingRight="@dimen/margin_base"
android:paddingLeft="@dimen/margin_base"
android:paddingBottom="@dimen/bookmark_hide_btn_padding_top"
android:paddingTop="@dimen/margin_half_plus"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/ugc_route_edit_list_name"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/edit_name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_category_name_view"
android:layout_weight="1"
android:layout_width="0dp"
android:background="@null"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginEnd="@dimen/margin_base"/>
<ImageView
android:id="@+id/edit_text_clear_btn"
android:src="@drawable/ic_clear_rounded"
android:background="?selectableItemBackgroundBorderless"
android:layout_gravity="center"
android:layout_weight="0"
android:layout_width="@dimen/margin_base_plus"
android:layout_height="@dimen/margin_base_plus"/>
</LinearLayout>
<View
android:background="@color/black_38"
android:layout_marginTop="@dimen/margin_half"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"/>
</LinearLayout>
<View
android:background="?android:attr/listDivider"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"/>
<LinearLayout
android:id="@+id/open_sharing_options_screen_btn_container"
android:orientation="vertical"
android:paddingStart="@dimen/margin_base"
android:paddingEnd="@dimen/margin_base"
android:paddingRight="@dimen/margin_base"
android:paddingLeft="@dimen/margin_base"
android:paddingTop="@dimen/margin_base"
android:paddingBottom="@dimen/margin_half_plus"
android:background="?attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/sharing_options_title"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/sharing_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/sharing_options_desc"
android:textAppearance="?android:attr/textAppearance"
android:text="@string/sharing_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<View
android:background="?android:attr/listDivider"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"/>
<EditText
android:id="@+id/edit_description"
android:background="@null"
android:hint="@string/ugc_route_edit_description_hint"
android:minHeight="@dimen/height_item_multiline"
android:paddingStart="@dimen/margin_base"
android:paddingEnd="@dimen/margin_base"
android:paddingRight="@dimen/margin_base"
android:paddingLeft="@dimen/margin_base"
android:textAppearance="?android:attr/textAppearance"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:background="?android:attr/windowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>

View file

@ -0,0 +1,13 @@
package com.mapswithme.maps.base;
import android.database.Observable;
import android.support.v7.widget.RecyclerView;
public class DataObservable extends Observable<RecyclerView.AdapterDataObserver>
{
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}

View file

@ -0,0 +1,11 @@
package com.mapswithme.maps.base;
import android.database.Observable;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
public interface ObservableHost<T extends Observable<RecyclerView.AdapterDataObserver>>
{
@NonNull
T getObservable();
}

View file

@ -42,17 +42,31 @@ public abstract class AbstractCategoriesSnapshot
public int indexOfOrThrow(@NonNull BookmarkCategory category)
{
List<BookmarkCategory> items = getItems();
int indexOf = items.indexOf(category);
return indexOfThrowInternal(getItems(), category);
}
private static int indexOfThrowInternal(@NonNull List<BookmarkCategory> categories,
@NonNull BookmarkCategory category)
{
int indexOf = categories.indexOf(category);
if (indexOf < 0)
{
throw new UnsupportedOperationException(new StringBuilder("This category absent in current snapshot ")
throw new UnsupportedOperationException(new StringBuilder("This category absent in " +
"current snapshot ")
.append(category)
.append("all items : ")
.append(Arrays.toString(items.toArray()))
.append(Arrays.toString(categories.toArray()))
.toString());
}
return indexOf;
}
@NonNull
public BookmarkCategory refresh(@NonNull BookmarkCategory category)
{
List<BookmarkCategory> items = getItems();
int index = indexOfThrowInternal(items, category);
return items.get(index);
}
}
}

View file

@ -7,11 +7,13 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.PluralsRes;
import android.support.annotation.StringRes;
import android.text.TextUtils;
import com.mapswithme.maps.R;
import com.mapswithme.maps.bookmarks.BookmarksPageFactory;
import com.mapswithme.util.TypeConverter;
import com.mapswithme.util.UiUtils;
public class BookmarkCategory implements Parcelable
{
@ -27,13 +29,16 @@ public class BookmarkCategory implements Parcelable
private final int mTracksCount;
private final int mBookmarksCount;
private final int mTypeIndex;
private final int mAccessRulesIndex;
private final boolean mIsMyCategory;
private final boolean mIsVisible;
public BookmarkCategory(long id, @NonNull String name, @NonNull String authorId,
@NonNull String authorName, @NonNull String annotation,
@NonNull String description, int tracksCount, int bookmarksCount,
boolean fromCatalog, boolean isMyCategory, boolean isVisible)
boolean fromCatalog, boolean isMyCategory, boolean isVisible,
int accessRulesIndex)
{
mId = id;
mName = name;
@ -47,6 +52,7 @@ public class BookmarkCategory implements Parcelable
mAuthor = TextUtils.isEmpty(authorId) || TextUtils.isEmpty(authorName)
? null
: new Author(authorId, authorName);
mAccessRulesIndex = accessRulesIndex;
}
@Override
@ -91,11 +97,18 @@ public class BookmarkCategory implements Parcelable
return mBookmarksCount;
}
@NonNull
public Type getType()
{
return Type.values()[mTypeIndex];
}
@NonNull
public AccessRules getAccessRules()
{
return AccessRules.values()[mAccessRulesIndex];
}
public boolean isFromCatalog()
{
return Type.values()[mTypeIndex] == Type.CATALOG;
@ -270,6 +283,7 @@ public class BookmarkCategory implements Parcelable
sb.append(", mType=").append(Type.values()[mTypeIndex]);
sb.append(", mIsMyCategory=").append(mIsMyCategory);
sb.append(", mIsVisible=").append(mIsVisible);
sb.append(", mAccessRules=").append(getAccessRules());
sb.append('}');
return sb.toString();
}
@ -331,6 +345,7 @@ public class BookmarkCategory implements Parcelable
dest.writeInt(this.mTypeIndex);
dest.writeByte(this.mIsMyCategory ? (byte) 1 : (byte) 0);
dest.writeByte(this.mIsVisible ? (byte) 1 : (byte) 0);
dest.writeInt(this.mAccessRulesIndex);
}
protected BookmarkCategory(Parcel in)
@ -345,6 +360,7 @@ public class BookmarkCategory implements Parcelable
this.mTypeIndex = in.readInt();
this.mIsMyCategory = in.readByte() != 0;
this.mIsVisible = in.readByte() != 0;
this.mAccessRulesIndex = in.readInt();
}
public static final Creator<BookmarkCategory> CREATOR = new Creator<BookmarkCategory>()
@ -361,4 +377,40 @@ public class BookmarkCategory implements Parcelable
return new BookmarkCategory[size];
}
};
public enum AccessRules
{
ACCESS_RULES_LOCAL(R.string.not_shared),
ACCESS_RULES_PUBLIC(R.string.public_access),
ACCESS_RULES_DIRECT_LINK(R.string.limited_access),
ACCESS_RULES_P2P(UiUtils.NO_ID)
{
@Override
public int getResId()
{
throw new IllegalStateException("Unsupported here");
}
},
ACCESS_RULES_PAID(UiUtils.NO_ID)
{
@Override
public int getResId()
{
throw new IllegalStateException("Unsupported here");
}
};
private final int mResId;
AccessRules(int resId)
{
mResId = resId;
}
@StringRes
public int getResId()
{
return mResId;
}
}
}

View file

@ -49,16 +49,6 @@ public enum BookmarkManager
public static final List<Icon> ICONS = new ArrayList<>();
@Retention(RetentionPolicy.SOURCE)
@IntDef({ ACCESS_RULES_LOCAL, ACCESS_RULES_PUBLIC, ACCESS_RULES_DIRECT_LINK,
ACCESS_RULES_P2P, ACCESS_RULES_PAID })
public @interface AccessRules {}
public static final int ACCESS_RULES_LOCAL = 0;
public static final int ACCESS_RULES_PUBLIC = 1;
public static final int ACCESS_RULES_DIRECT_LINK = 2;
public static final int ACCESS_RULES_P2P = 3;
public static final int ACCESS_RULES_PAID = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ UPLOAD_RESULT_SUCCESS, UPLOAD_RESULT_NETWORK_ERROR, UPLOAD_RESULT_SERVER_ERROR,
UPLOAD_RESULT_AUTH_ERROR, UPLOAD_RESULT_MALFORMED_DATA_ERROR,
@ -348,6 +338,21 @@ public enum BookmarkManager
nativeSetCategoryName(catId, name);
}
public void setCategoryDesc(long id, @NonNull String categoryDesc)
{
nativeSetCategoryDesc(id, categoryDesc);
}
public void setAccessRules(long id, @NonNull BookmarkCategory.AccessRules rules)
{
nativeSetCategoryAccessRules(id, rules.ordinal());
}
public void uploadToCatalog(@NonNull BookmarkCategory.AccessRules rules, long id)
{
nativeUploadToCatalog(rules.ordinal(), id);
}
/**
* @return total count - tracks + bookmarks
* @param category
@ -653,18 +658,17 @@ public enum BookmarkManager
private native void nativeSetCategoryName(long catId, @NonNull String n);
private native void nativeSetCategoryDesc(long catId, @NonNull String n);
private native void nativeSetCategoryTags(long catId, @NonNull String[] tagsIds);
private native void nativeSetCategoryAccessRules(long catId, @AccessRules int accessRules);
private native void nativeSetCategoryAccessRules(long catId, int accessRules);
private native void nativeSetCategoryCustomProperty(long catId, String key, String value);
@NonNull
private native String nativeGetCategoryAuthor(long catId);
@AccessRules
private native int nativeGetCategoryAccessRules(long catId);
private static native void nativeLoadBookmarks();
private native boolean nativeDeleteCategory(long catId);
@ -733,7 +737,7 @@ public enum BookmarkManager
private static native void nativeImportFromCatalog(@NonNull String serverId,
@NonNull String filePath);
private static native void nativeUploadToCatalog(@AccessRules int accessRules,
private static native void nativeUploadToCatalog(int accessRules,
long catId);
@NonNull

View file

@ -0,0 +1,57 @@
package com.mapswithme.maps.ugc.routes;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmFragmentActivity;
import com.mapswithme.maps.base.DataObservable;
import com.mapswithme.maps.base.ObservableHost;
public class UgcRouteEditSettingsActivity extends BaseMwmFragmentActivity implements ObservableHost<DataObservable>
{
public static final String EXTRA_BOOKMARK_CATEGORY = "bookmark_category";
private static final String FRAGMENT_TAG = "edit_settings_fragment_tag";
@SuppressWarnings("NullableProblems")
@NonNull
private DataObservable mObservable;
@Override
protected void safeOnCreate(@Nullable Bundle savedInstanceState)
{
super.safeOnCreate(savedInstanceState);
setContentView(R.layout.ugc_route_edit_settings_activity);
mObservable = new DataObservable();
addSettingsFragmentIfAbsent();
}
private void addSettingsFragmentIfAbsent()
{
FragmentManager fm = getSupportFragmentManager();
UgcRouteEditSettingsFragment fragment = (UgcRouteEditSettingsFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (fragment == null)
{
fragment = UgcRouteEditSettingsFragment.makeInstance(getIntent().getExtras());
fm.beginTransaction().add(R.id.fragment_container, fragment, FRAGMENT_TAG).commit();
}
}
@Override
protected Class<? extends Fragment> getFragmentClass()
{
return null;
}
@NonNull
@Override
public DataObservable getObservable()
{
return mObservable;
}
}

View file

@ -0,0 +1,190 @@
package com.mapswithme.maps.ugc.routes;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmToolbarFragment;
import com.mapswithme.maps.base.DataObservable;
import com.mapswithme.maps.base.ObservableHost;
import com.mapswithme.maps.bookmarks.data.BookmarkCategory;
import com.mapswithme.maps.bookmarks.data.BookmarkManager;
import com.mapswithme.maps.widget.ToolbarController;
import java.util.Objects;
public class UgcRouteEditSettingsFragment extends BaseMwmToolbarFragment
{
private static final String SHARING_OPTIONS_FRAGMENT_TAG = "sharing_options_fragment";
@SuppressWarnings("NullableProblems")
@NonNull
private BookmarkCategory mCategory;
@SuppressWarnings("NullableProblems")
@NonNull
private RecyclerView.AdapterDataObserver mObserver;
@SuppressWarnings("NullableProblems")
@NonNull
private TextView mAccessRulesView;
@SuppressWarnings("NullableProblems")
@NonNull
private ObservableHost<DataObservable> mObserverHost;
@SuppressWarnings("NullableProblems")
@NonNull
private EditText mEditDescView;
@SuppressWarnings("NullableProblems")
@NonNull
private EditText mEditCategoryNameView;
@Override
public void onAttach(Context context)
{
super.onAttach(context);
mObserverHost = (ObservableHost<DataObservable>) context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args == null)
throw new IllegalArgumentException("Args must be not null");
mCategory = Objects.requireNonNull(args.getParcelable(UgcRouteEditSettingsActivity.EXTRA_BOOKMARK_CATEGORY));
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
View root = inflater.inflate(R.layout.fragment_ugc_route_edit, container, false);
initViews(root);
mObserver = new CategoryObserver();
mObserverHost.getObservable().registerObserver(mObserver);
return root;
}
private void initViews(@NonNull View root)
{
View sharingOptionsBtn = root.findViewById(R.id.open_sharing_options_screen_btn_container);
mEditCategoryNameView = root.findViewById(R.id.edit_category_name_view);
mEditCategoryNameView.setText(mCategory.getName());
mEditCategoryNameView.requestFocus();
mAccessRulesView = root.findViewById(R.id.sharing_options_desc);
mAccessRulesView.setText(mCategory.getAccessRules().getResId());
mEditDescView = root.findViewById(R.id.edit_description);
mEditDescView.setText(mCategory.getDescription());
View clearNameBtn = root.findViewById(R.id.edit_text_clear_btn);
clearNameBtn.setOnClickListener(v -> mEditCategoryNameView.getEditableText().clear());
sharingOptionsBtn.setOnClickListener(v -> onSharingOptionsClicked());
}
@Override
public void onDestroyView()
{
super.onDestroyView();
mObserverHost.getObservable().unregisterObserver(mObserver);
}
private void onSharingOptionsClicked()
{
openSharingOptionsScreen();
}
private void openSharingOptionsScreen()
{
Fragment fragment = UgcSharingOptionsFragment.makeInstance(mCategory);
getFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment, SHARING_OPTIONS_FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
getToolbarController().setTitle(R.string.edit_list);
}
@NonNull
@Override
protected ToolbarController onCreateToolbarController(@NonNull View root)
{
return new EditSettingsController(root);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == R.id.done)
{
onEditDoneClicked();
return true;
}
return super.onOptionsItemSelected(item);
}
private void onEditDoneClicked()
{
String categoryName = mEditCategoryNameView.getEditableText().toString().trim();
if (!TextUtils.equals(categoryName, mCategory.getName()))
BookmarkManager.INSTANCE.setCategoryName(mCategory.getId(), categoryName);
String categoryDesc = mEditDescView.getEditableText().toString().trim();
if (!TextUtils.equals(mCategory.getDescription(), categoryDesc))
BookmarkManager.INSTANCE.setCategoryDesc(mCategory.getId(), categoryDesc);
}
@NonNull
public static UgcRouteEditSettingsFragment makeInstance(@Nullable Bundle extras)
{
UgcRouteEditSettingsFragment fragment = new UgcRouteEditSettingsFragment();
fragment.setArguments(extras);
return fragment;
}
public class CategoryObserver extends RecyclerView.AdapterDataObserver
{
@Override
public void onChanged()
{
mCategory = BookmarkManager.INSTANCE.getAllCategoriesSnapshot().refresh(mCategory);
mAccessRulesView.setText(mCategory.getAccessRules().getResId());
}
}
private class EditSettingsController extends ToolbarController
{
EditSettingsController(@NonNull View root)
{
super(root, getActivity());
}
@Override
public void onUpClick()
{
getActivity().finish();
}
}
}

View file

@ -22,6 +22,7 @@ import com.mapswithme.maps.auth.BaseMwmAuthorizationFragment;
import com.mapswithme.maps.bookmarks.data.BookmarkManager;
import com.mapswithme.maps.bookmarks.data.CatalogCustomProperty;
import com.mapswithme.maps.bookmarks.data.CatalogTagsGroup;
import com.mapswithme.maps.bookmarks.data.BookmarkCategory;
import com.mapswithme.maps.dialog.AlertDialog;
import com.mapswithme.maps.dialog.ProgressDialogFragment;
import com.mapswithme.maps.widget.ToolbarController;
@ -29,9 +30,11 @@ import com.mapswithme.util.ConnectionState;
import com.mapswithme.util.UiUtils;
import java.util.List;
import java.util.Objects;
public class UgcSharingOptionsFragment extends BaseMwmAuthorizationFragment implements BookmarkManager.BookmarksCatalogListener
{
public static final String EXTRA_BOOKMARK_CATEGORY = "bookmark_category";
private static final String NO_NETWORK_CONNECTION_DIALOG_TAG = "no_network_connection_dialog";
private static final String UPLOADING_PROGRESS_DIALOG_TAG = "uploading_progress_dialog";
@ -39,18 +42,16 @@ public class UgcSharingOptionsFragment extends BaseMwmAuthorizationFragment impl
@NonNull
private View mGetDirectLinkContainer;
@SuppressWarnings("NullableProblems")
@NonNull
private BookmarkCategory mCategory;
@Override
protected ToolbarController onCreateToolbarController(@NonNull View root)
public void onCreate(@Nullable Bundle savedInstanceState)
{
return new ToolbarController(root, getActivity())
{
@Override
public void onUpClick()
{
getActivity().finish();
}
};
super.onCreate(savedInstanceState);
Bundle args = Objects.requireNonNull(getArguments());
mCategory = Objects.requireNonNull(args.getParcelable(EXTRA_BOOKMARK_CATEGORY));
}
@Nullable
@ -78,6 +79,13 @@ public class UgcSharingOptionsFragment extends BaseMwmAuthorizationFragment impl
getToolbarController().setTitle(R.string.sharing_options);
}
@NonNull
@Override
protected ToolbarController onCreateToolbarController(@NonNull View root)
{
return new SharingOptionsController(root);
}
private void initClickListeners(@NonNull View root)
{
View getDirectLinkView = root.findViewById(R.id.get_direct_link_text);
@ -287,4 +295,21 @@ public class UgcSharingOptionsFragment extends BaseMwmAuthorizationFragment impl
{
return true;
}
private class SharingOptionsController extends ToolbarController
{
SharingOptionsController(@NonNull View root)
{
super(root, getActivity());
}
@Override
public void onUpClick()
{
if (getFragmentManager().getBackStackEntryCount() == 0)
getActivity().finish();
else
getFragmentManager().popBackStackImmediate();
}
}
}

View file

@ -13,6 +13,7 @@ import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.DimenRes;
import android.support.annotation.NonNull;