diff --git a/README.md b/README.md
index d2268f1..05ad3c6 100644
--- a/README.md
+++ b/README.md
@@ -1,90 +1,269 @@
+[](https://android-arsenal.com/details/1/1387) [](https://yalantis.com/?utm_source=github) [](https://jitpack.io/#yalantis/context-menu.android)
+
# ContextMenu
-#### You can easily add awesome animated context menu to your app. Made in [Yalantis] (http://yalantis.com/)
+#### You can easily add awesome animated context menu to your app.
-Check this [project on dribbble] (https://dribbble.com/shots/1785274-Menu-Animation-for-Additional-Functions?list=users&offset=17)
+Check this [project on dribbble](https://dribbble.com/shots/1785274-Menu-Animation-for-Additional-Functions?list=users&offset=17)
-Check this [project on Behance] (https://www.behance.net/gallery/20411445/Mobile-Animations-Interactions)
+Check this [project on Behance](https://www.behance.net/gallery/20411445/Mobile-Animations-Interactions)
-
+
### Usage:
-*For a working implementation, Have a look at the Sample Project - sample*
+*For a working implementation, have a look at the ```app``` module*
+
+#### 1. Clone repository and add sources into your project or use Gradle:
-#### 1. Clone repository and add sources into your project .
+Add it in your root build.gradle at the end of repositories:
+```
+ allprojects {
+ repositories {
+ ...
+ maven { url 'https://jitpack.io' }
+ }
+ }
+```
+Add the dependency
+```
+ dependencies {
+ implementation 'com.github.Yalantis:Context-Menu.Android:1.1.4'
+ }
+```
#### 2. Create list of `MenuObject`, which consists of icon or icon and description.
+You can use any `drawable, resource, bitmap, color` as image:
+```
+ menuObject.drawable = ...
+ menuObject.setResourceValue(...)
+ menuObject.setBitmapValue(...)
+ menuObject.setColorValue(...)
+ ```
+You can set image `ScaleType`:
+```
+ menuObject.scaleType = ScaleType.FIT_XY
+```
+You can use any `resource, drawable, color` as background:
+```
+ menuObject.setBgResourceValue(...)
+ menuObject.setBgDrawable(...)
+ menuObject.setBgColorValue(...)
+```
+Now You can easily add text appearance style for menu titles:
+```
+ In your project styles create style for text appearance
+ (For better visual effect extend it from TextView.DefaultStyle):
+
+
+ And set it's id to your MenuObject :
+
+ val bitmapDrawable = BitmapDrawable(
+ resources,
+ BitmapFactory.decodeResource(resources, R.drawable.icn_3)
+ )
+
+ val menuObject = MenuObject("Add to friends").apply {
+ drawable = bitmapDrawable
+ menuTextAppearanceStyle = R.style.TextViewStyle
+ }
+```
+You can use any `color` as text color:
+```
+ menuObject.textColor = ...
```
-ArrayList menuObjects = new ArrayList<>();
- menuObjects.add(new MenuObject(R.drawable.icn_close));
- menuObjects.add(new MenuObject(R.drawable.icn_1, "Send message"));
- menuObjects.add(new MenuObject(R.drawable.icn_2, "Like profile"));
- menuObjects.add(new MenuObject(R.drawable.icn_3, "Add to friends"));
- menuObjects.add(new MenuObject(R.drawable.icn_4, "Add to favorites"));
- menuObjects.add(new MenuObject(R.drawable.icn_5, "Block user"));
+You can set any `color` as divider color:
```
+ menuObject.dividerColor = ...
+```
+Example:
+```
+ val close = MenuObject().apply { setResourceValue(R.drawable.icn_close) }
+
+ val send = MenuObject("Send message").apply { setResourceValue(R.drawable.icn_1) }
+
+ val addFriend = MenuObject("Add to friends").apply {
+ drawable = BitmapDrawable(
+ resources,
+ BitmapFactory.decodeResource(resources, R.drawable.icn_3)
+ )
+ }
-#### 3. Create `newInstance` of `ContextMenuDialogFragment`, which received menu item size and list of `MenuObject`.
+ val menuObjects = mutableListOf().apply {
+ add(close)
+ add(send)
+ add(addFriend)
+ }
+```
+
+#### 3. Create `newInstance` of `ContextMenuDialogFragment`, which received `MenuParams` object.
```
- mMenuDialogFragment = ContextMenuDialogFragment.newInstance((int) getResources().getDimension(R.dimen.tool_bar_height), menuObjects );
+ val menuParams = MenuParams(
+ actionBarSize = resources.getDimension(R.dimen.tool_bar_height).toInt(),
+ menuObjects = getMenuObjects(),
+ isClosableOutside = false
+ // set other settings to meet your needs
+ )
+
+ // If you want to change the side you need to add 'gravity' parameter,
+ // by default it is MenuGravity.END.
+
+ // For example:
+
+ val menuParams = MenuParams(
+ actionBarSize = resources.getDimension(R.dimen.tool_bar_height).toInt(),
+ menuObjects = getMenuObjects(),
+ isClosableOutside = false,
+ gravity = MenuGravity.START
+ )
+
+ val contextMenuDialogFragment = ContextMenuDialogFragment.newInstance(menuParams)
```
#### 4. Set menu with button, which will open `ContextMenuDialogFragment`.
```
-@Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.menu_main, menu);
- return true;
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.context_menu:
- mMenuDialogFragment.show(fragmentManager, "ContextMenuDialogFragment");
- break;
+ override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ item?.let {
+ when (it.itemId) {
+ R.id.context_menu -> {
+ showContextMenuDialogFragment()
+ }
+ }
}
- return super.onOptionsItemSelected(item);
+
+ return super.onOptionsItemSelected(item)
}
```
-#### 5. Implement `ContextMenuDialogFragment.ItemClickListener` interface with `onItemClick` method.
-
-```
-public class MainActivity extends ActionBarActivity implements ContextMenuDialogFragment.ItemClickListener
-…
- @Override
- public void onItemClick(View clickedView, int position) {
-//Do something here
+#### 5. Add menu item listeners.
+```
+ contextMenuDialogFragment = menuItemClickListener = { view, position ->
+ // do something here
+ }
+
+ contextMenuDialogFragment = menuItemLongClickListener = { view, position ->
+ // do something here
}
```
## Customization:
For better experience menu item size should be equal to `ActionBar` height.
-`newInstance` of `ContextMenuDialogFragment` can receive:
+`newInstance` of `ContextMenuDialogFragment` receives `MenuParams` object that has the fields:
+
+`menuObjects` - list of MenuObject objects,
- `animationDelay` - delay in millis after fragment opening and before closing, which will make animation smoother on slow devices,
+`animationDelay` - delay in millis after fragment opening and before closing, which will make animation smoother on slow devices,
-`animationDuration` - duration of every piece of animation in millis.
+`animationDuration` - duration of every piece of animation in millis,
+
+`isClosableOutside` - if menu can be closed on touch to non-button area,
+
+`isFitSystemWindows` - if true, then the default implementation of fitSystemWindows(Rect) will be executed,
+
+`isClipToPadding` - true to clip children to the padding of the group, false otherwise.
+
+The last two parameters may be useful if you use _Translucent_ parameters in your theme:
+```
+ - true
+```
+To stay `Context Menu` below Status Bar set `fitSystemWindows` to true and `clipToPadding` to false.
## Compatibility
- * Android Honeycomb 3.0+
+ * Android KitKat 4.4+
# Changelog
+### Version: 1.1.4
+
+ * added background color animation
+
+### Version: 1.1.2
+
+ * added animation on close outside menu
+
+### Version: 1.1.1
+
+ * bug with `menuObject.textColor` was fixed
+
+### Version: 1.1.0
+
+ * library rewrited on Kotlin
+ * added `rtl` support
+ * added `gravity` parameter for `MenuParams`
+
+### Version: 1.0.8
+
+ * added transparent menu background support
+ * dependencies actualized
+
+### Version: 1.0.7
+
+ * Text in menu now also clickable
+ * Support libs and gradle updated
+
+### Version: 1.0.6
+
+ * com.android.tools.build:gradle:1.5.0
+ * Support libs and sdk updated to 23
+
+### Version: 1.0.5
+
+ * Fixed `setClosableOutside` [issue](https://github.com/Yalantis/Context-Menu.Android/issues/25).
+ * Fixed `setAnimationDuration` doesn´t work for open event [issue](https://github.com/Yalantis/Context-Menu.Android/issues/22).
+ * Fixed menu item listener setting mechanism. It can be not activity but any class that implements listeners now. [Issue](https://github.com/Yalantis/Context-Menu.Android/issues/24). Attention! You have to set listeners to the context fragment manually. Check block 5 in the `Usage`.
+
+### Version: 1.0.4
+
+ * Old `ContextMenuDialogFragment` `newInstance` methods are deprecated. Use new universal one that received `MenuParams`.
+ * Added possibility to dismiss menu by clicking on non-button area. See `MenuParams.setClosableOutside(boolean)`.
+
+### Version: 1.0.3
+
+ * Added menu text appearence style. (Note: other text style methods are deprecated).
+
+### Version: 1.0.2
+
+ * Changed `MenuObject` constructors. Image setting is moved to methods
+ * Added styling of `MenuObject` image, background, text color, divider color
+ * Added possibility to interact with translucent Status Bar
+
+### Version: 1.0.1
+
+ * Added `OnMenuItemLongClickListener` (usage: the same as `OnMenuItemClickListener`, check sample app)
+ * Renamed:
+```
+com.yalantis.contextmenu.lib.ContextMenuDialogFragment.ItemClickListener ->
+com.yalantis.contextmenu.lib.interfaces.OnMenuItemClickListener
+
+com.yalantis.contextmenu.lib.ContextMenuDialogFragment.ItemClickListener.onItemClick(...) ->
+com.yalantis.contextmenu.lib.interfaces.OnMenuItemClickListener.onMenuItemClick(...)
+```
+
### Version: 1.0
* Pilot version
+#### Let us know!
+
+We’d be really happy if you sent us links to your projects where you use our component. Just send an email to github@yalantis.com And do let us know if you have any questions or suggestion regarding the animation.
+
+P.S. We’re going to publish more awesomeness wrapped in code and a tutorial on how to make UI for Android (iOS) better than better. Stay tuned!
+
## License
- Copyright 2015, Yalantis
+ Copyright 2019, Yalantis
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index a5a3d2e..0000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
-
- defaultConfig {
- applicationId "com.yalantis.contextmenu.sample"
- minSdkVersion 11
- targetSdkVersion 21
- versionCode 1
- versionName "1.0"
- }
-}
-
-dependencies {
- compile project(':lib')
-}
diff --git a/app/src/main/java/com/yalantis/contextmenu/sample/MainActivity.java b/app/src/main/java/com/yalantis/contextmenu/sample/MainActivity.java
deleted file mode 100644
index 1a7d448..0000000
--- a/app/src/main/java/com/yalantis/contextmenu/sample/MainActivity.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package com.yalantis.contextmenu.sample;
-
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.ActionBarActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-
-import com.yalantis.contextmenu.R;
-import com.yalantis.contextmenu.lib.ContextMenuDialogFragment;
-import com.yalantis.contextmenu.lib.MenuObject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MainActivity extends ActionBarActivity implements ContextMenuDialogFragment.ItemClickListener {
-
- private FragmentManager fragmentManager;
- private DialogFragment mMenuDialogFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- fragmentManager = getSupportFragmentManager();
- initToolbar();
- mMenuDialogFragment = ContextMenuDialogFragment.newInstance((int) getResources().getDimension(R.dimen.tool_bar_height), getMenuObjects());
- addFragment(new MainFragment(), true, R.id.container);
- }
-
- private List getMenuObjects() {
- List menuObjects = new ArrayList<>();
- menuObjects.add(new MenuObject(R.drawable.icn_close));
- menuObjects.add(new MenuObject(R.drawable.icn_1, "Send message"));
- menuObjects.add(new MenuObject(R.drawable.icn_2, "Like profile"));
- menuObjects.add(new MenuObject(R.drawable.icn_3, "Add to friends"));
- menuObjects.add(new MenuObject(R.drawable.icn_4, "Add to favorites"));
- menuObjects.add(new MenuObject(R.drawable.icn_5, "Block user"));
- return menuObjects;
- }
-
- private void initToolbar() {
- Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
- TextView mToolBarTextView = (TextView) findViewById(R.id.text_view_toolbar_title);
- setSupportActionBar(mToolbar);
- getSupportActionBar().setHomeButtonEnabled(true);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setDisplayShowTitleEnabled(false);
- mToolbar.setNavigationIcon(R.drawable.btn_back);
- mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- onBackPressed();
- }
- });
- mToolBarTextView.setText("Samantha");
- }
-
- protected void addFragment(Fragment fragment, boolean addToBackStack, int containerId) {
- invalidateOptionsMenu();
- String backStackName = fragment.getClass().getName();
- boolean fragmentPopped = fragmentManager.popBackStackImmediate(backStackName, 0);
- if (!fragmentPopped) {
- FragmentTransaction transaction = fragmentManager.beginTransaction();
- transaction.add(containerId, fragment, backStackName)
- .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- if (addToBackStack)
- transaction.addToBackStack(backStackName);
- transaction.commit();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.context_menu:
- mMenuDialogFragment.show(fragmentManager, "DropDownMenuFragment");
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onBackPressed() {
- if (fragmentManager.getBackStackEntryCount() == 1) {
- finish();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public void onItemClick(View clickedView, int position) {
- Toast.makeText(this, "Position: " + position, Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/app/src/main/java/com/yalantis/contextmenu/sample/MainFragment.java b/app/src/main/java/com/yalantis/contextmenu/sample/MainFragment.java
deleted file mode 100644
index eecfe4e..0000000
--- a/app/src/main/java/com/yalantis/contextmenu/sample/MainFragment.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.yalantis.contextmenu.sample;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.yalantis.contextmenu.R;
-
-public class MainFragment extends Fragment {
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_main, container, false);
- return rootView;
- }
-}
diff --git a/app/src/main/res/drawable-hdpi/btn_back.png b/app/src/main/res/drawable-hdpi/btn_back.png
deleted file mode 100644
index e4fab71..0000000
Binary files a/app/src/main/res/drawable-hdpi/btn_back.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e..0000000
Binary files a/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/btn_back.png b/app/src/main/res/drawable-mdpi/btn_back.png
deleted file mode 100644
index 8a19ea1..0000000
Binary files a/app/src/main/res/drawable-mdpi/btn_back.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047d..0000000
Binary files a/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/btn_back.png b/app/src/main/res/drawable-xhdpi/btn_back.png
deleted file mode 100644
index aa010b8..0000000
Binary files a/app/src/main/res/drawable-xhdpi/btn_back.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d76..0000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/btn_back.png b/app/src/main/res/drawable-xxhdpi/btn_back.png
deleted file mode 100644
index 193484d..0000000
Binary files a/app/src/main/res/drawable-xxhdpi/btn_back.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 4df1894..0000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/btn_back.png b/app/src/main/res/drawable-xxxhdpi/btn_back.png
deleted file mode 100644
index 2841502..0000000
Binary files a/app/src/main/res/drawable-xxxhdpi/btn_back.png and /dev/null differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 3f315c8..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
deleted file mode 100644
index da38ed1..0000000
--- a/app/src/main/res/layout/fragment_main.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/badge.png b/badge.png
new file mode 100644
index 0000000..4c764a7
Binary files /dev/null and b/badge.png differ
diff --git a/build.gradle b/build.gradle
index 6356aab..f62da34 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,19 +1,25 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
buildscript {
+ ext.kotlin_version = '1.3.20'
+
repositories {
+ google()
jcenter()
}
- dependencies {
- classpath 'com.android.tools.build:gradle:1.0.0'
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.3.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
+ google()
jcenter()
+ maven { url 'https://jitpack.io' }
}
}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..f04974c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,4 +15,19 @@
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+
+VERSION_NAME=1.1.0
+VERSION_CODE=10
+GROUP=com.yalantis
+
+POM_DESCRIPTION=Android Library to display awesome context menu
+POM_URL=https://github.com/Yalantis/Context-Menu.Android
+POM_SCM_URL=https://github.com/Yalantis/Context-Menu.Android
+POM_SCM_CONNECTION=scm:git@github.com/Yalantis/Context-Menu.Android.git
+POM_SCM_DEV_CONNECTION=scm:git@github.com/Yalantis/Context-Menu.Android.git
+POM_LICENCE_NAME=The Apache Software License, Version 2.0
+POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0
+POM_LICENCE_DIST=repo
+POM_DEVELOPER_ID=yalantis
+POM_DEVELOPER_NAME=Yalantis
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..d7958b4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Jan 15 12:30:17 EET 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
\ No newline at end of file
diff --git a/lib/build.gradle b/lib/build.gradle
index 3fa3e47..96b49f1 100644
--- a/lib/build.gradle
+++ b/lib/build.gradle
@@ -1,19 +1,27 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion 28
defaultConfig {
- minSdkVersion 11
- targetSdkVersion 21
- versionCode 1
- versionName "1.0"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode Integer.parseInt(project.VERSION_CODE)
+ versionName VERSION_NAME
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:21.0.3'
- compile 'com.nineoldandroids:library:2.4.0'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ implementation 'com.android.support:appcompat-v7:28.0.0'
}
\ No newline at end of file
diff --git a/lib/gradle.properties b/lib/gradle.properties
new file mode 100644
index 0000000..10bfaff
--- /dev/null
+++ b/lib/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=Context Menu
+POM_ARTIFACT_ID=contextmenu
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml
index 749ab31..4b44572 100644
--- a/lib/src/main/AndroidManifest.xml
+++ b/lib/src/main/AndroidManifest.xml
@@ -1,7 +1,8 @@
-
-
+
-
+
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/AnimatorUtils.java b/lib/src/main/java/com/yalantis/contextmenu/lib/AnimatorUtils.java
deleted file mode 100644
index d1cc812..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/AnimatorUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.view.View;
-
-import com.nineoldandroids.animation.AnimatorSet;
-import com.nineoldandroids.animation.ObjectAnimator;
-
-public class AnimatorUtils {
-
- public static ObjectAnimator rotationCloseToRight(View v) {
- return ObjectAnimator.ofFloat(v, "rotationY", 0, -90);
- }
-
- public static ObjectAnimator rotationOpenFromRight(View v) {
- return ObjectAnimator.ofFloat(v, "rotationY", -90, 0);
- }
-
- public static ObjectAnimator rotationCloseVertical(View v) {
- return ObjectAnimator.ofFloat(v, "rotationX", 0, -90);
- }
-
- public static ObjectAnimator rotationOpenVertical(View v) {
- return ObjectAnimator.ofFloat(v, "rotationX", -90, 0);
- }
-
- public static ObjectAnimator alfaDisappear(View v) {
- return ObjectAnimator.ofFloat(v, "alpha", 1, 0);
- }
-
- public static ObjectAnimator alfaAppear(View v) {
- return ObjectAnimator.ofFloat(v, "alpha", 0, 1);
- }
-
- public static ObjectAnimator translationRight(View v, float x) {
- return ObjectAnimator.ofFloat(v, "translationX", 0, x);
- }
- public static ObjectAnimator translationLeft(View v, float x) {
- return ObjectAnimator.ofFloat(v, "translationX", x, 0);
- }
-
- public static AnimatorSet fadeOutSet(View v, float x){
- AnimatorSet fadeOutSet = new AnimatorSet();
- fadeOutSet.playTogether(alfaDisappear(v), translationRight(v,x));
- return fadeOutSet;
- }
-
-}
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.java b/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.java
deleted file mode 100644
index 82ddd15..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.DialogFragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ContextMenuDialogFragment extends DialogFragment implements MenuAdapter.OnItemClickListener {
-
- private static final String ACTION_BAR_SIZE = "action_bar_size";
- private static final String MENU_OBJECTS = "menu_objects";
- private static final String ANIMATION_DELAY = "animation_delay";
- private static final String ANIMATION_DURATION = "animation_duration";
-
- private LinearLayout mWrapperButtons;
- private LinearLayout mWrapperText;
- private MenuAdapter mDropDownMenuAdapter;
- private ArrayList mMenuObjects;
- private int mActionBarHeight;
- private ItemClickListener mItemClickListener;
- private int mAnimationDelay = 0; // delay after opening and before closing dialogfragent
- private int mAnimationDuration;
-
- public interface ItemClickListener {
- public void onItemClick(View clickedView, int position);
- }
-
- public static ContextMenuDialogFragment newInstance(int actionBarSize, List menuObjects) {
- ContextMenuDialogFragment contextMenuDialogFragment = new ContextMenuDialogFragment();
- Bundle args = new Bundle();
- args.putInt(ACTION_BAR_SIZE, actionBarSize);
- args.putParcelableArrayList(MENU_OBJECTS, new ArrayList<>(menuObjects));
- contextMenuDialogFragment.setArguments(args);
- return contextMenuDialogFragment;
- }
-
- public static ContextMenuDialogFragment newInstance(int actionBarSize, ArrayList menuObjects, int animationDelay) {
- ContextMenuDialogFragment contextMenuDialogFragment = new ContextMenuDialogFragment();
- Bundle args = new Bundle();
- args.putInt(ACTION_BAR_SIZE, actionBarSize);
- args.putParcelableArrayList(MENU_OBJECTS, menuObjects);
- args.putInt(ANIMATION_DELAY, animationDelay);
- contextMenuDialogFragment.setArguments(args);
- return contextMenuDialogFragment;
- }
-
- public static ContextMenuDialogFragment newInstance(int actionBarSize, ArrayList menuObjects, int animationDelay, int animationDuration) {
- ContextMenuDialogFragment contextMenuDialogFragment = new ContextMenuDialogFragment();
- Bundle args = new Bundle();
- args.putInt(ACTION_BAR_SIZE, actionBarSize);
- args.putParcelableArrayList(MENU_OBJECTS, menuObjects);
- args.putInt(ANIMATION_DELAY, animationDelay);
- args.putInt(ANIMATION_DURATION, animationDuration);
- contextMenuDialogFragment.setArguments(args);
- return contextMenuDialogFragment;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mItemClickListener = (ItemClickListener) activity;
- } catch (ClassCastException e) {
- Log.e(getClass().getName(), "Should implement ItemClickListener");
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setStyle(STYLE_NO_FRAME, R.style.MenuFragmentStyle);
- if (getArguments() != null) {
- mActionBarHeight = getArguments().getInt(ACTION_BAR_SIZE);
- mMenuObjects = getArguments().getParcelableArrayList(MENU_OBJECTS);
- if(getArguments().containsKey(ANIMATION_DELAY)){
- mAnimationDelay = getArguments().getInt(ANIMATION_DELAY);
- }
- mAnimationDuration = (getArguments().containsKey(ANIMATION_DELAY))?
- getArguments().getInt(ANIMATION_DELAY): MenuAdapter.ANIMATION_DURATION_MILLIS;
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_menu, container, false);
- initViews(rootView);
- getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- initDropDownMenuAdapter();
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- mDropDownMenuAdapter.menuToggle();
- }
- },mAnimationDelay);
- return rootView;
- }
-
- private void initViews(View view) {
- mWrapperButtons = (LinearLayout) view.findViewById(R.id.wrapper_buttons);
- mWrapperText = (LinearLayout) view.findViewById(R.id.wrapper_text);
- }
-
- private void initDropDownMenuAdapter() {
- mDropDownMenuAdapter = new MenuAdapter(getActivity(), mWrapperButtons, mWrapperText, mMenuObjects, mActionBarHeight, this);
- mDropDownMenuAdapter.setAnimationDuration(mAnimationDuration);
- }
-
- /**
- * Menu item click method
- */
- @Override
- public void onClick(View v) {
- mItemClickListener.onItemClick(v, mWrapperButtons.indexOfChild(v));
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- dismiss();
- }
- },mAnimationDelay);
- }
-}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.kt
new file mode 100644
index 0000000..d7964f6
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/ContextMenuDialogFragment.kt
@@ -0,0 +1,111 @@
+package com.yalantis.contextmenu.lib
+
+import android.os.Bundle
+import android.os.Handler
+import android.support.v4.app.DialogFragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import com.yalantis.contextmenu.lib.extensions.backgroundColorAppear
+import com.yalantis.contextmenu.lib.extensions.backgroundColorDisappear
+import kotlinx.android.synthetic.main.fragment_menu.*
+
+open class ContextMenuDialogFragment : DialogFragment() {
+
+ var menuItemClickListener: (view: View, position: Int) -> Unit = { _, _ -> }
+ var menuItemLongClickListener: (view: View, position: Int) -> Unit = { _, _ -> }
+
+ private lateinit var menuParams: MenuParams
+ private lateinit var dropDownMenuAdapter: MenuAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NO_FRAME, R.style.MenuFragmentStyle)
+ menuParams = arguments?.getParcelable(ARGS_MENU_PARAMS) as? MenuParams ?: MenuParams()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? = inflater.inflate(R.layout.fragment_menu, container, false)?.apply {
+ fitsSystemWindows = menuParams.isFitsSystemWindow
+ (this as ViewGroup).clipToPadding = menuParams.isClipToPadding
+ dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ initDropDownMenuAdapter()
+
+ Handler().postDelayed({
+ dropDownMenuAdapter.menuToggle()
+ }, menuParams.animationDelay)
+
+ wrapperView.apply {
+ backgroundColorAppear(menuParams.backgroundColorAnimationDuration)
+ show(menuParams.gravity)
+
+ if (menuParams.isClosableOutside) {
+ rootRelativeLayout.setOnClickListener {
+ if (isAdded) {
+ dropDownMenuAdapter.closeOutside()
+ }
+ }
+ }
+ }
+ }
+
+ private fun initDropDownMenuAdapter() {
+ activity?.let {
+ dropDownMenuAdapter = MenuAdapter(
+ it,
+ wrapperView.wrapperButtons,
+ wrapperView.wrapperText,
+ menuParams.menuObjects,
+ menuParams.actionBarSize,
+ menuParams.gravity
+ ).apply {
+ setAnimationDuration(menuParams.animationDuration)
+
+ onCloseOutsideClickListener = { _ ->
+ close()
+ }
+
+ onItemClickListener = { view ->
+ val position = (view.parent as ViewGroup).indexOfChild(view)
+ menuItemClickListener(view, position)
+ close()
+ }
+
+ onItemLongClickListener = { view ->
+ val position = (view.parent as ViewGroup).indexOfChild(view)
+ menuItemLongClickListener(view, position)
+ close()
+ }
+ }
+ }
+ }
+
+ private fun close() {
+ wrapperView.backgroundColorDisappear(menuParams.backgroundColorAnimationDuration) {
+ Handler().postDelayed({
+ dismissAllowingStateLoss()
+ }, menuParams.animationDelay)
+ }
+ }
+
+ companion object {
+
+ const val TAG = "ContextMenuDialogFragment"
+ private const val ARGS_MENU_PARAMS = "menuParams"
+
+ @JvmStatic
+ fun newInstance(menuParams: MenuParams): ContextMenuDialogFragment =
+ ContextMenuDialogFragment().apply {
+ arguments = Bundle().apply {
+ putParcelable(ARGS_MENU_PARAMS, menuParams)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.java b/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.java
deleted file mode 100644
index 8769fef..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.view.animation.Interpolator;
-
-public class HesitateInterpolator implements Interpolator {
-
- public HesitateInterpolator() {
- }
-
- public float getInterpolation(float t) {
- float x = 2.0f * t - 1.0f;
- return 0.5f * (x * x * x + 1.0f);
- }
-}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.kt
new file mode 100644
index 0000000..ba4f6f1
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/HesitateInterpolator.kt
@@ -0,0 +1,11 @@
+package com.yalantis.contextmenu.lib
+
+import android.view.animation.Interpolator
+
+class HesitateInterpolator : Interpolator {
+
+ override fun getInterpolation(input: Float): Float {
+ val x = 2.0f * input - 1.0f
+ return 0.5f * (x * x * x + 1.0f)
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.java b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.java
deleted file mode 100644
index 59a1200..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.java
+++ /dev/null
@@ -1,305 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorSet;
-import com.nineoldandroids.animation.ObjectAnimator;
-import com.nineoldandroids.view.ViewHelper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MenuAdapter {
-
- public static final int ANIMATION_DURATION_MILLIS = 100;
-
- private OnItemClickListener mOnItemClickListener;
- private Context mContext;
- private LinearLayout mMenuWrapper;
- private LinearLayout mTextWrapper;
- private View mClickedView;
- private List mMenuObjects;
- private AnimatorSet mAnimatorSetHideMenu;
- private AnimatorSet mAnimatorSetShowMenu;
- private boolean mIsMenuOpen = false;
- private boolean mIsAnimationRun = false;
- private int mMenuItemSize;
- private int mAnimationDurationMilis = ANIMATION_DURATION_MILLIS;
-
-
- public interface OnItemClickListener {
- public void onClick(View v);
- }
-
- public MenuAdapter(Context context, LinearLayout menuWrapper, LinearLayout textWrapper, List menuObjects,
- int actionBarHeight, OnItemClickListener onItemClickListener) {
- this.mContext = context;
- this.mMenuWrapper = menuWrapper;
- this.mTextWrapper = textWrapper;
- this.mMenuObjects = menuObjects;
- this.mOnItemClickListener = onItemClickListener;
-
-/**
- / Make menu looks better by setting toolbar height as itemSize.
- */
- this.mMenuItemSize = actionBarHeight;
- setViews();
- resetAnimations();
- mAnimatorSetShowMenu = setOpenCloseAnimation(false);
- mAnimatorSetHideMenu = setOpenCloseAnimation(true);
- }
-
- public int getItemCount() {
- return mMenuObjects.size();
- }
-
- /**
- * Creating views and filling to wrappers
- */
- private void setViews() {
- for (MenuObject menuObject : mMenuObjects) {
- mTextWrapper.addView(Utils.getItemTextView(mContext, menuObject.getTitle(), mMenuItemSize));
- mMenuWrapper.addView(Utils.getImageWrapper(mContext, mMenuItemSize, menuObject.getId(), clickItem));
- }
- }
-
- /**
- * Set starting params to vertical animations
- */
- private void resetVerticalAnimation(View view, boolean toTop) {
- if (!mIsMenuOpen) {
- ViewHelper.setRotation(view, 0);
- ViewHelper.setRotationY(view, 0);
- ViewHelper.setRotationX(view, -90);
- }
- ViewHelper.setPivotX(view, mMenuItemSize / 2);
- ViewHelper.setPivotY(view, !toTop ? 0 : mMenuItemSize);
- }
-
- /**
- * Set starting params to side animations
- */
- private void resetSideAnimation(View view) {
- if (!mIsMenuOpen) {
- ViewHelper.setRotation(view, 0);
- ViewHelper.setRotationY(view, -90);
- ViewHelper.setRotationX(view, 0);
- }
- ViewHelper.setPivotX(view, mMenuItemSize);
- ViewHelper.setPivotY(view, mMenuItemSize / 2);
- }
-
- /**
- * Set starting params to text animations
- */
- private void resetTextAnimation(View v) {
- ViewHelper.setAlpha(v, !mIsMenuOpen ? 0 : 1);
- ViewHelper.setTranslationX(v, !mIsMenuOpen ? mMenuItemSize : 0);
- }
-
- /**
- * Set starting params to all animations
- */
- private void resetAnimations() {
- for (int i = 0; i < getItemCount(); i++) {
- resetTextAnimation(mTextWrapper.getChildAt(i));
- if (i == 0) {
- resetSideAnimation(mMenuWrapper.getChildAt(i));
- } else {
- resetVerticalAnimation(mMenuWrapper.getChildAt(i), false);
- }
- }
- }
-
- /**
- * Creates Open / Close AnimatorSet
- */
- private AnimatorSet setOpenCloseAnimation(boolean isCloseAnimation) {
- List textAnimations = new ArrayList<>();
- List imageAnimations = new ArrayList<>();
-
- if (isCloseAnimation) {
- for (int i = getItemCount() - 1; i >= 0; i--) {
- fillOpenClosingAnimations(true, textAnimations, imageAnimations, i);
- }
- } else {
- for (int i = 0; i < getItemCount(); i++) {
- fillOpenClosingAnimations(false, textAnimations, imageAnimations, i);
- }
- }
-
- AnimatorSet textCloseAnimatorSet = new AnimatorSet();
- textCloseAnimatorSet.playSequentially(textAnimations);
-
- AnimatorSet imageCloseAnimatorSet = new AnimatorSet();
- imageCloseAnimatorSet.playSequentially(imageAnimations);
-
- AnimatorSet animatorFullSet = new AnimatorSet();
- animatorFullSet.playTogether(imageCloseAnimatorSet, textCloseAnimatorSet);
- animatorFullSet.setDuration(mAnimationDurationMilis);
- animatorFullSet.addListener(mCloseOpenAnimatorListener);
- animatorFullSet.setStartDelay(0);
- animatorFullSet.setInterpolator(new HesitateInterpolator());
- return animatorFullSet;
- }
-
- /**
- * Filling arrays of animations to build Set of Closing / Opening animations
- */
- private void fillOpenClosingAnimations(boolean isCloseAnimation, List textAnimations, List imageAnimations, int wrapperPosition) {
- AnimatorSet textAnimatorSet = new AnimatorSet();
- Animator textAppearance = isCloseAnimation ?
- AnimatorUtils.alfaDisappear(mTextWrapper.getChildAt(wrapperPosition))
- : AnimatorUtils.alfaAppear(mTextWrapper.getChildAt(wrapperPosition));
-
- Animator textTranslation = isCloseAnimation ?
- AnimatorUtils.translationRight(mTextWrapper.getChildAt(wrapperPosition), mContext.getResources().getDimension(R.dimen.text_right_translation))
- : AnimatorUtils.translationLeft(mTextWrapper.getChildAt(wrapperPosition), mContext.getResources().getDimension(R.dimen.text_right_translation));
-
- textAnimatorSet.playTogether(textAppearance, textTranslation);
- textAnimations.add(textAnimatorSet);
-
- Animator imageRotation = isCloseAnimation ?
- wrapperPosition == 0 ? AnimatorUtils.rotationCloseToRight(mMenuWrapper.getChildAt(wrapperPosition)) : AnimatorUtils.rotationCloseVertical(mMenuWrapper.getChildAt(wrapperPosition))
- : wrapperPosition == 0 ? AnimatorUtils.rotationOpenFromRight(mMenuWrapper.getChildAt(wrapperPosition)) : AnimatorUtils.rotationOpenVertical(mMenuWrapper.getChildAt(wrapperPosition));
- imageAnimations.add(imageRotation);
- }
-
- private View.OnClickListener clickItem = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mIsMenuOpen && !mIsAnimationRun) {
- mClickedView = v;
- int childIndex = mMenuWrapper.indexOfChild(v);
- if (childIndex == -1) {
- return;
- }
- toggleIsAnimationRun();
- buildChosenAnimation(childIndex);
- toggleIsMenuOpen();
- }
- }
- };
-
- /**
- * Builds and runs chosen item and menu closing animation
- */
- private void buildChosenAnimation(int childIndex) {
- List fadeOutTextTopAnimatorList = new ArrayList<>();
- List closeToBottomImageAnimatorList = new ArrayList<>();
- for (int i = 0; i < childIndex; i++) {
- View view = mMenuWrapper.getChildAt(i);
- resetVerticalAnimation(view, true);
- closeToBottomImageAnimatorList.add(AnimatorUtils.rotationCloseVertical(view));
- fadeOutTextTopAnimatorList.add(AnimatorUtils.fadeOutSet(mTextWrapper.getChildAt(i), mContext.getResources().getDimension(R.dimen.text_right_translation)));
- }
- AnimatorSet closeToBottom = new AnimatorSet();
- closeToBottom.playSequentially(closeToBottomImageAnimatorList);
- AnimatorSet fadeOutTop = new AnimatorSet();
- fadeOutTop.playSequentially(fadeOutTextTopAnimatorList);
-
- List fadeOutTextBottomAnimatorList = new ArrayList<>();
- List closeToTopAnimatorObjects = new ArrayList<>();
- for (int i = getItemCount() - 1; i > childIndex; i--) {
- View view = mMenuWrapper.getChildAt(i);
- resetVerticalAnimation(view, false);
- closeToTopAnimatorObjects.add(AnimatorUtils.rotationCloseVertical(view));
- fadeOutTextBottomAnimatorList.add(AnimatorUtils.fadeOutSet(mTextWrapper.getChildAt(i), mContext.getResources().getDimension(R.dimen.text_right_translation)));
- }
- AnimatorSet closeToTop = new AnimatorSet();
- closeToTop.playSequentially(closeToTopAnimatorObjects);
- AnimatorSet fadeOutBottom = new AnimatorSet();
- fadeOutBottom.playSequentially(fadeOutTextBottomAnimatorList);
-
- resetSideAnimation(mMenuWrapper.getChildAt(childIndex));
- ObjectAnimator closeToRight = AnimatorUtils.rotationCloseToRight(mMenuWrapper.getChildAt(childIndex));
- closeToRight.addListener(mChosenItemFinishAnimatorListener);
- AnimatorSet fadeOutChosenText = AnimatorUtils.fadeOutSet(mTextWrapper.getChildAt(childIndex), mContext.getResources().getDimension(R.dimen.text_right_translation));
-
- AnimatorSet imageFullAnimatorSet = new AnimatorSet();
- imageFullAnimatorSet.play(closeToBottom).with(closeToTop);
- AnimatorSet textFullAnimatorSet = new AnimatorSet();
- textFullAnimatorSet.play(fadeOutTop).with(fadeOutBottom);
- if (closeToBottomImageAnimatorList.size() >= closeToTopAnimatorObjects.size()) {
- imageFullAnimatorSet.play(closeToBottom).before(closeToRight);
- textFullAnimatorSet.play(fadeOutTop).before(fadeOutChosenText);
- } else {
- imageFullAnimatorSet.play(closeToTop).before(closeToRight);
- textFullAnimatorSet.play(fadeOutBottom).before(fadeOutChosenText);
- }
-
- AnimatorSet fullAnimatorSet = new AnimatorSet();
- fullAnimatorSet.playTogether(imageFullAnimatorSet, textFullAnimatorSet);
- fullAnimatorSet.setDuration(mAnimationDurationMilis);
- fullAnimatorSet.setInterpolator(new HesitateInterpolator());
- fullAnimatorSet.start();
- }
-
- public void menuToggle() {
- if (!mIsAnimationRun) {
- resetAnimations();
- mIsAnimationRun = true;
- if (mIsMenuOpen) {
- mAnimatorSetHideMenu.start();
- } else {
- mAnimatorSetShowMenu.start();
- }
- toggleIsMenuOpen();
- }
- }
-
- private void toggleIsAnimationRun() {
- mIsAnimationRun = !mIsAnimationRun;
- }
-
- private void toggleIsMenuOpen() {
- mIsMenuOpen = !mIsMenuOpen;
- }
-
- public void setAnimationDuration(int durationMillis){
- mAnimationDurationMilis = durationMillis;
- }
-
- private Animator.AnimatorListener mCloseOpenAnimatorListener = new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- toggleIsAnimationRun();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- };
-
- private Animator.AnimatorListener mChosenItemFinishAnimatorListener = new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- toggleIsAnimationRun();
- mOnItemClickListener.onClick(mClickedView);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- };
-
-}
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.kt
new file mode 100644
index 0000000..794d6de
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuAdapter.kt
@@ -0,0 +1,415 @@
+package com.yalantis.contextmenu.lib
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import com.yalantis.contextmenu.lib.extensions.*
+
+open class MenuAdapter(
+ private val context: Context,
+ private val menuWrapper: LinearLayout,
+ private val textWrapper: LinearLayout,
+ private val menuObjects: List,
+ private val actionBarSize: Int,
+ private val gravity: MenuGravity
+) {
+
+ var onCloseOutsideClickListener: (view: View) -> Unit = {}
+ var onItemClickListener: (view: View) -> Unit = {}
+ var onItemLongClickListener: (view: View) -> Unit = {}
+ private var onItemClickListenerCalled: (view: View) -> Unit = {}
+ private var onItemLongClickListenerCalled: (view: View) -> Unit = {}
+
+ private var clickedView: View? = null
+
+ private val hideMenuAnimatorSet: AnimatorSet by lazy { setOpenCloseAnimation(true) }
+ private val showMenuAnimatorSet: AnimatorSet by lazy { setOpenCloseAnimation(false) }
+
+ private var isMenuOpen = false
+ private var isAnimationRun = false
+
+ private var animationDuration = MenuParams.ANIMATION_DURATION
+
+ private val itemClickListener = View.OnClickListener { view ->
+ onItemClickListenerCalled = onItemClickListener
+ viewClicked(view)
+ }
+
+ private val itemLongClickListener = View.OnLongClickListener { view ->
+ onItemLongClickListenerCalled = onItemLongClickListener
+ viewClicked(view)
+ true
+ }
+
+ init {
+ setViews()
+ }
+
+ open fun setAnimationDuration(duration: Long) {
+ animationDuration = duration
+ showMenuAnimatorSet.duration = animationDuration
+ hideMenuAnimatorSet.duration = animationDuration
+ }
+
+ fun menuToggle() {
+ if (!isAnimationRun) {
+ resetAnimations()
+ isAnimationRun = true
+
+ if (isMenuOpen) {
+ hideMenuAnimatorSet.start()
+ } else {
+ showMenuAnimatorSet.start()
+ }
+
+ toggleIsMenuOpen()
+ }
+ }
+
+ fun closeOutside() {
+ onItemClickListenerCalled = onCloseOutsideClickListener
+ menuWrapper.getChildAt(FIRST_CHILD_INDEX)?.let {
+ viewClicked(it)
+ }
+ }
+
+ fun getItemCount() = menuObjects.size
+
+ private fun getLastItemPosition() = getItemCount() - 1
+
+ /**
+ * Creating views and filling to wrappers
+ */
+ private fun setViews() {
+ menuObjects.forEachIndexed { index, menuObject ->
+ context.apply {
+ textWrapper.addView(
+ getItemTextView(
+ menuObject,
+ actionBarSize,
+ itemClickListener,
+ itemLongClickListener
+ )
+ )
+ menuWrapper.addView(
+ getImageWrapper(
+ menuObject,
+ actionBarSize,
+ itemClickListener,
+ itemLongClickListener,
+ index != getLastItemPosition()
+ )
+ )
+ }
+ }
+ }
+
+ /**
+ * Set starting params to vertical animations
+ */
+ private fun resetVerticalAnimation(view: View, toTop: Boolean) {
+ view.apply {
+ if (!isMenuOpen) {
+ rotation = ROTATION_ZERO_DEGREES
+ rotationY = ROTATION_ZERO_DEGREES
+ rotationX = -ROTATION_NINETY_DEGREES
+ }
+
+ pivotX = (actionBarSize / 2).toFloat()
+ pivotY = (if (!toTop) 0 else actionBarSize).toFloat()
+ }
+ }
+
+ /**
+ * Set starting params to side animations
+ */
+ private fun resetSideAnimation(view: View) {
+ view.apply {
+ if (!isMenuOpen) {
+ rotation = ROTATION_ZERO_DEGREES
+ rotationY = this@MenuAdapter.getRotationY()
+ rotationX = ROTATION_ZERO_DEGREES
+ }
+
+ pivotX = this@MenuAdapter.getPivotX()
+ pivotY = (actionBarSize / 2).toFloat()
+ }
+ }
+
+ private fun getRotationY() =
+ when (gravity) {
+ MenuGravity.END -> if (context.isLayoutDirectionRtl()) {
+ ROTATION_NINETY_DEGREES
+ } else {
+ -ROTATION_NINETY_DEGREES
+ }
+ MenuGravity.START -> if (context.isLayoutDirectionRtl()) {
+ -ROTATION_NINETY_DEGREES
+ } else {
+ ROTATION_NINETY_DEGREES
+ }
+ }
+
+ private fun getPivotX() =
+ when (gravity) {
+ MenuGravity.END -> if (context.isLayoutDirectionRtl()) {
+ ROTATION_ZERO_DEGREES
+ } else {
+ actionBarSize.toFloat()
+ }
+ MenuGravity.START -> if (context.isLayoutDirectionRtl()) {
+ actionBarSize.toFloat()
+ } else {
+ ROTATION_ZERO_DEGREES
+ }
+ }
+
+ /**
+ * Set starting params to text animations
+ */
+ private fun resetTextAnimation(view: View) {
+ view.apply {
+ alpha = if (!isMenuOpen) ALPHA_INVISIBLE else ALPHA_VISIBLE
+ translationX = if (!isMenuOpen) actionBarSize.toFloat() else TRANSLATION_ZERO_VALUE
+ }
+ }
+
+ /**
+ * Set starting params to all animations
+ */
+ private fun resetAnimations() {
+ for (i in 0 until getItemCount()) {
+ resetTextAnimation(textWrapper.getChildAt(i))
+
+ if (i == 0) {
+ resetSideAnimation(menuWrapper.getChildAt(i))
+ } else {
+ resetVerticalAnimation(menuWrapper.getChildAt(i), false)
+ }
+ }
+ }
+
+ /**
+ * Creates open/close AnimatorSet
+ */
+ private fun setOpenCloseAnimation(isCloseAnimation: Boolean): AnimatorSet {
+ val textAnimations = mutableListOf()
+ val imageAnimations = mutableListOf()
+
+ if (isCloseAnimation) {
+ for (i in getLastItemPosition() downTo 0) {
+ fillOpenClosingAnimations(true, textAnimations, imageAnimations, i)
+ }
+ } else {
+ for (i in 0 until getItemCount()) {
+ fillOpenClosingAnimations(false, textAnimations, imageAnimations, i)
+ }
+ }
+
+ return AnimatorSet().apply {
+ duration = animationDuration
+ startDelay = 0
+ interpolator = HesitateInterpolator()
+
+ playTogether(
+ AnimatorSet().apply { playSequentially(textAnimations) },
+ AnimatorSet().apply { playSequentially(imageAnimations) }
+ )
+ onAnimationEnd { toggleIsAnimationRun() }
+ }
+ }
+
+ /**
+ * Filling arrays of animations to build Set of Closing / Opening animations
+ */
+ private fun fillOpenClosingAnimations(
+ isCloseAnimation: Boolean,
+ textAnimations: MutableList,
+ imageAnimations: MutableList,
+ wrapperPosition: Int
+ ) {
+ textWrapper.getChildAt(wrapperPosition).apply {
+ textAnimations.add(AnimatorSet().apply {
+ val textAppearance = if (isCloseAnimation) {
+ alphaDisappear()
+ } else {
+ alphaAppear()
+ }
+
+ val textTranslation = if (isCloseAnimation) {
+ when (gravity) {
+ MenuGravity.END -> translationEnd(getTextEndTranslation())
+ MenuGravity.START -> translationStart(getTextEndTranslation())
+ }
+ } else {
+ when (gravity) {
+ MenuGravity.END -> translationStart(getTextEndTranslation())
+ MenuGravity.START -> translationEnd(getTextEndTranslation())
+ }
+ }
+
+ playTogether(textAppearance, textTranslation)
+ })
+ }
+
+ menuWrapper.getChildAt(wrapperPosition).apply {
+ imageAnimations.add(
+ if (isCloseAnimation) {
+ if (wrapperPosition == 0) {
+ rotationCloseHorizontal(gravity)
+ } else {
+ rotationCloseVertical()
+ }
+ } else {
+ if (wrapperPosition == 0) {
+ rotationOpenHorizontal(gravity)
+ } else {
+ rotationOpenVertical()
+ }
+ }
+ )
+ }
+ }
+
+ private fun viewClicked(view: View) {
+ if (isMenuOpen && !isAnimationRun) {
+ clickedView = view
+
+ val childIndex = (view.parent as ViewGroup).indexOfChild(view)
+ if (childIndex == -1) {
+ return
+ }
+
+ toggleIsAnimationRun()
+ buildChosenAnimation(childIndex)
+ toggleIsMenuOpen()
+ }
+ }
+
+ private fun buildChosenAnimation(childIndex: Int) {
+ val fadeOutTextTopAnimatorList = mutableListOf()
+ val closeToBottomImageAnimatorList = mutableListOf()
+ val fadeOutTextBottomAnimatorList = mutableListOf()
+ val closeToTopImageAnimatorList = mutableListOf()
+
+ fillAnimatorLists(
+ childIndex,
+ fadeOutTextTopAnimatorList,
+ closeToBottomImageAnimatorList,
+ fadeOutTextBottomAnimatorList,
+ closeToTopImageAnimatorList
+ )
+
+ resetSideAnimation(menuWrapper.getChildAt(childIndex))
+
+ val fullAnimatorSetPair = getFullAnimatorSetPair(
+ childIndex,
+ fadeOutTextTopAnimatorList,
+ closeToBottomImageAnimatorList,
+ fadeOutTextBottomAnimatorList,
+ closeToTopImageAnimatorList
+ )
+
+ AnimatorSet().apply {
+ duration = animationDuration
+ interpolator = HesitateInterpolator()
+ playTogether(fullAnimatorSetPair.first, fullAnimatorSetPair.second)
+ start()
+ }
+ }
+
+ private fun fillAnimatorLists(
+ childIndex: Int,
+ fadeOutTextTopAnimatorList: MutableList,
+ closeToBottomImageAnimatorList: MutableList,
+ fadeOutTextBottomAnimatorList: MutableList,
+ closeToTopImageAnimatorList: MutableList
+ ) {
+ for (i in 0..getLastItemPosition()) {
+ val menuWrapperChild = menuWrapper.getChildAt(i)
+ val menuWrapperChildRotation = menuWrapperChild.rotationCloseVertical()
+ val textWrapperChildFadeOut =
+ textWrapper.getChildAt(i).fadeOutSet(getTextEndTranslation(), gravity)
+
+ when (i) {
+ in 0 until childIndex -> {
+ resetVerticalAnimation(menuWrapperChild, true)
+ closeToBottomImageAnimatorList.add(menuWrapperChildRotation)
+ fadeOutTextTopAnimatorList.add(textWrapperChildFadeOut)
+ }
+ in childIndex + 1..getLastItemPosition() -> {
+ resetVerticalAnimation(menuWrapperChild, false)
+ closeToTopImageAnimatorList.add(menuWrapperChildRotation)
+ fadeOutTextBottomAnimatorList.add(textWrapperChildFadeOut)
+ }
+ }
+ }
+
+ closeToTopImageAnimatorList.reverse()
+ fadeOutTextBottomAnimatorList.reverse()
+ }
+
+ private fun getFullAnimatorSetPair(
+ childIndex: Int,
+ fadeOutTextTopAnimatorList: MutableList,
+ closeToBottomImageAnimatorList: MutableList,
+ fadeOutTextBottomAnimatorList: MutableList,
+ closeToTopImageAnimatorList: MutableList
+ ): Pair {
+ val closeToBottom = AnimatorSet()
+ closeToBottom.playSequentially(closeToBottomImageAnimatorList)
+ val fadeOutTop = AnimatorSet()
+ fadeOutTop.playSequentially(fadeOutTextTopAnimatorList)
+
+ val closeToTop = AnimatorSet()
+ closeToTop.playSequentially(closeToTopImageAnimatorList)
+ val fadeOutBottom = AnimatorSet()
+ fadeOutBottom.playSequentially(fadeOutTextBottomAnimatorList)
+
+ val closeToEnd = menuWrapper.getChildAt(childIndex).rotationCloseHorizontal(gravity)
+ closeToEnd.onAnimationEnd {
+ toggleIsAnimationRun()
+
+ clickedView?.let { notNullView ->
+ onItemClickListenerCalled(notNullView)
+ onItemLongClickListenerCalled(notNullView)
+ }
+ }
+ val fadeOutChosenText =
+ textWrapper.getChildAt(childIndex).fadeOutSet(getTextEndTranslation(), gravity)
+
+ val imageFullAnimatorSet = AnimatorSet()
+ imageFullAnimatorSet.play(closeToBottom).with(closeToTop)
+ val textFullAnimatorSet = AnimatorSet()
+ textFullAnimatorSet.play(fadeOutTop).with(fadeOutBottom)
+
+ if (closeToBottomImageAnimatorList.size >= closeToTopImageAnimatorList.size) {
+ imageFullAnimatorSet.play(closeToBottom).before(closeToEnd)
+ textFullAnimatorSet.play(fadeOutTop).before(fadeOutChosenText)
+ } else {
+ imageFullAnimatorSet.play(closeToTop).before(closeToEnd)
+ textFullAnimatorSet.play(fadeOutBottom).before(fadeOutChosenText)
+ }
+
+ return Pair(imageFullAnimatorSet, textFullAnimatorSet)
+ }
+
+ private fun getTextEndTranslation() =
+ context.getDimension(R.dimen.text_translation).toFloat()
+
+ private fun toggleIsAnimationRun() {
+ isAnimationRun = !isAnimationRun
+ }
+
+ private fun toggleIsMenuOpen() {
+ isMenuOpen = !isMenuOpen
+ }
+
+ companion object {
+
+ private const val FIRST_CHILD_INDEX = 0
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuGravity.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuGravity.kt
new file mode 100644
index 0000000..f2ae3ec
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuGravity.kt
@@ -0,0 +1,5 @@
+package com.yalantis.contextmenu.lib
+
+enum class MenuGravity {
+ END, START
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.java b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.java
deleted file mode 100644
index 6c7a492..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class MenuObject implements Parcelable {
-
- private String mTitle;
- private int mId;
-
- public MenuObject(int id, String title) {
- this.mId = id;
- this.mTitle = title;
- }
-
- public MenuObject(int id) {
- this.mId = id;
- this.mTitle = "";
- }
-
- private MenuObject(Parcel in) {
- mTitle = in.readString();
- mId = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mTitle);
- dest.writeInt(mId);
- }
-
- public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- @Override
- public MenuObject createFromParcel(Parcel in) {
- return new MenuObject(in);
- }
-
- @Override
- public MenuObject[] newArray(int size) {
- return new MenuObject[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public String getTitle() {
- return mTitle;
- }
-
- public void setTitle(String title) {
- this.mTitle = title;
- }
-
- public int getId() {
- return mId;
- }
-
- public void setId(int mId) {
- this.mId = mId;
- }
-
-// TODO: Realize drawable constructor
-// private Drawable mDrawable;
-// public MenuObject(Drawable drawable, String title) {
-// this.mDrawable = drawable;
-// this.mTitle = title;
-// }
-//
-// public MenuObject(Drawable drawable) {
-// this.mDrawable = drawable;
-// this.mTitle = "";
-// }
-// public Drawable getDrawable() {
-// return mDrawable;
-// }
-//
-// public void setDrawable(Drawable drawable) {
-// this.mDrawable = drawable;
-// }
-
-}
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.kt
new file mode 100644
index 0000000..e737697
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuObject.kt
@@ -0,0 +1,144 @@
+package com.yalantis.contextmenu.lib
+
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Parcel
+import android.os.Parcelable
+import android.widget.ImageView
+
+open class MenuObject(var title: String = "") : Parcelable {
+
+ var bgDrawable: Drawable? = null
+
+ var bgColor: Int = 0
+ private set
+
+ var bgResource: Int = 0
+ private set
+
+ var drawable: Drawable? = null
+
+ var color: Int = 0
+ private set
+
+ var bitmap: Bitmap? = null
+ private set
+
+ var resource: Int = 0
+ private set
+
+ var scaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_INSIDE
+ var textColor: Int = 0
+ var dividerColor: Int = Integer.MAX_VALUE
+ var menuTextAppearanceStyle: Int = 0
+
+ private constructor(parcel: Parcel) : this(parcel.readString() ?: "") {
+ val bitmapBgDrawable = parcel.readParcelable(Bitmap::class.java.classLoader)
+ bgDrawable = if (bitmapBgDrawable == null) {
+ ColorDrawable(parcel.readInt())
+ } else {
+ // TODO create BitmapDrawable with resources
+ BitmapDrawable(bitmapBgDrawable)
+ }
+
+ bgColor = parcel.readInt()
+ bgResource = parcel.readInt()
+
+ val bitmapDrawable = parcel.readParcelable(Bitmap::class.java.classLoader)
+ // TODO create BitmapDrawable with resources
+ bitmapDrawable?.let { drawable = BitmapDrawable(it) }
+
+ color = parcel.readInt()
+ bitmap = parcel.readParcelable(Bitmap::class.java.classLoader)
+ resource = parcel.readInt()
+ scaleType = ImageView.ScaleType.values()[parcel.readInt()]
+ textColor = parcel.readInt()
+ dividerColor = parcel.readInt()
+ menuTextAppearanceStyle = parcel.readInt()
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.apply {
+ writeString(title)
+
+ when (bgDrawable) {
+ null -> writeParcelable(null, flags)
+ is BitmapDrawable -> writeParcelable((bgDrawable as BitmapDrawable).bitmap, flags)
+ is ColorDrawable -> writeInt((bgDrawable as ColorDrawable).color)
+ }
+
+ writeInt(bgColor)
+ writeInt(bgResource)
+
+ when (drawable) {
+ null -> writeParcelable(null, flags)
+ is BitmapDrawable -> writeParcelable((drawable as BitmapDrawable).bitmap, flags)
+ }
+
+ writeInt(color)
+ writeParcelable(bitmap, flags)
+ writeInt(resource)
+ writeInt(scaleType.ordinal)
+ writeInt(textColor)
+ writeInt(dividerColor)
+ writeInt(menuTextAppearanceStyle)
+ }
+ }
+
+ override fun describeContents(): Int = 0
+
+ fun setBgDrawable(drawable: ColorDrawable) {
+ setBgDrawableInternal(drawable)
+ }
+
+ fun setBgDrawable(drawable: BitmapDrawable) {
+ setBgDrawableInternal(drawable)
+ }
+
+ fun setBgColorValue(value: Int) {
+ bgColor = value
+ bgDrawable = null
+ bgResource = 0
+ }
+
+ fun setBgResourceValue(value: Int) {
+ bgResource = value
+ bgDrawable = null
+ bgColor = 0
+ }
+
+ fun setColorValue(value: Int) {
+ color = value
+ drawable = null
+ bitmap = null
+ resource = 0
+ }
+
+ fun setBitmapValue(value: Bitmap) {
+ bitmap = value
+ drawable = null
+ color = 0
+ resource = 0
+ }
+
+ fun setResourceValue(value: Int) {
+ resource = value
+ drawable = null
+ color = 0
+ bitmap = null
+ }
+
+ private fun setBgDrawableInternal(drawable: Drawable) {
+ bgDrawable = drawable
+ bgColor = 0
+ bgResource = 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): MenuObject = MenuObject(parcel)
+
+ override fun newArray(size: Int): Array = arrayOfNulls(size)
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/MenuParams.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuParams.kt
new file mode 100644
index 0000000..04f72f8
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/MenuParams.kt
@@ -0,0 +1,70 @@
+package com.yalantis.contextmenu.lib
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * @property [animationDelay]
+ * Delay after opening and before closing.
+ * @see ContextMenuDialogFragment
+ *
+ * @property [isClosableOutside]
+ * If option menu can be closed on touch to non-button area.
+ *
+ * @property [gravity]
+ * You can change the side. By default - MenuGravity.END
+ * @see MenuGravity
+ */
+data class MenuParams(
+ var actionBarSize: Int = 0,
+ var menuObjects: List = listOf(),
+ var animationDelay: Long = 0L,
+ var animationDuration: Long = ANIMATION_DURATION,
+ var backgroundColorAnimationDuration: Long = BACKGROUND_COLOR_ANIMATION_DURATION,
+ var isFitsSystemWindow: Boolean = false,
+ var isClipToPadding: Boolean = true,
+ var isClosableOutside: Boolean = false,
+ var gravity: MenuGravity = MenuGravity.END
+) : Parcelable {
+
+ private constructor(parcel: Parcel) : this(
+ parcel.readInt(),
+ parcel.createTypedArrayList(MenuObject.CREATOR) ?: listOf(),
+ parcel.readLong(),
+ parcel.readLong(),
+ parcel.readLong(),
+ parcel.readByte() != 0.toByte(),
+ parcel.readByte() != 0.toByte(),
+ parcel.readByte() != 0.toByte(),
+ parcel.readSerializable() as? MenuGravity ?: MenuGravity.END
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.apply {
+ writeInt(actionBarSize)
+ writeTypedList(menuObjects)
+ writeLong(animationDelay)
+ writeLong(animationDuration)
+ writeLong(backgroundColorAnimationDuration)
+ writeByte(if (isFitsSystemWindow) 1 else 0)
+ writeByte(if (isClipToPadding) 1 else 0)
+ writeByte(if (isClosableOutside) 1 else 0)
+ writeSerializable(gravity)
+ }
+ }
+
+ override fun describeContents(): Int = 0
+
+ companion object {
+
+ const val ANIMATION_DURATION = 100L
+ const val BACKGROUND_COLOR_ANIMATION_DURATION = 200L
+
+ @JvmField
+ val CREATOR = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): MenuParams = MenuParams(parcel)
+
+ override fun newArray(size: Int): Array = arrayOfNulls(size)
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/Utils.java b/lib/src/main/java/com/yalantis/contextmenu/lib/Utils.java
deleted file mode 100644
index e0d3e6a..0000000
--- a/lib/src/main/java/com/yalantis/contextmenu/lib/Utils.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.yalantis.contextmenu.lib;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-public class Utils {
-
- public static int getDefaultActionBarSize(Context context) {
- final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
- new int[]{android.R.attr.actionBarSize});
- int actionBarSize = (int) styledAttributes.getDimension(0, 0);
- styledAttributes.recycle();
- return actionBarSize;
- }
-
- public static TextView getItemTextView(Context context, String title, int menuItemSize) {
- TextView itemTextView = new TextView(context);
- RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, menuItemSize);
- itemTextView.setLayoutParams(textLayoutParams);
- itemTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.menu_text_size));
- itemTextView.setTextColor(context.getResources().getColor(android.R.color.white));
- itemTextView.setText(title);
- itemTextView.setPadding(0, 0, (int) context.getResources().getDimension(R.dimen.text_right_padding), 0);
- itemTextView.setGravity(Gravity.CENTER_VERTICAL);
- return itemTextView;
- }
-
- public static ImageView getItemImageButton(Context context, Drawable drawable) {
- ImageView imageView = new ImageButton(context);
- RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- lp.addRule(RelativeLayout.CENTER_IN_PARENT);
- imageView.setLayoutParams(lp);
- imageView.setPadding((int) context.getResources().getDimension(R.dimen.menu_item_padding),
- (int) context.getResources().getDimension(R.dimen.menu_item_padding),
- (int) context.getResources().getDimension(R.dimen.menu_item_padding),
- (int) context.getResources().getDimension(R.dimen.menu_item_padding));
- imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- imageView.setBackgroundColor(context.getResources().getColor(android.R.color.transparent));
- imageView.setImageDrawable(drawable);
- imageView.setClickable(false);
- imageView.setFocusable(false);
- return imageView;
- }
-
- public static View getDivider(Context context) {
- View dividerView = new View(context);
- RelativeLayout.LayoutParams viewLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.divider_height));
- viewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- dividerView.setLayoutParams(viewLayoutParams);
- dividerView.setClickable(true);
- dividerView.setBackgroundColor(context.getResources().getColor(R.color.divider_color));
- return dividerView;
- }
-
- public static RelativeLayout getImageWrapper(Context context, int menuItemSize, int drawableId, View.OnClickListener onCLick) {
- RelativeLayout imageWrapper = new RelativeLayout(context);
- LinearLayout.LayoutParams imageWrapperLayoutParams = new LinearLayout.LayoutParams(menuItemSize, menuItemSize);
- imageWrapper.setLayoutParams(imageWrapperLayoutParams);
- imageWrapper.setBackgroundColor(context.getResources().getColor(R.color.menu_item_background));
- imageWrapper.setOnClickListener(onCLick);
- imageWrapper.addView(Utils.getItemImageButton(context, context.getResources().getDrawable(drawableId)));
- imageWrapper.addView(getDivider(context));
- return imageWrapper;
- }
-
-}
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/WrapperView.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/WrapperView.kt
new file mode 100644
index 0000000..e482335
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/WrapperView.kt
@@ -0,0 +1,102 @@
+package com.yalantis.contextmenu.lib
+
+import android.content.Context
+import android.support.v4.view.ViewCompat
+import android.util.AttributeSet
+import android.view.Gravity
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import android.widget.ScrollView
+import com.yalantis.contextmenu.lib.extensions.getDimension
+
+open class WrapperView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ScrollView(context, attrs, defStyleAttr) {
+
+ val rootRelativeLayout: RelativeLayout by lazy { RelativeLayout(context) }
+ val wrapperButtons: LinearLayout by lazy { LinearLayout(context) }
+ val wrapperText: LinearLayout by lazy { LinearLayout(context) }
+
+ init {
+ setupScrollView()
+ setupRootRelativeLayout()
+ setupWrappers()
+ }
+
+ open fun show(menuGravity: MenuGravity) {
+ wrapperButtons.layoutParams =
+ RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.WRAP_CONTENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT
+ ).apply {
+ addRule(
+ if (menuGravity == MenuGravity.START) {
+ RelativeLayout.ALIGN_PARENT_START
+ } else {
+ RelativeLayout.ALIGN_PARENT_END
+ }
+ )
+ }
+
+ wrapperText.apply {
+ gravity = if (menuGravity == MenuGravity.START) {
+ Gravity.START
+ } else {
+ Gravity.END
+ }
+ layoutParams =
+ RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.WRAP_CONTENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT
+ ).apply {
+ val verb = if (menuGravity == MenuGravity.START) {
+ RelativeLayout.END_OF
+ } else {
+ RelativeLayout.START_OF
+ }
+ addRule(verb, wrapperButtons.id)
+ setMargins(
+ 0,
+ 0,
+ context.getDimension(R.dimen.text_start_end_margin),
+ 0
+ )
+ }
+ }
+ }
+
+ private fun setupScrollView() {
+ isFillViewport = true
+ }
+
+ private fun setupRootRelativeLayout() {
+ addView(
+ rootRelativeLayout.apply {
+ id = ViewCompat.generateViewId()
+ layoutParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT
+ )
+ }
+ )
+ }
+
+ private fun setupWrappers() {
+ rootRelativeLayout.addView(
+ wrapperButtons.apply {
+ id = ViewCompat.generateViewId()
+ orientation = LinearLayout.VERTICAL
+ }
+ )
+
+ rootRelativeLayout.addView(
+ wrapperText.apply {
+ id = ViewCompat.generateViewId()
+ orientation = LinearLayout.VERTICAL
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Animator.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Animator.kt
new file mode 100644
index 0000000..b40d492
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Animator.kt
@@ -0,0 +1,23 @@
+package com.yalantis.contextmenu.lib.extensions
+
+import android.animation.Animator
+
+internal fun Animator.onAnimationEnd(onAnimationEnd: (Animator?) -> Unit) {
+ this.addListener(object : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animation: Animator?) {
+ // do nothing
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd(animation)
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ // do nothing
+ }
+
+ override fun onAnimationStart(animation: Animator?) {
+ // do nothing
+ }
+ })
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Context.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Context.kt
new file mode 100644
index 0000000..f717860
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/Context.kt
@@ -0,0 +1,140 @@
+package com.yalantis.contextmenu.lib.extensions
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Build
+import android.support.annotation.ColorRes
+import android.support.annotation.DimenRes
+import android.support.v4.content.ContextCompat
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.*
+import com.yalantis.contextmenu.lib.MenuObject
+import com.yalantis.contextmenu.lib.R
+
+@JvmName("getDefaultActionBarSize")
+fun Context.getDefaultActionBarSize(): Int {
+ val styledAttrs = theme.obtainStyledAttributes(intArrayOf(android.R.attr.actionBarSize))
+ val actionBarSize = styledAttrs.getDimension(0, 0f).toInt()
+ styledAttrs.recycle()
+ return actionBarSize
+}
+
+internal fun Context.getColorCompat(@ColorRes color: Int) = ContextCompat.getColor(this, color)
+
+internal fun Context.getDimension(@DimenRes dimen: Int) = resources.getDimension(dimen).toInt()
+
+internal fun Context.isLayoutDirectionRtl(): Boolean =
+ resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
+
+internal fun Context.getItemTextView(
+ menuItem: MenuObject,
+ menuItemSize: Int,
+ onCLick: View.OnClickListener,
+ onLongClick: View.OnLongClickListener
+): TextView = TextView(this).apply {
+ val textColor = if (menuItem.textColor == 0) {
+ android.R.color.white
+ } else {
+ menuItem.textColor
+ }
+
+ val styleResId = if (menuItem.menuTextAppearanceStyle > 0) {
+ menuItem.menuTextAppearanceStyle
+ } else {
+ R.style.TextView_DefaultStyle
+ }
+
+ layoutParams = RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ menuItemSize
+ )
+ text = menuItem.title
+ gravity = Gravity.CENTER_VERTICAL
+
+ setOnClickListener(onCLick)
+ setOnLongClickListener(onLongClick)
+ setPadding(
+ getDimension(R.dimen.text_start_end_padding_medium),
+ 0,
+ getDimension(R.dimen.text_start_end_padding_small),
+ 0
+ )
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ setTextAppearance(context, styleResId)
+ } else {
+ setTextAppearance(styleResId)
+ }
+
+ setTextColor(getColorCompat(textColor))
+}
+
+internal fun Context.getItemImageButton(menuItem: MenuObject): ImageView =
+ ImageButton(this).apply {
+ val paddingValue = getDimension(R.dimen.menu_item_padding)
+
+ layoutParams = RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ isClickable = false
+ isFocusable = false
+ scaleType = menuItem.scaleType
+
+ setPadding(paddingValue, paddingValue, paddingValue, paddingValue)
+ setBackgroundColor(Color.TRANSPARENT)
+
+ menuItem.apply {
+ when {
+ color != 0 -> setImageDrawable(ColorDrawable(color))
+ resource != 0 -> setImageResource(resource)
+ bitmap != null -> setImageBitmap(bitmap)
+ drawable != null -> setImageDrawable(drawable)
+ }
+ }
+ }
+
+internal fun Context.getDivider(menuItem: MenuObject): View = View(this).apply {
+ val dividerColor = if (menuItem.dividerColor == Integer.MAX_VALUE) {
+ R.color.divider_color
+ } else {
+ menuItem.dividerColor
+ }
+
+ layoutParams = RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ getDimension(R.dimen.divider_height)
+ ).apply { addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) }
+ isClickable = true
+
+ setBackgroundColor(getColorCompat(dividerColor))
+}
+
+internal fun Context.getImageWrapper(
+ menuItem: MenuObject,
+ menuItemSize: Int,
+ onCLick: View.OnClickListener,
+ onLongClick: View.OnLongClickListener,
+ showDivider: Boolean
+): RelativeLayout = RelativeLayout(this).apply {
+ layoutParams = LinearLayout.LayoutParams(menuItemSize, menuItemSize)
+
+ setOnClickListener(onCLick)
+ setOnLongClickListener(onLongClick)
+ addView(getItemImageButton(menuItem))
+ if (showDivider) {
+ addView(getDivider(menuItem))
+ }
+
+ menuItem.apply {
+ when {
+ bgColor != 0 -> setBackgroundColor(bgColor)
+ bgDrawable != null -> background = bgDrawable
+ bgResource != 0 -> setBackgroundResource(bgResource)
+ else -> setBackgroundColor(getColorCompat(R.color.menu_item_background))
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/View.kt b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/View.kt
new file mode 100644
index 0000000..3b52deb
--- /dev/null
+++ b/lib/src/main/java/com/yalantis/contextmenu/lib/extensions/View.kt
@@ -0,0 +1,126 @@
+package com.yalantis.contextmenu.lib.extensions
+
+import android.animation.AnimatorSet
+import android.animation.ArgbEvaluator
+import android.animation.ObjectAnimator
+import android.support.annotation.ColorRes
+import android.view.View
+import com.yalantis.contextmenu.lib.MenuGravity
+import com.yalantis.contextmenu.lib.R
+
+const val ROTATION_ZERO_DEGREES = 0f
+const val ROTATION_NINETY_DEGREES = 90f
+const val ALPHA_INVISIBLE = 0f
+const val ALPHA_VISIBLE = 1f
+const val TRANSLATION_ZERO_VALUE = 0f
+
+private const val ROTATION_Y_PROPERTY = "rotationY"
+private const val ROTATION_X_PROPERTY = "rotationX"
+private const val ALPHA_PROPERTY = "alpha"
+private const val TRANSLATION_X_PROPERTY = "translationX"
+private const val BACKGROUND_COLOR_PROPERTY = "backgroundColor"
+
+internal fun View.rotationCloseHorizontal(gravity: MenuGravity): ObjectAnimator {
+ val from = ROTATION_ZERO_DEGREES
+ var to = when (gravity) {
+ MenuGravity.END -> -ROTATION_NINETY_DEGREES
+ MenuGravity.START -> ROTATION_NINETY_DEGREES
+ }
+
+ if (context.isLayoutDirectionRtl()) {
+ to *= -1f
+ }
+
+ return ObjectAnimator.ofFloat(this, ROTATION_Y_PROPERTY, from, to)
+}
+
+internal fun View.rotationOpenHorizontal(gravity: MenuGravity): ObjectAnimator {
+ var from = when (gravity) {
+ MenuGravity.END -> -ROTATION_NINETY_DEGREES
+ MenuGravity.START -> ROTATION_NINETY_DEGREES
+ }
+ val to = ROTATION_ZERO_DEGREES
+
+ if (context.isLayoutDirectionRtl()) {
+ from *= -1f
+ }
+
+ return ObjectAnimator.ofFloat(this, ROTATION_Y_PROPERTY, from, to)
+}
+
+internal fun View.rotationCloseVertical(): ObjectAnimator =
+ ObjectAnimator.ofFloat(this, ROTATION_X_PROPERTY, ROTATION_ZERO_DEGREES, -ROTATION_NINETY_DEGREES)
+
+internal fun View.rotationOpenVertical(): ObjectAnimator =
+ ObjectAnimator.ofFloat(this, ROTATION_X_PROPERTY, -ROTATION_NINETY_DEGREES, ROTATION_ZERO_DEGREES)
+
+internal fun View.alphaDisappear(): ObjectAnimator =
+ ObjectAnimator.ofFloat(this, ALPHA_PROPERTY, ALPHA_VISIBLE, ALPHA_INVISIBLE)
+
+internal fun View.alphaAppear(): ObjectAnimator =
+ ObjectAnimator.ofFloat(this, ALPHA_PROPERTY, ALPHA_INVISIBLE, ALPHA_VISIBLE)
+
+internal fun View.translationEnd(x: Float): ObjectAnimator {
+ var from = TRANSLATION_ZERO_VALUE
+ var to = x
+
+ if (context.isLayoutDirectionRtl()) {
+ from = x
+ to = TRANSLATION_ZERO_VALUE
+ }
+
+ return ObjectAnimator.ofFloat(this, TRANSLATION_X_PROPERTY, from, to)
+}
+
+internal fun View.translationStart(x: Float): ObjectAnimator {
+ var from = x
+ var to = TRANSLATION_ZERO_VALUE
+
+ if (context.isLayoutDirectionRtl()) {
+ from = TRANSLATION_ZERO_VALUE
+ to = x
+ }
+
+ return ObjectAnimator.ofFloat(this, TRANSLATION_X_PROPERTY, from, to)
+}
+
+internal fun View.fadeOutSet(x: Float, gravity: MenuGravity): AnimatorSet = AnimatorSet().apply {
+ val translation = when (gravity) {
+ MenuGravity.END -> translationEnd(x)
+ MenuGravity.START -> translationStart(x)
+ }
+ playTogether(alphaDisappear(), translation)
+}
+
+internal fun View.colorAnimation(
+ duration: Long,
+ @ColorRes startColorResId: Int,
+ @ColorRes endColorResId: Int
+): ObjectAnimator =
+ ObjectAnimator.ofObject(
+ this,
+ BACKGROUND_COLOR_PROPERTY,
+ ArgbEvaluator(),
+ context.getColorCompat(startColorResId),
+ context.getColorCompat(endColorResId)
+ ).apply {
+ this.duration = duration
+ start()
+ }
+
+internal fun View.backgroundColorAppear(duration: Long): ObjectAnimator =
+ colorAnimation(duration, android.R.color.transparent, R.color.menu_fragment_background)
+
+internal fun View.backgroundColorDisappear(
+ duration: Long,
+ onAnimationEnd: () -> Unit
+): ObjectAnimator =
+ colorAnimation(
+ duration,
+ R.color.menu_fragment_background,
+ android.R.color.transparent
+ ).apply {
+ onAnimationEnd {
+ onAnimationEnd()
+ }
+ }
\ No newline at end of file
diff --git a/lib/src/main/res/layout/fragment_menu.xml b/lib/src/main/res/layout/fragment_menu.xml
index caff7b3..72e7d57 100644
--- a/lib/src/main/res/layout/fragment_menu.xml
+++ b/lib/src/main/res/layout/fragment_menu.xml
@@ -1,28 +1,5 @@
-
+
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/lib/src/main/res/values/dimens.xml b/lib/src/main/res/values/dimens.xml
index 0fabc8f..a7aac53 100644
--- a/lib/src/main/res/values/dimens.xml
+++ b/lib/src/main/res/values/dimens.xml
@@ -1,10 +1,13 @@
2dp
- 28dp
- 8dp
- -8dp
1dp
+
+ 8dp
+ 16dp
+ 8dp
+ 16dp
+
15sp
diff --git a/lib/src/main/res/values/styles.xml b/lib/src/main/res/values/styles.xml
index bd42768..88a14dc 100644
--- a/lib/src/main/res/values/styles.xml
+++ b/lib/src/main/res/values/styles.xml
@@ -16,4 +16,10 @@
- @android:color/transparent
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/sample/.gitignore
similarity index 100%
rename from app/.gitignore
rename to sample/.gitignore
diff --git a/sample/build.gradle b/sample/build.gradle
new file mode 100644
index 0000000..77f7676
--- /dev/null
+++ b/sample/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ applicationId "com.yalantis.contextmenu.sample"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode Integer.parseInt(project.VERSION_CODE)
+ versionName VERSION_NAME
+ vectorDrawables.useSupportLibrary = true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+
+ implementation 'com.github.yalantis:context-menu.android:1.1.4'
+}
\ No newline at end of file
diff --git a/sample/gradle.properties b/sample/gradle.properties
new file mode 100644
index 0000000..709085f
--- /dev/null
+++ b/sample/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=ContextMenu
+POM_ARTIFACT_ID=contextmenu
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/sample/proguard-rules.pro
similarity index 100%
rename from app/proguard-rules.pro
rename to sample/proguard-rules.pro
diff --git a/app/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
similarity index 63%
rename from app/src/main/AndroidManifest.xml
rename to sample/src/main/AndroidManifest.xml
index cd7c542..25164f7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,22 +1,26 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.yalantis.contextmenu">
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning">
+
-
+
-
+
\ No newline at end of file
diff --git a/sample/src/main/java/com/yalantis/contextmenu/sample/SampleActivity.kt b/sample/src/main/java/com/yalantis/contextmenu/sample/SampleActivity.kt
new file mode 100644
index 0000000..507aee3
--- /dev/null
+++ b/sample/src/main/java/com/yalantis/contextmenu/sample/SampleActivity.kt
@@ -0,0 +1,159 @@
+package com.yalantis.contextmenu.sample
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.Menu
+import android.view.MenuItem
+import android.widget.Toast
+import com.yalantis.contextmenu.R
+import com.yalantis.contextmenu.lib.ContextMenuDialogFragment
+import com.yalantis.contextmenu.lib.MenuObject
+import com.yalantis.contextmenu.lib.MenuParams
+import kotlinx.android.synthetic.main.toolbar.*
+
+class SampleActivity : AppCompatActivity() {
+
+ private lateinit var contextMenuDialogFragment: ContextMenuDialogFragment
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_sample)
+
+ initToolbar()
+ initMenuFragment()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ item?.let {
+ when (it.itemId) {
+ R.id.context_menu -> {
+ showContextMenuDialogFragment()
+ }
+ }
+ }
+
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onBackPressed() {
+ if (::contextMenuDialogFragment.isInitialized && contextMenuDialogFragment.isAdded) {
+ contextMenuDialogFragment.dismiss()
+ } else {
+ finish()
+ }
+ }
+
+ private fun initToolbar() {
+ setSupportActionBar(toolbar)
+
+ supportActionBar?.apply {
+ setHomeButtonEnabled(true)
+ setDisplayHomeAsUpEnabled(true)
+ setDisplayShowTitleEnabled(false)
+ }
+
+ toolbar.apply {
+ setNavigationIcon(R.drawable.ic_arrow_back)
+ setNavigationOnClickListener { onBackPressed() }
+ }
+
+ tvToolbarTitle.text = "Samantha"
+ }
+
+ /**
+ * If you want to change the side you need to add 'gravity' parameter,
+ * by default it is MenuGravity.END.
+ *
+ * For example:
+ *
+ * MenuParams(
+ * actionBarSize = resources.getDimension(R.dimen.tool_bar_height).toInt(),
+ * menuObjects = getMenuObjects(),
+ * isClosableOutside = false,
+ * gravity = MenuGravity.START
+ * )
+ */
+ private fun initMenuFragment() {
+ val menuParams = MenuParams(
+ actionBarSize = resources.getDimension(R.dimen.tool_bar_height).toInt(),
+ menuObjects = getMenuObjects(),
+ isClosableOutside = false
+ )
+
+ contextMenuDialogFragment = ContextMenuDialogFragment.newInstance(menuParams).apply {
+ menuItemClickListener = { view, position ->
+ Toast.makeText(
+ this@SampleActivity,
+ "Clicked on position: $position",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ menuItemLongClickListener = { view, position ->
+ Toast.makeText(
+ this@SampleActivity,
+ "Long clicked on position: $position",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ }
+
+ /**
+ * You can use any (drawable, resource, bitmap, color) as image:
+ * menuObject.drawable = ...
+ * menuObject.setResourceValue(...)
+ * menuObject.setBitmapValue(...)
+ * menuObject.setColorValue(...)
+ *
+ * You can set image ScaleType:
+ * menuObject.scaleType = ScaleType.FIT_XY
+ *
+ * You can use any [resource, drawable, color] as background:
+ * menuObject.setBgResourceValue(...)
+ * menuObject.setBgDrawable(...)
+ * menuObject.setBgColorValue(...)
+ *
+ * You can use any (color) as text color:
+ * menuObject.textColor = ...
+ *
+ * You can set any (color) as divider color:
+ * menuObject.dividerColor = ...
+ */
+ private fun getMenuObjects() = mutableListOf().apply {
+ val close = MenuObject().apply { setResourceValue(R.drawable.icn_close) }
+ val send = MenuObject("Send message").apply { setResourceValue(R.drawable.icn_1) }
+ val like = MenuObject("Like profile").apply {
+ setBitmapValue(BitmapFactory.decodeResource(resources, R.drawable.icn_2))
+ }
+ val addFriend = MenuObject("Add to friends").apply {
+ drawable = BitmapDrawable(
+ resources,
+ BitmapFactory.decodeResource(resources, R.drawable.icn_3)
+ )
+ }
+ val addFavorite = MenuObject("Add to favorites").apply {
+ setResourceValue(R.drawable.icn_4)
+ }
+ val block = MenuObject("Block user").apply { setResourceValue(R.drawable.icn_5) }
+
+ add(close)
+ add(send)
+ add(like)
+ add(addFriend)
+ add(addFavorite)
+ add(block)
+ }
+
+ private fun showContextMenuDialogFragment() {
+ if (supportFragmentManager.findFragmentByTag(ContextMenuDialogFragment.TAG) == null) {
+ contextMenuDialogFragment.show(supportFragmentManager, ContextMenuDialogFragment.TAG)
+ }
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/btn_add.png b/sample/src/main/res/drawable-hdpi/btn_add.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/btn_add.png
rename to sample/src/main/res/drawable-hdpi/btn_add.png
diff --git a/sample/src/main/res/drawable-hdpi/ic_launcher.png b/sample/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..15f6cdf
Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-hdpi/icn_1.png b/sample/src/main/res/drawable-hdpi/icn_1.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_1.png
rename to sample/src/main/res/drawable-hdpi/icn_1.png
diff --git a/app/src/main/res/drawable-hdpi/icn_2.png b/sample/src/main/res/drawable-hdpi/icn_2.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_2.png
rename to sample/src/main/res/drawable-hdpi/icn_2.png
diff --git a/app/src/main/res/drawable-hdpi/icn_3.png b/sample/src/main/res/drawable-hdpi/icn_3.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_3.png
rename to sample/src/main/res/drawable-hdpi/icn_3.png
diff --git a/app/src/main/res/drawable-hdpi/icn_4.png b/sample/src/main/res/drawable-hdpi/icn_4.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_4.png
rename to sample/src/main/res/drawable-hdpi/icn_4.png
diff --git a/app/src/main/res/drawable-hdpi/icn_5.png b/sample/src/main/res/drawable-hdpi/icn_5.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_5.png
rename to sample/src/main/res/drawable-hdpi/icn_5.png
diff --git a/app/src/main/res/drawable-hdpi/icn_close.png b/sample/src/main/res/drawable-hdpi/icn_close.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/icn_close.png
rename to sample/src/main/res/drawable-hdpi/icn_close.png
diff --git a/app/src/main/res/drawable-hdpi/photo.png b/sample/src/main/res/drawable-hdpi/photo.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/photo.png
rename to sample/src/main/res/drawable-hdpi/photo.png
diff --git a/app/src/main/res/drawable-mdpi/btn_add.png b/sample/src/main/res/drawable-mdpi/btn_add.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/btn_add.png
rename to sample/src/main/res/drawable-mdpi/btn_add.png
diff --git a/sample/src/main/res/drawable-mdpi/ic_launcher.png b/sample/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a17ef93
Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-mdpi/icn_1.png b/sample/src/main/res/drawable-mdpi/icn_1.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_1.png
rename to sample/src/main/res/drawable-mdpi/icn_1.png
diff --git a/app/src/main/res/drawable-mdpi/icn_2.png b/sample/src/main/res/drawable-mdpi/icn_2.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_2.png
rename to sample/src/main/res/drawable-mdpi/icn_2.png
diff --git a/app/src/main/res/drawable-mdpi/icn_3.png b/sample/src/main/res/drawable-mdpi/icn_3.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_3.png
rename to sample/src/main/res/drawable-mdpi/icn_3.png
diff --git a/app/src/main/res/drawable-mdpi/icn_4.png b/sample/src/main/res/drawable-mdpi/icn_4.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_4.png
rename to sample/src/main/res/drawable-mdpi/icn_4.png
diff --git a/app/src/main/res/drawable-mdpi/icn_5.png b/sample/src/main/res/drawable-mdpi/icn_5.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_5.png
rename to sample/src/main/res/drawable-mdpi/icn_5.png
diff --git a/app/src/main/res/drawable-mdpi/icn_close.png b/sample/src/main/res/drawable-mdpi/icn_close.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/icn_close.png
rename to sample/src/main/res/drawable-mdpi/icn_close.png
diff --git a/app/src/main/res/drawable-mdpi/photo.png b/sample/src/main/res/drawable-mdpi/photo.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/photo.png
rename to sample/src/main/res/drawable-mdpi/photo.png
diff --git a/app/src/main/res/drawable-xhdpi/btn_add.png b/sample/src/main/res/drawable-xhdpi/btn_add.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/btn_add.png
rename to sample/src/main/res/drawable-xhdpi/btn_add.png
diff --git a/sample/src/main/res/drawable-xhdpi/ic_launcher.png b/sample/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..7d007fe
Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xhdpi/icn_1.png b/sample/src/main/res/drawable-xhdpi/icn_1.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_1.png
rename to sample/src/main/res/drawable-xhdpi/icn_1.png
diff --git a/app/src/main/res/drawable-xhdpi/icn_2.png b/sample/src/main/res/drawable-xhdpi/icn_2.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_2.png
rename to sample/src/main/res/drawable-xhdpi/icn_2.png
diff --git a/app/src/main/res/drawable-xhdpi/icn_3.png b/sample/src/main/res/drawable-xhdpi/icn_3.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_3.png
rename to sample/src/main/res/drawable-xhdpi/icn_3.png
diff --git a/app/src/main/res/drawable-xhdpi/icn_4.png b/sample/src/main/res/drawable-xhdpi/icn_4.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_4.png
rename to sample/src/main/res/drawable-xhdpi/icn_4.png
diff --git a/app/src/main/res/drawable-xhdpi/icn_5.png b/sample/src/main/res/drawable-xhdpi/icn_5.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_5.png
rename to sample/src/main/res/drawable-xhdpi/icn_5.png
diff --git a/app/src/main/res/drawable-xhdpi/icn_close.png b/sample/src/main/res/drawable-xhdpi/icn_close.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/icn_close.png
rename to sample/src/main/res/drawable-xhdpi/icn_close.png
diff --git a/app/src/main/res/drawable-xhdpi/photo.png b/sample/src/main/res/drawable-xhdpi/photo.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/photo.png
rename to sample/src/main/res/drawable-xhdpi/photo.png
diff --git a/app/src/main/res/drawable-xxhdpi/btn_add.png b/sample/src/main/res/drawable-xxhdpi/btn_add.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/btn_add.png
rename to sample/src/main/res/drawable-xxhdpi/btn_add.png
diff --git a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..0e39e6b
Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/icn_1.png b/sample/src/main/res/drawable-xxhdpi/icn_1.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_1.png
rename to sample/src/main/res/drawable-xxhdpi/icn_1.png
diff --git a/app/src/main/res/drawable-xxhdpi/icn_2.png b/sample/src/main/res/drawable-xxhdpi/icn_2.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_2.png
rename to sample/src/main/res/drawable-xxhdpi/icn_2.png
diff --git a/app/src/main/res/drawable-xxhdpi/icn_3.png b/sample/src/main/res/drawable-xxhdpi/icn_3.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_3.png
rename to sample/src/main/res/drawable-xxhdpi/icn_3.png
diff --git a/app/src/main/res/drawable-xxhdpi/icn_4.png b/sample/src/main/res/drawable-xxhdpi/icn_4.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_4.png
rename to sample/src/main/res/drawable-xxhdpi/icn_4.png
diff --git a/app/src/main/res/drawable-xxhdpi/icn_5.png b/sample/src/main/res/drawable-xxhdpi/icn_5.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_5.png
rename to sample/src/main/res/drawable-xxhdpi/icn_5.png
diff --git a/app/src/main/res/drawable-xxhdpi/icn_close.png b/sample/src/main/res/drawable-xxhdpi/icn_close.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/icn_close.png
rename to sample/src/main/res/drawable-xxhdpi/icn_close.png
diff --git a/app/src/main/res/drawable-xxhdpi/photo.png b/sample/src/main/res/drawable-xxhdpi/photo.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/photo.png
rename to sample/src/main/res/drawable-xxhdpi/photo.png
diff --git a/app/src/main/res/drawable-xxxhdpi/btn_add.png b/sample/src/main/res/drawable-xxxhdpi/btn_add.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/btn_add.png
rename to sample/src/main/res/drawable-xxxhdpi/btn_add.png
diff --git a/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6bb59a5
Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_1.png b/sample/src/main/res/drawable-xxxhdpi/icn_1.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_1.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_1.png
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_2.png b/sample/src/main/res/drawable-xxxhdpi/icn_2.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_2.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_2.png
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_3.png b/sample/src/main/res/drawable-xxxhdpi/icn_3.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_3.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_3.png
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_4.png b/sample/src/main/res/drawable-xxxhdpi/icn_4.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_4.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_4.png
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_5.png b/sample/src/main/res/drawable-xxxhdpi/icn_5.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_5.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_5.png
diff --git a/app/src/main/res/drawable-xxxhdpi/icn_close.png b/sample/src/main/res/drawable-xxxhdpi/icn_close.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/icn_close.png
rename to sample/src/main/res/drawable-xxxhdpi/icn_close.png
diff --git a/app/src/main/res/drawable-xxxhdpi/photo.png b/sample/src/main/res/drawable-xxxhdpi/photo.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/photo.png
rename to sample/src/main/res/drawable-xxxhdpi/photo.png
diff --git a/sample/src/main/res/drawable/ic_arrow_back.xml b/sample/src/main/res/drawable/ic_arrow_back.xml
new file mode 100644
index 0000000..bdec838
--- /dev/null
+++ b/sample/src/main/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/sample/src/main/res/layout/activity_sample.xml b/sample/src/main/res/layout/activity_sample.xml
new file mode 100644
index 0000000..1e4d827
--- /dev/null
+++ b/sample/src/main/res/layout/activity_sample.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/layout/scrollable_content.xml b/sample/src/main/res/layout/scrollable_content.xml
new file mode 100644
index 0000000..b7505cf
--- /dev/null
+++ b/sample/src/main/res/layout/scrollable_content.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbar.xml b/sample/src/main/res/layout/toolbar.xml
similarity index 70%
rename from app/src/main/res/layout/toolbar.xml
rename to sample/src/main/res/layout/toolbar.xml
index b6b12c1..34893a8 100644
--- a/app/src/main/res/layout/toolbar.xml
+++ b/sample/src/main/res/layout/toolbar.xml
@@ -2,19 +2,19 @@
+ app:theme="@style/ThemeOverlay.AppCompat.ActionBar">
+ android:textSize="@dimen/tool_bar_text_size" />
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/sample/src/main/res/menu/menu_main.xml
similarity index 100%
rename from app/src/main/res/menu/menu_main.xml
rename to sample/src/main/res/menu/menu_main.xml
diff --git a/app/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml
similarity index 100%
rename from app/src/main/res/values/colors.xml
rename to sample/src/main/res/values/colors.xml
diff --git a/app/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml
similarity index 100%
rename from app/src/main/res/values/dimens.xml
rename to sample/src/main/res/values/dimens.xml
diff --git a/app/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
similarity index 100%
rename from app/src/main/res/values/strings.xml
rename to sample/src/main/res/values/strings.xml
diff --git a/app/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml
similarity index 100%
rename from app/src/main/res/values/styles.xml
rename to sample/src/main/res/values/styles.xml
diff --git a/screenshot.jpg b/screenshot.jpg
new file mode 100644
index 0000000..d7a5722
Binary files /dev/null and b/screenshot.jpg differ
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..9c38812
Binary files /dev/null and b/screenshot.png differ
diff --git a/settings.gradle b/settings.gradle
index 3cbe249..035bb58 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':lib'
+include ':sample', ':lib'