Adding sort switch in FolderPagedView

> Adding options column in DB to store generation purpose flags
> Storing isSorted flag in FolderInfo
> Adding a switch for A-Z sorting (only visible if pageCount > 1)
> When in sorted mode, spring-load snaps to the target location for 1.5 seconds

Change-Id: I8c7c778d2cc3ccbd35a2890a1a705e1c1a7e9a66
This commit is contained in:
Sunny Goyal
2015-03-10 13:14:47 -07:00
parent ce53e6dd12
commit 5d85c44fd8
9 changed files with 336 additions and 47 deletions
+31 -3
View File
@@ -45,7 +45,9 @@
android:id="@+id/folder_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="8dp" >
<com.android.launcher3.FolderEditText
android:id="@+id/folder_name"
@@ -55,7 +57,7 @@
android:layout_weight="1"
android:background="#00000000"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:gravity="start"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
android:paddingBottom="@dimen/folder_name_padding"
@@ -71,8 +73,34 @@
android:id="@+id/folder_page_indicator"
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
android:layout_gravity="top"
android:layout_marginTop="5dp"
layout="@layout/page_indicator" />
<LinearLayout
android:id="@+id/folder_sort"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:gravity="end|center_vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/sort_alphabetical"
android:textColor="#ff777777"
android:textSize="14sp" />
<Switch
android:id="@+id/folder_sort_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:duplicateParentState="true"
android:focusable="false" />
</LinearLayout>
</LinearLayout>
</com.android.launcher3.Folder>
+2
View File
@@ -89,6 +89,8 @@
<string name="rename_action">OK</string>
<!-- Buttons in Rename folder dialog box -->
<string name="cancel_action">Cancel</string>
<!-- Label for button to sort folder contents. [CHAR_LIMIT=10] -->
<string name="sort_alphabetical">A-Z</string>
<!-- Shortcuts -->
<skip />
+22 -6
View File
@@ -85,6 +85,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
*/
public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY;
/**
* Time in milliseconds for which an icon sticks to the target position
* in case of a sorted folder.
*/
private static final int SORTED_STICKY_REORDER_DELAY = 1500;
/**
* Fraction of icon width which behave as scroll region.
*/
@@ -423,8 +429,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (!(getParent() instanceof DragLayer)) return;
if (ALLOW_FOLDER_SCROLL) {
// Always open on the first page.
mPagedView.snapToPageImmediately(0);
mPagedView.completePendingPageChanges();
if (!(mDragInProgress && mPagedView.mIsSorted)) {
// Open on the first page.
mPagedView.snapToPageImmediately(0);
}
}
Animator openFolderAnim = null;
@@ -531,9 +540,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void beginExternalDrag(ShortcutInfo item) {
mCurrentDragInfo = item;
mEmptyCellRank = mContent.allocateNewLastItemRank();
mEmptyCellRank = mContent.allocateRankForNewItem(item);
mIsExternalDrag = true;
mDragInProgress = true;
if (ALLOW_FOLDER_SCROLL && mPagedView.mIsSorted) {
mScrollPauseAlarm.setOnAlarmListener(null);
mScrollPauseAlarm.cancelAlarm();
mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY);
}
}
private void sendCustomAccessibilityEvent(int type, String text) {
@@ -747,6 +762,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (!successfulDrop) {
mSuppressFolderDeletion = true;
}
mScrollPauseAlarm.cancelAlarm();
completeDragExit();
}
}
@@ -1155,7 +1171,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// If the item was dropped onto this open folder, we have done the work associated
// with adding the item to the folder, as indicated by mSuppressOnAdd being set
if (mSuppressOnAdd) return;
mContent.createAndAddViewForRank(item, mContent.allocateNewLastItemRank());
mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
LauncherModel.addOrMoveItemInDatabase(
mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
}
@@ -1304,10 +1320,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> children);
/**
* Create space for a new item at the end, and returns the rank for that item.
* Create space for a new item, and returns the rank for that item.
* Resizes the content if necessary.
*/
int allocateNewLastItemRank();
int allocateRankForNewItem(ShortcutInfo info);
View createAndAddViewForRank(ShortcutInfo item, int rank);
@@ -133,7 +133,7 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent
}
@Override
public int allocateNewLastItemRank() {
public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
mFolder.rearrangeChildren(rank + 1);
return rank;
+32
View File
@@ -29,11 +29,20 @@ import java.util.Arrays;
*/
public class FolderInfo extends ItemInfo {
public static final int NO_FLAGS = 0x00000000;
/**
* The folder is locked in sorted mode
*/
public static final int FLAG_ITEMS_SORTED = 0x00000001;
/**
* Whether this folder has been opened
*/
boolean opened;
public int options;
/**
* The apps and shortcuts
*/
@@ -83,6 +92,8 @@ public class FolderInfo extends ItemInfo {
void onAddToDatabase(Context context, ContentValues values) {
super.onAddToDatabase(context, values);
values.put(LauncherSettings.Favorites.TITLE, title.toString());
values.put(LauncherSettings.Favorites.OPTIONS, options);
}
void addListener(FolderListener listener) {
@@ -121,4 +132,25 @@ public class FolderInfo extends ItemInfo {
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
+ " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
}
public boolean hasOption(int optionFlag) {
return (options & optionFlag) != 0;
}
/**
* @param option flag to set or clear
* @param isEnabled whether to set or clear the flag
* @param context if not null, save changes to the db.
*/
public void setOption(int option, boolean isEnabled, Context context) {
int oldOptions = options;
if (isEnabled) {
options |= option;
} else {
options &= ~option;
}
if (context != null && oldOptions != options) {
LauncherModel.updateItemInDatabase(context, this);
}
}
}
+216 -12
View File
@@ -20,15 +20,22 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.Switch;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
import com.android.launcher3.Workspace.ItemOperator;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -40,14 +47,19 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
private static final int REORDER_ANIMATION_DURATION = 230;
private static final int START_VIEW_REORDER_DELAY = 30;
private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
private static final int SPAN_TO_PAGE_DURATION = 350;
private static final int SORT_ANIM_HIDE_DURATION = 130;
private static final int SORT_ANIM_SHOW_DURATION = 160;
private static final int[] sTempPosArray = new int[2];
// TODO: Remove this restriction
private static final int MAX_ITEMS_PER_PAGE = 3;
private static final int MAX_ITEMS_PER_PAGE = 4;
private final LayoutInflater mInflater;
private final IconCache mIconCache;
private final HashMap<View, Runnable> mPageChangingViews = new HashMap<>();
private final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
private final int mMaxCountX;
private final int mMaxCountY;
@@ -61,6 +73,13 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
private FocusIndicatorView mFocusIndicatorView;
private PagedFolderKeyEventListener mKeyListener;
private View mSortButton;
private Switch mSortSwitch;
private View mPageIndicator;
private boolean mSortOperationPending;
boolean mIsSorted;
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
LauncherAppState app = LauncherAppState.getInstance();
@@ -80,6 +99,134 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
mFolder = folder;
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
mKeyListener = new PagedFolderKeyEventListener(folder);
mSortButton = folder.findViewById(R.id.folder_sort);
mSortButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onSortClicked();
}
});
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
mSortSwitch = (Switch) folder.findViewById(R.id.folder_sort_switch);
}
private void onSortClicked() {
if (mSortOperationPending) {
return;
}
if (mIsSorted) {
setIsSorted(false, true);
} else {
mSortOperationPending = true;
doSort();
}
}
private void setIsSorted(boolean isSorted, boolean saveChanges) {
mIsSorted = isSorted;
mSortSwitch.setChecked(isSorted);
mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted,
saveChanges ? mFolder.mLauncher : null);
}
/**
* Sorts the contents of the folder and animates the icons on the first page to reflect
* the changes.
* Steps:
* 1. Scroll to first page
* 2. Sort all icons in one go
* 3. Re-apply the old IconInfos on the first page (so that there is no instant change)
* 4. Animate each view individually to reflect the new icon.
*/
private void doSort() {
if (!mSortOperationPending) {
return;
}
if (getNextPage() != 0) {
snapToPage(0, SPAN_TO_PAGE_DURATION, new DecelerateInterpolator());
return;
}
mSortOperationPending = false;
ShortcutInfo[][] oldItems = new ShortcutInfo[mGridCountX][mGridCountY];
CellLayout currentPage = getCurrentCellLayout();
for (int x = 0; x < mGridCountX; x++) {
for (int y = 0; y < mGridCountY; y++) {
View v = currentPage.getChildAt(x, y);
if (v != null) {
oldItems[x][y] = (ShortcutInfo) v.getTag();
}
}
}
ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
Collections.sort(views, new ViewComparator());
arrangeChildren(views, views.size());
int delay = 0;
float delayAmount = START_VIEW_REORDER_DELAY;
final Interpolator hideInterpolator = new DecelerateInterpolator(2);
final Interpolator showInterpolator = new OvershootInterpolator(0.8f);
currentPage = getCurrentCellLayout();
for (int x = 0; x < mGridCountX; x++) {
for (int y = 0; y < mGridCountY; y++) {
final BubbleTextView v = (BubbleTextView) currentPage.getChildAt(x, y);
if (v != null) {
final ShortcutInfo info = (ShortcutInfo) v.getTag();
final Runnable clearPending = new Runnable() {
@Override
public void run() {
mPendingAnimations.remove(v);
v.setScaleX(1);
v.setScaleY(1);
}
};
if (oldItems[x][y] == null) {
v.setScaleX(0);
v.setScaleY(0);
v.animate().setDuration(SORT_ANIM_SHOW_DURATION)
.setStartDelay(SORT_ANIM_HIDE_DURATION + delay)
.scaleX(1).scaleY(1).setInterpolator(showInterpolator)
.withEndAction(clearPending);
mPendingAnimations.put(v, clearPending);
} else {
// Apply the old iconInfo so that there is no sudden change.
v.applyFromShortcutInfo(oldItems[x][y], mIconCache, false);
v.animate().setStartDelay(delay).setDuration(SORT_ANIM_HIDE_DURATION)
.scaleX(0).scaleY(0)
.setInterpolator(hideInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
// Apply the new iconInfo as part of the animation.
v.applyFromShortcutInfo(info, mIconCache, false);
v.animate().scaleX(1).scaleY(1)
.setDuration(SORT_ANIM_SHOW_DURATION).setStartDelay(0)
.setInterpolator(showInterpolator)
.withEndAction(clearPending);
}
});
mPendingAnimations.put(v, new Runnable() {
@Override
public void run() {
clearPending.run();
v.applyFromShortcutInfo(info, mIconCache, false);
}
});
}
delay += delayAmount;
delayAmount *= VIEW_REORDER_DELAY_FACTOR;
}
}
}
setIsSorted(true, true);
}
/**
@@ -125,6 +272,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
@Override
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
mIsSorted = mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED);
ArrayList<View> icons = new ArrayList<View>();
for (ShortcutInfo item : items) {
icons.add(createNewView(item));
@@ -138,20 +286,33 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
* Also sets the current page to the last page.
*/
@Override
public int allocateNewLastItemRank() {
public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
int total = rank + 1;
// Rearrange the items as the grid size might change.
mFolder.rearrangeChildren(total);
ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
if (mIsSorted) {
View tmp = new View(getContext());
tmp.setTag(info);
int index = Collections.binarySearch(views, tmp, new ViewComparator());
if (index < 0) {
rank = -index - 1;
} else {
// Item with same name already exists.
// We will just insert it before that item.
rank = index;
}
setCurrentPage(getChildCount() - 1);
}
views.add(rank, null);
arrangeChildren(views, views.size(), false);
setCurrentPage(rank / mMaxItemsPerPage);
return rank;
}
@Override
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
View icon = createNewView(item);
addViewForRank(createNewView(item), item, rank);
addViewForRank(icon, item, rank);
return icon;
}
@@ -259,6 +420,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
int position = 0;
int newX, newY, rank;
boolean isSorted = mIsSorted;
ViewComparator comparator = new ViewComparator();
View lastView = null;
rank = 0;
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
@@ -273,6 +438,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
}
if (v != null) {
if (lastView != null) {
isSorted &= comparator.compare(lastView, v) <= 0;
}
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
newX = position % mGridCountX;
newY = position / mGridCountX;
@@ -292,6 +461,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
}
lastView = v;
rank ++;
position++;
}
@@ -305,6 +475,19 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
if (removed) {
setCurrentPage(0);
}
setIsSorted(isSorted, saveChanges);
// Update footer
if (getPageCount() > 1) {
mPageIndicator.setVisibility(View.VISIBLE);
mSortButton.setVisibility(View.VISIBLE);
mFolder.mFolderName.setGravity(Gravity.START);
} else {
mPageIndicator.setVisibility(View.GONE);
mSortButton.setVisibility(View.GONE);
mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL);
}
}
@Override
@@ -407,6 +590,17 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
if (mFolder != null) {
mFolder.updateTextViewFocus();
}
if (mSortOperationPending && getNextPage() == 0) {
post(new Runnable() {
@Override
public void run() {
if (mSortOperationPending) {
doSort();
}
}
});
}
}
/**
@@ -433,8 +627,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
* Finish animation all the views which are animating across pages
*/
public void completePendingPageChanges() {
if (!mPageChangingViews.isEmpty()) {
HashMap<View, Runnable> pendingViews = new HashMap<>(mPageChangingViews);
if (!mPendingAnimations.isEmpty()) {
HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
e.getKey().animate().cancel();
e.getValue().run();
@@ -533,7 +727,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
@Override
public void run() {
mPageChangingViews.remove(v);
mPendingAnimations.remove(v);
v.setTranslationX(oldTranslateX);
((CellLayout) v.getParent().getParent()).removeView(v);
addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
@@ -544,7 +738,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
.setDuration(REORDER_ANIMATION_DURATION)
.setStartDelay(0)
.withEndAction(endAction);
mPageChangingViews.put(v, endAction);
mPendingAnimations.put(v, endAction);
}
}
moveStart = rankToMove;
@@ -569,4 +763,14 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent {
}
}
}
private static class ViewComparator implements Comparator<View> {
private final Collator mCollator = Collator.getInstance();
@Override
public int compare(View lhs, View rhs) {
return mCollator.compare( ((ShortcutInfo) lhs.getTag()).title.toString(),
((ShortcutInfo) rhs.getTag()).title.toString());
}
}
}
+5 -3
View File
@@ -961,6 +961,7 @@ public class LauncherModel extends BroadcastReceiver
final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
FolderInfo folderInfo = null;
switch (c.getInt(itemTypeIndex)) {
@@ -975,6 +976,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.screenId = c.getInt(screenIndex);
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.options = c.getInt(optionsIndex);
return folderInfo;
}
@@ -1862,9 +1864,8 @@ public class LauncherModel extends BroadcastReceiver
LauncherSettings.Favorites.RESTORED);
final int profileIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.PROFILE_ID);
//final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
//final int displayModeIndex = c.getColumnIndexOrThrow(
// LauncherSettings.Favorites.DISPLAY_MODE);
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
ShortcutInfo info;
String intentDescription;
@@ -2114,6 +2115,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo)) {
+21 -22
View File
@@ -57,7 +57,7 @@ public class LauncherProvider extends ContentProvider {
private static final String TAG = "Launcher.LauncherProvider";
private static final boolean LOGD = false;
private static final int DATABASE_VERSION = 22;
private static final int DATABASE_VERSION = 23;
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -413,7 +413,8 @@ public class LauncherProvider extends ContentProvider {
"modified INTEGER NOT NULL DEFAULT 0," +
"restored INTEGER NOT NULL DEFAULT 0," +
"profileId INTEGER DEFAULT " + userSerialNumber + "," +
"rank INTEGER NOT NULL DEFAULT 0" +
"rank INTEGER NOT NULL DEFAULT 0," +
"options INTEGER NOT NULL DEFAULT 0" +
");");
addWorkspacesTable(db);
@@ -524,18 +525,9 @@ public class LauncherProvider extends ContentProvider {
}
}
case 15: {
db.beginTransaction();
try {
// Insert new column for holding restore status
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
db.setTransactionSuccessful();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
if (!addIntegerColumn(db, Favorites.RESTORED, 0)) {
// Old version remains, which means we wipe old data
break;
} finally {
db.endTransaction();
}
}
case 16: {
@@ -573,6 +565,12 @@ public class LauncherProvider extends ContentProvider {
break;
}
case 22: {
if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
// Old version remains, which means we wipe old data
break;
}
}
case 23: {
// DB Upgraded successfully
return;
}
@@ -682,20 +680,21 @@ public class LauncherProvider extends ContentProvider {
}
private boolean addProfileColumn(SQLiteDatabase db) {
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
// Default to the serial number of this user, for older
// shortcuts.
long userSerialNumber = userManager.getSerialNumberForUser(
UserHandleCompat.myUserHandle());
return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber);
}
private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
db.beginTransaction();
try {
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
// Default to the serial number of this user, for older
// shortcuts.
long userSerialNumber = userManager.getSerialNumberForUser(
UserHandleCompat.myUserHandle());
// Insert new column for holding user serial number
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN profileId INTEGER DEFAULT "
+ userSerialNumber + ";");
db.execSQL("ALTER TABLE favorites ADD COLUMN "
+ columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";");
db.setTransactionSuccessful();
} catch (SQLException ex) {
// Old version remains, which means we wipe old data
Log.e(TAG, ex.getMessage(), ex);
return false;
} finally {
@@ -309,5 +309,11 @@ class LauncherSettings {
* <p>Type: INTEGER</p>
*/
static final String RANK = "rank";
/**
* Stores general flag based options for {@link ItemInfo}s.
* <p>Type: INTEGER</p>
*/
static final String OPTIONS = "options";
}
}