diff --git a/android/3rd_party/BottomSheet/AndroidManifest.xml b/android/3rd_party/BottomSheet/AndroidManifest.xml new file mode 100644 index 0000000000..96bc9ad32a --- /dev/null +++ b/android/3rd_party/BottomSheet/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/3rd_party/BottomSheet/build.gradle b/android/3rd_party/BottomSheet/build.gradle new file mode 100644 index 0000000000..375b7ce36c --- /dev/null +++ b/android/3rd_party/BottomSheet/build.gradle @@ -0,0 +1,23 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.2.3' + } +} + + +apply plugin: 'com.android.library' + + +dependencies { + compile 'com.android.support:support-v4:22.2.0' +} + + +android { + compileSdkVersion propTargetSdkVersion.toInteger() + buildToolsVersion propBuildToolsVersion +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/gradle.properties b/android/3rd_party/BottomSheet/gradle.properties new file mode 100644 index 0000000000..c29e2d0984 --- /dev/null +++ b/android/3rd_party/BottomSheet/gradle.properties @@ -0,0 +1 @@ +manifestmerger.enabled=false diff --git a/android/3rd_party/BottomSheet/gradlew b/android/3rd_party/BottomSheet/gradlew new file mode 100644 index 0000000000..91a7e269e1 --- /dev/null +++ b/android/3rd_party/BottomSheet/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/3rd_party/BottomSheet/gradlew.bat b/android/3rd_party/BottomSheet/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/android/3rd_party/BottomSheet/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/3rd_party/BottomSheet/src/main/AndroidManifest.xml b/android/3rd_party/BottomSheet/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..942c6c4b05 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenu.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenu.java new file mode 100644 index 0000000000..2d1672465c --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenu.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cocosw.bottomsheet; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.v4.internal.view.SupportMenu; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; + +/** + * @hide + */ + class ActionMenu implements SupportMenu { + private Context mContext; + + private boolean mIsQwerty; + + private static final int[] sCategoryToOrder = new int[]{ + 1, /* No category */ + 4, /* CONTAINER */ + 5, /* SYSTEM */ + 3, /* SECONDARY */ + 2, /* ALTERNATIVE */ + 0, /* SELECTED_ALTERNATIVE */ + }; + + private ArrayList mItems; + + public ActionMenu(Context context) { + mContext = context; + mItems = new ArrayList<>(); + } + + public Context getContext() { + return mContext; + } + + public MenuItem add(CharSequence title) { + return add(0, 0, 0, title); + } + + public MenuItem add(int titleRes) { + return add(0, 0, 0, titleRes); + } + + public MenuItem add(int groupId, int itemId, int order, int titleRes) { + return add(groupId, itemId, order, mContext.getResources().getString(titleRes)); + } + + public MenuItem add(int groupId, int itemId, int order, CharSequence title) { + ActionMenuItem item = new ActionMenuItem(getContext(), + groupId, itemId, 0, order, title); + mItems.add(findInsertIndex(mItems, getOrdering(order)), item); + return item; + } + + + private static int findInsertIndex(ArrayList items, int ordering) { + for (int i = items.size() - 1; i >= 0; i--) { + ActionMenuItem item = items.get(i); + if (item.getOrder() <= ordering) { + return i + 1; + } + } + return 0; + } + + MenuItem add(ActionMenuItem item) { + mItems.add(findInsertIndex(mItems, getOrdering(item.getOrder())),item); + return item; + } + + /** + * Returns the ordering across all items. This will grab the category from + * the upper bits, find out how to order the category with respect to other + * categories, and combine it with the lower bits. + * + * @param categoryOrder The category order for a particular item (if it has + * not been or/add with a category, the default category is + * assumed). + * @return An ordering integer that can be used to order this item across + * all the items (even from other categories). + */ + private static int getOrdering(int categoryOrder) { + final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT; + + if (index < 0 || index >= sCategoryToOrder.length) { + throw new IllegalArgumentException("order does not contain a valid category."); + } + + return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK); + } + + public int addIntentOptions(int groupId, int itemId, int order, + ComponentName caller, Intent[] specifics, Intent intent, int flags, + MenuItem[] outSpecificItems) { + PackageManager pm = mContext.getPackageManager(); + final List lri = + pm.queryIntentActivityOptions(caller, specifics, intent, 0); + final int N = lri != null ? lri.size() : 0; + + if ((flags & FLAG_APPEND_TO_GROUP) == 0) { + removeGroup(groupId); + } + + for (int i=0; i= 0) { + outSpecificItems[ri.specificIndex] = item; + } + } + + return N; + } + + public SubMenu addSubMenu(CharSequence title) { + // TODO Implement submenus + return null; + } + + public SubMenu addSubMenu(int titleRes) { + // TODO Implement submenus + return null; + } + + public SubMenu addSubMenu(int groupId, int itemId, int order, + CharSequence title) { + // TODO Implement submenus + return null; + } + + public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { + // TODO Implement submenus + return null; + } + + public void clear() { + mItems.clear(); + } + + public void close() { + } + + private int findItemIndex(int id) { + final ArrayList items = mItems; + final int itemCount = items.size(); + for (int i = 0; i < itemCount; i++) { + if (items.get(i).getItemId() == id) { + return i; + } + } + return -1; + } + + public MenuItem findItem(int id) { + return mItems.get(findItemIndex(id)); + } + + public MenuItem getItem(int index) { + return mItems.get(index); + } + + public boolean hasVisibleItems() { + final ArrayList items = mItems; + final int itemCount = items.size(); + + for (int i = 0; i < itemCount; i++) { + if (items.get(i).isVisible()) { + return true; + } + } + + return false; + } + + private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) { + // TODO Make this smarter. + final boolean qwerty = mIsQwerty; + final ArrayList items = mItems; + final int itemCount = items.size(); + + for (int i = 0; i < itemCount; i++) { + ActionMenuItem item = items.get(i); + final char shortcut = qwerty ? item.getAlphabeticShortcut() : + item.getNumericShortcut(); + if (keyCode == shortcut) { + return item; + } + } + return null; + } + + public boolean isShortcutKey(int keyCode, KeyEvent event) { + return findItemWithShortcut(keyCode, event) != null; + } + + public boolean performIdentifierAction(int id, int flags) { + final int index = findItemIndex(id); + if (index < 0) { + return false; + } + + return mItems.get(index).invoke(); + } + + public boolean performShortcut(int keyCode, KeyEvent event, int flags) { + ActionMenuItem item = findItemWithShortcut(keyCode, event); + if (item == null) { + return false; + } + + return item.invoke(); + } + + public void removeGroup(int groupId) { + final ArrayList items = mItems; + int itemCount = items.size(); + int i = 0; + while (i < itemCount) { + if (items.get(i).getGroupId() == groupId) { + items.remove(i); + itemCount--; + } else { + i++; + } + } + } + + public void removeItem(int id) { + mItems.remove(findItemIndex(id)); + } + + public void setGroupCheckable(int group, boolean checkable, + boolean exclusive) { + final ArrayList items = mItems; + final int itemCount = items.size(); + + for (int i = 0; i < itemCount; i++) { + ActionMenuItem item = items.get(i); + if (item.getGroupId() == group) { + item.setCheckable(checkable); + item.setExclusiveCheckable(exclusive); + } + } + } + + public void setGroupEnabled(int group, boolean enabled) { + final ArrayList items = mItems; + final int itemCount = items.size(); + + for (int i = 0; i < itemCount; i++) { + ActionMenuItem item = items.get(i); + if (item.getGroupId() == group) { + item.setEnabled(enabled); + } + } + } + + public void setGroupVisible(int group, boolean visible) { + final ArrayList items = mItems; + final int itemCount = items.size(); + + for (int i = 0; i < itemCount; i++) { + ActionMenuItem item = items.get(i); + if (item.getGroupId() == group) { + item.setVisible(visible); + } + } + } + + public void setQwertyMode(boolean isQwerty) { + mIsQwerty = isQwerty; + } + + public int size() { + return mItems.size(); + } + + ActionMenu clone(int size) { + ActionMenu out = new ActionMenu(getContext()); + out.mItems = new ArrayList<>(this.mItems.subList(0,size)); + return out; + } + + void removeInvisible() { + Iterator iter = mItems.iterator(); + while (iter.hasNext()) { + ActionMenuItem item = iter.next(); + if (!item.isVisible()) iter.remove(); + } + } +} diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenuItem.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenuItem.java new file mode 100644 index 0000000000..26719ade26 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ActionMenuItem.java @@ -0,0 +1,296 @@ +package com.cocosw.bottomsheet; + +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v4.internal.view.SupportMenuItem; +import android.support.v4.view.MenuItemCompat; +import android.view.ActionProvider; +import android.view.ContextMenu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; + + + class ActionMenuItem implements SupportMenuItem { + + + private final int mId; + private final int mGroup; + private final int mCategoryOrder; + private final int mOrdering; + + private CharSequence mTitle; + private CharSequence mTitleCondensed; + private Intent mIntent; + private char mShortcutNumericChar; + private char mShortcutAlphabeticChar; + + private Drawable mIconDrawable; + private int mIconResId = NO_ICON; + + private Context mContext; + + private SupportMenuItem.OnMenuItemClickListener mClickListener; + + private static final int NO_ICON = 0; + + private int mFlags = ENABLED; + private static final int CHECKABLE = 0x00000001; + private static final int CHECKED = 0x00000002; + private static final int EXCLUSIVE = 0x00000004; + private static final int HIDDEN = 0x00000008; + private static final int ENABLED = 0x00000010; + + public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering, + CharSequence title) { + mContext = context; + mId = id; + mGroup = group; + mCategoryOrder = categoryOrder; + mOrdering = ordering; + mTitle = title; + } + + public char getAlphabeticShortcut() { + return mShortcutAlphabeticChar; + } + + public int getGroupId() { + return mGroup; + } + + public Drawable getIcon() { + return mIconDrawable; + } + + public Intent getIntent() { + return mIntent; + } + + public int getItemId() { + return mId; + } + + public ContextMenu.ContextMenuInfo getMenuInfo() { + return null; + } + + public char getNumericShortcut() { + return mShortcutNumericChar; + } + + public int getOrder() { + return mOrdering; + } + + public SubMenu getSubMenu() { + return null; + } + + public CharSequence getTitle() { + return mTitle; + } + + public CharSequence getTitleCondensed() { + return mTitleCondensed != null ? mTitleCondensed : mTitle; + } + + public boolean hasSubMenu() { + return false; + } + + public boolean isCheckable() { + return (mFlags & CHECKABLE) != 0; + } + + public boolean isChecked() { + return (mFlags & CHECKED) != 0; + } + + public boolean isEnabled() { + return (mFlags & ENABLED) != 0; + } + + public boolean isVisible() { + return (mFlags & HIDDEN) == 0; + } + + public MenuItem setAlphabeticShortcut(char alphaChar) { + mShortcutAlphabeticChar = alphaChar; + return this; + } + + public MenuItem setCheckable(boolean checkable) { + mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); + return this; + } + + public ActionMenuItem setExclusiveCheckable(boolean exclusive) { + mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); + return this; + } + + public MenuItem setChecked(boolean checked) { + mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); + return this; + } + + public MenuItem setEnabled(boolean enabled) { + mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0); + return this; + } + + public MenuItem setIcon(Drawable icon) { + mIconDrawable = icon; + mIconResId = NO_ICON; + return this; + } + + public MenuItem setIcon(int iconRes) { + mIconResId = iconRes; + if (iconRes>0) + mIconDrawable = ContextCompat.getDrawable(mContext, iconRes); + return this; + } + + public MenuItem setIntent(Intent intent) { + mIntent = intent; + return this; + } + + public MenuItem setNumericShortcut(char numericChar) { + mShortcutNumericChar = numericChar; + return this; + } + + public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { + mClickListener = menuItemClickListener; + return this; + } + + public MenuItem setShortcut(char numericChar, char alphaChar) { + mShortcutNumericChar = numericChar; + mShortcutAlphabeticChar = alphaChar; + return this; + } + + public MenuItem setTitle(CharSequence title) { + mTitle = title; + return this; + } + + public MenuItem setTitle(int title) { + mTitle = mContext.getResources().getString(title); + return this; + } + + public MenuItem setTitleCondensed(CharSequence title) { + mTitleCondensed = title; + return this; + } + + public MenuItem setVisible(boolean visible) { + mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN); + return this; + } + + public boolean invoke() { + if (mClickListener != null && mClickListener.onMenuItemClick(this)) { + return true; + } + + if (mIntent != null) { + mContext.startActivity(mIntent); + return true; + } + + return false; + } + + public void setShowAsAction(int show) { + // Do nothing. ActionMenuItems always show as action buttons. + } + + public SupportMenuItem setActionView(View actionView) { + throw new UnsupportedOperationException(); + } + + public View getActionView() { + return null; + } + + @Override + public MenuItem setActionProvider(android.view.ActionProvider actionProvider) { + throw new UnsupportedOperationException(); + } + + @Override + public android.view.ActionProvider getActionProvider() { + throw new UnsupportedOperationException(); + } + + @Override + public SupportMenuItem setActionView(int resId) { + throw new UnsupportedOperationException(); + } + + @Override + public android.support.v4.view.ActionProvider getSupportActionProvider() { + return null; + } + + @Override + public SupportMenuItem setSupportActionProvider(android.support.v4.view.ActionProvider actionProvider) { + throw new UnsupportedOperationException(); + } + + @Override + public SupportMenuItem setShowAsActionFlags(int actionEnum) { + setShowAsAction(actionEnum); + return this; + } + + @Override + public boolean expandActionView() { + return false; + } + + @Override + public boolean collapseActionView() { + return false; + } + + @Override + public boolean isActionViewExpanded() { + return false; + } + + @Override + public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public SupportMenuItem setSupportOnActionExpandListener(MenuItemCompat.OnActionExpandListener listener) { + // No need to save the listener; ActionMenuItem does not support collapsing items. + return this; + } + } 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 new file mode 100644 index 0000000000..0fd5cbe07f --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/BottomSheet.java @@ -0,0 +1,860 @@ +/* + * Copyright 2011, 2015 Kai Liao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cocosw.bottomsheet; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.DrawableRes; +import android.support.annotation.IntegerRes; +import android.support.annotation.MenuRes; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.annotation.StyleRes; +import android.transition.ChangeBounds; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.util.DisplayMetrics; +import android.util.SparseIntArray; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.Window; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.GridView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; + + +/** + * One way to present a set of actions to a user is with bottom sheets, a sheet of paper that slides up from the bottom edge of the screen. Bottom sheets offer flexibility in the display of clear and simple actions that do not need explanation. + *

+ * https://www.google.com/design/spec/components/bottom-sheets.html + *

+ * Project: BottomSheet + * Created by Kai Liao on 2014/9/21. + */ +@SuppressWarnings("unused") +public class BottomSheet extends Dialog implements DialogInterface { + + private String moreText; + private Drawable close; + private Drawable more; + private boolean collapseListIcons; + private int mStatusBarHeight; + private GridView list; + private SimpleSectionedGridAdapter adapter; + private Builder builder; + + private final SparseIntArray hidden = new SparseIntArray(); + + // translucent support + private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height"; + private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height"; + private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape"; + private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar"; + private boolean mInPortrait; + private String sNavBarOverride; + private boolean mNavBarAvailable; + private float mSmallestWidthDp; + + + private ImageView icon; + + private int limit = -1; + private boolean cancelOnTouchOutside = true; + private boolean cancelOnSwipeDown = true; + private ActionMenu fullMenuItem; + private ActionMenu menuItem; + private ActionMenu actions; + private OnDismissListener dismissListener; + + BottomSheet(Context context) { + super(context, R.style.BottomSheet_Dialog); + } + + @SuppressWarnings("WeakerAccess") + BottomSheet(Context context, int theme) { + super(context, theme); + + TypedArray a = getContext() + .obtainStyledAttributes(null, R.styleable.BottomSheet, R.attr.bottomSheetStyle, 0); + try { + more = a.getDrawable(R.styleable.BottomSheet_bs_moreDrawable); + close = a.getDrawable(R.styleable.BottomSheet_bs_closeDrawable); + moreText = a.getString(R.styleable.BottomSheet_bs_moreText); + collapseListIcons = a.getBoolean(R.styleable.BottomSheet_bs_collapseListIcons, true); + } finally { + a.recycle(); + } + + // https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mInPortrait = (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT); + try { + Class c = Class.forName("android.os.SystemProperties"); + @SuppressWarnings("unchecked") Method m = c.getDeclaredMethod("get", String.class); + m.setAccessible(true); + sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys"); + } catch (Throwable e) { + sNavBarOverride = null; + } + + // check theme attrs + int[] as = {android.R.attr.windowTranslucentNavigation}; + a = context.obtainStyledAttributes(as); + try { + mNavBarAvailable = a.getBoolean(0, false); + } finally { + a.recycle(); + } + + // check window flags + WindowManager.LayoutParams winParams = ((Activity) context).getWindow().getAttributes(); + + int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + if ((winParams.flags & bits) != 0) { + mNavBarAvailable = true; + } + + mSmallestWidthDp = getSmallestWidthDp(wm); + if (mNavBarAvailable) + setTranslucentStatus(true); + mStatusBarHeight = getInternalDimensionSize(context.getResources(), STATUS_BAR_HEIGHT_RES_NAME); + } + } + + @SuppressLint("NewApi") + private float getSmallestWidthDp(WindowManager wm) { + DisplayMetrics metrics = new DisplayMetrics(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + wm.getDefaultDisplay().getRealMetrics(metrics); + } else { + //this is not correct, but we don't really care pre-kitkat + wm.getDefaultDisplay().getMetrics(metrics); + } + float widthDp = metrics.widthPixels / metrics.density; + float heightDp = metrics.heightPixels / metrics.density; + return Math.min(widthDp, heightDp); + } + + @TargetApi(14) + private int getNavigationBarHeight(Context context) { + Resources res = context.getResources(); + int result = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (hasNavBar(context)) { + String key; + if (mInPortrait) { + key = NAV_BAR_HEIGHT_RES_NAME; + } else { + if (!isNavigationAtBottom()) + return 0; + key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME; + } + return getInternalDimensionSize(res, key); + } + } + return result; + } + + @TargetApi(14) + private boolean hasNavBar(Context context) { + Resources res = context.getResources(); + int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android"); + if (resourceId != 0) { + boolean hasNav = res.getBoolean(resourceId); + // check override flag (see static block) + if ("1".equals(sNavBarOverride)) { + hasNav = false; + } else if ("0".equals(sNavBarOverride)) { + hasNav = true; + } + return hasNav; + } else { // fallback + return !ViewConfiguration.get(context).hasPermanentMenuKey(); + } + } + + private int getInternalDimensionSize(Resources res, String key) { + int result = 0; + int resourceId = res.getIdentifier(key, "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + /** + * Should a navigation bar appear at the bottom of the screen in the current + * device configuration? A navigation bar may appear on the right side of + * the screen in certain configurations. + * + * @return True if navigation should appear at the bottom of the screen, False otherwise. + */ + private boolean isNavigationAtBottom() { + return (mSmallestWidthDp >= 600 || mInPortrait); + } + + /** + * Hacky way to get gridview's column number + */ + private int getNumColumns() { + try { + Field numColumns = GridView.class.getDeclaredField("mRequestedNumColumns"); + numColumns.setAccessible(true); + return numColumns.getInt(list); + } catch (Exception e) { + return 1; + } + } + + @Override + public void setCanceledOnTouchOutside(boolean cancel) { + super.setCanceledOnTouchOutside(cancel); + cancelOnTouchOutside = cancel; + } + + /** + * Sets whether this dialog is canceled when swipe it down + * + * @param cancel whether this dialog is canceled when swipe it down + */ + public void setCanceledOnSwipeDown(boolean cancel) { + cancelOnSwipeDown = cancel; + } + + private void init(final Context context) { + setCanceledOnTouchOutside(cancelOnTouchOutside); + final ClosableSlidingLayout mDialogView = (ClosableSlidingLayout) View.inflate(context, R.layout.bottom_sheet_dialog, null); + setContentView(mDialogView); + if (!cancelOnSwipeDown) + mDialogView.swipeable = cancelOnSwipeDown; + mDialogView.setSlideListener(new ClosableSlidingLayout.SlideListener() { + @Override + public void onClosed() { + BottomSheet.this.dismiss(); + } + + @Override + public void onOpened() { + showFullItems(); + } + }); + + + this.setOnShowListener(new OnShowListener() { + @Override + public void onShow(DialogInterface dialogInterface) { + list.setAdapter(adapter); + list.startLayoutAnimation(); + if (builder.icon == null) + icon.setVisibility(View.GONE); + else { + icon.setVisibility(View.VISIBLE); + icon.setImageDrawable(builder.icon); + } + } + }); + int[] location = new int[2]; + mDialogView.getLocationOnScreen(location); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mDialogView.setPadding(0, location[0] == 0 ? mStatusBarHeight : 0, 0, 0); + mDialogView.getChildAt(0).setPadding(0, 0, 0, mNavBarAvailable ? getNavigationBarHeight(getContext()) + mDialogView.getPaddingBottom() : 0); + } + + final TextView title = (TextView) mDialogView.findViewById(R.id.bottom_sheet_title); + if (builder.title != null) { + title.setVisibility(View.VISIBLE); + title.setText(builder.title); + } + + icon = (ImageView) mDialogView.findViewById(R.id.bottom_sheet_title_image); + + + list = (GridView) mDialogView.findViewById(R.id.bottom_sheet_gridview); + mDialogView.mTarget = list; + if (!builder.grid) { + list.setNumColumns(1); + } + + if (builder.grid) { + for (int i = 0; i < getMenu().size(); i++) { + if (getMenu().getItem(i).getIcon() == null) + throw new IllegalArgumentException("You must set icon for each items in grid style"); + } + } + + if (builder.limit > 0) + limit = builder.limit * getNumColumns(); + else + limit = Integer.MAX_VALUE; + + mDialogView.setCollapsible(false); + + actions = builder.menu; + menuItem = actions; + // over the initial numbers + if (getMenu().size() > limit) { + fullMenuItem = builder.menu; + menuItem = builder.menu.clone(limit - 1); + ActionMenuItem item = new ActionMenuItem(context, 0, R.id.bs_more, 0, limit - 1, moreText); + item.setIcon(more); + menuItem.add(item); + actions = menuItem; + mDialogView.setCollapsible(true); + } + + BaseAdapter baseAdapter = new BaseAdapter() { + + + @Override + public int getCount() { + return actions.size() - hidden.size(); + } + + @Override + public MenuItem getItem(int position) { + return actions.getItem(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public boolean isEnabled(int position) { + return getItem(position).isEnabled(); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) getContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + if (builder.grid) + convertView = inflater.inflate(R.layout.bs_grid_entry, parent, false); + else + convertView = inflater.inflate(R.layout.bs_list_entry, parent, false); + holder = new ViewHolder(); + holder.title = (TextView) convertView.findViewById(R.id.bs_list_title); + holder.image = (ImageView) convertView.findViewById(R.id.bs_list_image); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + for (int i = 0; i < hidden.size(); i++) { + if (hidden.valueAt(i) <= position) + position++; + } + + MenuItem item = getItem(position); + + holder.title.setText(item.getTitle()); + if (item.getIcon() == null) + holder.image.setVisibility(collapseListIcons ? View.GONE : View.INVISIBLE); + else { + holder.image.setVisibility(View.VISIBLE); + holder.image.setImageDrawable(item.getIcon()); + } + + holder.image.setEnabled(item.isEnabled()); + holder.title.setEnabled(item.isEnabled()); + + return convertView; + } + + class ViewHolder { + private TextView title; + private ImageView image; + } + }; + + adapter = new SimpleSectionedGridAdapter(context, baseAdapter, R.layout.bs_list_divider, R.id.headerlayout, R.id.header); + list.setAdapter(adapter); + adapter.setGridView(list); + + updateSection(); + + list.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (((MenuItem) adapter.getItem(position)).getItemId() == R.id.bs_more) { + showFullItems(); + mDialogView.setCollapsible(false); + return; + } + + if (!((ActionMenuItem) adapter.getItem(position)).invoke()) { + if (builder.menulistener != null) + builder.menulistener.onMenuItemClick((MenuItem) adapter.getItem(position)); + else if (builder.listener != null) + builder.listener.onClick(BottomSheet.this, ((MenuItem) adapter.getItem(position)).getItemId()); + } + dismiss(); + } + }); + + if (builder.dismissListener != null) { + setOnDismissListener(builder.dismissListener); + } + setListLayout(); + } + + + private void updateSection() { + actions.removeInvisible(); + + if (!builder.grid && actions.size() > 0) { + int groupId = actions.getItem(0).getGroupId(); + ArrayList sections = new ArrayList<>(); + for (int i = 0; i < actions.size(); i++) { + if (actions.getItem(i).getGroupId() != groupId) { + groupId = actions.getItem(i).getGroupId(); + sections.add(new SimpleSectionedGridAdapter.Section(i, null)); + } + } + if (sections.size() > 0) { + SimpleSectionedGridAdapter.Section[] s = new SimpleSectionedGridAdapter.Section[sections.size()]; + sections.toArray(s); + adapter.setSections(s); + } else { + adapter.mSections.clear(); + } + } + } + + private void showFullItems() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Transition changeBounds = new ChangeBounds(); + changeBounds.setDuration(300); + TransitionManager.beginDelayedTransition(list, changeBounds); + } + actions = fullMenuItem; + updateSection(); + adapter.notifyDataSetChanged(); + list.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); + icon.setVisibility(View.VISIBLE); + icon.setImageDrawable(close); + icon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showShortItems(); + } + }); + setListLayout(); + } + + private void showShortItems() { + actions = menuItem; + updateSection(); + adapter.notifyDataSetChanged(); + setListLayout(); + + if (builder.icon == null) + icon.setVisibility(View.GONE); + else { + icon.setVisibility(View.VISIBLE); + icon.setImageDrawable(builder.icon); + } + } + + private boolean hasDivider() { + return adapter.mSections.size() > 0; + } + + private void setListLayout() { + // without divider, the height of gridview is correct + if (!hasDivider()) + return; + list.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT < 16) { + //noinspection deprecation + list.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + list.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + View lastChild = list.getChildAt(list.getChildCount() - 1); + if (lastChild != null) + list.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, lastChild.getBottom() + lastChild.getPaddingBottom() + list.getPaddingBottom())); + } + }); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + init(getContext()); + + WindowManager.LayoutParams params = getWindow().getAttributes(); + params.height = ViewGroup.LayoutParams.WRAP_CONTENT; + params.gravity = Gravity.BOTTOM; + + TypedArray a = getContext().obtainStyledAttributes(new int[]{android.R.attr.layout_width}); + try { + params.width = a.getLayoutDimension(0, ViewGroup.LayoutParams.MATCH_PARENT); + } finally { + a.recycle(); + } + super.setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (dismissListener != null) + dismissListener.onDismiss(dialog); + if (limit != Integer.MAX_VALUE) + showShortItems(); + } + }); + getWindow().setAttributes(params); + } + + @SuppressWarnings("SameParameterValue") + @TargetApi(19) + private void setTranslucentStatus(boolean on) { + Window win = getWindow(); + WindowManager.LayoutParams winParams = win.getAttributes(); + final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + if (on) { + winParams.flags |= bits; + } else { + winParams.flags &= ~bits; + } + + win.setAttributes(winParams); + // instance + win.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, + WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + } + + public Menu getMenu() { + return builder.menu; + } + + /** + * If you make any changes to menu and try to apply it immediately to your bottomsheet, you should call this. + */ + public void invalidate() { + updateSection(); + adapter.notifyDataSetChanged(); + setListLayout(); + } + + @Override + public void setOnDismissListener(OnDismissListener listener) { + this.dismissListener = listener; + } + + /** + * Constructor using a context for this builder and the {@link com.cocosw.bottomsheet.BottomSheet} it creates. + */ + public static class Builder { + + private final Context context; + private int theme; + private final ActionMenu menu; + private CharSequence title; + private boolean grid; + private OnClickListener listener; + private OnDismissListener dismissListener; + private Drawable icon; + private int limit = -1; + private MenuItem.OnMenuItemClickListener menulistener; + + + /** + * Constructor using a context for this builder and the {@link com.cocosw.bottomsheet.BottomSheet} it creates. + * + * @param context A Context for built BottomSheet. + */ + public Builder(@NonNull Activity context) { + this(context, R.style.BottomSheet_Dialog); + TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.bottomSheetStyle}); + try { + theme = ta.getResourceId(0, R.style.BottomSheet_Dialog); + } finally { + ta.recycle(); + } + } + + /** + * Constructor using a context for this builder and the {@link com.cocosw.bottomsheet.BottomSheet} it creates with given style + * + * @param context A Context for built BottomSheet. + * @param theme The theme id will be apply to BottomSheet + */ + public Builder(Context context, @StyleRes int theme) { + this.context = context; + this.theme = theme; + this.menu = new ActionMenu(context); + } + + /** + * Set menu resources as list item to display in BottomSheet + * + * @param xmlRes menu resource id + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder sheet(@MenuRes int xmlRes) { + new MenuInflater(context).inflate(xmlRes, menu); + return this; + } + + + /** + * Add one item into BottomSheet + * + * @param id ID of item + * @param iconRes icon resource + * @param textRes text resource + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder sheet(int id, @DrawableRes int iconRes, @StringRes int textRes) { + ActionMenuItem item = new ActionMenuItem(context, 0, id, 0, 0, context.getText(textRes)); + item.setIcon(iconRes); + menu.add(item); + return this; + } + + /** + * Add one item into BottomSheet + * + * @param id ID of item + * @param icon icon + * @param text text + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder sheet(int id, @NonNull Drawable icon, @NonNull CharSequence text) { + ActionMenuItem item = new ActionMenuItem(context, 0, id, 0, 0, text); + item.setIcon(icon); + menu.add(item); + return this; + } + + /** + * Add one item without icon into BottomSheet + * + * @param id ID of item + * @param textRes text resource + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder sheet(int id, @StringRes int textRes) { + menu.add(0, id, 0, textRes); + return this; + } + + /** + * Add one item without icon into BottomSheet + * + * @param id ID of item + * @param text text + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder sheet(int id, @NonNull CharSequence text) { + menu.add(0, id, 0, text); + return this; + } + + /** + * Set title for BottomSheet + * + * @param titleRes title for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder title(@StringRes int titleRes) { + title = context.getText(titleRes); + return this; + } + + /** + * Remove an item from BottomSheet + * + * @param id ID of item + * @return This Builder object to allow for chaining of calls to set methods + */ + @Deprecated + public Builder remove(int id) { + menu.removeItem(id); + return this; + } + + /** + * Set title for BottomSheet + * + * @param icon icon for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder icon(Drawable icon) { + this.icon = icon; + return this; + } + + /** + * Set title for BottomSheet + * + * @param iconRes icon resource id for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder icon(@DrawableRes int iconRes) { + this.icon = context.getResources().getDrawable(iconRes); + return this; + } + + /** + * Set OnclickListener for BottomSheet + * + * @param listener OnclickListener for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder listener(@NonNull OnClickListener listener) { + this.listener = listener; + return this; + } + + /** + * Set OnMenuItemClickListener for BottomSheet + * + * @param listener OnMenuItemClickListener for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder listener(@NonNull MenuItem.OnMenuItemClickListener listener) { + this.menulistener = listener; + return this; + } + + + /** + * Show BottomSheet in dark color theme looking + * + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder darkTheme() { + theme = R.style.BottomSheet_Dialog_Dark; + return this; + } + + + /** + * Show BottomSheet + * + * @return Instance of bottomsheet + */ + public BottomSheet show() { + BottomSheet dialog = build(); + dialog.show(); + return dialog; + } + + /** + * Show items in grid instead of list + * + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder grid() { + this.grid = true; + return this; + } + + /** + * Set initial number of actions which will be shown in current sheet. + * If more actions need to be shown, a "more" action will be displayed in the last position. + * + * @param limitRes resource id for initial number of actions + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder limit(@IntegerRes int limitRes) { + limit = context.getResources().getInteger(limitRes); + return this; + } + + + /** + * Create a BottomSheet but not show it + * + * @return Instance of bottomsheet + */ + @SuppressLint("Override") + public BottomSheet build() { + BottomSheet dialog = new BottomSheet(context, theme); + dialog.builder = this; + return dialog; + } + + /** + * Set title for BottomSheet + * + * @param title title for BottomSheet + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder title(CharSequence title) { + this.title = title; + return this; + } + + /** + * Set the OnDismissListener for BottomSheet + * + * @param listener OnDismissListener for Bottom + * @return This Builder object to allow for chaining of calls to set methods + */ + public Builder setOnDismissListener(@NonNull OnDismissListener listener) { + this.dismissListener = listener; + return this; + } + } + + +} diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ClosableSlidingLayout.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ClosableSlidingLayout.java new file mode 100644 index 0000000000..beee1783e5 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/ClosableSlidingLayout.java @@ -0,0 +1,228 @@ +package com.cocosw.bottomsheet; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.ViewDragHelper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.AbsListView; +import android.widget.FrameLayout; + +/** + * Project: gradle + * Created by LiaoKai(soarcn) on 2014/11/25. + */ +class ClosableSlidingLayout extends FrameLayout { + + private final float MINVEL; + private ViewDragHelper mDragHelper; + private SlideListener mListener; + private int height; + private int top; + private int mActivePointerId; + private boolean mIsBeingDragged; + private float mInitialMotionY; + private static final int INVALID_POINTER = -1; + View mTarget; + + private boolean collapsible = false; + private float yDiff; + + boolean swipeable = true; + + public ClosableSlidingLayout(Context context) { + this(context, null); + } + + public ClosableSlidingLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public ClosableSlidingLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mDragHelper = ViewDragHelper.create(this, 0.8f, new ViewDragCallback()); + MINVEL = getResources().getDisplayMetrics().density * 400; + } + + @Override + public boolean onInterceptTouchEvent(@NonNull MotionEvent event) { + final int action = MotionEventCompat.getActionMasked(event); + + if (!isEnabled() || canChildScrollUp()) { + // Fail fast if we're not in a state where a swipe is possible + return false; + } + + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { + mActivePointerId = INVALID_POINTER; + mIsBeingDragged = false; + if (collapsible && -yDiff > mDragHelper.getTouchSlop()) { + expand(mDragHelper.getCapturedView(), 0); + } + mDragHelper.cancel(); + return false; + } + + switch (action) { + case MotionEvent.ACTION_DOWN: + height = getChildAt(0).getHeight(); + top = getChildAt(0).getTop(); + mActivePointerId = MotionEventCompat.getPointerId(event, 0); + mIsBeingDragged = false; + final float initialMotionY = getMotionEventY(event, mActivePointerId); + if (initialMotionY == -1) { + return false; + } + mInitialMotionY = initialMotionY; + yDiff = 0; + break; + case MotionEvent.ACTION_MOVE: + if (mActivePointerId == INVALID_POINTER) { + return false; + } + final float y = getMotionEventY(event, mActivePointerId); + if (y == -1) { + return false; + } + yDiff = y - mInitialMotionY; + if (swipeable && yDiff > mDragHelper.getTouchSlop() && !mIsBeingDragged) { + mIsBeingDragged = true; + mDragHelper.captureChildView(getChildAt(0), 0); + } + break; + } + mDragHelper.shouldInterceptTouchEvent(event); + return mIsBeingDragged; + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean b) { + // Nope. + } + + /** + * @return Whether it is possible for the child view of this layout to + * scroll up. Override this if the child view is a custom view. + */ + private boolean canChildScrollUp() { + if (android.os.Build.VERSION.SDK_INT < 14) { + if (mTarget instanceof AbsListView) { + final AbsListView absListView = (AbsListView) mTarget; + return absListView.getChildCount() > 0 + && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) + .getTop() < absListView.getPaddingTop()); + } else { + return mTarget.getScrollY() > 0; + } + } else { + return ViewCompat.canScrollVertically(mTarget, -1); + } + } + + private float getMotionEventY(MotionEvent ev, int activePointerId) { + final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); + if (index < 0) { + return -1; + } + return MotionEventCompat.getY(ev, index); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!isEnabled() || canChildScrollUp()) { + return super.onTouchEvent(ev); + } + + try { + if (swipeable) + mDragHelper.processTouchEvent(ev); + } catch (Exception ignored) { + } + return true; + } + + @Override + public void computeScroll() { + if (mDragHelper.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + public void setSlideListener(SlideListener listener) { + mListener = listener; + } + + void setCollapsible(boolean collapsible) { + this.collapsible = collapsible; + } + + /** + * Callback + */ + private class ViewDragCallback extends ViewDragHelper.Callback { + + + @Override + public boolean tryCaptureView(View child, int pointerId) { + return true; + } + + @Override + public void onViewReleased(View releasedChild, float xvel, float yvel) { + if (yvel > MINVEL) { + dismiss(releasedChild, yvel); + } else { + if (releasedChild.getTop() >= top + height / 2) { + dismiss(releasedChild, yvel); + } else { + mDragHelper.smoothSlideViewTo(releasedChild, 0, top); + } + } + ViewCompat.postInvalidateOnAnimation(ClosableSlidingLayout.this); + } + + @Override + public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + invalidate(); + } + if (height-top <1 && mListener!=null) { + mListener.onClosed(); + } + } + + @Override + public int clampViewPositionVertical(View child, int top, int dy) { + return Math.max(top, ClosableSlidingLayout.this.top); + } + } + + private void expand(View releasedChild, float yvel) { + if (mListener != null) { + mListener.onOpened(); + } + } + + private void dismiss(View view, float yvel) { + mDragHelper.smoothSlideViewTo(view, 0, top + height); + mDragHelper.cancel(); + ViewCompat.postInvalidateOnAnimation(ClosableSlidingLayout.this); + } + + + /** + * set listener + */ + interface SlideListener { + void onClosed(); + + void onOpened(); + } + +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/FillerView.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/FillerView.java new file mode 100644 index 0000000000..0f205c7089 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/FillerView.java @@ -0,0 +1,34 @@ +package com.cocosw.bottomsheet; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +class FillerView extends LinearLayout { + private View mMeasureTarget; + + + public FillerView(Context context) { + super(context); + } + + + public void setMeasureTarget(View lastViewSeen) { + mMeasureTarget = lastViewSeen; + } + + + public FillerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if(null != mMeasureTarget) + heightMeasureSpec = MeasureSpec.makeMeasureSpec( + mMeasureTarget.getMeasuredHeight(), MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/HeaderLayout.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/HeaderLayout.java new file mode 100644 index 0000000000..f04b410d12 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/HeaderLayout.java @@ -0,0 +1,40 @@ +package com.cocosw.bottomsheet; + + + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + + +class HeaderLayout extends FrameLayout { + private int mHeaderWidth = 1; + + + public HeaderLayout(Context context) { + super(context); + } + + + public HeaderLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + public HeaderLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setHeaderWidth(int width) { + mHeaderWidth = width; + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMeasureSpecNew = mHeaderWidth == 1 + ? widthMeasureSpec + : MeasureSpec.makeMeasureSpec(mHeaderWidth, MeasureSpec.getMode(widthMeasureSpec)); + super.onMeasure(widthMeasureSpecNew, heightMeasureSpec); + } +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/PinnedSectionGridView.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/PinnedSectionGridView.java new file mode 100644 index 0000000000..a422ec3ee3 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/PinnedSectionGridView.java @@ -0,0 +1,120 @@ +package com.cocosw.bottomsheet; + + +/* + * Copyright 2013 Hari Krishna Dulipudi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + import android.content.Context; + import android.database.DataSetObserver; + import android.graphics.Canvas; + import android.graphics.Color; + import android.graphics.PointF; + import android.graphics.Rect; + import android.graphics.drawable.GradientDrawable; + import android.graphics.drawable.GradientDrawable.Orientation; + import android.os.Parcelable; + import android.util.AttributeSet; + import android.view.MotionEvent; + import android.view.SoundEffectConstants; + import android.view.View; + import android.view.ViewConfiguration; + import android.view.accessibility.AccessibilityEvent; + import android.widget.AbsListView; + import android.widget.GridView; + +/** + * ListView capable to pin views at its top while the rest is still scrolled. + */ + class PinnedSectionGridView extends GridView { + + + // -- class fields + + private int mNumColumns; + private int mHorizontalSpacing; + private int mColumnWidth; + private int mAvailableWidth; + + public PinnedSectionGridView(Context context) { + super(context); + } + + public PinnedSectionGridView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PinnedSectionGridView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + + @Override + public void setNumColumns(int numColumns) { + mNumColumns = numColumns; + super.setNumColumns(numColumns); + } + + public int getNumColumns(){ + return mNumColumns; + } + + @Override + public void setHorizontalSpacing(int horizontalSpacing) { + mHorizontalSpacing = horizontalSpacing; + super.setHorizontalSpacing(horizontalSpacing); + } + + public int getHorizontalSpacing(){ + return mHorizontalSpacing; + } + + @Override + public void setColumnWidth(int columnWidth) { + mColumnWidth = columnWidth; + super.setColumnWidth(columnWidth); + } + + public int getColumnWidth(){ + return mColumnWidth; + } + + public int getAvailableWidth(){ + return mAvailableWidth != 0 ? mAvailableWidth : getWidth(); + } + +// @Override +// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +// if (mNumColumns == GridView.AUTO_FIT) { +// mAvailableWidth = MeasureSpec.getSize(widthMeasureSpec); +// if (mColumnWidth > 0) { +// int availableSpace = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); +// // Client told us to pick the number of columns +// mNumColumns = (availableSpace + mHorizontalSpacing) / +// (mColumnWidth + mHorizontalSpacing); +// } else { +// // Just make up a number if we don't have enough info +// mNumColumns = 2; +// } +// if(null != getAdapter()){ +// if(getAdapter() instanceof SimpleSectionedGridAdapter){ +// ((SimpleSectionedGridAdapter)getAdapter()).setSections(); +// } +// } +// } +// super.onMeasure(widthMeasureSpec, heightMeasureSpec); +// } +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/SimpleSectionedGridAdapter.java b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/SimpleSectionedGridAdapter.java new file mode 100644 index 0000000000..df5f6ed213 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/java/com/cocosw/bottomsheet/SimpleSectionedGridAdapter.java @@ -0,0 +1,348 @@ +package com.cocosw.bottomsheet; + + +import java.util.Arrays; +import java.util.Comparator; + +import android.content.Context; +import android.database.DataSetObserver; +import android.text.TextUtils; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.GridView; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.TextView; + +class SimpleSectionedGridAdapter extends BaseAdapter{ + protected static final int TYPE_FILLER = 0; + protected static final int TYPE_HEADER = 1; + protected static final int TYPE_HEADER_FILLER = 2; + private boolean mValid = true; + private int mSectionResourceId; + private LayoutInflater mLayoutInflater; + private ListAdapter mBaseAdapter; + SparseArray

mSections = new SparseArray
(); + private Section[] mInitialSections = new Section[0]; + private Context mContext; + private View mLastViewSeen; + private int mHeaderWidth; + private int mNumColumns; + private int mWidth; + private int mColumnWidth; + private int mHorizontalSpacing; + private int mStrechMode; + private int requestedColumnWidth; + private int requestedHorizontalSpacing; + private GridView mGridView; + private int mHeaderLayoutResId; + private int mHeaderTextViewResId; + + public static class Section { + int firstPosition; + int sectionedPosition; + CharSequence title; + int type = 0; + + public Section(int firstPosition, CharSequence title) { + this.firstPosition = firstPosition; + this.title =title; + } + + public CharSequence getTitle() { + return title; + } + } + + public SimpleSectionedGridAdapter(Context context, BaseAdapter baseAdapter, int sectionResourceId, int headerLayoutResId, + int headerTextViewResId) { + mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mSectionResourceId = sectionResourceId; + mHeaderLayoutResId = headerLayoutResId; + mHeaderTextViewResId = headerTextViewResId; + mBaseAdapter = baseAdapter; + mContext = context; + mBaseAdapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + mValid = !mBaseAdapter.isEmpty(); + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + mValid = false; + notifyDataSetInvalidated(); + } + }); + } + + public void setGridView(GridView gridView){ + if(!(gridView instanceof PinnedSectionGridView)){ + throw new IllegalArgumentException("Does your grid view extends PinnedSectionGridView?"); + } + mGridView = gridView; + mStrechMode = gridView.getStretchMode(); + mWidth = gridView.getWidth() - (mGridView.getPaddingLeft() + mGridView.getPaddingRight()); + mNumColumns = ((PinnedSectionGridView)gridView).getNumColumns(); + requestedColumnWidth = ((PinnedSectionGridView)gridView).getColumnWidth(); + requestedHorizontalSpacing = ((PinnedSectionGridView)gridView).getHorizontalSpacing(); + } + + private int getHeaderSize(){ + if(mHeaderWidth > 0){ + return mHeaderWidth; + } + if(mWidth != mGridView.getWidth()){ + mStrechMode = mGridView.getStretchMode(); + mWidth = ((PinnedSectionGridView)mGridView).getAvailableWidth() - (mGridView.getPaddingLeft() + mGridView.getPaddingRight()); + mNumColumns = ((PinnedSectionGridView)mGridView).getNumColumns(); + requestedColumnWidth = ((PinnedSectionGridView)mGridView).getColumnWidth(); + requestedHorizontalSpacing = ((PinnedSectionGridView)mGridView).getHorizontalSpacing(); + } + + int spaceLeftOver = mWidth - (mNumColumns * requestedColumnWidth) - + ((mNumColumns - 1) * requestedHorizontalSpacing); + switch (mStrechMode) { + case GridView.NO_STRETCH: // Nobody stretches + mWidth -= spaceLeftOver; + mColumnWidth = requestedColumnWidth; + mHorizontalSpacing = requestedHorizontalSpacing; + break; + + case GridView.STRETCH_COLUMN_WIDTH: + mColumnWidth = requestedColumnWidth + spaceLeftOver / mNumColumns; + mHorizontalSpacing = requestedHorizontalSpacing; + break; + + case GridView.STRETCH_SPACING: + mColumnWidth = requestedColumnWidth; + if (mNumColumns > 1) { + mHorizontalSpacing = requestedHorizontalSpacing + + spaceLeftOver / (mNumColumns - 1); + } else { + mHorizontalSpacing = requestedHorizontalSpacing + spaceLeftOver; + } + break; + + case GridView.STRETCH_SPACING_UNIFORM: + mColumnWidth = requestedColumnWidth; + mHorizontalSpacing = requestedHorizontalSpacing; + mWidth = mWidth - spaceLeftOver + (2 * mHorizontalSpacing); + break; + } + mHeaderWidth = mWidth + ((mNumColumns - 1) * (mColumnWidth + mHorizontalSpacing)) ; + return mHeaderWidth; + } + + + public void setSections(Section... sections) { + mInitialSections = sections; + setSections(); + } + + public void setSections() { + mSections.clear(); + + getHeaderSize(); + Arrays.sort(mInitialSections, new Comparator
() { + @Override + public int compare(Section o, Section o1) { + return (o.firstPosition == o1.firstPosition) + ? 0 + : ((o.firstPosition < o1.firstPosition) ? -1 : 1); + } + }); + + int offset = 0; // offset positions for the headers we're adding + for (int i = 0; i < mInitialSections.length; i++) { + Section section = mInitialSections[i]; + Section sectionAdd; + + for (int j = 0; j < mNumColumns - 1; j++) { + sectionAdd = new Section(section.firstPosition, section.title); + sectionAdd.type = TYPE_HEADER_FILLER; + sectionAdd.sectionedPosition = sectionAdd.firstPosition + offset; + mSections.append(sectionAdd.sectionedPosition, sectionAdd); + ++offset; + } + + sectionAdd = new Section(section.firstPosition, section.title); + sectionAdd.type = TYPE_HEADER; + sectionAdd.sectionedPosition = sectionAdd.firstPosition + offset; + mSections.append(sectionAdd.sectionedPosition, sectionAdd); + ++offset; + + if(i < mInitialSections.length - 1){ + int nextPos = mInitialSections[i+1].firstPosition; + int itemsCount = nextPos - section.firstPosition; + int dummyCount = mNumColumns - (itemsCount % mNumColumns); + if(mNumColumns != dummyCount){ + for (int k = 0 ;k < dummyCount; k++) { + sectionAdd = new Section(section.firstPosition, section.title); + sectionAdd.type = TYPE_FILLER; + sectionAdd.sectionedPosition = nextPos + offset; + mSections.append(sectionAdd.sectionedPosition, sectionAdd); + ++offset; + } + } + } + } + + notifyDataSetChanged(); + } + + public int positionToSectionedPosition(int position) { + int offset = 0; + for (int i = 0; i < mSections.size(); i++) { + if (mSections.valueAt(i).firstPosition > position) { + break; + } + ++offset; + } + return position + offset; + } + + public int sectionedPositionToPosition(int sectionedPosition) { + if (isSectionHeaderPosition(sectionedPosition)) { + return ListView.INVALID_POSITION; + } + + int offset = 0; + for (int i = 0; i < mSections.size(); i++) { + if (mSections.valueAt(i).sectionedPosition > sectionedPosition) { + break; + } + --offset; + } + return sectionedPosition + offset; + } + + public boolean isSectionHeaderPosition(int position) { + return mSections.get(position) != null; + } + + @Override + public int getCount() { + return (mValid ? mBaseAdapter.getCount() + mSections.size() : 0); + } + + @Override + public Object getItem(int position) { + return isSectionHeaderPosition(position) + ? mSections.get(position) + : mBaseAdapter.getItem(sectionedPositionToPosition(position)); + } + + @Override + public long getItemId(int position) { + return isSectionHeaderPosition(position) + ? Integer.MAX_VALUE - mSections.indexOfKey(position) + : mBaseAdapter.getItemId(sectionedPositionToPosition(position)); + } + + @Override + public int getItemViewType(int position) { + return isSectionHeaderPosition(position) + ? getViewTypeCount() - 1 + : mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)); + } + + @Override + public boolean isEnabled(int position) { + //noinspection SimplifiableConditionalExpression + return isSectionHeaderPosition(position) + ? false + : mBaseAdapter.isEnabled(sectionedPositionToPosition(position)); + } + + @Override + public int getViewTypeCount() { + return mBaseAdapter.getViewTypeCount() + 1; // the section headings + } + + @Override + public boolean areAllItemsEnabled() { + return mBaseAdapter.areAllItemsEnabled(); + } + + @Override + public boolean hasStableIds() { + return mBaseAdapter.hasStableIds(); + } + + @Override + public boolean isEmpty() { + return mBaseAdapter.isEmpty(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (isSectionHeaderPosition(position)) { + HeaderLayout header; + TextView view; + if (null == convertView) { + convertView = mLayoutInflater.inflate(mSectionResourceId, parent, false); + } else { + if (null == convertView.findViewById(mHeaderLayoutResId)) { + convertView = mLayoutInflater.inflate(mSectionResourceId, parent, false); + } + } + switch (mSections.get(position).type) { + case TYPE_HEADER: + header = (HeaderLayout) convertView.findViewById(mHeaderLayoutResId); + if (!TextUtils.isEmpty(mSections.get(position).title)) { + view = (TextView) convertView.findViewById(mHeaderTextViewResId); + view.setText(mSections.get(position).title); + } + header.setHeaderWidth(getHeaderSize()); + break; + case TYPE_HEADER_FILLER: + header = (HeaderLayout) convertView.findViewById(mHeaderLayoutResId); + if (!TextUtils.isEmpty(mSections.get(position).title)) { + view = (TextView) convertView.findViewById(mHeaderTextViewResId); + view.setText(mSections.get(position).title); + } + header.setHeaderWidth(0); + break; + default: + convertView = getFillerView(mLastViewSeen); + } + } else { + convertView = mBaseAdapter.getView(sectionedPositionToPosition(position), convertView, parent); + mLastViewSeen = convertView; + } + return convertView; + } + + private FillerView getFillerView(final View lastViewSeen) { + final FillerView fillerView = new FillerView(mContext); + fillerView.setMeasureTarget(lastViewSeen); + return fillerView; + } + + public int getHeaderLayoutResId() { + return mHeaderLayoutResId; + } + + public static class ViewHolder { + @SuppressWarnings("unchecked") + public static T get(View view, int id) { + SparseArray viewHolder = (SparseArray) view.getTag(); + if (viewHolder == null) { + viewHolder = new SparseArray(); + view.setTag(viewHolder); + } + View childView = viewHolder.get(id); + if (childView == null) { + childView = view.findViewById(id); + viewHolder.put(id, childView); + } + return (T) childView; + } + } + +} \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_item_in.xml b/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_item_in.xml new file mode 100644 index 0000000000..6e63d9b837 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_item_in.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_layout_anim_in.xml b/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_layout_anim_in.xml new file mode 100644 index 0000000000..0219dbbe4b --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim-v21/bs_list_layout_anim_in.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_item_in.xml b/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_item_in.xml new file mode 100644 index 0000000000..7c696e0779 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_item_in.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_layout_anim_in.xml b/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_layout_anim_in.xml new file mode 100644 index 0000000000..8d994ee2e1 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim/bs_list_layout_anim_in.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_enter.xml b/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_enter.xml new file mode 100644 index 0000000000..c9d4283d20 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_enter.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_exit.xml b/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_exit.xml new file mode 100644 index 0000000000..d01e1e4526 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/anim/dock_bottom_exit.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear.png b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear.png new file mode 100644 index 0000000000..45fef78a24 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear_light.png new file mode 100644 index 0000000000..ea21b1bf2d Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_clear_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more.png b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more.png new file mode 100644 index 0000000000..7f1e16254a Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more_light.png new file mode 100644 index 0000000000..9c7e47428c Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-hdpi/bs_ic_more_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear.png b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear.png new file mode 100644 index 0000000000..6c3a59fe65 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear_light.png new file mode 100644 index 0000000000..cfb1679945 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_clear_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more.png b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more.png new file mode 100644 index 0000000000..4e4f883b08 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more_light.png new file mode 100644 index 0000000000..0e483d5f3b Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-mdpi/bs_ic_more_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_dark_selector.xml b/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_dark_selector.xml new file mode 100644 index 0000000000..b5e07b3f29 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_dark_selector.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_selector.xml b/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_selector.xml new file mode 100644 index 0000000000..bc8e1afdb6 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/drawable-v21/bs_list_selector.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear.png new file mode 100644 index 0000000000..2eb9589dd6 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear_light.png new file mode 100644 index 0000000000..0aaf7e2df4 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_clear_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more.png new file mode 100644 index 0000000000..36328793dd Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more_light.png new file mode 100644 index 0000000000..dd8d74338f Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xhdpi/bs_ic_more_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear.png new file mode 100644 index 0000000000..3ec1adb77d Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear_light.png new file mode 100644 index 0000000000..61817c96bb Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_clear_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more.png new file mode 100644 index 0000000000..045c2ae440 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more_light.png b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more_light.png new file mode 100644 index 0000000000..dea04ab546 Binary files /dev/null and b/android/3rd_party/BottomSheet/src/main/res/drawable-xxhdpi/bs_ic_more_light.png differ diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_dark_selector.xml b/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_dark_selector.xml new file mode 100644 index 0000000000..6ac9df0b3e --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_dark_selector.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_selector.xml b/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_selector.xml new file mode 100644 index 0000000000..8d993f4f27 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/drawable/bs_list_selector.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/layout/bottom_sheet_dialog.xml b/android/3rd_party/BottomSheet/src/main/res/layout/bottom_sheet_dialog.xml new file mode 100644 index 0000000000..c68e74a4b6 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/layout/bottom_sheet_dialog.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/layout/bs_grid_entry.xml b/android/3rd_party/BottomSheet/src/main/res/layout/bs_grid_entry.xml new file mode 100644 index 0000000000..77883fd0f9 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/layout/bs_grid_entry.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_divider.xml b/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_divider.xml new file mode 100644 index 0000000000..627e9b77ea --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_divider.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_entry.xml b/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_entry.xml new file mode 100644 index 0000000000..bf3907f7a3 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/layout/bs_list_entry.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values-land/integer.xml b/android/3rd_party/BottomSheet/src/main/res/values-land/integer.xml new file mode 100644 index 0000000000..14b618e725 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values-land/integer.xml @@ -0,0 +1,6 @@ + + + 4 + 2 + 3 + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values-sw600dp-land/integer.xml b/android/3rd_party/BottomSheet/src/main/res/values-sw600dp-land/integer.xml new file mode 100644 index 0000000000..b9f4d35128 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values-sw600dp-land/integer.xml @@ -0,0 +1,6 @@ + + + 5 + 2 + 4 + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values-sw600dp/integer.xml b/android/3rd_party/BottomSheet/src/main/res/values-sw600dp/integer.xml new file mode 100644 index 0000000000..59e2da2350 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values-sw600dp/integer.xml @@ -0,0 +1,6 @@ + + + 4 + 3 + 5 + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values-zh/string.xml b/android/3rd_party/BottomSheet/src/main/res/values-zh/string.xml new file mode 100644 index 0000000000..3028014861 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values-zh/string.xml @@ -0,0 +1,4 @@ + + + 更多 + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml b/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..efeacc3378 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/attrs.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/colors.xml b/android/3rd_party/BottomSheet/src/main/res/values/colors.xml new file mode 100644 index 0000000000..c9cfaa2198 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #1f000000 + #1fffffff + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/dimens.xml b/android/3rd_party/BottomSheet/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..f44cb5bb3f --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + +8dp + 0dp + 8dp + 8dp + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/ids.xml b/android/3rd_party/BottomSheet/src/main/res/values/ids.xml new file mode 100644 index 0000000000..267747edcc --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/integer.xml b/android/3rd_party/BottomSheet/src/main/res/values/integer.xml new file mode 100644 index 0000000000..151505b1b0 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/integer.xml @@ -0,0 +1,6 @@ + + + 3 + 3 + 5 + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/string.xml b/android/3rd_party/BottomSheet/src/main/res/values/string.xml new file mode 100644 index 0000000000..b3d0b5166a --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/string.xml @@ -0,0 +1,4 @@ + + + More + \ No newline at end of file diff --git a/android/3rd_party/BottomSheet/src/main/res/values/styles.xml b/android/3rd_party/BottomSheet/src/main/res/values/styles.xml new file mode 100644 index 0000000000..4644e2b4a3 --- /dev/null +++ b/android/3rd_party/BottomSheet/src/main/res/values/styles.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 53be1a2866..4c45dd80be 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -33,6 +33,8 @@ dependencies { // for Parse SDK compile 'com.parse.bolts:bolts-android:1.+' compile fileTree(dir: '3rd_party', include: '*.jar', exclude: 'mrgsmanifest.jar') + // BottomSheet + compile project(":3rd_party:BottomSheet") } def getDate() { diff --git a/android/gradle.properties b/android/gradle.properties index 30c851a766..29944072e8 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -7,3 +7,4 @@ propDebugNdkFlags=V=1 NDK_DEBUG=1 DEBUG=1 propReleaseNdkFlags=V=1 NDK_DEBUG=0 PRODUCTION=1 propStatisticsUrl="http://localhost:8080" +manifestmerger.enabled=false diff --git a/android/settings.gradle b/android/settings.gradle index e69de29bb2..1908e4c7a3 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -0,0 +1 @@ +include '3rd_party:BottomSheet' \ No newline at end of file