diff options
| author | Griffin Millender <griffinn.millender@gmail.com> | 2015-06-01 08:54:24 -0400 |
|---|---|---|
| committer | Griffin Millender <griffinn.millender@gmail.com> | 2015-06-12 03:35:49 -0500 |
| commit | 8d769985a59484a0a2be38a8c4ad1a2824e04eae (patch) | |
| tree | e4b7a1a5bd6bfb55344eb81f07a88be274d3ce03 | |
| parent | a03a4546fe42c66ccca3f9813babcbf9869750a5 (diff) | |
Update cards lib
squashed all changes from: https://github.com/gabrielemariotti/cardslib
Thanks a bunch to gabrielemariotti for the cardslib
Change-Id: I96922b3ca76a7b89af7fce039eb33f499790f0a0
172 files changed, 8887 insertions, 1680 deletions
diff --git a/res/drawable-hdpi/ic_chevron_right_grey600_24dp.png b/res/drawable-hdpi/ic_chevron_right_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..7eef500 --- /dev/null +++ b/res/drawable-hdpi/ic_chevron_right_grey600_24dp.png diff --git a/res/drawable-hdpi/ic_expand_less_black_24dp.png b/res/drawable-hdpi/ic_expand_less_black_24dp.png Binary files differnew file mode 100644 index 0000000..35e3b42 --- /dev/null +++ b/res/drawable-hdpi/ic_expand_less_black_24dp.png diff --git a/res/drawable-hdpi/ic_expand_less_grey600_24dp.png b/res/drawable-hdpi/ic_expand_less_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..96758c8 --- /dev/null +++ b/res/drawable-hdpi/ic_expand_less_grey600_24dp.png diff --git a/res/drawable-hdpi/ic_expand_more_black_24dp.png b/res/drawable-hdpi/ic_expand_more_black_24dp.png Binary files differnew file mode 100644 index 0000000..ed993f3 --- /dev/null +++ b/res/drawable-hdpi/ic_expand_more_black_24dp.png diff --git a/res/drawable-hdpi/ic_expand_more_grey600_24dp.png b/res/drawable-hdpi/ic_expand_more_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..00b9f64 --- /dev/null +++ b/res/drawable-hdpi/ic_expand_more_grey600_24dp.png diff --git a/res/drawable-hdpi/ic_menu_expand_card_dark_normal.png b/res/drawable-hdpi/ic_menu_expand_card_dark_normal.png Binary files differindex c691aeb..c5a494c 100644 --- a/res/drawable-hdpi/ic_menu_expand_card_dark_normal.png +++ b/res/drawable-hdpi/ic_menu_expand_card_dark_normal.png diff --git a/res/drawable-hdpi/ic_menu_expand_card_dark_pressed.png b/res/drawable-hdpi/ic_menu_expand_card_dark_pressed.png Binary files differindex 584d599..3d0a792 100644 --- a/res/drawable-hdpi/ic_menu_expand_card_dark_pressed.png +++ b/res/drawable-hdpi/ic_menu_expand_card_dark_pressed.png diff --git a/res/drawable-hdpi/ic_more_vert_black_24dp.png b/res/drawable-hdpi/ic_more_vert_black_24dp.png Binary files differnew file mode 100644 index 0000000..f22e713 --- /dev/null +++ b/res/drawable-hdpi/ic_more_vert_black_24dp.png diff --git a/res/drawable-hdpi/ic_more_vert_grey600_24dp.png b/res/drawable-hdpi/ic_more_vert_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..e141502 --- /dev/null +++ b/res/drawable-hdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-mdpi/ic_chevron_right_grey600_24dp.png b/res/drawable-mdpi/ic_chevron_right_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..d6581fe --- /dev/null +++ b/res/drawable-mdpi/ic_chevron_right_grey600_24dp.png diff --git a/res/drawable-mdpi/ic_expand_less_black_24dp.png b/res/drawable-mdpi/ic_expand_less_black_24dp.png Binary files differnew file mode 100644 index 0000000..a5ab2c5 --- /dev/null +++ b/res/drawable-mdpi/ic_expand_less_black_24dp.png diff --git a/res/drawable-mdpi/ic_expand_less_grey600_24dp.png b/res/drawable-mdpi/ic_expand_less_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..d83b2e1 --- /dev/null +++ b/res/drawable-mdpi/ic_expand_less_grey600_24dp.png diff --git a/res/drawable-mdpi/ic_expand_more_black_24dp.png b/res/drawable-mdpi/ic_expand_more_black_24dp.png Binary files differnew file mode 100644 index 0000000..73fc3b4 --- /dev/null +++ b/res/drawable-mdpi/ic_expand_more_black_24dp.png diff --git a/res/drawable-mdpi/ic_expand_more_grey600_24dp.png b/res/drawable-mdpi/ic_expand_more_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..b2c6b76 --- /dev/null +++ b/res/drawable-mdpi/ic_expand_more_grey600_24dp.png diff --git a/res/drawable-mdpi/ic_menu_expand_card_dark_normal.png b/res/drawable-mdpi/ic_menu_expand_card_dark_normal.png Binary files differindex fe1c3e5..4cfa220 100644 --- a/res/drawable-mdpi/ic_menu_expand_card_dark_normal.png +++ b/res/drawable-mdpi/ic_menu_expand_card_dark_normal.png diff --git a/res/drawable-mdpi/ic_menu_expand_card_dark_pressed.png b/res/drawable-mdpi/ic_menu_expand_card_dark_pressed.png Binary files differindex 2cafbd1..60136bf 100644 --- a/res/drawable-mdpi/ic_menu_expand_card_dark_pressed.png +++ b/res/drawable-mdpi/ic_menu_expand_card_dark_pressed.png diff --git a/res/drawable-mdpi/ic_more_vert_black_24dp.png b/res/drawable-mdpi/ic_more_vert_black_24dp.png Binary files differnew file mode 100644 index 0000000..991ad6c --- /dev/null +++ b/res/drawable-mdpi/ic_more_vert_black_24dp.png diff --git a/res/drawable-mdpi/ic_more_vert_grey600_24dp.png b/res/drawable-mdpi/ic_more_vert_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..4ed3435 --- /dev/null +++ b/res/drawable-mdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-v21/card_foreground_kitkat_selector.xml b/res/drawable-v21/card_foreground_kitkat_selector.xml new file mode 100644 index 0000000..068560f --- /dev/null +++ b/res/drawable-v21/card_foreground_kitkat_selector.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:drawable="@android:color/transparent"/> + <item> + <selector + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + <item + android:state_activated="true" + android:drawable="@drawable/activated_foreground_kitkat_card"/> + + <item android:drawable="@android:color/transparent"/> + </selector> + </item> +</ripple> diff --git a/res/drawable-v21/card_foreground_selector.xml b/res/drawable-v21/card_foreground_selector.xml new file mode 100644 index 0000000..e976af2 --- /dev/null +++ b/res/drawable-v21/card_foreground_selector.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:drawable="@android:color/transparent"/> + <item> + <selector + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + + <item + android:state_activated="true" + android:drawable="@drawable/activated_foreground_card"/> + + <item android:drawable="@android:color/transparent"/> + </selector> + </item> +</ripple> diff --git a/res/drawable-v21/card_kitkat_selector.xml b/res/drawable-v21/card_kitkat_selector.xml new file mode 100644 index 0000000..b8b47f0 --- /dev/null +++ b/res/drawable-v21/card_kitkat_selector.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:drawable="@android:color/transparent"/> + <item> + <selector + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + + <item + android:state_activated="true" + android:drawable="@drawable/activated_background_kitkat_card"/> + + <item android:drawable="@drawable/card_background"/> + </selector> + </item> +</ripple>
\ No newline at end of file diff --git a/res/drawable-v21/card_menu_button_expand.xml b/res/drawable-v21/card_menu_button_expand.xml new file mode 100644 index 0000000..ff2b0bc --- /dev/null +++ b/res/drawable-v21/card_menu_button_expand.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:state_focused="true" + android:drawable="@drawable/ic_menu_expand_card_dark_pressed" /> + <item + android:drawable="@drawable/ic_menu_expand_card_dark_pressed" + android:state_selected="true"> + + </item> + <item android:drawable="@drawable/ic_menu_expand_card_dark_normal"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable-v21/card_menu_button_expand_material.xml b/res/drawable-v21/card_menu_button_expand_material.xml new file mode 100644 index 0000000..310a320 --- /dev/null +++ b/res/drawable-v21/card_menu_button_expand_material.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:state_focused="true" + android:drawable="@drawable/ic_expand_more_black_24dp" /> + <item + android:drawable="@drawable/ic_expand_more_black_24dp" + android:state_selected="true"> + + </item> + <item android:drawable="@drawable/ic_expand_more_grey600_24dp"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable-v21/card_menu_button_expand_material_animator.xml b/res/drawable-v21/card_menu_button_expand_material_animator.xml new file mode 100644 index 0000000..1390e5b --- /dev/null +++ b/res/drawable-v21/card_menu_button_expand_material_animator.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> +<!-- res/drawable/myanimstatedrawable.xml --> +<animated-selector + xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- provide a different drawable for each state--> + <item android:id="@+id/pressed" android:drawable="@drawable/ic_expand_less_black_24dp" + android:state_pressed="true"/> + <item android:id="@+id/selected" android:drawable="@drawable/ic_expand_less_black_24dp" + android:state_selected="true"/> + <item android:id="@+id/normal" + android:drawable="@drawable/ic_expand_more_grey600_24dp"/> + + <!-- specify a transition --> + <transition android:fromId="@+id/normal" android:toId="@+id/selected"> + <animation-list> + <item android:duration="15" android:drawable="@drawable/ic_chevron_right_grey600_24dp"/> + <item android:duration="15" android:drawable="@drawable/ic_expand_less_grey600_24dp"/> + </animation-list> + </transition> + + <transition android:fromId="@+id/selected" android:toId="@+id/normal"> + <animation-list> + <item android:duration="15" android:drawable="@drawable/ic_expand_less_grey600_24dp"/> + <item android:duration="15" android:drawable="@drawable/ic_chevron_right_grey600_24dp"/> + </animation-list> + </transition> + +</animated-selector>
\ No newline at end of file diff --git a/res/drawable-v21/card_menu_button_overflow_material.xml b/res/drawable-v21/card_menu_button_overflow_material.xml new file mode 100644 index 0000000..c37241d --- /dev/null +++ b/res/drawable-v21/card_menu_button_overflow_material.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:drawable="@drawable/ic_more_vert_black_24dp" + android:state_focused="true"/> + <item + android:drawable="@drawable/ic_more_vert_black_24dp" + android:state_selected="true"/> + + <item android:state_pressed="true"> + <ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="oval" android:innerRadius="40dp"/> + </item> + <item android:drawable="@drawable/ic_more_vert_black_24dp"/> + </ripple> + </item> + + <item android:drawable="@drawable/ic_more_vert_grey600_24dp"/> + +</selector> diff --git a/res/drawable-v21/card_selector.xml b/res/drawable-v21/card_selector.xml new file mode 100644 index 0000000..10e15a3 --- /dev/null +++ b/res/drawable-v21/card_selector.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:drawable="@android:color/transparent"/> + <item> + <selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + <item + android:state_activated="true" + android:drawable="@drawable/activated_background_card"/> + <item android:drawable="@drawable/card_background"/> + </selector> + </item> +</ripple>
\ No newline at end of file diff --git a/res/drawable-xhdpi/ic_chevron_right_grey600_24dp.png b/res/drawable-xhdpi/ic_chevron_right_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..7b82b8a --- /dev/null +++ b/res/drawable-xhdpi/ic_chevron_right_grey600_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_less_black_24dp.png b/res/drawable-xhdpi/ic_expand_less_black_24dp.png Binary files differnew file mode 100644 index 0000000..47c7b52 --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_less_black_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_less_grey600_24dp.png b/res/drawable-xhdpi/ic_expand_less_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..f117dc6 --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_less_grey600_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_more_black_24dp.png b/res/drawable-xhdpi/ic_expand_more_black_24dp.png Binary files differnew file mode 100644 index 0000000..45d30d9 --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_more_black_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_more_black_36dp.png b/res/drawable-xhdpi/ic_expand_more_black_36dp.png Binary files differnew file mode 100644 index 0000000..aadd04a --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_more_black_36dp.png diff --git a/res/drawable-xhdpi/ic_expand_more_grey600_24dp.png b/res/drawable-xhdpi/ic_expand_more_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..8702c9a --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_more_grey600_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_more_grey600_36dp.png b/res/drawable-xhdpi/ic_expand_more_grey600_36dp.png Binary files differnew file mode 100644 index 0000000..921249f --- /dev/null +++ b/res/drawable-xhdpi/ic_expand_more_grey600_36dp.png diff --git a/res/drawable-xhdpi/ic_menu_expand_card_dark_normal.png b/res/drawable-xhdpi/ic_menu_expand_card_dark_normal.png Binary files differindex b681a23..5947996 100644 --- a/res/drawable-xhdpi/ic_menu_expand_card_dark_normal.png +++ b/res/drawable-xhdpi/ic_menu_expand_card_dark_normal.png diff --git a/res/drawable-xhdpi/ic_menu_expand_card_dark_pressed.png b/res/drawable-xhdpi/ic_menu_expand_card_dark_pressed.png Binary files differindex 7b27084..1abc4e7 100644 --- a/res/drawable-xhdpi/ic_menu_expand_card_dark_pressed.png +++ b/res/drawable-xhdpi/ic_menu_expand_card_dark_pressed.png diff --git a/res/drawable-xhdpi/ic_more_vert_black_36dp.png b/res/drawable-xhdpi/ic_more_vert_black_36dp.png Binary files differnew file mode 100644 index 0000000..d32453d --- /dev/null +++ b/res/drawable-xhdpi/ic_more_vert_black_36dp.png diff --git a/res/drawable-xhdpi/ic_more_vert_grey600_36dp.png b/res/drawable-xhdpi/ic_more_vert_grey600_36dp.png Binary files differnew file mode 100644 index 0000000..44012b8 --- /dev/null +++ b/res/drawable-xhdpi/ic_more_vert_grey600_36dp.png diff --git a/res/drawable-xxhdpi/ic_chevron_right_grey600_24dp.png b/res/drawable-xxhdpi/ic_chevron_right_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..360d3c5 --- /dev/null +++ b/res/drawable-xxhdpi/ic_chevron_right_grey600_24dp.png diff --git a/res/drawable-xxhdpi/ic_expand_less_black_24dp.png b/res/drawable-xxhdpi/ic_expand_less_black_24dp.png Binary files differnew file mode 100644 index 0000000..0470e3f --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_less_black_24dp.png diff --git a/res/drawable-xxhdpi/ic_expand_less_grey600_24dp.png b/res/drawable-xxhdpi/ic_expand_less_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..63b7299 --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_less_grey600_24dp.png diff --git a/res/drawable-xxhdpi/ic_expand_more_black_18dp.png b/res/drawable-xxhdpi/ic_expand_more_black_18dp.png Binary files differnew file mode 100644 index 0000000..733c54c --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_more_black_18dp.png diff --git a/res/drawable-xxhdpi/ic_expand_more_black_24dp.png b/res/drawable-xxhdpi/ic_expand_more_black_24dp.png Binary files differnew file mode 100644 index 0000000..aadd04a --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_more_black_24dp.png diff --git a/res/drawable-xxhdpi/ic_expand_more_grey600_18dp.png b/res/drawable-xxhdpi/ic_expand_more_grey600_18dp.png Binary files differnew file mode 100644 index 0000000..0f0ddf7 --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_more_grey600_18dp.png diff --git a/res/drawable-xxhdpi/ic_expand_more_grey600_24dp.png b/res/drawable-xxhdpi/ic_expand_more_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..921249f --- /dev/null +++ b/res/drawable-xxhdpi/ic_expand_more_grey600_24dp.png diff --git a/res/drawable-xxhdpi/ic_menu_expand_card_dark_normal.png b/res/drawable-xxhdpi/ic_menu_expand_card_dark_normal.png Binary files differindex 784ab7f..bd48681 100644 --- a/res/drawable-xxhdpi/ic_menu_expand_card_dark_normal.png +++ b/res/drawable-xxhdpi/ic_menu_expand_card_dark_normal.png diff --git a/res/drawable-xxhdpi/ic_menu_expand_card_dark_pressed.png b/res/drawable-xxhdpi/ic_menu_expand_card_dark_pressed.png Binary files differindex 7edf16f..c95eb4a 100644 --- a/res/drawable-xxhdpi/ic_menu_expand_card_dark_pressed.png +++ b/res/drawable-xxhdpi/ic_menu_expand_card_dark_pressed.png diff --git a/res/drawable-xxhdpi/ic_menu_overflow_card.png b/res/drawable-xxhdpi/ic_menu_overflow_card.png Binary files differnew file mode 100644 index 0000000..0c951b9 --- /dev/null +++ b/res/drawable-xxhdpi/ic_menu_overflow_card.png diff --git a/res/drawable-xxhdpi/ic_more_vert_black_18dp.png b/res/drawable-xxhdpi/ic_more_vert_black_18dp.png Binary files differnew file mode 100644 index 0000000..050baec --- /dev/null +++ b/res/drawable-xxhdpi/ic_more_vert_black_18dp.png diff --git a/res/drawable-xxhdpi/ic_more_vert_black_24dp.png b/res/drawable-xxhdpi/ic_more_vert_black_24dp.png Binary files differnew file mode 100644 index 0000000..d32453d --- /dev/null +++ b/res/drawable-xxhdpi/ic_more_vert_black_24dp.png diff --git a/res/drawable-xxhdpi/ic_more_vert_grey600_18dp.png b/res/drawable-xxhdpi/ic_more_vert_grey600_18dp.png Binary files differnew file mode 100644 index 0000000..abd7493 --- /dev/null +++ b/res/drawable-xxhdpi/ic_more_vert_grey600_18dp.png diff --git a/res/drawable-xxhdpi/ic_more_vert_grey600_24dp.png b/res/drawable-xxhdpi/ic_more_vert_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..44012b8 --- /dev/null +++ b/res/drawable-xxhdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_chevron_right_grey600_24dp.png b/res/drawable-xxxhdpi/ic_chevron_right_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..a71c5b3 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_chevron_right_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png b/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png Binary files differnew file mode 100644 index 0000000..08ae545 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png diff --git a/res/drawable-xxxhdpi/ic_expand_less_grey600_24dp.png b/res/drawable-xxxhdpi/ic_expand_less_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..ed72b07 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_expand_less_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png b/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png Binary files differnew file mode 100644 index 0000000..228b2a9 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png diff --git a/res/drawable-xxxhdpi/ic_expand_more_grey600_24dp.png b/res/drawable-xxxhdpi/ic_expand_more_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..55f8396 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_expand_more_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png b/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png Binary files differnew file mode 100644 index 0000000..037b6f1 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png diff --git a/res/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png b/res/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png Binary files differnew file mode 100644 index 0000000..0042578 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable/activated_foreground_card.xml b/res/drawable/activated_foreground_card.xml new file mode 100644 index 0000000..314f6bb --- /dev/null +++ b/res/drawable/activated_foreground_card.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/card_foreground_activated"/> + <corners android:radius="@dimen/card_background_default_radius"/> +</shape> diff --git a/res/drawable/activated_foreground_kitkat_card.xml b/res/drawable/activated_foreground_kitkat_card.xml new file mode 100644 index 0000000..e83419d --- /dev/null +++ b/res/drawable/activated_foreground_kitkat_card.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/card_foreground_activated_kitkat"/> + <corners android:radius="@dimen/card_background_default_radius"/> +</shape> diff --git a/res/drawable/card_foreground_kitkat_selector.xml b/res/drawable/card_foreground_kitkat_selector.xml index 8d79991..28b6259 100644 --- a/res/drawable/card_foreground_kitkat_selector.xml +++ b/res/drawable/card_foreground_kitkat_selector.xml @@ -21,7 +21,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - <item android:state_activated="true" android:drawable="@drawable/activated_background_kitkat_card"/> + <item android:state_activated="true" android:drawable="@drawable/activated_foreground_kitkat_card"/> <item android:state_pressed="true" android:drawable="@drawable/pressed_background_kitkat_card"/> <item android:drawable="@android:color/transparent"/> -</selector>
\ No newline at end of file +</selector> diff --git a/res/drawable/card_foreground_selector.xml b/res/drawable/card_foreground_selector.xml index f8d73e0..958bac2 100644 --- a/res/drawable/card_foreground_selector.xml +++ b/res/drawable/card_foreground_selector.xml @@ -21,7 +21,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - <item android:state_activated="true" android:drawable="@drawable/activated_background_card"/> + <item android:state_activated="true" android:drawable="@drawable/activated_foreground_card"/> <item android:state_pressed="true" android:drawable="@drawable/pressed_background_card"/> <item android:drawable="@android:color/transparent"/> -</selector>
\ No newline at end of file +</selector> diff --git a/res/drawable/card_menu_button_expand_material.xml b/res/drawable/card_menu_button_expand_material.xml new file mode 100644 index 0000000..a2cf1a5 --- /dev/null +++ b/res/drawable/card_menu_button_expand_material.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/ic_expand_more_black_24dp" + android:state_pressed="true"/> + <item android:drawable="@drawable/ic_expand_more_black_24dp" + android:state_focused="true"/> + <item android:drawable="@drawable/ic_expand_more_black_24dp" + android:state_selected="true"/> + <item android:drawable="@drawable/ic_expand_more_grey600_24dp"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable/card_menu_button_expand_material_animator.xml b/res/drawable/card_menu_button_expand_material_animator.xml new file mode 100644 index 0000000..fa923f4 --- /dev/null +++ b/res/drawable/card_menu_button_expand_material_animator.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/ic_expand_less_black_24dp" + android:state_pressed="true"/> + <item android:drawable="@drawable/ic_expand_less_black_24dp" + android:state_focused="true"/> + <item android:drawable="@drawable/ic_expand_less_black_24dp" + android:state_selected="true"/> + <item android:drawable="@drawable/ic_expand_more_grey600_24dp"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable/card_menu_button_overflow_material.xml b/res/drawable/card_menu_button_overflow_material.xml new file mode 100644 index 0000000..fa08832 --- /dev/null +++ b/res/drawable/card_menu_button_overflow_material.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/ic_more_vert_black_24dp" + android:state_pressed="true"/> + <item android:drawable="@drawable/ic_more_vert_black_24dp" + android:state_focused="true"/> + <item android:drawable="@drawable/ic_more_vert_black_24dp" + android:state_selected="true"/> + <item android:drawable="@drawable/ic_more_vert_grey600_24dp"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable/card_menu_button_rounded_overflow_selector.xml b/res/drawable/card_menu_button_rounded_overflow_selector.xml new file mode 100644 index 0000000..84917da --- /dev/null +++ b/res/drawable/card_menu_button_rounded_overflow_selector.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/ic_more_vert_black_36dp" + android:state_pressed="true"/> + <item android:drawable="@drawable/ic_more_vert_black_36dp" + android:state_focused="true"/> + <item android:drawable="@drawable/ic_more_vert_black_36dp" + android:state_selected="true"/> + <item android:drawable="@drawable/ic_more_vert_grey600_36dp"/> + +</selector>
\ No newline at end of file diff --git a/res/drawable/native_card_selector.xml b/res/drawable/native_card_selector.xml new file mode 100644 index 0000000..6f97f14 --- /dev/null +++ b/res/drawable/native_card_selector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + + <item android:state_activated="true" android:drawable="@color/card_activated"/> + <item android:state_pressed="true" android:drawable="@color/card_pressed"/> + <item android:drawable="@color/card_background"/> + +</selector>
\ No newline at end of file diff --git a/res/layout/base_header_layout.xml b/res/layout/base_header_layout.xml index a031925..841d4fa 100644 --- a/res/layout/base_header_layout.xml +++ b/res/layout/base_header_layout.xml @@ -25,7 +25,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" style="@style/card.header_compound_view"> <!-- This is the Inner Content Header which you can populate runtime @@ -42,7 +42,7 @@ android:id="@+id/card_header_button_frame" style="@style/card.header_button_frame" android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content"> diff --git a/res/layout/base_list_expandable_children_layout.xml b/res/layout/base_list_expandable_children_layout.xml new file mode 100644 index 0000000..708fa5d --- /dev/null +++ b/res/layout/base_list_expandable_children_layout.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="40dp" + android:clickable="true" + android:orientation="vertical" + android:paddingLeft="40dp" + tools:context=".MainActivity" > + + <TextView + android:id="@+id/card_children_simple_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawablePadding="5dp" + android:gravity="center_vertical" + android:textSize="14sp" + android:textStyle="bold" > + </TextView> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/base_section_layout.xml b/res/layout/base_section_layout.xml new file mode 100644 index 0000000..46a5b34 --- /dev/null +++ b/res/layout/base_section_layout.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + style="@style/card_section_container"> + + + <TextView + android:id="@+id/card_section_simple_title" + style="@style/card_section_title" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/base_withlist_empty.xml b/res/layout/base_withlist_empty.xml new file mode 100644 index 0000000..cc6e4fc --- /dev/null +++ b/res/layout/base_withlist_empty.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:text="@string/card_empty_cardwithlist_text" + android:id="@+id/card_base_empty_cardwithlist_text" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/base_withlist_progress.xml b/res/layout/base_withlist_progress.xml new file mode 100644 index 0000000..a258804 --- /dev/null +++ b/res/layout/base_withlist_progress.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:gravity="center_horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ProgressBar + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|center_horizontal"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|center_horizontal" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/card_progressbar_cardwithlist_text" + android:singleLine="true" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/card_base_layout.xml b/res/layout/card_base_layout.xml index 2a564b1..925caac 100644 --- a/res/layout/card_base_layout.xml +++ b/res/layout/card_base_layout.xml @@ -42,4 +42,4 @@ android:layout_height="wrap_content"/> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/card_layout.xml b/res/layout/card_layout.xml index 4ff042b..e0a128f 100644 --- a/res/layout/card_layout.xml +++ b/res/layout/card_layout.xml @@ -72,4 +72,4 @@ </FrameLayout> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/card_overlay_layout.xml b/res/layout/card_overlay_layout.xml index 00ea1d2..28d97a7 100644 --- a/res/layout/card_overlay_layout.xml +++ b/res/layout/card_overlay_layout.xml @@ -78,4 +78,4 @@ </FrameLayout> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/card_thumbnail_layout.xml b/res/layout/card_thumbnail_layout.xml index afc7bad..9c48a26 100644 --- a/res/layout/card_thumbnail_layout.xml +++ b/res/layout/card_thumbnail_layout.xml @@ -82,4 +82,4 @@ > </FrameLayout> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/card_thumbnail_overlay_layout.xml b/res/layout/card_thumbnail_overlay_layout.xml index b0d6912..0567493 100644 --- a/res/layout/card_thumbnail_overlay_layout.xml +++ b/res/layout/card_thumbnail_overlay_layout.xml @@ -88,4 +88,4 @@ > </FrameLayout> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/inner_base_expand.xml b/res/layout/inner_base_expand.xml index 1dfa2d2..219e2e6 100644 --- a/res/layout/inner_base_expand.xml +++ b/res/layout/inner_base_expand.xml @@ -30,4 +30,4 @@ android:id="@+id/card_expand_inner_simple_title" style="@style/card.expand_simple_title"/> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/inner_base_header.xml b/res/layout/inner_base_header.xml index 4ae8b27..6d9d2f8 100644 --- a/res/layout/inner_base_header.xml +++ b/res/layout/inner_base_header.xml @@ -27,9 +27,11 @@ <!-- This is the base Inner View inside a CardHeader. You can customize it with your layout xml file and your CardHeader. - You can popolate your elements with CardHeader#setupInnerViewElements(android.view.ViewGroup, android.view.View) method --> + You can populate your elements with CardHeader#setupInnerViewElements(android.view.ViewGroup, android.view.View) method --> <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:id="@+id/card_header_inner_simple_title" style="@style/card.header_simple_title"/> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/inner_base_main.xml b/res/layout/inner_base_main.xml index 90a0d57..d9cc34e 100644 --- a/res/layout/inner_base_main.xml +++ b/res/layout/inner_base_main.xml @@ -24,9 +24,9 @@ android:layout_height="wrap_content"> - <!-- This is the base Inner View inside a CardHeader. - You can customize it with your layout xml file and your CardHeader. - You can popolate your element with CardHeader#setupInnerViewElements() method --> + <!-- This is the base Inner View inside a Card. + You can customize it with your layout xml file and your Card. + You can populate your element with Card#setupInnerViewElements() method --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/res/layout/inner_base_main_cardwithlist.xml b/res/layout/inner_base_main_cardwithlist.xml new file mode 100644 index 0000000..caa7235 --- /dev/null +++ b/res/layout/inner_base_main_cardwithlist.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_marginLeft="@dimen/card_base_cardwithlist_layout_leftmargin" + android:layout_marginRight="@dimen/card_base_cardwithlist_layout_rightmargin" + android:layout_height="wrap_content"> + + + <!-- This is the base Inner View inside a CardWithList. + You can customize it with your layout xml file using the CardWithList constructor. + If you change the id name, pay attention to update your getListViewId() method in your Card. + --> + <com.android.cards.prototypes.LinearListView + android:id="@+id/card_inner_base_main_cardwithlist" + style="@style/cardwithlist" + card:card_list_item_dividerHeight="@dimen/card_base_cardwithlist_dividerHeight" + /> + + <!-- This provide a default empty-view built-in feature. + You can customize this layout in manu ways. Check the doc--> + <ViewStub + android:id="@+id/card_inner_base_empty_cardwithlist" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + /> + + <ViewStub + android:id="@+id/card_inner_base_progressbar_cardwithlist" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="gone" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/list_card_undo_material_message.xml b/res/layout/list_card_undo_material_message.xml new file mode 100644 index 0000000..0f93e0c --- /dev/null +++ b/res/layout/list_card_undo_material_message.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ /******************************************************************************* + ~ Copyright (c) 2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ******************************************************************************/ + --> +<!-- Use this layout for a default undo message --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/list_card_undobar" + style="@style/list_card_UndoBar_material" + android:orientation="horizontal"> + + <TextView + tools:text="Marked as read" + android:id="@+id/list_card_undobar_message" + android:fontFamily="@string/font_fontFamily_regular" + style="@style/list_card_UndoBarMessage_material"/> + + <TextView + android:id="@+id/list_card_undobar_button" + android:fontFamily="@string/font_fontFamily_medium" + style="@style/list_card_UndoBarButton_material"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/list_card_undo_materialmobile_message.xml b/res/layout/list_card_undo_materialmobile_message.xml new file mode 100644 index 0000000..605f13d --- /dev/null +++ b/res/layout/list_card_undo_materialmobile_message.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ /******************************************************************************* + ~ Copyright (c) 2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ******************************************************************************/ + --> +<!-- Use this layout for a default undo message --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/list_card_undobar" + style="@style/list_card_UndoBar_materialmobile" + android:orientation="horizontal"> + + <TextView + tools:text="Marked as read" + android:id="@+id/list_card_undobar_message" + android:fontFamily="@string/font_fontFamily_regular" + style="@style/list_card_UndoBarMessage_materialmobile"/> + + <TextView + android:id="@+id/list_card_undobar_button" + android:fontFamily="@string/font_fontFamily_medium" + style="@style/list_card_UndoBarButton_materialmobile"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_base_header_layout.xml b/res/layout/native_base_header_layout.xml new file mode 100644 index 0000000..aec3528 --- /dev/null +++ b/res/layout/native_base_header_layout.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- This is the Header Layout + If you customize your header layout, if you want to preserve built-in feature use same IDs. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + style="@style/card.native.header_compound_view"> + + <!-- This is the Inner Content Header which you can populate runtime + with setupInnerViewElements(android.view.ViewGroup, android.view.View) method in CardHeader class.--> + <FrameLayout + android:id="@+id/card_header_inner_frame" + style="@style/card.native.header_inner_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + + <!-- This is the Button Frame. + You can custom its behaviour with some methods in CardHeader class --> + <FrameLayout + android:id="@+id/card_header_button_frame" + style="@style/card.native.header_button_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <!-- overflow button. It has a Popup Menu --> + <ImageButton + android:id="@+id/card_header_button_overflow" + style="@style/card.native.header_button_base.overflow" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <!-- Expand/Collapse button. It shows/hides a Hidden Frame--> + <ImageButton + android:id="@+id/card_header_button_expand" + style="@style/card.native.header_button_base.expand" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <!-- Other button. You can customize it with your style file --> + <ImageButton + android:id="@+id/card_header_button_other" + style="@style/card.native.header_button_base.other" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </FrameLayout> + +</RelativeLayout> + diff --git a/res/layout/native_base_thumbnail_layout.xml b/res/layout/native_base_thumbnail_layout.xml new file mode 100644 index 0000000..553cfdd --- /dev/null +++ b/res/layout/native_base_thumbnail_layout.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<ImageView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/card_thumbnail_image" + style="@style/card.native.card_thumbnail_image"/> + + diff --git a/res/layout/native_card_layout.xml b/res/layout/native_card_layout.xml new file mode 100644 index 0000000..6dee707 --- /dev/null +++ b/res/layout/native_card_layout.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + style="@style/card.native" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <!-- Card visible layout --> + <com.android.cards.view.ForegroundLinearLayout + android:id="@+id/card_main_layout" + style="@style/card.native.main_layout_foreground" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + + <!-- Compound view for Header Card + If you want to customize this element use attr card:card_header_layout_resourceID + You can also use your CardHeader subclass--> + <com.android.cards.view.component.CardHeaderView + style="@style/card.native.header_outer_layout" + android:id="@+id/card_header_layout" + android:layout_width="match_parent" + card:card_header_layout_resourceID="@layout/native_base_header_layout" + android:layout_height="wrap_content"/> + + <!-- Main Content View --> + <FrameLayout + android:id="@+id/card_main_content_layout" + style="@style/card.native.content_outer_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + </com.android.cards.view.ForegroundLinearLayout> + + <!-- Expand layout. You can customize this element with your CardExpand class --> + <FrameLayout + android:id="@+id/card_content_expand_layout" + style="@style/card.native.main_contentExpand" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + + </FrameLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_card_thumbnail_layout.xml b/res/layout/native_card_thumbnail_layout.xml new file mode 100644 index 0000000..2a36b6b --- /dev/null +++ b/res/layout/native_card_thumbnail_layout.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <!-- Card visible layout --> + <com.android.cards.view.ForegroundLinearLayout + android:id="@+id/card_main_layout" + style="@style/card.native.main_layout_foreground" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + + <!-- Compound view for Header Card + If you want to customize this element use attr card:card_header_layout_resourceID + You can also use your CardHeader subclass--> + <com.android.cards.view.component.CardHeaderView + style="@style/card.native.header_outer_layout" + android:id="@+id/card_header_layout" + android:layout_width="match_parent" + card:card_header_layout_resourceID="@layout/native_base_header_layout" + android:layout_height="wrap_content"/> + + <LinearLayout + android:id="@+id/card_thumb_and_content_layout" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.android.cards.view.component.CardThumbnailView + style="@style/card.native.card_thumbnail_outer_layout" + android:id="@+id/card_thumbnail_layout" + android:layout_width="wrap_content" + card:card_thumbnail_layout_resourceID="@layout/native_base_thumbnail_layout" + android:layout_height="match_parent"/> + + <!-- Main Content View --> + <FrameLayout + android:id="@+id/card_main_content_layout" + style="@style/card.native.content_outer_layout" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + </LinearLayout> + + </com.android.cards.view.ForegroundLinearLayout> + + <!-- Expand layout. You can customize this element with your CardExpand class --> + <FrameLayout + android:id="@+id/card_content_expand_layout" + style="@style/card.native.main_contentExpand" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + </FrameLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_cardwithlist_layout.xml b/res/layout/native_cardwithlist_layout.xml new file mode 100644 index 0000000..611b8b7 --- /dev/null +++ b/res/layout/native_cardwithlist_layout.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + style="@style/card.native" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <!-- Card visible layout --> + <com.android.cards.view.ForegroundLinearLayout + android:id="@+id/card_main_layout" + style="@style/card.native.main_layout_foreground" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + + <!-- Compound view for Header Card + If you want to customize this element use attr card:card_header_layout_resourceID + You can also use your CardHeader subclass--> + <com.android.cards.view.component.CardHeaderView + style="@style/card.native.header_outer_layout" + android:id="@+id/card_header_layout" + android:layout_width="match_parent" + card:card_header_layout_resourceID="@layout/native_base_header_layout" + android:layout_height="wrap_content"/> + + <!-- Main Content View --> + <FrameLayout + android:id="@+id/card_main_content_layout" + style="@style/card.native.content_outer_layout.cardwithlist" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + </com.android.cards.view.ForegroundLinearLayout> + + <!-- Expand layout. You can customize this element with your CardExpand class --> + <FrameLayout + android:id="@+id/card_content_expand_layout" + style="@style/card.native.main_contentExpand" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + </FrameLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_inner_base_expand.xml b/res/layout/native_inner_base_expand.xml new file mode 100644 index 0000000..6fa78d1 --- /dev/null +++ b/res/layout/native_inner_base_expand.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- This is the InnerContent of Expand/Collapse Area --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/card_expand_inner_simple_title" + style="@style/card.native.expand_simple_title"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_inner_base_header.xml b/res/layout/native_inner_base_header.xml new file mode 100644 index 0000000..87fdb64 --- /dev/null +++ b/res/layout/native_inner_base_header.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- This is the InnerContent of Header --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + + <!-- This is the base Inner View inside a CardHeader. + You can customize it with your layout xml file and your CardHeader. + You can populate your elements with CardHeader#setupInnerViewElements(android.view.ViewGroup, android.view.View) method --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/card_header_inner_simple_title" + style="@style/card.native.header_simple_title"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_inner_base_main.xml b/res/layout/native_inner_base_main.xml new file mode 100644 index 0000000..43c74b2 --- /dev/null +++ b/res/layout/native_inner_base_main.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + + <!-- This is the base Inner View inside a Card. + You can customize it with your layout xml file and your Card. + You can populate your element with Card#setupInnerViewElements() method --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/card_main_inner_simple_title" + style="@style/card.native.base_simple_title"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_inner_base_main_cardwithlist.xml b/res/layout/native_inner_base_main_cardwithlist.xml new file mode 100644 index 0000000..77bbbbd --- /dev/null +++ b/res/layout/native_inner_base_main_cardwithlist.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_marginLeft="@dimen/card_base_cardwithlist_layout_leftmargin" + android:layout_marginRight="@dimen/card_base_cardwithlist_layout_rightmargin" + android:layout_height="wrap_content"> + + + <!-- This is the base Inner View inside a CardWithList. + You can customize it with your layout xml file using the CardWithList constructor. + If you change the id name, pay attention to update your getListViewId() method in your Card. + --> + <com.android.cards.prototypes.LinearListView + android:id="@+id/card_inner_base_main_cardwithlist" + style="@style/native_cardwithlist" + card:card_list_item_dividerHeight="@dimen/card_base_cardwithlist_dividerHeight" + /> + + <!-- This provide a default empty-view built-in feature. + You can customize this layout in manu ways. Check the doc--> + <ViewStub + android:id="@+id/card_inner_base_empty_cardwithlist" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + /> + + <ViewStub + android:id="@+id/card_inner_base_progressbar_cardwithlist" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="gone" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/native_list_card_layout.xml b/res/layout/native_list_card_layout.xml new file mode 100644 index 0000000..ecef674 --- /dev/null +++ b/res/layout/native_list_card_layout.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- You can customize this layout. + You need to have in your layout a `CardView` with the ID `list_cardId` --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent"> + + <com.android.cards.view.CardViewNative + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:id="@+id/list_cardId" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/native_list_card.base" + card:card_layout_resourceID="@layout/native_card_layout"/> + +</LinearLayout> diff --git a/res/layout/native_list_card_thumbnail_layout.xml b/res/layout/native_list_card_thumbnail_layout.xml new file mode 100644 index 0000000..a9b6bdc --- /dev/null +++ b/res/layout/native_list_card_thumbnail_layout.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- You can customize this layout. + You need to have in your layout a `CardView` with the ID `list_cardId` --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent"> + +<com.android.cards.view.CardViewNative + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:id="@+id/list_cardId" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/native_list_card.thumbnail" + card:card_layout_resourceID="@layout/native_card_thumbnail_layout" + /> + + +</LinearLayout>
\ No newline at end of file diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml new file mode 100644 index 0000000..1281dd4 --- /dev/null +++ b/res/values-de/strings.xml @@ -0,0 +1,41 @@ +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + + + <!-- Undo Controller--> + + <string name="list_card_undo_title">Rückgängig</string> + <!--<string name="undo_card">Karte entfernt</string>--> + + <plurals name="list_card_undo_items"> + <item quantity="one">1 Karte entfernt</item> + <item quantity="other">%d Karten entfernt</item> + </plurals> + + <!-- Card selected item with CAB --> + <plurals name="card_selected_items"> + <item quantity="one">1 Element gewählt</item> + <item quantity="other">%d Elemente gewählt</item> + </plurals> + + + + +</resources> diff --git a/res/values-v16/fonts.xml b/res/values-v16/fonts.xml new file mode 100644 index 0000000..98bd445 --- /dev/null +++ b/res/values-v16/fonts.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + <string name="card_font_fontFamily_header">sans-serif-condensed</string> + <string name="card_native_font_fontFamily_header">sans-serif-medium</string> + + <string name="card_font_fontFamily_main_content">sans-serif</string> + <string name="card_native_font_fontFamily_main_content">sans-serif</string> + + <string name="card_font_fontFamily_expand">sans-serif-condensed</string> + <string name="card_native_font_fontFamily_expand">sans-serif</string> +</resources>
\ No newline at end of file diff --git a/res/values-v16/styles.xml b/res/values-v16/styles.xml new file mode 100644 index 0000000..66eadac --- /dev/null +++ b/res/values-v16/styles.xml @@ -0,0 +1,99 @@ +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + + <style name="card"></style> + + + <!-- Style for Header ******************************************************--> + + <!--Style for simple title header inner view--> + <style name="card.header_simple_title"> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:fontFamily">@string/card_font_fontFamily_header</item> + <item name="android:textColor">@color/card_text_color_header</item> + <item name="android:textSize">@dimen/card_header_simple_title_text_size</item> + <item name="android:layout_marginLeft">@dimen/card_header_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_header_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_header_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_header_simple_title_margin_bottom</item> + </style> + + <style name="card.native.header_simple_title" > + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:textSize">@dimen/card_header_native_simple_title_text_size</item> + <item name="android:textColor">@color/card_text_color_header</item> + <item name="android:fontFamily">@string/card_native_font_fontFamily_header</item> + <item name="android:layout_marginLeft">@dimen/card_header_native_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_header_native_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_header_native_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_header_native_simple_title_margin_bottom</item> + </style> + + + <!-- Style for Content ******************************************************--> + + <!--Style for simple title inner main view--> + <style name="card.base_simple_title"> + <item name="android:textStyle">bold</item> + <item name="android:fontFamily">@string/card_font_fontFamily_main_content</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_main_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_main_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_main_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_main_simple_title_margin_bottom</item> + </style> + + <!--Style for simple title inner main view--> + <style name="card.native.base_simple_title"> + <item name="android:textStyle">bold</item> + <item name="android:fontFamily">@string/card_native_font_fontFamily_main_content</item> + <item name="android:textSize">@dimen/card_main_content_native_simple_title_text_size</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_main_native_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_main_native_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_header_native_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_header_native_simple_title_margin_bottom</item> + </style> + + <!-- Style for Expand/Collapse *************************************************--> + + <!--Style for simple title expand/collapse inner view--> + <style name="card.expand_simple_title"> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:gravity">center_vertical</item> + <item name="android:fontFamily">@string/card_font_fontFamily_expand</item> + <item name="android:textSize">@dimen/card_expand_simple_title_text_size</item> + <item name="android:paddingLeft">@dimen/card_expand_simple_title_paddingLeft</item> + <item name="android:paddingRight">@dimen/card_expand_simple_title_paddingRight</item> + <item name="android:textColor">@color/card_expand_title_color</item> + </style> + + <!--Style for simple title expand/collapse inner view--> + <style name="card.native.expand_simple_title"> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textSize">@dimen/card_expand_native_simple_title_text_size</item> + <item name="android:paddingLeft">@dimen/card_expand_native_simple_title_padding_left</item> + <item name="android:paddingRight">@dimen/card_expand_native_simple_title_padding_right</item> + <item name="android:textColor">@color/card_expand_title_color</item> + <item name="android:fontFamily">@string/card_native_font_fontFamily_expand</item> + </style> + +</resources> diff --git a/res/values-v21/fonts.xml b/res/values-v21/fonts.xml new file mode 100644 index 0000000..226c74b --- /dev/null +++ b/res/values-v21/fonts.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + <string name="card_font_fontFamily_header">sans-serif-condensed</string> + <string name="card_native_font_fontFamily_header">sans-serif-medium</string> + + <string name="card_font_fontFamily_main_content">sans-serif</string> + <string name="card_native_font_fontFamily_main_content">sans-serif</string> +</resources>
\ No newline at end of file diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml new file mode 100644 index 0000000..51fa5e0 --- /dev/null +++ b/res/values-v21/styles.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + + <!-- Button Base in Header--> + <style name="card.header_button_base"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentRight">true</item> + <item name="android:paddingLeft">@dimen/card_header_button_padding_left</item> + <item name="android:focusable">false</item> + <item name="android:focusableInTouchMode">false</item> + <item name="android:clickable">true</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginRight">@dimen/card_header_button_margin_right</item> + <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item> + </style> + + <style name="card.native.header_button_base"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentRight">true</item> + <item name="android:focusable">false</item> + <item name="android:focusableInTouchMode">false</item> + <item name="android:clickable">true</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item> + <item name="android:layout_marginLeft">@dimen/card_header_native_button_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_header_native_button_margin_right</item> + <item name="android:paddingLeft">0dp</item> + </style> + + <!-- Button Overflow in Header --> + <style name="card.header_button_base.overflow" > + <item name="android:src">@drawable/ic_menu_overflow_card_rounded_dark_normal</item> + </style> + + <style name="card.native.header_button_base.overflow" > + <item name="android:src">@drawable/card_menu_button_overflow_material</item> + </style> + + <!-- Button to Expand/Collapse in Header --> + <style name="card.header_button_base.expand"> + <item name="android:src">@drawable/ic_menu_expand_card_dark_normal</item> + </style> + + <style name="card.native.header_button_base.expand"> + <item name="android:src">@drawable/card_menu_button_expand_material_animator</item> + </style> + + + <!-- Style for Main Layout ****************************************--> + +</resources>
\ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 0ab9e6a..b33ea39 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -34,6 +34,9 @@ <!-- Layout used by card in a ListView. Default value is @layout/list_card_layout --> <attr name="list_card_layout_resourceID" format="reference"/> + <!-- Layout used by card in a ListView. Default value is @layout/list_card_layout --> + <attr name="list_card_layout_resourceIDs" format="reference"/> + </declare-styleable> <!-- Used for ForegroundLinearLayout --> @@ -44,5 +47,10 @@ <attr name="android:foregroundGravity" /> </declare-styleable> + <!-- Used in card with list--> + <declare-styleable name="card_listItem"> + <!-- Height of divider used in the list --> + <attr name="card_list_item_dividerHeight" format="dimension" /> + </declare-styleable> </resources>
\ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index 4937c53..3cd6bab 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -21,6 +21,7 @@ <color name="card_background">#FFF</color> <color name="card_background_header">#FFF</color> <color name="card_backgroundExpand">#515254</color> + <color name="card_native_background">@android:color/transparent</color> <color name="card_text_color_header">#000</color> <color name="card_expand_title_color">#B3B2B2</color> @@ -28,8 +29,23 @@ <!-- Used by selector --> <color name="card_pressed">#7E0099CC</color> <color name="card_activated">#CFE9F3</color> + <color name="card_foreground_activated">#99CFE9F3</color> <color name="card_pressed_kitkat">#10000000</color> <color name="card_activated_kitkat">#ADE1F4</color> + <color name="card_foreground_activated_kitkat">#99ADE1F4</color> -</resources>
\ No newline at end of file + <!-- Used by card with list --> + <color name="card_base_cardwithlist_divider_color">#E5E5E5</color> + <color name="card_base_cardwithlist_background_list_color">#10000000</color> + + <color name="card_native_base_cardwithlist_divider_color">#E5E5E5</color> + <color name="card_native_base_cardwithlist_background_list_color">#10000000</color> + + <!-- Used by sectioned list --> + <color name="card_section_container_color">#10000000</color> + <color name="card_section_title_color">@android:color/black</color> + + + +</resources> diff --git a/res/values/colors_undo.xml b/res/values/colors_undo.xml new file mode 100644 index 0000000..3af0abc --- /dev/null +++ b/res/values/colors_undo.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ /******************************************************************************* + ~ Copyright (c) 2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ******************************************************************************/ + --> + +<resources> + <color name="card_undobar_material_text_color">#4CAF50</color> + <color name="card_undobar_material_background_color">#323232</color> +</resources>
\ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 74bd074..b05ddf1 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -21,50 +21,95 @@ <!-- Card --> <dimen name="card_base_empty_height">96dp</dimen> - <dimen name="card_background_default_radius">2dip</dimen> + <dimen name="card_background_default_radius">2dp</dimen> + + <!-- Native card margin --> + <dimen name="card_default_native_margin_top">12dp</dimen> + <dimen name="card_default_native_margin_bottom">12dp</dimen> + <dimen name="card_default_native_margin_left">8dp</dimen> + <dimen name="card_default_native_margin_right">8dp</dimen> <!-- Main --> <dimen name="card_main_layout_view_margin_top">0dp</dimen> - <dimen name="card_main_layout_view_margin_start">0dp</dimen> + <dimen name="card_main_layout_view_margin_left">0dp</dimen> <dimen name="card_main_layout_view_margin_bottom">0dp</dimen> - <dimen name="card_main_layout_view_margin_end">0dp</dimen> + <dimen name="card_main_layout_view_margin_right">0dp</dimen> + + <dimen name="card_main_layout_native_view_margin_top">0dp</dimen> + <dimen name="card_main_layout_native_view_margin_left">0dp</dimen> + <dimen name="card_main_layout_native_view_margin_bottom">0dp</dimen> + <dimen name="card_main_layout_native_view_margin_right">0dp</dimen> <!-- Header --> + <dimen name="card_header_default_padding">12dp</dimen> + <dimen name="card_header_native_default_paddingLeft">12dp</dimen> + <dimen name="card_header_native_default_paddingRight">12dp</dimen> + <dimen name="card_header_native_default_paddingTop">12dp</dimen> + <dimen name="card_header_native_default_paddingBottom">0dp</dimen> + <dimen name="card_header_outer_view_margin_top">0dp</dimen> - <dimen name="card_header_outer_view_margin_start">0dp</dimen> + <dimen name="card_header_outer_view_margin_left">0dp</dimen> <dimen name="card_header_outer_view_margin_bottom">0dp</dimen> - <dimen name="card_header_outer_view_margin_end">0dp</dimen> + <dimen name="card_header_outer_view_margin_right">0dp</dimen> <!--Button Header --> - <dimen name="card_header_button_padding_start">10dp</dimen> - <dimen name="card_header_button_padding_end">0dp</dimen> + <dimen name="card_header_button_padding_left">10dp</dimen> + <dimen name="card_header_button_padding_right">0dp</dimen> <dimen name="card_header_button_padding_bottom">0dp</dimen> <dimen name="card_header_button_padding_top">0dp</dimen> - <dimen name="card_header_button_margin_end">2dp</dimen> - <dimen name="card_header_button_overflow_margin_end">3dp</dimen> + <dimen name="card_header_button_margin_right">2dp</dimen> + <dimen name="card_header_button_overflow_margin_right">3dp</dimen> + + <dimen name="card_header_native_button_margin_right">0dp</dimen> + <dimen name="card_header_native_button_margin_left">0dp</dimen> <!-- Title Header --> - <dimen name="card_header_simple_title_margin_start">10dp</dimen> + <dimen name="card_header_simple_title_margin_left">10dp</dimen> <dimen name="card_header_simple_title_margin_top">5dp</dimen> - <dimen name="card_header_simple_title_text_size">20sp</dimen> - <dimen name="card_header_simple_title_margin_end">20dp</dimen> + <dimen name="card_header_simple_title_margin_right">20dp</dimen> <dimen name="card_header_simple_title_margin_bottom">0dp</dimen> + <dimen name="card_header_simple_title_text_size">20sp</dimen> + + <dimen name="card_header_native_simple_title_margin_left">0dp</dimen> + <dimen name="card_header_native_simple_title_margin_top">0dp</dimen> + <dimen name="card_header_native_simple_title_margin_right">0dp</dimen> + <dimen name="card_header_native_simple_title_margin_bottom">0dp</dimen> + <dimen name="card_header_native_simple_title_margin">0dp</dimen> + <dimen name="card_header_native_simple_title_text_size">18sp</dimen> <!-- Content --> <dimen name="card_content_outer_view_margin_top">0dp</dimen> - <dimen name="card_content_outer_view_margin_start">0dp</dimen> + <dimen name="card_content_outer_view_margin_left">0dp</dimen> <dimen name="card_content_outer_view_margin_bottom">0dp</dimen> - <dimen name="card_content_outer_view_margin_end">0dp</dimen> + <dimen name="card_content_outer_view_margin_right">0dp</dimen> + + <dimen name="card_main_content_native_default_paddingLeft">12dp</dimen> + <dimen name="card_main_content_native_default_paddingRight">12dp</dimen> + <dimen name="card_main_content_native_default_paddingTop">12dp</dimen> + <dimen name="card_main_content_native_default_paddingBottom">12dp</dimen> + + <dimen name="card_main_content_native_cardwithlist_paddingLeft">0dp</dimen> + <dimen name="card_main_content_native_cardwithlist_paddingRight">0dp</dimen> + <dimen name="card_main_content_native_cardwithlist_paddingTop">12dp</dimen> + <dimen name="card_main_content_native_cardwithlist_paddingBottom">12dp</dimen> + - <dimen name="card_main_simple_title_margin_start">5dp</dimen> + <dimen name="card_main_simple_title_margin_left">5dp</dimen> <dimen name="card_main_simple_title_margin_top">0dp</dimen> + <dimen name="card_main_simple_title_margin_right">0dp</dimen> + <dimen name="card_main_simple_title_margin_bottom">0dp</dimen> + + <dimen name="card_main_native_simple_title_margin_left">0dp</dimen> + <dimen name="card_main_native_simple_title_margin_top">0dp</dimen> + + <dimen name="card_main_content_native_simple_title_text_size">16sp</dimen> <!-- Shadow --> <dimen name="card_shadow_height">3dp</dimen> <dimen name="card_shadow_view_margin_top">0dp</dimen> - <dimen name="card_shadow_view_margin_start">@dimen/card_background_default_radius</dimen> + <dimen name="card_shadow_view_margin_left">@dimen/card_background_default_radius</dimen> <dimen name="card_shadow_view_margin_bottom">0dp</dimen> - <dimen name="card_shadow_view_margin_end">@dimen/card_background_default_radius</dimen> + <dimen name="card_shadow_view_margin_right">@dimen/card_background_default_radius</dimen> <!-- Thumbnail --> <dimen name="card_thumbnail_width">72dp</dimen> @@ -72,23 +117,60 @@ <!-- Expand --> <dimen name="card_expand_layout_padding">2dp</dimen> - <dimen name="card_expand_simple_title_paddingStart">10dp</dimen> - <dimen name="card_expand_simple_title_paddingEnd">10dp</dimen> + <dimen name="card_expand_simple_title_paddingLeft">10dp</dimen> + <dimen name="card_expand_simple_title_paddingRight">10dp</dimen> <dimen name="card_expand_simple_title_text_size">14sp</dimen> + <dimen name="card_expand_native_layout_padding_left">12dp</dimen> + <dimen name="card_expand_native_layout_padding_right">12dp</dimen> + <dimen name="card_expand_native_layout_padding_top">12dp</dimen> + <dimen name="card_expand_native_layout_padding_bottom">12dp</dimen> + + <dimen name="card_expand_native_margin_right">0dp</dimen> + <dimen name="card_expand_native_margin_left">0dp</dimen> + <dimen name="card_expand_native_simple_title_text_size">14sp</dimen> + <dimen name="card_expand_native_simple_title_padding_left">0dp</dimen> + <dimen name="card_expand_native_simple_title_padding_right">0dp</dimen> <!-- List --> - <dimen name="list_card_padding_start">8dp</dimen> - <dimen name="list_card_padding_end">8dp</dimen> + <dimen name="list_card_padding_left">8dp</dimen> + <dimen name="list_card_padding_right">8dp</dimen> <dimen name="list_card_padding_bottom">6dp</dimen> <dimen name="list_card_padding_top">2dp</dimen> + <!-- List --> + <dimen name="native_list_card_margin_left">0dp</dimen> + <dimen name="native_list_card_margin_right">0dp</dimen> + <dimen name="native_list_card_margin_bottom">6dp</dimen> + <dimen name="native_list_card_margin_top">6dp</dimen> + + <!-- RecyclerView --> + <dimen name="native_recyclerview_card_margin_left">0dp</dimen> + <dimen name="native_recyclerview_card_margin_right">0dp</dimen> + <dimen name="native_recyclerview_card_margin_bottom">6dp</dimen> + <dimen name="native_recyclerview_card_margin_top">6dp</dimen> + <!-- Grid --> - <dimen name="grid_card_padding_start">2dp</dimen> - <dimen name="grid_card_padding_end">2dp</dimen> + <dimen name="grid_card_padding_left">2dp</dimen> + <dimen name="grid_card_padding_right">2dp</dimen> <dimen name="grid_card_padding_bottom">2dp</dimen> <dimen name="grid_card_padding_top">2dp</dimen> + <!-- Card with list --> + <dimen name="card_base_cardwithlist_layout_leftmargin">0dp</dimen> + <dimen name="card_base_cardwithlist_layout_rightmargin">0dp</dimen> + <dimen name="card_base_cardwithlist_list_margin_top">10dp</dimen> + <dimen name="card_base_cardwithlist_list_margin_left">0dp</dimen> + + <dimen name="card_base_cardwithlist_dividerHeight">1dp</dimen> + + + <!--Section --> + <dimen name="card_section_title">18sp</dimen> + <dimen name="card_section_container_padding_left">15dp</dimen> + <dimen name="card_section_container_padding_right">15dp</dimen> + <dimen name="card_section_title_margin_top">10dp</dimen> + -</resources> +</resources>
\ No newline at end of file diff --git a/res/values/fonts.xml b/res/values/fonts.xml new file mode 100644 index 0000000..3d5bf60 --- /dev/null +++ b/res/values/fonts.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ /******************************************************************************* + ~ Copyright (c) 2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ******************************************************************************/ + --> + +<resources> + <string name="font_fontFamily_regular">sans-serif</string> + <string name="font_fontFamily_medium">sans-serif</string> +</resources>
\ No newline at end of file diff --git a/res/values/integers.xml b/res/values/integers.xml new file mode 100644 index 0000000..32779ba --- /dev/null +++ b/res/values/integers.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<resources> + <integer name="list_card_swipe_distance_divisor">2</integer> +</resources>
\ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 0ac9f34..8a7847d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -17,8 +17,6 @@ --> <resources> - - <string name="app_name">CardsLib</string> <!-- Undo Controller--> @@ -37,7 +35,9 @@ <item quantity="other">%d items selected</item> </plurals> - + <!-- Card with List --> + <string name="card_empty_cardwithlist_text">No data....</string> + <string name="card_progressbar_cardwithlist_text">Loading data...</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 9c04d11..5fe4d68 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -19,7 +19,18 @@ <resources> <style name="card"> + <!-- this style should be empty --> + </style> + + <style name="card.native"> + <!-- this style should be empty --> + </style> + <style name="card_external"> + <item name="android:layout_marginTop">@dimen/card_default_native_margin_top</item> + <item name="android:layout_marginBottom">@dimen/card_default_native_margin_bottom</item> + <item name="android:layout_marginLeft">@dimen/card_default_native_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_default_native_margin_right</item> </style> <!-- Style for Shadow ******************************************************--> @@ -28,8 +39,8 @@ <style name="card.shadow_outer_layout"> <item name="android:layout_marginTop">@dimen/card_shadow_view_margin_top</item> <item name="android:layout_marginBottom">@dimen/card_shadow_view_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/card_shadow_view_margin_start</item> - <item name="android:layout_marginEnd">@dimen/card_shadow_view_margin_end</item> + <item name="android:layout_marginLeft">@dimen/card_shadow_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_shadow_view_margin_right</item> </style> <!--Style for shadow image--> @@ -42,11 +53,26 @@ <!-- Style for Header ******************************************************--> + <style name="card.header_outer_layout"> <item name="android:layout_marginTop">@dimen/card_header_outer_view_margin_top</item> <item name="android:layout_marginBottom">@dimen/card_header_outer_view_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/card_header_outer_view_margin_start</item> - <item name="android:layout_marginEnd">@dimen/card_header_outer_view_margin_end</item> + <item name="android:layout_marginLeft">@dimen/card_header_outer_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_header_outer_view_margin_right</item> + </style> + + <style name="card.native.header_outer_layout" > + <item name="android:paddingLeft">@dimen/card_header_native_default_paddingLeft</item> + <item name="android:paddingRight">@dimen/card_header_native_default_paddingRight</item> + <item name="android:paddingTop">@dimen/card_header_native_default_paddingTop</item> + <item name="android:paddingBottom">@dimen/card_header_native_default_paddingBottom</item> + </style> + + <style name="card.native.header_outer_layout.nomargin" > + <item name="android:paddingLeft">0dp</item> + <item name="android:paddingRight">0dp</item> + <item name="android:paddingTop">0dp</item> + <item name="android:paddingBottom">0dp</item> </style> <!--Style for Header Compound View --> @@ -54,43 +80,82 @@ </style> - <!--Style for simple title header inner view--> + <style name="card.native.header_compound_view"> + + </style> + + <style name="card.native.header_inner_frame"> + <item name="android:layout_centerVertical">true</item> + </style> + + <!--Style for simple title inner header view--> <style name="card.header_simple_title"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_gravity">start|center_vertical</item> - <item name="android:fontFamily">sans-serif-condensed</item> - <item name="android:textColor">@color/card_text_color_header</item> <item name="android:textSize">@dimen/card_header_simple_title_text_size</item> - <item name="android:layout_marginStart">@dimen/card_header_simple_title_margin_start</item> + <item name="android:textColor">@color/card_text_color_header</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_header_simple_title_margin_left</item> <item name="android:layout_marginTop">@dimen/card_header_simple_title_margin_top</item> - <item name="android:layout_marginEnd">@dimen/card_header_simple_title_margin_end</item> + <item name="android:layout_marginRight">@dimen/card_header_simple_title_margin_right</item> <item name="android:layout_marginBottom">@dimen/card_header_simple_title_margin_bottom</item> </style> + <style name="card.native.header_simple_title" > + <item name="android:textSize">@dimen/card_header_native_simple_title_text_size</item> + <item name="android:textColor">@color/card_text_color_header</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_header_native_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_header_native_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_header_native_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_header_native_simple_title_margin_bottom</item> + </style> + <!-- Style for Header Buttons ***********************************************--> <style name="card.header_button_frame"> </style> + + <style name="card.native.header_button_frame"> + <item name="android:layout_centerVertical">true</item> + <item name="android:layout_alignParentRight">true</item> + </style> + <!-- Button Base in Header--> <style name="card.header_button_base"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="android:layout_alignParentEnd">true</item> - <item name="android:paddingStart">@dimen/card_header_button_padding_start</item> + <item name="android:layout_alignParentRight">true</item> + <item name="android:paddingLeft">@dimen/card_header_button_padding_left</item> + <item name="android:focusable">false</item> + <item name="android:focusableInTouchMode">false</item> + <item name="android:clickable">true</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginRight">@dimen/card_header_button_margin_right</item> + </style> + + <style name="card.native.header_button_base"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentRight">true</item> <item name="android:focusable">false</item> <item name="android:focusableInTouchMode">false</item> <item name="android:clickable">true</item> <item name="android:layout_centerVertical">true</item> - <item name="android:layout_marginEnd">@dimen/card_header_button_margin_end</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_header_native_button_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_header_native_button_margin_right</item> + <item name="android:paddingLeft">0dp</item> </style> <!-- Button Overflow in Header --> <style name="card.header_button_base.overflow" > <item name="android:background">@drawable/card_menu_button_rounded_overflow</item> - <item name="android:layout_marginEnd">@dimen/card_header_button_overflow_margin_end</item> + </style> + + <style name="card.native.header_button_base.overflow" > + <item name="android:background">@drawable/card_menu_button_overflow_material</item> </style> <!-- Button to Expand/Collapse in Header --> @@ -98,11 +163,19 @@ <item name="android:background">@drawable/card_menu_button_expand</item> </style> + <style name="card.native.header_button_base.expand"> + <item name="android:background">@drawable/card_menu_button_expand_material_animator</item> + </style> + + <!-- Other Button in Header --> <style name="card.header_button_base.other"> </style> + <style name="card.native.header_button_base.other"> + + </style> <!-- Style for Content ******************************************************--> @@ -112,19 +185,38 @@ <item name="android:minHeight">@dimen/card_base_empty_height</item> <item name="android:layout_marginTop">@dimen/card_content_outer_view_margin_top</item> <item name="android:layout_marginBottom">@dimen/card_content_outer_view_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/card_content_outer_view_margin_start</item> - <item name="android:layout_marginEnd">@dimen/card_content_outer_view_margin_end</item> + <item name="android:layout_marginLeft">@dimen/card_content_outer_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_content_outer_view_margin_right</item> + </style> + + <!--Style for Content View--> + <style name="card.native.content_outer_layout" > + <item name="android:layout_width">match_parent</item> + <item name="android:minHeight">@dimen/card_base_empty_height</item> + <item name="android:paddingLeft">@dimen/card_main_content_native_default_paddingLeft</item> + <item name="android:paddingRight">@dimen/card_main_content_native_default_paddingRight</item> + <item name="android:paddingTop">@dimen/card_main_content_native_default_paddingTop</item> + <item name="android:paddingBottom">@dimen/card_main_content_native_default_paddingBottom</item> </style> <!--Style for simple title inner main view--> <style name="card.base_simple_title"> <item name="android:textStyle">bold</item> - <item name="android:fontFamily">sans-serif</item> - <item name="android:layout_gravity">start|center_vertical</item> - <item name="android:layout_marginStart">@dimen/card_main_simple_title_margin_start</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_main_simple_title_margin_left</item> <item name="android:layout_marginTop">@dimen/card_main_simple_title_margin_top</item> + <item name="android:layout_marginRight">@dimen/card_main_simple_title_margin_right</item> + <item name="android:layout_marginBottom">@dimen/card_main_simple_title_margin_bottom</item> </style> + <!--Style for simple title inner main view--> + <style name="card.native.base_simple_title"> + <item name="android:textStyle">bold</item> + <item name="android:textSize">@dimen/card_main_content_native_simple_title_text_size</item> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:layout_marginLeft">@dimen/card_main_native_simple_title_margin_left</item> + <item name="android:layout_marginTop">@dimen/card_main_native_simple_title_margin_top</item> + </style> <!-- Style for Main Layout ****************************************--> @@ -133,8 +225,8 @@ <item name="android:background">@drawable/card_selector</item> <item name="android:layout_marginTop">@dimen/card_main_layout_view_margin_top</item> <item name="android:layout_marginBottom">@dimen/card_main_layout_view_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/card_main_layout_view_margin_start</item> - <item name="android:layout_marginEnd">@dimen/card_main_layout_view_margin_end</item> + <item name="android:layout_marginLeft">@dimen/card_main_layout_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_main_layout_view_margin_right</item> </style> <!-- Style for Main Layout kitkat--> @@ -142,14 +234,22 @@ <item name="android:background">@drawable/card_kitkat_selector</item> </style> + <style name="card.native.main_layout" > + <item name="android:background">?android:selectableItemBackground</item> + <item name="android:layout_marginTop">@dimen/card_main_layout_native_view_margin_top</item> + <item name="android:layout_marginBottom">@dimen/card_main_layout_native_view_margin_bottom</item> + <item name="android:layout_marginLeft">@dimen/card_main_layout_native_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_main_layout_native_view_margin_right</item> + </style> + <!-- Style for Main Layout with foreground selector--> <style name="card.main_layout_foreground"> <item name="android:background">@drawable/card_background</item> <item name="android:foreground">@drawable/card_foreground_selector</item> <item name="android:layout_marginTop">@dimen/card_main_layout_view_margin_top</item> <item name="android:layout_marginBottom">@dimen/card_main_layout_view_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/card_main_layout_view_margin_start</item> - <item name="android:layout_marginEnd">@dimen/card_main_layout_view_margin_end</item> + <item name="android:layout_marginLeft">@dimen/card_main_layout_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_main_layout_view_margin_right</item> </style> <!-- Style for Main Layout with foreground selector kitkat--> @@ -157,6 +257,14 @@ <item name="android:foreground">@drawable/card_foreground_kitkat_selector</item> </style> + <style name="card.native.main_layout_foreground"> + <item name="android:foreground">?android:selectableItemBackground</item> + <item name="android:background">@color/card_native_background</item> + <item name="android:layout_marginTop">@dimen/card_main_layout_native_view_margin_top</item> + <item name="android:layout_marginBottom">@dimen/card_main_layout_native_view_margin_bottom</item> + <item name="android:layout_marginLeft">@dimen/card_main_layout_native_view_margin_left</item> + <item name="android:layout_marginRight">@dimen/card_main_layout_native_view_margin_right</item> + </style> <!-- Style for Hidden Expand Layout ****************************************--> @@ -165,18 +273,35 @@ <item name="android:padding">@dimen/card_expand_layout_padding</item> </style> + <style name="card.native.main_contentExpand" > + <item name="android:background">@color/card_backgroundExpand</item> + <item name="android:layout_marginLeft">@dimen/card_expand_native_margin_right</item> + <item name="android:layout_marginRight">@dimen/card_expand_native_margin_right</item> + <item name="android:paddingLeft">@dimen/card_expand_native_layout_padding_left</item> + <item name="android:paddingRight">@dimen/card_expand_native_layout_padding_right</item> + <item name="android:paddingTop">@dimen/card_expand_native_layout_padding_top</item> + <item name="android:paddingBottom">@dimen/card_expand_native_layout_padding_bottom</item> + </style> <!--Style for simple title expand/collapse inner view--> <style name="card.expand_simple_title"> - <item name="android:layout_gravity">start|center_vertical</item> + <item name="android:layout_gravity">left|center_vertical</item> <item name="android:gravity">center_vertical</item> - <item name="android:fontFamily">sans-serif-condensed</item> <item name="android:textSize">@dimen/card_expand_simple_title_text_size</item> - <item name="android:paddingStart">@dimen/card_expand_simple_title_paddingStart</item> - <item name="android:paddingEnd">@dimen/card_expand_simple_title_paddingEnd</item> + <item name="android:paddingLeft">@dimen/card_expand_simple_title_paddingLeft</item> + <item name="android:paddingRight">@dimen/card_expand_simple_title_paddingRight</item> <item name="android:textColor">@color/card_expand_title_color</item> </style> + <!--Style for simple title expand/collapse inner view--> + <style name="card.native.expand_simple_title"> + <item name="android:layout_gravity">left|center_vertical</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textSize">@dimen/card_expand_native_simple_title_text_size</item> + <item name="android:paddingLeft">@dimen/card_expand_native_simple_title_padding_left</item> + <item name="android:paddingRight">@dimen/card_expand_native_simple_title_padding_right</item> + <item name="android:textColor">@color/card_expand_title_color</item> + </style> <!-- Style for Thumbnail ******************************************************--> @@ -195,12 +320,39 @@ <style name="card.thumbnail_compound_view"></style> + <style name="card.native.card_thumbnail_image"> + <item name="android:layout_width">@dimen/card_thumbnail_width</item> + <item name="android:layout_height">@dimen/card_thumbnail_height</item> + <item name="android:layout_gravity">center</item> + <item name="android:gravity">center</item> + <item name="android:scaleType">centerCrop</item> + </style> + + <style name="card.native.card_thumbnail_outer_layout"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + </style> + + <style name="card.native.thumbnail_compound_view"> + + </style> + + <!-- CardWithList ******************************************************--> + + <!--Style for Content View--> + <style name="card.native.content_outer_layout.cardwithlist" > + <item name="android:paddingLeft">@dimen/card_main_content_native_cardwithlist_paddingLeft</item> + <item name="android:paddingRight">@dimen/card_main_content_native_cardwithlist_paddingRight</item> + <item name="android:paddingTop">@dimen/card_main_content_native_cardwithlist_paddingTop</item> + <item name="android:paddingBottom">@dimen/card_main_content_native_cardwithlist_paddingBottom</item> + </style> + <!-- Style for Lists ******************************************************--> <style name="list_card"> - <item name="android:paddingStart">@dimen/list_card_padding_start</item> - <item name="android:paddingEnd">@dimen/list_card_padding_end</item> + <item name="android:paddingLeft">@dimen/list_card_padding_left</item> + <item name="android:paddingRight">@dimen/list_card_padding_right</item> <item name="android:paddingBottom">@dimen/list_card_padding_bottom</item> <item name="android:paddingTop">@dimen/list_card_padding_top</item> </style> @@ -216,11 +368,92 @@ </style> + + <style name="native_list_external"> + <item name="android:clipToPadding">false</item> + <item name="android:scrollbarStyle">outsideOverlay</item> + </style> + + <style name="native_list_card"> + <item name="android:layout_marginLeft">@dimen/native_list_card_margin_left</item> + <item name="android:layout_marginRight">@dimen/native_list_card_margin_right</item> + <item name="android:layout_marginBottom">@dimen/native_list_card_margin_bottom</item> + <item name="android:layout_marginTop">@dimen/native_list_card_margin_top</item> + </style> + + <!-- Base list --> + <style name="native_list_card.base" > + + </style> + + + <!-- Thumbnail list --> + <style name="native_list_card.thumbnail"> + + </style> + + + + + <style name="grid_card"> - <item name="android:paddingStart">@dimen/grid_card_padding_start</item> - <item name="android:paddingEnd">@dimen/grid_card_padding_end</item> + <item name="android:paddingLeft">@dimen/grid_card_padding_left</item> + <item name="android:paddingRight">@dimen/grid_card_padding_right</item> + <item name="android:paddingBottom">@dimen/grid_card_padding_bottom</item> + <item name="android:paddingTop">@dimen/grid_card_padding_top</item> + </style> + + <style name="native_grid_card"> + <item name="android:paddingLeft">@dimen/grid_card_padding_left</item> + <item name="android:paddingRight">@dimen/grid_card_padding_right</item> <item name="android:paddingBottom">@dimen/grid_card_padding_bottom</item> <item name="android:paddingTop">@dimen/grid_card_padding_top</item> </style> + <!-- Style for card with list --> + <style name="cardwithlist"> + <item name="android:layout_marginTop">@dimen/card_base_cardwithlist_list_margin_top</item> + <item name="android:layout_marginLeft">@dimen/card_base_cardwithlist_list_margin_left</item> + <item name="android:divider">@color/card_base_cardwithlist_divider_color</item> + <item name="android:showDividers">middle|beginning</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">match_parent</item> + <item name="android:background">@color/card_base_cardwithlist_background_list_color</item> + </style> + + <style name="native_cardwithlist"> + <item name="android:layout_marginTop">@dimen/card_base_cardwithlist_list_margin_top</item> + <item name="android:layout_marginLeft">@dimen/card_base_cardwithlist_list_margin_left</item> + <item name="android:divider">@color/card_native_base_cardwithlist_divider_color</item> + <item name="android:showDividers">middle|beginning</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">match_parent</item> + <item name="android:background">@color/card_native_base_cardwithlist_background_list_color</item> + </style> + + + <!-- Style used for Sectioned List --> + <style name="card_section_container"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:paddingLeft">@dimen/card_section_container_padding_left</item> + <item name="android:paddingRight">@dimen/card_section_container_padding_right</item> + <item name="android:background">@color/card_section_container_color</item> + </style> + + <style name="card_section_title"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentLeft">true</item> + <item name="android:textSize">@dimen/card_section_title</item> + <item name="android:textColor">@color/card_section_title_color</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:singleLine">true</item> + <item name="android:layout_marginTop">@dimen/card_section_title_margin_top</item> + <item name="android:layout_marginBottom">@dimen/card_section_title_margin_top</item> + </style> + + + </resources> diff --git a/res/values/styles_undo.xml b/res/values/styles_undo.xml index b52f5c3..ff5c98d 100644 --- a/res/values/styles_undo.xml +++ b/res/values/styles_undo.xml @@ -24,8 +24,8 @@ <item name="android:layout_width">match_parent</item> <item name="android:layout_height">48dp</item> <item name="android:layout_gravity">bottom</item> - <item name="android:layout_marginStart">8dp</item> - <item name="android:layout_marginEnd">8dp</item> + <item name="android:layout_marginLeft">8dp</item> + <item name="android:layout_marginRight">8dp</item> <item name="android:layout_marginBottom">16dp</item> <item name="android:orientation">horizontal</item> <item name="android:background">@drawable/undobar</item> @@ -39,9 +39,9 @@ <item name="android:layout_width">0dp</item> <item name="android:layout_weight">1</item> <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginStart">16dp</item> + <item name="android:layout_marginLeft">16dp</item> <item name="android:layout_gravity">center_vertical</item> - <item name="android:layout_marginEnd">16dp</item> + <item name="android:layout_marginRight">16dp</item> <item name="android:textAppearance">?android:textAppearanceSmall</item> <item name="android:textColor">#fff</item> @@ -50,10 +50,10 @@ <style name="list_card_UndoBarButton"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">match_parent</item> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">16dp</item> + <item name="android:paddingLeft">16dp</item> + <item name="android:paddingRight">16dp</item> <item name="android:background">@drawable/card_undo</item> - <item name="android:drawableStart">@drawable/ic_undobar_undo</item> + <item name="android:drawableLeft">@drawable/ic_undobar_undo</item> <item name="android:drawablePadding">12dp</item> <item name="android:textAppearance">?android:textAppearanceSmall</item> <item name="android:textAllCaps">true</item> @@ -62,4 +62,90 @@ <item name="android:text">@string/list_card_undo_title</item> </style> -</resources> + <style name="list_card_UndoBar_material"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">56dp</item> + <item name="android:layout_gravity">bottom</item> + <item name="android:layout_marginLeft">32dp</item> + <item name="android:layout_marginRight">32dp</item> + <item name="android:layout_marginBottom">24dp</item> + <item name="android:paddingBottom">18dp</item> + <item name="android:paddingTop">18dp</item> + <item name="android:paddingLeft">24dp</item> + <item name="android:paddingRight">24dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@color/card_undobar_material_background_color</item> + <item name="android:clickable">true</item> + </style> + + <style name="list_card_UndoBarMessage_material"> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginLeft">0dp</item> + <item name="android:layout_marginRight">24dp</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">#fff</item> + </style> + + <style name="list_card_UndoBarButton_material"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:gravity">right</item> + <item name="android:background">?android:selectableItemBackground</item> + <item name="android:textSize">14sp</item> + <item name="android:paddingLeft">24dp</item> + <item name="android:textAllCaps">true</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">@color/card_undobar_material_text_color</item> + <item name="android:text">@string/list_card_undo_title</item> + </style> + + <style name="list_card_UndoBar_materialmobile"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minHeight">56dp</item> + <item name="android:maxHeight">80dp</item> + <item name="android:layout_gravity">bottom</item> + <item name="android:layout_marginLeft">0dp</item> + <item name="android:layout_marginRight">0dp</item> + <item name="android:layout_marginBottom">0dp</item> + <item name="android:paddingLeft">24dp</item> + <item name="android:paddingRight">0dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@color/card_undobar_material_background_color</item> + <item name="android:clickable">true</item> + </style> + + <style name="list_card_UndoBarMessage_materialmobile"> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginLeft">0dp</item> + <item name="android:layout_marginRight">24dp</item> + <item name="android:layout_marginBottom">18dp</item> + <item name="android:layout_marginTop">18dp</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">#fff</item> + </style> + + <style name="list_card_UndoBarButton_materialmobile"> + <item name="android:layout_gravity">center_vertical|right</item> + <item name="android:gravity">right</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:paddingBottom">18dp</item> + <item name="android:paddingTop">18dp</item> + <item name="android:paddingLeft">24dp</item> + <item name="android:paddingRight">24dp</item> + <item name="android:background">?android:selectableItemBackground</item> + <item name="android:textSize">14sp</item> + <item name="android:textAllCaps">true</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">@color/card_undobar_material_text_color</item> + <item name="android:text">@string/list_card_undo_title</item> + </style> + +</resources>
\ No newline at end of file diff --git a/src/com/android/cards/internal/overflowanimation/BaseCardOverlayAnimation.java b/src/com/android/cards/internal/overflowanimation/BaseCardOverlayAnimation.java deleted file mode 100644 index 99b5760..0000000 --- a/src/com/android/cards/internal/overflowanimation/BaseCardOverlayAnimation.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2013-2014 Gabriele Mariotti. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ***************************************************************************** - */ - -package com.android.cards.internal.overflowanimation; - -import android.content.Context; -import android.view.View; - -import com.android.cards.R; -import com.android.cards.internal.Card; - -/** - * BaseAnimation to overlay the Card - * - * @author Gabriele Mariotti (gabri.mariotti@gmail.com) - */ -public abstract class BaseCardOverlayAnimation extends BaseOverflowAnimation { - - /** - * Original card - */ - protected Card originalCard; - - /** - * Animation Duration - */ - protected int mAnimationDuration; - - // ------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------- - - public BaseCardOverlayAnimation(Context context, Card card) { - super(context); - this.originalCard = card; - } - - // ------------------------------------------------------------- - // CardInfoToAnimate model - // ------------------------------------------------------------- - - /** - * Public interface for Card Animation - */ - public interface CardInfo { - /** - * - * @param layoutId - */ - public void setupLayoutsIdToRemove(int[] layoutId); - - /** - * - */ - public int[] getLayoutsIdToAdd(); - } - - /** - * CardInfo provides info about layouts to remove, and layouts to add - */ - public abstract class CardInfoToAnimate implements CardInfo { - - /* - * Layouts to Remove - */ - protected int[] mLayoutsIdToRemove; - - // ----------------------------- - // Constructors - // ----------------------------- - - public CardInfoToAnimate() { - defaultIdToRemove(); - } - - /** - * Default layouts to remove - */ - private void defaultIdToRemove(){ - mLayoutsIdToRemove = new int[] - { - R.id.card_header_inner_frame, - R.id.card_thumbnail_layout, - R.id.card_main_content_layout - }; - } - - @Override - public void setupLayoutsIdToRemove(int[] layoutId) { - this.mLayoutsIdToRemove=layoutId; - } - - @Override - public abstract int[] getLayoutsIdToAdd(); - - /** - * Indicates if card need a navigator - * - * @return - */ - protected boolean isWithNavigator(){ - if (getLayoutsIdToAdd()!=null && getLayoutsIdToAdd().length>1) - return true; - else - return false; - } - } - - protected abstract CardInfoToAnimate setCardToAnimate(Card card); - - // ------------------------------------------------------------- - // Animation - // ------------------------------------------------------------- - - @Override - public void doAnimation(final Card card, View imageOverflow) { - //Store selected values - super.doAnimation(card, imageOverflow); - - if (card == null || card.getCardView() == null) return; - - //animation duration - //final int mShortAnimationDuration = getAnimationDuration(); - - final CardInfoToAnimate infoAnimation = setCardToAnimate(card); - - if (infoAnimation == null) return; - - if (!selected) { - doOverFirstAnimation(card,infoAnimation, imageOverflow); - } else { - //TODO More Cards - doOverOtherAnimation(card,infoAnimation, imageOverflow); - } - - //Update icon and model status - toggleOverflowIcon(); - } - - protected abstract void doOverOtherAnimation(Card card,CardInfoToAnimate infoAnimation,View imageOverflow); - - protected abstract void doOverFirstAnimation(Card card,CardInfoToAnimate infoAnimation,View imageOverflow); - - - // ------------------------------------------------------------- - // Getters and Setters - // ------------------------------------------------------------- - - /** - * Returns the animation duration - * - * @return - */ - protected int getAnimationDuration() { - return mAnimationDuration = mContext.getResources().getInteger( - android.R.integer.config_shortAnimTime); - } - - -} diff --git a/src/com/android/cards/internal/overflowanimation/BaseOverflowAnimation.java b/src/com/android/cards/internal/overflowanimation/BaseOverflowAnimation.java deleted file mode 100644 index 93757be..0000000 --- a/src/com/android/cards/internal/overflowanimation/BaseOverflowAnimation.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2013-2014 Gabriele Mariotti. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ***************************************************************************** - */ - -package com.android.cards.internal.overflowanimation; - -import android.content.Context; -import android.view.View; - -import com.android.cards.internal.Card; -import com.android.cards.internal.CardHeader; -import com.android.cards.view.CardView; - -/** - * Base implementation of CustomOverflowAnimator - * This class helps to store the card and the selected value on overflow icon - * - * @author Gabriele Mariotti (gabri.mariotti@gmail.com) - */ -public abstract class BaseOverflowAnimation implements CardHeader.CustomOverflowAnimation{ - - /** - * Context - */ - protected Context mContext; - - /** - * Overflow icon state - */ - protected boolean selected=false; - - /** - * Card - */ - private Card mCard; - - protected static String TAG="BaseOverflowAnimation"; - - // ------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------- - - public BaseOverflowAnimation(Context context){ - mContext=context; - } - - // ------------------------------------------------------------- - // Base Animation - // ------------------------------------------------------------- - - @Override - public void doAnimation(Card card, View imageOverflow) { - - //Store the Card - if (card==null) return; - mCard=card; - - //Get the selected value from the Header - CardHeader header= card.getCardHeader(); - if (header!=null){ - selected=header.isOverflowSelected(); - } - } - - // ------------------------------------------------------------- - // Select and deselect the overflow icon - // ------------------------------------------------------------- - - /** - * Selects overflow icon - */ - protected void selectOverflowIcon(){ - changeOverflowIconSelection(true); - } - - /** - * Deselects overflow icon - */ - protected void deselectOverflowIcon(){ - changeOverflowIconSelection(false); - } - - /** - * Toggles the overflow icon - */ - protected void toggleOverflowIcon(){ - - if (mCard==null) return; - changeOverflowIconSelection(!selected); - } - - /** - * Internal method to change the state of overflow icon on the view and on the model - * - * @param selected - */ - protected void changeOverflowIconSelection(boolean selected){ - - if (mCard==null) return; - - //Change the value on the card and inside - CardHeader header = mCard.getCardHeader(); - if (header!=null){ - this.selected=selected; - header.setOverflowSelected(selected); - } - - //Change the imageButton state - CardView cardView = mCard.getCardView(); - if (cardView!=null){ - if (cardView.getInternalHeaderLayout()!=null && cardView.getInternalHeaderLayout().getImageButtonOverflow()!=null) - cardView.getInternalHeaderLayout().getImageButtonOverflow().setSelected(selected); - } - } - - - // ------------------------------------------------------------- - // Getters and Setters - // ------------------------------------------------------------- - - /** - * Return the context - * - * @return - */ - protected Context getContext() { - return mContext; - } - - /** - * Returns the overflow icon state - * @return - */ - public boolean isSelected() { - return selected; - } - - /** - * Sets the overflow icon state - * @param selected - */ - public void setSelected(boolean selected) { - this.selected = selected; - } - - /** - * Returns the card - * @return - */ - public Card getCard() { - return mCard; - } - -} diff --git a/src/com/android/cards/internal/overflowanimation/TwoCardOverlayAnimation.java b/src/com/android/cards/internal/overflowanimation/TwoCardOverlayAnimation.java deleted file mode 100644 index 26fa2ae..0000000 --- a/src/com/android/cards/internal/overflowanimation/TwoCardOverlayAnimation.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2013-2014 Gabriele Mariotti. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ***************************************************************************** - */ - -package com.android.cards.internal.overflowanimation; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; - -import com.android.cards.R; -import com.android.cards.internal.Card; - -/** - * Simple implementation with only 2 card (the original and the overlay) - * - * @author Gabriele Mariotti (gabri.mariotti@gmail.com) - */ -public abstract class TwoCardOverlayAnimation extends BaseCardOverlayAnimation { - - // ------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------- - - public TwoCardOverlayAnimation(Context context, Card card) { - super(context,card); - } - - /** - * - */ - public abstract class TwoCardToAnimate extends CardInfoToAnimate{ - - @Override - public int[] getLayoutsIdToAdd() { - int[] layouts= new int[1]; - layouts[0]=getLayoutIdToAdd(); - return layouts; - } - - public abstract int getLayoutIdToAdd(); - - } - - - // ------------------------------------------------------------- - // Animation - // ------------------------------------------------------------- - - - @Override - protected void doOverFirstAnimation(final Card card, CardInfoToAnimate infoAnimation, View imageOverflow) { - - if (infoAnimation==null) return; - - final ViewGroup mInternalLayoutOverlay = (ViewGroup)card.getCardView().findViewById(R.id.card_overlap); - - //Checks - if (mInternalLayoutOverlay==null){ - Log.e(TAG, "Overlap layout not found!"); - return; - } - if (infoAnimation.getLayoutsIdToAdd()==null){ - Log.e(TAG,"You have to specify layouts to add!"); - return; - } - - //Views to remove - View[] viewsOut= getOutViews(card,infoAnimation); - - //Get the layout to add - final int layoutIdIn= infoAnimation.getLayoutsIdToAdd()[0]; - - AnimatorSet animAlpha = new AnimatorSet(); - if (viewsOut != null && layoutIdIn > 0) { - - ArrayList<Animator> animators= new ArrayList<Animator>(); - - for (final View viewOut:viewsOut){ - if (viewOut!=null){ - ObjectAnimator anim = ObjectAnimator.ofFloat(viewOut, "alpha", 1f, 0f); - anim.setDuration(getAnimationDuration()); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - viewOut.setVisibility(View.GONE); - } - }); - animators.add(anim); - } - } - animAlpha.playTogether(animators); - } - - - animAlpha.addListener(new AnimatorListenerAdapter(){ - - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - - LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View viewIn= inflater.inflate(layoutIdIn,mInternalLayoutOverlay,false); - if (viewIn!=null){ - - if (card.getCardView()!=null && - card.getCardView().getInternalMainCardLayout()!=null && - card.getCardView().getInternalHeaderLayout()!=null && - card.getCardView().getInternalHeaderLayout().getFrameButton()!=null){ - int h1=card.getCardView().getInternalMainCardLayout().getMeasuredHeight(); - int h2=card.getCardView().getInternalHeaderLayout().getFrameButton().getMeasuredHeight(); - viewIn.setMinimumHeight(h1-h2); - } - mInternalLayoutOverlay.addView(viewIn); - - viewIn.setAlpha(0); - viewIn.setVisibility(View.VISIBLE); - - viewIn.animate() - .alpha(1f) - .setDuration(getAnimationDuration()) - .setListener(null); - } - } - }); - - animAlpha.start(); - } - - @Override - protected void doOverOtherAnimation(final Card card, CardInfoToAnimate infoAnimation, View imageOverflow) { - - //Checks - if (infoAnimation == null) return; - - final ViewGroup mInternalLayoutOverlay = (ViewGroup) card.getCardView().findViewById(R.id.card_overlap); - if (mInternalLayoutOverlay == null) { - Log.e(TAG, "Overlap layout not found!"); - return; - } - if (infoAnimation.getLayoutsIdToAdd() == null) { - Log.e(TAG, "You have to specify layouts to add!"); - return; - } - - final View[] viewsLastOut = getOutViews(card, infoAnimation); - - //int layoutIdIn= infoAnimation.getLayoutsIdToAdd()[0]; - final View viewLastIn = mInternalLayoutOverlay.getChildAt(0); - //final View viewIn = card.getCardView().findViewById(R.id.afterContent); - - //Views to remove - final View[] viewsFirstOut = getOutViews(card, infoAnimation); - - if (viewLastIn != null) { - viewLastIn.animate() - .alpha(0f) - .setDuration(getAnimationDuration()) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - viewLastIn.setVisibility(View.GONE); - - - for (final View viewOut : viewsLastOut) { - if (viewOut != null) { - viewOut.setVisibility(View.VISIBLE); - } - } - - if (mInternalLayoutOverlay != null) { - mInternalLayoutOverlay.removeView(viewLastIn); - } - - for (final View viewOut : viewsLastOut) { - if (viewOut != null) { - viewOut.animate() - .alpha(1f) - .setDuration(getAnimationDuration()); - } - } - - } - }); - } - } - - protected View[] getOutViews(Card card,CardInfoToAnimate infoAnimation){ - //Views to remove - View[] viewsOut=null; - if (infoAnimation.mLayoutsIdToRemove!=null){ - - viewsOut = new View[infoAnimation.mLayoutsIdToRemove.length]; - int i=0; - for (int layoutIdOut:infoAnimation.mLayoutsIdToRemove){ - View viewOut =card.getCardView().findViewById(layoutIdOut); - viewsOut[i]=viewOut; - i++; - } - } - return viewsOut; - } - - - // ------------------------------------------------------------- - // Getters and Setters - // ------------------------------------------------------------- - - /** - * Returns the animation duration - * - * @return - */ - protected int getAnimationDuration() { - return mAnimationDuration = mContext.getResources().getInteger( - android.R.integer.config_shortAnimTime); - } - - -} diff --git a/src/com/android/cards/view/listener/UndoBarController.java b/src/com/android/cards/view/listener/UndoBarController.java deleted file mode 100644 index 2c5318c..0000000 --- a/src/com/android/cards/view/listener/UndoBarController.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2013 Roman Nurik, 2013-2014 Gabriele Mariotti. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ***************************************************************************** - */ - -package com.android.cards.view.listener; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.os.Bundle; -import android.os.Handler; -import android.os.Parcelable; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewPropertyAnimator; -import android.widget.TextView; - -import com.android.cards.R; - -/** - * It is based on Roman Nurik code. - * See this link for original code: - * https://code.google.com/p/romannurik-code/source/browse/#git%2Fmisc%2Fundobar - * - * - */ -public class UndoBarController { - - private View mBarView; - private TextView mMessageView; - private ViewPropertyAnimator mBarAnimator; - private Handler mHideHandler = new Handler(); - - private UndoListener mUndoListener; - - // State objects - private Parcelable mUndoToken; - private CharSequence mUndoMessage; - - private UndoBarUIElements mUndoBarUIElements; - - /** - * Interface to listen the undo controller actions - */ - public interface UndoListener { - /* - * Called when you undo the action - */ - void onUndo(Parcelable undoToken); - } - - public UndoBarController(View undoBarView, UndoListener undoListener) { - this (undoBarView,undoListener,null); - } - - public UndoBarController(View undoBarView, UndoListener undoListener,UndoBarUIElements undoBarUIElements) { - mBarView = undoBarView; - mBarAnimator = mBarView.animate(); - mUndoListener = undoListener; - - if (undoBarUIElements==null) - undoBarUIElements = new DefaultUndoBarUIElements(); - mUndoBarUIElements = undoBarUIElements; - - mMessageView = (TextView) mBarView.findViewById(mUndoBarUIElements.getUndoBarMessageId()); - mBarView.findViewById(mUndoBarUIElements.getUndoBarButtonId()) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - hideUndoBar(false); - mUndoListener.onUndo(mUndoToken); - } - }); - - hideUndoBar(true); - } - - public void showUndoBar(boolean immediate, CharSequence message, Parcelable undoToken) { - - mUndoToken = undoToken; - mUndoMessage = message; - mMessageView.setText(mUndoMessage); - - mHideHandler.removeCallbacks(mHideRunnable); - mHideHandler.postDelayed(mHideRunnable, - mBarView.getResources().getInteger(R.integer.list_card_undobar_hide_delay)); - - mBarView.setVisibility(View.VISIBLE); - if (immediate) { - mBarView.setAlpha(1); - } else { - mBarAnimator.cancel(); - mBarAnimator - .alpha(1) - .setDuration( - mBarView.getResources() - .getInteger(android.R.integer.config_shortAnimTime)) - .setListener(null); - } - } - - public void hideUndoBar(boolean immediate) { - mHideHandler.removeCallbacks(mHideRunnable); - if (immediate) { - mBarView.setVisibility(View.GONE); - mBarView.setAlpha(0); - mUndoMessage = null; - mUndoToken = null; - - } else { - mBarAnimator.cancel(); - mBarAnimator - .alpha(0) - .setDuration(mBarView.getResources() - .getInteger(android.R.integer.config_shortAnimTime)) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mBarView.setVisibility(View.GONE); - mUndoMessage = null; - mUndoToken = null; - } - }); - } - } - - public void onSaveInstanceState(Bundle outState) { - outState.putCharSequence("undo_message", mUndoMessage); - outState.putParcelable("undo_token", mUndoToken); - } - - public void onRestoreInstanceState(Bundle savedInstanceState) { - if (savedInstanceState != null) { - mUndoMessage = savedInstanceState.getCharSequence("undo_message"); - mUndoToken = savedInstanceState.getParcelable("undo_token"); - - if (mUndoToken != null || !TextUtils.isEmpty(mUndoMessage)) { - showUndoBar(true, mUndoMessage, mUndoToken); - } - } - } - - private Runnable mHideRunnable = new Runnable() { - @Override - public void run() { - hideUndoBar(false); - } - }; - - public Parcelable getUndoToken(){ - return mUndoToken; - } - - - // ------------------------------------------------------------- - // Undo Custom Bar - // ------------------------------------------------------------- - - /** - * Interface to set the ui elements in undo bar - */ - public interface UndoBarUIElements{ - - /** - * UndoBar id - * @return - */ - public int getUndoBarId(); - - /** - * TextView Id which displays message - * - * @return - */ - public int getUndoBarMessageId(); - - /** - * UndoButton Id - * - * @return - */ - public int getUndoBarButtonId(); - - - } - - /** - * Default UndoBar - */ - public static class DefaultUndoBarUIElements implements UndoBarUIElements { - - public DefaultUndoBarUIElements(){}; - - @Override - public int getUndoBarId() { - return R.id.list_card_undobar; - } - - @Override - public int getUndoBarMessageId() { - return R.id.list_card_undobar_message; - } - - @Override - public int getUndoBarButtonId() { - return R.id.list_card_undobar_button; - } - }; - - - /** - * Sets UndoBar UI Elements - * - * @return - */ - public UndoBarUIElements getUndoBarUIElements() { - return mUndoBarUIElements; - } - - /** - * Returns UndoBar UI Elements - * @param undoBarUIElements - */ - public void setUndoBarUIElements(UndoBarUIElements undoBarUIElements) { - this.mUndoBarUIElements = undoBarUIElements; - } -} diff --git a/src/com/android/cards/Constants.java b/src/it/gmariotti/cardslib/library/Constants.java index cfe510d..65bc538 100644 --- a/src/com/android/cards/Constants.java +++ b/src/it/gmariotti/cardslib/library/Constants.java @@ -18,12 +18,16 @@ package com.android.cards; +import android.os.Build; + /** * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ public class Constants { + public static int API_L = Build.VERSION_CODES.LOLLIPOP; + public static class IntentManager{ /** diff --git a/src/it/gmariotti/cardslib/library/internal/BaseGroupExpandableCard.java b/src/it/gmariotti/cardslib/library/internal/BaseGroupExpandableCard.java new file mode 100644 index 0000000..8b6147b --- /dev/null +++ b/src/it/gmariotti/cardslib/library/internal/BaseGroupExpandableCard.java @@ -0,0 +1,47 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.internal; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class BaseGroupExpandableCard<T> extends Card{ + + protected List<T> children = new ArrayList<T>(); + + //-------------------------------------------------------------------------- + // Constructors + //-------------------------------------------------------------------------- + + public BaseGroupExpandableCard(Context context,List<T> children) { + super(context); + this.children = children; + } + + public BaseGroupExpandableCard(Context context, int innerLayout,List<T> children) { + super(context, innerLayout); + this.children = children; + } + +} diff --git a/src/com/android/cards/internal/Card.java b/src/it/gmariotti/cardslib/library/internal/Card.java index 1d464f1..f3bdf52 100644 --- a/src/com/android/cards/internal/Card.java +++ b/src/it/gmariotti/cardslib/library/internal/Card.java @@ -60,6 +60,8 @@ public class Card extends BaseCard { protected static String TAG = "Card"; + public static int DEFAULT_COLOR= 0; + /** * Used to enable a onClick Action on card */ @@ -125,14 +127,19 @@ public class Card extends BaseCard { protected OnCollapseAnimatorEndListener mOnCollapseAnimatorEndListener; /** - * Partial OnClickListener + * Listener invoked when Expand Animator starts */ - protected HashMap<Integer, OnCardClickListener> mMultipleOnClickListener; + protected OnExpandAnimatorStartListener mOnExpandAnimatorStartListener; + + /** + * Listener invoked when Collapse Animator starts + */ + protected OnCollapseAnimatorStartListener mOnCollapseAnimatorStartListener; /** * Partial OnClickListener */ - protected HashMap<Integer, OnLongCardClickListener> mMultipleOnLongClickListener; + protected HashMap<Integer, OnCardClickListener> mMultipleOnClickListener; /** * Global area @@ -153,27 +160,32 @@ public class Card extends BaseCard { public static final int CLICK_LISTENER_HEADER_VIEW = 2; /** - * Expand Area - * It is used by partial click listener - */ - public static final int CLICK_LISTENER_EXPAND_VIEW = 3; - - /** * Content Main area. It is used by partial click listener */ public static final int CLICK_LISTENER_CONTENT_VIEW = 10; /** + * All card area except the supplemental area with actions. + It is used by partial click listener + */ + public static final int CLICK_LISTENER_ACTIONAREA1_VIEW = 9; + + /** * Listener invoked when the user undo a swipe action in a List */ protected OnUndoSwipeListListener mOnUndoSwipeListListener; /** + * Listener invoked when the Undo controller hides the Undo Bar after a swipe action in a List + */ + protected OnUndoHideSwipeListListener mOnUndoHideSwipeListListener; + + /** * It identifies the background resource of view with this id: * android:id="@+id/card_main_layout" * <p/> - * In a standard card it identifies the main background. + * In a native card it identifies the main background. */ private int mBackgroundResourceId =0; @@ -181,11 +193,17 @@ public class Card extends BaseCard { * It identifies the background resource of view with this id: * android:id="@+id/card_main_layout" * <p/> - * In a standard card it identifies the main background. + * In a native card it identifies the main background. */ private Drawable mBackgroundResource =null; /** + * In a native card it identifies the main background color. + * Use it only with a NativeCard + */ + private int mBackgroundColorResourceId =0; + + /** * Used to enable a onLongClick Action on multichoiceAdapter */ private boolean mCheckable= true; @@ -200,6 +218,14 @@ public class Card extends BaseCard { */ protected ViewToClickToExpand viewToClickToExpand=null; + /** + * Custom Elevation + */ + protected Float mCardElevation; + + + private boolean couldUseNativeInnerLayout = false; + // ------------------------------------------------------------- // Constructors // ------------------------------------------------------------- @@ -223,6 +249,9 @@ public class Card extends BaseCard { super(context); mParentCard = null; mInnerLayout = innerLayout; + + if (innerLayout == R.layout.inner_base_main) + couldUseNativeInnerLayout = true; } // ------------------------------------------------------------- @@ -247,6 +276,9 @@ public class Card extends BaseCard { @Override public View getInnerView(Context context, ViewGroup parent) { + //Check if the default inner layout could be the native layout + setupInnerLayout(); + View view = super.getInnerView(context, parent); //This provides a simple implementation with a single title @@ -269,7 +301,6 @@ public class Card extends BaseCard { * This method sets values to header elements and customizes view. * <p/> * Override this method to set your elements inside InnerView. - * If you use listviews it is recommend to user a Viewholder like here. * * @param parent parent view (Inner Frame) * @param view Inner View @@ -279,24 +310,19 @@ public class Card extends BaseCard { //Add simple title to header if (view != null) { - ViewHolder holder; - holder = (ViewHolder) view.getTag(); - - if (holder == null) { - holder = new ViewHolder(); - holder.titleView = - (TextView) view.findViewById(R.id.card_main_inner_simple_title); - view.setTag(holder); - } - if (holder.titleView != null) { - holder.titleView.setText(mTitle); - } + TextView mTitleView = (TextView) view.findViewById(R.id.card_main_inner_simple_title); + if (mTitleView != null) + mTitleView.setText(mTitle); } - } - static class ViewHolder { - TextView titleView; + /** + * Setup the inner layout + */ + protected void setupInnerLayout(){ + //Check if the default inner layout could be the native layout + if (couldUseNativeInnerLayout && isNative()) + mInnerLayout = R.layout.native_inner_base_main; } // ------------------------------------------------------------- @@ -374,6 +400,14 @@ public class Card extends BaseCard { } // ------------------------------------------------------------- + // Supplemental Actions + // ------------------------------------------------------------- + + public void setupSupplementalActions() { + //Do Nothing + } + + // ------------------------------------------------------------- // On Swipe Interface and Listener // ------------------------------------------------------------- @@ -429,6 +463,13 @@ public class Card extends BaseCard { } /** + * Interface to listen for when the Undo controller hides the Undo Bar + */ + public interface OnUndoHideSwipeListListener { + public void onUndoHideSwipe(Card card); + } + + /** * Called when user undo a swipe action */ public void onUndoSwipeListCard() { @@ -455,6 +496,23 @@ public class Card extends BaseCard { this.mOnUndoSwipeListListener = onUndoSwipeListListener; } + /** + * Returns listener invoked the Undo controller hides the Undo Bar + * + * @return listener + */ + public OnUndoHideSwipeListListener getOnUndoHideSwipeListListener() { + return mOnUndoHideSwipeListListener; + } + + /** + * Sets listener invoked when the Undo controller hides the Undo Bar + * + * @param onUndoHideSwipeListListener + */ + public void setOnUndoHideSwipeListListener(OnUndoHideSwipeListListener onUndoHideSwipeListListener) { + mOnUndoHideSwipeListListener = onUndoHideSwipeListListener; + } // ------------------------------------------------------------- // OnClickListener @@ -567,6 +625,40 @@ public class Card extends BaseCard { this.mOnExpandAnimatorEndListener = onExpandAnimatorEndListener; } + /** + * Interface to listen any callbacks when expand animation starts + */ + public interface OnExpandAnimatorStartListener { + public void onExpandStart(Card card); + } + + /** + * Called at the start of Expand Animator + */ + public void onExpandStart() { + if (mOnExpandAnimatorStartListener != null) { + mOnExpandAnimatorStartListener.onExpandStart(this); + } + } + + /** + * Returns the listener invoked when expand animation starts + * + * @return listener + */ + public OnExpandAnimatorStartListener getOnExpandAnimatorStartListener() { + return mOnExpandAnimatorStartListener; + } + + /** + * Sets the listener invoked when expand animation ends + * + * @param onExpandAnimatorStartListener listener + */ + public void setOnExpandAnimatorStartListener(OnExpandAnimatorStartListener onExpandAnimatorStartListener) { + this.mOnExpandAnimatorStartListener = onExpandAnimatorStartListener; + } + // ------------------------------------------------------------- // OnAnimationExpandEnd Interface and Listener // ------------------------------------------------------------- @@ -605,6 +697,53 @@ public class Card extends BaseCard { this.mOnCollapseAnimatorEndListener = onCollapseAnimatorEndListener; } + /** + * Interface to listen any callbacks when collapse animation starts + */ + public interface OnCollapseAnimatorStartListener { + public void onCollapseStart(Card card); + } + + /** + * Call at the beginning of collapse animation + */ + public void onCollapseStart() { + if (mOnCollapseAnimatorStartListener != null) { + mOnCollapseAnimatorStartListener.onCollapseStart(this); + } + } + + /** + * Returns the listener invoked when collapse animation starts + * + * @return listener + */ + public OnCollapseAnimatorStartListener getOnCollapseAnimatorStartListener() { + return mOnCollapseAnimatorStartListener; + } + + /** + * Sets the listener when collapse animation starts + * + * @param onCollapseAnimatorStartListener + */ + public void setOnCollapseAnimatorStartListener(OnCollapseAnimatorStartListener onCollapseAnimatorStartListener) { + this.mOnCollapseAnimatorStartListener = onCollapseAnimatorStartListener; + } + + + public void doExpand(){ + getCardView().doExpand(); + } + + public void doCollapse(){ + getCardView().doCollapse(); + } + + public void doToogleExpand(){ + getCardView().doToggleExpand(); + } + // ------------------------------------------------------------- /** @@ -621,6 +760,24 @@ public class Card extends BaseCard { // ------------------------------------------------------------- /** + * Sets the shadow elevation. + * This method works only with the CardViewNative. + * @param elevation + */ + public void setCardElevation(float elevation) { + this.mCardElevation = elevation; + } + + /** + * Returns the elevation + * + * @return + */ + public Float getCardElevation() { + return mCardElevation; + } + + /** * Indicates if card has a shadow * * @return <code>true</code> if card has a shadow @@ -692,19 +849,15 @@ public class Card extends BaseCard { /** * Indicates if the card is long clickable - * If card hasn't a {@link OnLongCardClickListener} - * or any partial Listener return <code>true</code> in any cases. + * If card hasn't a {@link OnLongCardClickListener} return <code>false</code> in any cases. * * @return */ public boolean isLongClickable() { - if (mIsLongClickable) { - if (mOnLongClickListener == null - && (mMultipleOnLongClickListener == null - || mMultipleOnLongClickListener.isEmpty())) { + if (mOnLongClickListener == null) { + if (mIsLongClickable) Log.w(TAG, "LongClickable set to true without onLongClickListener"); - return false; - } + return false; } return mIsLongClickable; } @@ -777,68 +930,6 @@ public class Card extends BaseCard { } /** - * Adds a LongClickListener on a specific area - * </p> - * You can use one of these values: - * {@link Card#CLICK_LISTENER_ALL_VIEW} - * {@link Card#CLICK_LISTENER_HEADER_VIEW} - * {@link Card#CLICK_LISTENER_THUMBNAIL_VIEW} - * {@link Card#CLICK_LISTENER_CONTENT_VIEW} - * - * @param area - * @param onLongClickListener - */ - public void addPartialOnLongClickListener( - int area, OnLongCardClickListener onLongClickListener) { - - if (area < CLICK_LISTENER_ALL_VIEW && area > CLICK_LISTENER_CONTENT_VIEW) { - Log.w(TAG, "area value not valid in addPartialOnLongClickListner"); - } - - HashMap multipleOnLongClickListener = getMultipleOnLongClickListener(); - if (onLongClickListener != null) { - multipleOnLongClickListener.put(area, onLongClickListener); - mIsLongClickable = true; - } else { - removePartialOnLongClickListener(area); - } - } - - /** - * Remove LongClickListener from a specif area - * </p> - * You can use one of these values: - * {@link Card#CLICK_LISTENER_ALL_VIEW} - * {@link Card#CLICK_LISTENER_HEADER_VIEW} - * {@link Card#CLICK_LISTENER_THUMBNAIL_VIEW} - * {@link Card#CLICK_LISTENER_CONTENT_VIEW} - * - * - * @param area - */ - public void removePartialOnLongClickListener(int area) { - - HashMap multipleOnLongClickListener = getMultipleOnLongClickListener(); - multipleOnLongClickListener.remove(area); - - if (mOnLongClickListener == null && multipleOnLongClickListener.isEmpty()) { - mIsLongClickable = false; - } - } - - /** - * Map for all partial listeners - * - * @return a map with partial listeners - */ - public HashMap<Integer, OnLongCardClickListener> getMultipleOnLongClickListener() { - if (mMultipleOnLongClickListener != null) { - return mMultipleOnLongClickListener; - } - return mMultipleOnLongClickListener = new HashMap<Integer, OnLongCardClickListener>(); - } - - /** * Indicates if the card is expanded or collapsed * * @return <code>true</code> if card is ExpandLayout is visible, otherwise returns <code>false</code> @@ -911,6 +1002,13 @@ public class Card extends BaseCard { } /** + * Refreshes the card content (it doesn't inflate layouts again) + */ + public void notifyDataSetChanged(){ + getCardView().refreshCard(this); + } + + /** * Sets the background drawable resource to override the style of MainLayout (card.main_layout) * * @param drawableResourceId drawable resource Id @@ -1011,4 +1109,30 @@ public class Card extends BaseCard { public void setViewToClickToExpand(ViewToClickToExpand viewToClickToExpand) { this.viewToClickToExpand = viewToClickToExpand; } + + /** + * Returns true if the card is using the native card + * @return + */ + protected boolean isNative(){ + if (mCardView!=null) + return mCardView.isNative(); + return false; + } + + /** + * Set the background color assigned to the Native CardView + * @param backgroundColorResourceId + */ + public void setBackgroundColorResourceId(int backgroundColorResourceId) { + mBackgroundColorResourceId = backgroundColorResourceId; + } + + /** + * Returns the background color assigned to the Native CardView + * @return + */ + public int getBackgroundColorResourceId() { + return mBackgroundColorResourceId; + } } diff --git a/src/com/android/cards/internal/CardArrayAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardArrayAdapter.java index 7ec50a7..6c59787 100644 --- a/src/com/android/cards/internal/CardArrayAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardArrayAdapter.java @@ -29,17 +29,22 @@ import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ListView; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import com.android.cards.R; import com.android.cards.internal.base.BaseCardArrayAdapter; import com.android.cards.view.CardListView; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.CardView; import com.android.cards.view.listener.SwipeDismissListViewTouchListener; import com.android.cards.view.listener.SwipeOnScrollListener; import com.android.cards.view.listener.UndoBarController; import com.android.cards.view.listener.UndoCard; +import com.android.cards.view.listener.dismiss.DefaultDismissableManager; +import com.android.cards.view.listener.dismiss.Dismissable; /** * Array Adapter for {@link Card} model @@ -103,6 +108,10 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon */ protected HashMap<String /* id */,Card> mInternalObjects; + /** + * Dismissable Manager + */ + protected Dismissable mDismissable; // ------------------------------------------------------------- // Constructors @@ -126,128 +135,107 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; - ViewHolder holder; + CardViewWrapper mCardView; + Card mCard; + + LayoutInflater mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + //Retrieve card from items + mCard = (Card) getItem(position); + if (mCard != null) { - // Retrieve card from items - Card card = (Card) getItem(position); - if (card != null) { int layout = mRowLayoutId; boolean recycle = false; - // Inflate layout + //Inflate layout if (view == null) { recycle = false; - LayoutInflater inflater = - (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(layout, parent, false); - - holder = new ViewHolder(); - holder.cardView = (CardView) view.findViewById(R.id.list_cardId); - view.setTag(holder); + view = mInflater.inflate(layout, parent, false); } else { recycle = true; - holder = (ViewHolder) view.getTag(); } - // Setup card - CardView cardView = holder.cardView; - if (cardView != null) { - // It is important to set recycle value for inner layout elements - cardView.setForceReplaceInnerLayout( - Card.equalsInnerLayout(cardView.getCard(), card)); + //Setup card + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); + if (mCardView != null) { + //It is important to set recycle value for inner layout elements + mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(),mCard)); - // It is important to set recycle value for performance issue - cardView.setRecycle(recycle); + //It is important to set recycle value for performance issue + mCardView.setRecycle(recycle); - // Save original swipeable to prevent cardSwipeListener - // (listView requires another cardSwipeListener) - boolean origianlSwipeable = card.isSwipeable(); - card.setSwipeable(false); + //Save original swipeable to prevent cardSwipeListener (listView requires another cardSwipeListener) + boolean origianlSwipeable = mCard.isSwipeable(); + mCard.setSwipeable(false); - cardView.setCard(card); + mCardView.setCard(mCard); - // Set originalValue - card.setSwipeable(origianlSwipeable); + //Set originalValue + mCard.setSwipeable(origianlSwipeable); - // If card has an expandable button override animation - if ((card.getCardHeader() != null - && card.getCardHeader().isButtonExpandVisible()) - || card.getViewToClickToExpand() != null) { - setupExpandCollapseListAnimation(cardView); + //If card has an expandable button override animation + if ((mCard.getCardHeader() != null && mCard.getCardHeader().isButtonExpandVisible()) || mCard.getViewToClickToExpand()!=null ){ + setupExpandCollapseListAnimation(mCardView); } - // Setup swipeable animation - setupSwipeableAnimation(card, cardView); + //Setup swipeable animation + setupSwipeableAnimation(mCard, mCardView); //setupMultiChoice - setupMultichoice(view, card, cardView, position); + setupMultichoice(view,mCard,mCardView,position); } } return view; } - static class ViewHolder { - CardView cardView; - } /** * Sets SwipeAnimation on List * * @param card {@link Card} - * @param cardView {@link CardView} + * @param cardView {@link com.android.cards.view.base.CardViewWrapper} */ - protected void setupSwipeableAnimation(final Card card, CardView cardView) { - HashMap<Integer, Card.OnLongCardClickListener> multipleOnLongClickListner = - card.getMultipleOnLongClickListener(); - if (card.isSwipeable()) { - if (mOnTouchListener == null) { + protected void setupSwipeableAnimation(final Card card, CardViewWrapper cardView) { + + if (card.isSwipeable()){ + if (mOnTouchListener == null){ mOnTouchListener = new SwipeDismissListViewTouchListener(mCardListView, mCallback); + + //Configure the default DismissableManager + if (mDismissable == null) mDismissable = new DefaultDismissableManager(); + mDismissable.setAdapter(this); + mOnTouchListener.setDismissable(mDismissable); + // Setting this scroll listener is required to ensure that during // ListView scrolling, we don't look for swipes. - if (mCardListView.getOnScrollListener() == null) { + if (mCardListView.getOnScrollListener() == null){ SwipeOnScrollListener scrollListener = new SwipeOnScrollListener(); scrollListener.setTouchListener(mOnTouchListener); mCardListView.setOnScrollListener(scrollListener); - } else { + }else{ AbsListView.OnScrollListener onScrollListener=mCardListView.getOnScrollListener(); - if (onScrollListener instanceof SwipeOnScrollListener) { + if (onScrollListener instanceof SwipeOnScrollListener) ((SwipeOnScrollListener) onScrollListener).setTouchListener(mOnTouchListener); - } } + mCardListView.setOnTouchListener(mOnTouchListener); } cardView.setOnTouchListener(mOnTouchListener); - // We may have partial onlongclicklistener. Restore onTouchListener for this views. - setPartialOnTouchListeners(cardView, mOnTouchListener, multipleOnLongClickListner); - } else { + }else{ //prevent issue with recycle view cardView.setOnTouchListener(null); - setPartialOnTouchListeners(cardView, null, multipleOnLongClickListner); - } - } - - private void setPartialOnTouchListeners(CardView cardView, - SwipeDismissListViewTouchListener onTouchListener, - HashMap<Integer, Card.OnLongCardClickListener> multipleOnLongClickListner) { - if (multipleOnLongClickListner != null && !multipleOnLongClickListner.isEmpty()) { - for (int key : multipleOnLongClickListner.keySet()) { - View viewLongClickable = cardView.decodeAreaOnClickListener(key); - if (viewLongClickable != null) { - viewLongClickable.setOnTouchListener(mOnTouchListener); - } - } } } /** * Overrides the default collapse/expand animation in a List * - * @param cardView {@link CardView} + * @param cardView {@link com.android.cards.view.base.CardViewWrapper} */ - protected void setupExpandCollapseListAnimation(CardView cardView) { + protected void setupExpandCollapseListAnimation(CardViewWrapper cardView) { if (cardView == null) return; cardView.setOnExpandListAnimatorListener(mCardListView); @@ -263,7 +251,7 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon @Override public boolean canDismiss(int position, Card card) { - return card.isSwipeable(); + return mDismissable.isDismissable(position, card); } @Override @@ -273,16 +261,36 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon String[] itemIds=new String[reverseSortedPositions.length]; int i=0; + // Keep track of the cards that will be removed + final ArrayList<Card> removedCards = new ArrayList<Card>(); + //Remove cards and notifyDataSetChanged for (int position : reverseSortedPositions) { - Card card = getItem(position); - itemPositions[i]=position; - itemIds[i]=card.getId(); - i++; - remove(card); - if (card.getOnSwipeListener() != null){ + Card card = null; + if (listView.getAdapter() != null && listView.getAdapter().getItem(position) instanceof Card) + card = (Card) listView.getAdapter().getItem(position); + //Card card = getItem(position); + + if (card != null) { + itemPositions[i] = position; + itemIds[i] = card.getId(); + i++; + + /* + if (card.isExpanded()){ + if (card.getCardView()!=null && card.getCardView().getOnExpandListAnimatorListener()!=null){ + //There is a List Animator. + card.getCardView().getOnExpandListAnimatorListener().onCollapseStart(card.getCardView(), card.getCardView().getInternalExpandLayout()); + } + }*/ + removedCards.add(card); + remove(card); + if (card.getOnSwipeListener() != null) { card.getOnSwipeListener().onSwipe(card); + } + }else{ + Log.e(TAG,"Error on swipe action. Impossible to retrieve the card from position"); } } notifyDataSetChanged(); @@ -293,18 +301,41 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon //Show UndoBar UndoCard itemUndo=new UndoCard(itemPositions,itemIds); - if (getContext()!=null){ - Resources res = getContext().getResources(); - if (res!=null){ - String messageUndoBar = res.getQuantityString(R.plurals.list_card_undo_items, reverseSortedPositions.length, reverseSortedPositions.length); + //MessageUndoBar + String messageUndoBar=null; + if (getUndoBarController().getUndoBarUIElements()!=null){ + messageUndoBar = getUndoBarController().getUndoBarUIElements().getMessageUndo(CardArrayAdapter.this,itemIds,itemPositions); + } - mUndoBarController.showUndoBar( - false, - messageUndoBar, - itemUndo); + //Default message if null + if (messageUndoBar == null) { + if (getContext() != null) { + Resources res = getContext().getResources(); + if (res != null) { + messageUndoBar = res.getQuantityString(R.plurals.list_card_undo_items, reverseSortedPositions.length, reverseSortedPositions.length); + } } } + mUndoBarController.showUndoBar( + false, + messageUndoBar, + itemUndo, + new UndoBarController.UndoBarHideListener() { + @Override + public void onUndoBarHide(boolean undoOccurred) { + // Remove the items from mInternalObjects, if + // the undo was not triggered, since they are + // now permanently removed from the underlying Array. + if (!undoOccurred) { + for (Card card : removedCards) { + if (card.getOnUndoHideSwipeListListener()!=null) + card.getOnUndoHideSwipeListListener().onUndoHideSwipe(card); + mInternalObjects.remove(card.getId()); + } + } + } + }); } } }; @@ -374,9 +405,14 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon if (mUndoBarUIElements==null) mUndoBarUIElements=new UndoBarController.DefaultUndoBarUIElements(); - View undobar = ((Activity)mContext).findViewById(mUndoBarUIElements.getUndoBarId()); - if (undobar != null) { - mUndoBarController = new UndoBarController(undobar, this,mUndoBarUIElements); + if (mContext!=null && mContext instanceof Activity) { + View undobar = ((Activity) mContext).findViewById(mUndoBarUIElements.getUndoBarId()); + if (undobar != null) { + mUndoBarController = new UndoBarController(undobar, this, mUndoBarUIElements); + } + }else{ + Log.e(TAG,"Undo Action requires a valid Activity context"); + throw new IllegalArgumentException("Undo Action requires a valid Activity context"); } } }else{ @@ -384,6 +420,57 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon } } + // --------------------------------------------------------------------- + // Override Array Manipulation Methods To Keep mInternalObjects in Sync + // --------------------------------------------------------------------- + + // public void remove() intentionally omitted, since mInternalObjects needs + // to keep a reference so the remove can be undone, if necessary. + + @Override + public void add(Card card) { + super.add(card); + if (mEnableUndo) { + mInternalObjects.put(card.getId(), card); + } + } + + @Override + public void addAll(Collection<? extends Card> cardCollection) { + super.addAll(cardCollection); + if (mEnableUndo) { + for (Card card : cardCollection) { + mInternalObjects.put(card.getId(), card); + } + } + } + + @Override + public void addAll(Card...cards) { + super.addAll(cards); + if (mEnableUndo) { + for (Card card : cards) { + mInternalObjects.put(card.getId(), card); + } + } + } + + @Override + public void clear() { + super.clear(); + if (mEnableUndo) { + mInternalObjects.clear(); + } + } + + @Override + public void insert(Card card, int index) { + super.insert(card, index); + if (mEnableUndo) { + mInternalObjects.put(card.getId(), card); + } + } + // ------------------------------------------------------------- // Getters and Setters // ------------------------------------------------------------- @@ -414,4 +501,13 @@ public class CardArrayAdapter extends BaseCardArrayAdapter implements UndoBarCon public UndoBarController getUndoBarController() { return mUndoBarController; } + + /** + * Sets a custom DismissableManager + * @param dismissable + */ + public void setDismissable(Dismissable dismissable) { + mDismissable = dismissable; + } + } diff --git a/src/com/android/cards/internal/CardArrayMultiChoiceAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardArrayMultiChoiceAdapter.java index 2ba4735..2d15bbb 100644 --- a/src/com/android/cards/internal/CardArrayMultiChoiceAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardArrayMultiChoiceAdapter.java @@ -33,7 +33,8 @@ import com.android.cards.internal.multichoice.MultiChoiceAdapter; import com.android.cards.internal.multichoice.MultiChoiceAdapterHelperBase; import com.android.cards.internal.multichoice.OptionMultiChoice; import com.android.cards.view.CardListView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * @author Gabriele Mariotti (gabri.mariotti@gmail.com) @@ -96,7 +97,7 @@ public abstract class CardArrayMultiChoiceAdapter extends CardArrayAdapter imple * @param position */ @Override - protected void setupMultichoice(View view, Card mCard, CardView mCardView, long position) { + protected void setupMultichoice(View view, Card mCard, CardViewWrapper mCardView, long position) { super.setupMultichoice(view, mCard, mCardView, position); mHelper.setupMultichoice(view, mCard, mCardView, position); } diff --git a/src/com/android/cards/internal/CardCursorAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardCursorAdapter.java index 65f77bd..7520c04 100644 --- a/src/com/android/cards/internal/CardCursorAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardCursorAdapter.java @@ -32,7 +32,8 @@ import java.util.List; import com.android.cards.R; import com.android.cards.internal.base.BaseCardCursorAdapter; import com.android.cards.view.CardListView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * Cursor Adapter for {@link com.android.cards.internal.Card} model @@ -57,31 +58,31 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { protected HashMap<String /* id */,Card> mInternalObjects; + /** + * All ids expanded + */ protected final List<String> mExpandedIds; /** * Recycle */ - private boolean recycle = false; + protected boolean recycle = false; // ------------------------------------------------------------- // Constructors // ------------------------------------------------------------- public CardCursorAdapter(Context context) { - super(context, null, false); - mContext= context; + super(context, null, 0); mExpandedIds = new ArrayList<String>(); } protected CardCursorAdapter(Context context, Cursor c, boolean autoRequery) { super(context, c, autoRequery); - mContext= context; mExpandedIds = new ArrayList<String>(); } protected CardCursorAdapter(Context context, Cursor c, int flags) { super(context, c, flags); - mContext= context; mExpandedIds = new ArrayList<String>(); } @@ -111,12 +112,12 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { @Override public void bindView(View view, Context context, Cursor cursor) { - CardView mCardView; + CardViewWrapper mCardView; Card mCard; mCard = (Card) getCardFromCursor(cursor); if (mCard != null) { - mCardView = (CardView) view.findViewById(R.id.list_cardId); + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); if (mCardView != null) { //It is important to set recycle value for inner layout elements mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(),mCard)); @@ -145,6 +146,8 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { //Setup swipeable animation setupSwipeableAnimation(mCard, mCardView); + //setupMultiChoice + setupMultichoice(view,mCard,mCardView,cursor.getPosition()); } } } @@ -156,7 +159,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * @param card {@link com.android.cards.internal.Card} * @param cardView {@link com.android.cards.view.CardView} */ - protected void setupSwipeableAnimation(final Card card, CardView cardView) { + protected void setupSwipeableAnimation(final Card card, CardViewWrapper cardView) { cardView.setOnTouchListener(null); } @@ -166,7 +169,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * * @param cardView {@link com.android.cards.view.CardView} */ - protected void setupExpandCollapseListAnimation(CardView cardView) { + protected void setupExpandCollapseListAnimation(CardViewWrapper cardView) { if (cardView == null) return; cardView.setOnExpandListAnimatorListener(mCardListView); @@ -236,7 +239,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * @param viewCard * @return */ - public boolean onExpandStart(CardView viewCard) { + public boolean onExpandStart(CardViewWrapper viewCard) { Card card = viewCard.getCard(); if (card!=null){ String itemId = card.getId(); @@ -253,7 +256,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * @param viewCard * @return */ - public boolean onCollapseStart(CardView viewCard) { + public boolean onCollapseStart(CardViewWrapper viewCard) { Card card = viewCard.getCard(); if (card!=null){ String itemId = card.getId(); @@ -269,7 +272,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * * @param viewCard */ - public void onExpandEnd(CardView viewCard) { + public void onExpandEnd(CardViewWrapper viewCard) { Card card = viewCard.getCard(); if (card!=null){ setExpanded(card); @@ -281,7 +284,7 @@ public abstract class CardCursorAdapter extends BaseCardCursorAdapter { * * @param viewCard */ - public void onCollapseEnd(CardView viewCard) { + public void onCollapseEnd(CardViewWrapper viewCard) { Card card = viewCard.getCard(); if (card!=null){ setCollapsed(card); diff --git a/src/it/gmariotti/cardslib/library/internal/CardCursorMultiChoiceAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardCursorMultiChoiceAdapter.java new file mode 100644 index 0000000..87a6498 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/internal/CardCursorMultiChoiceAdapter.java @@ -0,0 +1,162 @@ +package com.android.cards.internal; + +import android.app.Activity; +import android.content.Context; +import android.view.ActionMode; +import android.view.Menu; +import android.view.View; +import android.widget.AbsListView; + +import java.util.ArrayList; + +import com.android.cards.internal.multichoice.DefaultOptionMultiChoice; +import com.android.cards.internal.multichoice.MultiChoiceAdapter; +import com.android.cards.internal.multichoice.MultiChoiceAdapterHelperBase; +import com.android.cards.internal.multichoice.OptionMultiChoice; +import com.android.cards.view.CardListView; +import com.android.cards.view.base.CardViewWrapper; + + +public abstract class CardCursorMultiChoiceAdapter extends CardCursorAdapter implements MultiChoiceAdapter, AbsListView.MultiChoiceModeListener { + + /** + * Helper + */ + private MultiChoiceAdapterHelperBase mHelper = new MultiChoiceAdapterHelperBase(this); + + + /** + * Option for multichoice + */ + protected OptionMultiChoice mOptions; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + /** + * Constructor + * + * @param context The current context. + */ + public CardCursorMultiChoiceAdapter(Context context) { + this(context, new DefaultOptionMultiChoice()); + } + + /** + * Constructor + * + * @param context The current context. + */ + public CardCursorMultiChoiceAdapter(Context context, OptionMultiChoice options) { + super(context, null, true); + this.mOptions = options; + mHelper.setMultiChoiceModeListener(this); + } + + // ------------------------------------------------------------- + // Adapter + // ------------------------------------------------------------- + + @Override + public void setCardListView(CardListView cardListView) { + super.setCardListView(cardListView); + mHelper.setAdapterView(cardListView); + } + + /** + * Used to setup some element events for multichoice + * + * @param view + * @param mCard + * @param mCardView + * @param position + */ + @Override + protected void setupMultichoice(View view, Card mCard, CardViewWrapper mCardView, long position) { + super.setupMultichoice(view, mCard, mCardView, position); + mHelper.setupMultichoice(view, mCard, mCardView, position); + } + + + @Override + public Card getItem(int position) { + Card card = super.getItem(position); + card.mMultiChoiceEnabled = true; + return card; + } + + // ------------------------------------------------------------- + // ActionMode + // ------------------------------------------------------------- + + public boolean startActionMode(Activity activity) { + return mHelper.startActionMode(activity); + } + + /** + * Called when action mode is first created. The menu supplied will be used to + * generate action buttons for the action mode. + * + * @param mode ActionMode being created + * @param menu Menu used to populate action buttons + * @return true if the action mode should be created, false if entering this + * mode should be aborted. + */ + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + return mHelper.onCreateActionMode(mode,menu); + } + + + /** + * Called when an action mode is about to be exited and destroyed. + * + * @param mode The current ActionMode being destroyed + */ + @Override + public void onDestroyActionMode(ActionMode mode) { + mHelper.onDestroyActionMode(mode); + } + + /** + * Called when an item is checked or unchecked during selection mode. + * + * @param mode The {@link ActionMode} providing the selection mode + * @param position Adapter position of the item that was checked or unchecked + * @param id Adapter ID of the item that was checked or unchecked + * @param checked <code>true</code> if the item is now checked, <code>false</code> + * if the item is now unchecked. + */ + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + mHelper.onItemCheckedStateChanged(mode,position,id,checked); + } + + /** + * Indicate if action mode is started + * + * @return + */ + @Override + public boolean isActionModeStarted() { + return mHelper.isActionModeStarted(); + } + + // ------------------------------------------------------------- + // MultiChoice + // ------------------------------------------------------------- + + /** + * Returns the selected cards + * @return + */ + protected ArrayList<Card> getSelectedCards() { + return mHelper.getSelectedCards(); + } + + @Override + public OptionMultiChoice getOptionMultiChoice() { + return mOptions; + } +} diff --git a/src/com/android/cards/internal/CardExpand.java b/src/it/gmariotti/cardslib/library/internal/CardExpand.java index e0d34fe..c1e313e 100644 --- a/src/com/android/cards/internal/CardExpand.java +++ b/src/it/gmariotti/cardslib/library/internal/CardExpand.java @@ -83,6 +83,7 @@ import com.android.cards.internal.base.BaseCard; */ public class CardExpand extends BaseCard { + private boolean couldUseNativeInnerLayout = false; // ------------------------------------------------------------- // Constructors @@ -106,6 +107,9 @@ public class CardExpand extends BaseCard { public CardExpand(Context context, int innerLayout) { super(context); mInnerLayout= innerLayout; + + if (innerLayout == R.layout.inner_base_expand) + couldUseNativeInnerLayout = true; } // ------------------------------------------------------------- @@ -124,6 +128,10 @@ public class CardExpand extends BaseCard { @Override public View getInnerView(Context context, ViewGroup parent) { + //Check if the default inner layout could be the native layout + if (couldUseNativeInnerLayout && isNative()) + mInnerLayout = R.layout.native_inner_base_expand; + //Inflate the inner layout View view= super.getInnerView(context, parent); @@ -145,7 +153,6 @@ public class CardExpand extends BaseCard { * This method sets values to expand elements and customizes view. * * Override this method to customize your Expand View - * If you use listviews it is recommend to user a Viewholder like here. * * @param parent Expand external Layout * @param view inner-expand view @@ -153,25 +160,23 @@ public class CardExpand extends BaseCard { @Override public void setupInnerViewElements(ViewGroup parent, View view) { - // Add simple title to expand area - if (view != null) { - ViewHolder holder; - holder = (ViewHolder) view.getTag(); - - if (holder == null) { - holder = new ViewHolder(); - holder.titleView = - (TextView) view.findViewById(R.id.card_expand_inner_simple_title); - view.setTag(holder); - } - if (holder.titleView != null) { - holder.titleView.setText(mTitle); - } + //Add simple title to expand area + if (view!=null){ + TextView mTitleView=(TextView) view.findViewById(R.id.card_expand_inner_simple_title); + if (mTitleView!=null) + mTitleView.setText(mTitle); } } - static class ViewHolder { - TextView titleView; + /** + * Returns true if the card is using the native card + * @return + */ + protected boolean isNative(){ + if (getParentCard() != null) + return getParentCard().isNative(); + return false; } + } diff --git a/src/it/gmariotti/cardslib/library/internal/CardExpandableListAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardExpandableListAdapter.java new file mode 100644 index 0000000..5d82d7a --- /dev/null +++ b/src/it/gmariotti/cardslib/library/internal/CardExpandableListAdapter.java @@ -0,0 +1,220 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.internal; + +import android.content.Context; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseExpandableListAdapter; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.cards.R; +import com.android.cards.view.CardExpandableListView; +import com.android.cards.view.base.CardViewWrapper; + + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardExpandableListAdapter<T> extends BaseExpandableListAdapter { + + protected final SparseArray<BaseGroupExpandableCard<T>> cards; + public LayoutInflater mInflater; + + /** + * Current context + */ + protected Context mContext; + + /** + * Default layout used for each row + */ + protected int mGroupLayoutId = R.layout.list_card_layout; + + /** + * Default layout used for each row + */ + protected int mChildLayoutId = R.layout.base_list_expandable_children_layout; + + /** + * {@link CardExpandableListView} + */ + protected CardExpandableListView mCardListView; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + public CardExpandableListAdapter(Context context, SparseArray<BaseGroupExpandableCard<T>> cards) { + this.cards = cards; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public int getGroupCount() { + return cards.size(); + } + + @Override + public int getChildrenCount(int groupPosition) { + return cards.get(groupPosition).children.size(); + } + + + @Override + public Card getGroup(int groupPosition) { + return cards.get(groupPosition); + } + + + @Override + public T getChild(int groupPosition, int childPosition) { + return cards.get(groupPosition).children.get(childPosition); + } + + @Override + public long getGroupId(int groupPosition) { + if (getGroup(groupPosition).getId()!=null) + return getGroup(groupPosition).getId().hashCode(); + else + return groupPosition; + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return childPosition; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + + View view = convertView; + CardViewWrapper mCardView; + + Card mCard = (Card) getGroup(groupPosition); + if (mCard != null) { + int layout = mGroupLayoutId; + boolean recycle = false; + + //Inflate layout + if (view == null) { + recycle = false; + view = mInflater.inflate(layout, parent, false); + } else { + recycle = true; + } + + //Setup card + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); + if (mCardView != null) { + //It is important to set recycle value for inner layout elements + mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(), mCard)); + + //It is important to set recycle value for performance issue + mCardView.setRecycle(recycle); + + mCard.setSwipeable(false); + + mCardView.setCard(mCard); + + } + } + + return view; + } + + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { + + T obj = getChild(groupPosition, childPosition); + + if (obj != null && obj instanceof String) { + final String children = (String) obj; + + TextView text = null; + if (convertView == null) { + convertView = mInflater.inflate(mChildLayoutId, null); + } + text = (TextView) convertView.findViewById(R.id.card_children_simple_title); + text.setText(children); + + registerClickListener(convertView, obj, groupPosition, childPosition); + } + return convertView; + } + + + protected void registerClickListener(View convertView, T obj, int groupPosition, int childPosition) { + if (isChildSelectable(groupPosition, childPosition)) { + final T children = obj; + convertView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(mContext, children.toString(), Toast.LENGTH_SHORT).show(); + } + }); + } + } + + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + // ------------------------------------------------------------- + // Getters and Setters + // ------------------------------------------------------------- + + /** + * Returns current context + * + * @return current context + */ + public Context getContext() { + return mContext; + } + + /** + * Sets layout resource ID used by rows + * + * @param groupLayoutId layout resource id + */ + public void setGroupLayoutId(int groupLayoutId) { + this.mGroupLayoutId = groupLayoutId; + } + + + public CardExpandableListView getCardListView() { + return mCardListView; + } + + public void setCardListView(CardExpandableListView cardListView) { + mCardListView = cardListView; + } + +} diff --git a/src/com/android/cards/internal/CardGridArrayAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardGridArrayAdapter.java index 6fefa0a..132198b 100644 --- a/src/com/android/cards/internal/CardGridArrayAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardGridArrayAdapter.java @@ -29,7 +29,7 @@ import java.util.List; import com.android.cards.R; import com.android.cards.internal.base.BaseCardArrayAdapter; import com.android.cards.view.CardGridView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.listener.SwipeDismissListViewTouchListener; /** @@ -86,11 +86,6 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { */ protected SwipeDismissListViewTouchListener mOnTouchListener; - /** - * List of cards represented in the ListView. - */ - private List<Card> cards; - // ------------------------------------------------------------- // Constructors @@ -104,7 +99,6 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { */ public CardGridArrayAdapter(Context context, List<Card> cards) { super(context, cards); - this.cards = cards; } // ------------------------------------------------------------- @@ -115,13 +109,13 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; - CardView mCardView; + CardViewWrapper mCardView; Card mCard; LayoutInflater mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); //Retrieve card from items - mCard = getItem(position); + mCard = (Card) getItem(position); if (mCard != null) { int layout = mRowLayoutId; @@ -136,7 +130,7 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { } //Setup card - mCardView = (CardView) view.findViewById(R.id.list_cardId); + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); if (mCardView != null) { //It is important to set recycle value for inner layout elements mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(),mCard)); @@ -171,23 +165,13 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { return view; } - @Override - public int getCount() { - return cards.size(); - } - - @Override - public Card getItem(int pos){ - return cards.get(pos); - } - /** * Removes SwipeAnimation on Grid * * @param card {@link Card} - * @param cardView {@link CardView} + * @param cardView {@link com.android.cards.view.base.CardViewWrapper} */ - protected void setupSwipeableAnimation(final Card card, CardView cardView) { + protected void setupSwipeableAnimation(final Card card, CardViewWrapper cardView) { cardView.setOnTouchListener(null); } @@ -197,7 +181,7 @@ public class CardGridArrayAdapter extends BaseCardArrayAdapter { * * @param cardView {@link com.android.cards.view.CardView} */ - protected void setupExpandCollapseListAnimation(CardView cardView) { + protected void setupExpandCollapseListAnimation(CardViewWrapper cardView) { if (cardView == null) return; cardView.setOnExpandListAnimatorListener(mCardGridView); diff --git a/src/com/android/cards/internal/CardGridArrayMultiChoiceAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardGridArrayMultiChoiceAdapter.java index 286feab..5b36174 100644 --- a/src/com/android/cards/internal/CardGridArrayMultiChoiceAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardGridArrayMultiChoiceAdapter.java @@ -33,7 +33,8 @@ import com.android.cards.internal.multichoice.MultiChoiceAdapter; import com.android.cards.internal.multichoice.MultiChoiceAdapterHelperBase; import com.android.cards.internal.multichoice.OptionMultiChoice; import com.android.cards.view.CardGridView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * @author Gabriele Mariotti (gabri.mariotti@gmail.com) @@ -96,7 +97,7 @@ public abstract class CardGridArrayMultiChoiceAdapter extends CardGridArrayAdapt * @param position */ @Override - protected void setupMultichoice(View view, Card mCard, CardView mCardView, long position) { + protected void setupMultichoice(View view, Card mCard, CardViewWrapper mCardView, long position) { super.setupMultichoice(view, mCard, mCardView, position); mHelper.setupMultichoice(view,mCard,mCardView,position); } diff --git a/src/com/android/cards/internal/CardGridCursorAdapter.java b/src/it/gmariotti/cardslib/library/internal/CardGridCursorAdapter.java index c87ee3e..d111303 100644 --- a/src/com/android/cards/internal/CardGridCursorAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/CardGridCursorAdapter.java @@ -30,7 +30,8 @@ import java.util.HashMap; import com.android.cards.R; import com.android.cards.internal.base.BaseCardCursorAdapter; import com.android.cards.view.CardGridView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * Cursor Adapter for {@link Card} model @@ -63,18 +64,15 @@ public abstract class CardGridCursorAdapter extends BaseCardCursorAdapter { // ------------------------------------------------------------- public CardGridCursorAdapter(Context context) { - super(context, null, false); - mContext= context; + super(context, null, 0); } protected CardGridCursorAdapter(Context context, Cursor c, boolean autoRequery) { super(context, c, autoRequery); - mContext= context; } protected CardGridCursorAdapter(Context context, Cursor c, int flags) { super(context, c, flags); - mContext= context; } // ------------------------------------------------------------- @@ -103,12 +101,12 @@ public abstract class CardGridCursorAdapter extends BaseCardCursorAdapter { @Override public void bindView(View view, Context context, Cursor cursor) { - CardView mCardView; + CardViewWrapper mCardView; Card mCard; mCard = (Card) getCardFromCursor(cursor); if (mCard != null) { - mCardView = (CardView) view.findViewById(R.id.list_cardId); + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); if (mCardView != null) { //It is important to set recycle value for inner layout elements mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(),mCard)); @@ -148,18 +146,9 @@ public abstract class CardGridCursorAdapter extends BaseCardCursorAdapter { * @param card {@link Card} * @param cardView {@link com.android.cards.view.CardView} */ - protected void setupSwipeableAnimation(final Card card, CardView cardView) { - cardView.setOnTouchListener(null); - } + protected void setupSwipeableAnimation(final Card card, CardViewWrapper cardView) { - /** - * Overrides the default collapse/expand animation in a List - * - * @param cardView {@link com.android.cards.view.CardView} - */ - protected void setupExpandCollapseListAnimation(CardView cardView) { - if (cardView == null) return; - cardView.setOnExpandListAnimatorListener(mCardGridView); + cardView.setOnTouchListener(null); } diff --git a/src/com/android/cards/internal/CardHeader.java b/src/it/gmariotti/cardslib/library/internal/CardHeader.java index 49b8bed..6fd3461 100644 --- a/src/com/android/cards/internal/CardHeader.java +++ b/src/it/gmariotti/cardslib/library/internal/CardHeader.java @@ -167,6 +167,8 @@ public class CardHeader extends BaseCard { */ protected boolean mIsOverflowSelected=false; + private boolean couldUseNativeInnerLayout = false; + // ------------------------------------------------------------- // Constructors // ------------------------------------------------------------- @@ -189,6 +191,9 @@ public class CardHeader extends BaseCard { public CardHeader(Context context,int innerLayout) { super(context); mInnerLayout= innerLayout; + + if (innerLayout == R.layout.inner_base_header) + couldUseNativeInnerLayout = true; } @@ -223,14 +228,6 @@ public class CardHeader extends BaseCard { } /** - * Interface to handle callbacks when ExpandButton is clicked - * Currently is never used - */ - public interface OnClickExpandListener { - public void onButtonExpandClick(BaseCard card, MenuItem item); - } - - /** * Interface to handle callbacks when Other Button is clicked */ public interface OnClickCardHeaderOtherButtonListener { @@ -344,6 +341,10 @@ public class CardHeader extends BaseCard { @Override public View getInnerView(Context context, ViewGroup parent) { + //Check if the default inner layout could be the native layout + if (couldUseNativeInnerLayout && isNative()) + mInnerLayout = R.layout.native_inner_base_header; + View view= super.getInnerView(context, parent); //This provide a simple implementation with a single title @@ -367,37 +368,22 @@ public class CardHeader extends BaseCard { * This method sets values to header elements and customizes view. * * Override this method to set your elements inside InnerView. - * If you use listviews it is recommend to user a Viewholder like here. * * @param parent parent view (Inner Frame) * @param view Inner View */ @Override - public void setupInnerViewElements(ViewGroup parent, View view) { - - // Add simple title to header - if (view != null) { - ViewHolder holder; - holder = (ViewHolder) view.getTag(); - - if (holder == null) { - holder = new ViewHolder(); - holder.titleView = - (TextView) view.findViewById(R.id.card_header_inner_simple_title); - view.setTag(holder); - } + public void setupInnerViewElements(ViewGroup parent,View view){ - if (holder.titleView != null) { - holder.titleView.setText(mTitle); - } + //Add simple title to header + if (view!=null){ + TextView mTitleView=(TextView) view.findViewById(R.id.card_header_inner_simple_title); + if (mTitleView!=null) + mTitleView.setText(mTitle); } } - static class ViewHolder { - TextView titleView; - } - // ------------------------------------------------------------- // Getters and Setters // ------------------------------------------------------------- @@ -458,17 +444,11 @@ public class CardHeader extends BaseCard { /** * Indicates if overflow button is visible. - * If the Popup Menu is =-1 return in any case <code>false</code> * - * @return <code>true</code> if the button is visible and Popup Menu is assigned. + * @return <code>true</code> if the button is visible */ public boolean isButtonOverflowVisible() { - //Without a PopupMenu, the button is not visible - if (mPopupMenu==NO_POPUP_MENU && mCustomOverflowAnimation==null){ - if (mIsButtonOverflowVisible) - Log.w("CardHeader","You set visible=true to overflow menu, but you don't add any Popup Menu or a CustomOverflowAnimator"); - return false; - } + return mIsButtonOverflowVisible; } @@ -556,5 +536,14 @@ public class CardHeader extends BaseCard { mOtherButtonDrawable = otherButtonDrawable; } + /** + * Returns true if the card is using the native card + * @return + */ + protected boolean isNative(){ + if (getParentCard() != null) + return getParentCard().isNative(); + return false; + } } diff --git a/src/com/android/cards/internal/CardThumbnail.java b/src/it/gmariotti/cardslib/library/internal/CardThumbnail.java index 30b7199..30b7199 100644 --- a/src/com/android/cards/internal/CardThumbnail.java +++ b/src/it/gmariotti/cardslib/library/internal/CardThumbnail.java diff --git a/src/com/android/cards/internal/ViewToClickToExpand.java b/src/it/gmariotti/cardslib/library/internal/ViewToClickToExpand.java index 212b500..96a0ee8 100644 --- a/src/com/android/cards/internal/ViewToClickToExpand.java +++ b/src/it/gmariotti/cardslib/library/internal/ViewToClickToExpand.java @@ -50,6 +50,16 @@ public class ViewToClickToExpand { */ protected CardElementUI cardElementUIToClick; + /** + * Indicates the expand action will be used in programmatic way + */ + protected boolean enableForCode = false; + + /** + * Use the longClick to enable expand/collapse action + */ + protected boolean useLongClick = false; + // ------------------------------------------------------------- // Constructors // ------------------------------------------------------------- @@ -89,7 +99,7 @@ public class ViewToClickToExpand { // ------------------------------------------------------------- /** - * Sets the view to click to enable the expand/collpase action + * Sets the view to click to enable the expand/collapse action * * @param viewToClick view to click * @return @@ -115,12 +125,33 @@ public class ViewToClickToExpand { return this; } + /** + * Indicates if the expand action will be enabled in a programmatic way + * + * @return + */ + public ViewToClickToExpand enableForExpandAction() { + this.enableForCode = true; + return this; + } + + /** + * Indicates if the expand action will be enabled with a long click + * + * @return + */ + public ViewToClickToExpand useLongClick(boolean useLongClick) { + this.useLongClick = useLongClick; + return this; + } + + // ------------------------------------------------------------- // Getters // ------------------------------------------------------------- /** - * Returns the view to Click to enable the expand/collapse action + * Returns the view to Click to enable the expand action * @return */ public View getViewToClick() { @@ -143,5 +174,19 @@ public class ViewToClickToExpand { return cardElementUIToClick; } + /** + * Indicates if the expand action will be enabled in a programmatic way + * @return + */ + public boolean isEnableForCode() { + return enableForCode; + } + + /** + * Indicates if the expand action will be enabled with a long click + */ + public boolean isUseLongClick() { + return useLongClick; + } } diff --git a/src/com/android/cards/internal/base/BaseCard.java b/src/it/gmariotti/cardslib/library/internal/base/BaseCard.java index aae06dd..98eb26d 100644 --- a/src/com/android/cards/internal/base/BaseCard.java +++ b/src/it/gmariotti/cardslib/library/internal/base/BaseCard.java @@ -24,14 +24,14 @@ import android.view.View; import android.view.ViewGroup; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; /** * Base Abstract Card model * * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ -public abstract class BaseCard implements CardUIInterface { +public abstract class BaseCard implements CardUIInferface { /** * Context @@ -46,7 +46,7 @@ public abstract class BaseCard implements CardUIInterface { /** * Outer View */ - protected CardView mCardView; + protected CardViewWrapper mCardView; /** * Inner View @@ -92,7 +92,7 @@ public abstract class BaseCard implements CardUIInterface { * @return the complete View component */ - public CardView getCardView() { + public CardViewWrapper getCardView() { return mCardView; } @@ -170,10 +170,10 @@ public abstract class BaseCard implements CardUIInterface { } /** - * Set the linked {@link CardView} - * @param cardView {@link CardView} + * Set the linked {@link com.android.cards.view.base.CardViewWrapper} + * @param cardView {@link com.android.cards.view.base.CardViewWrapper} */ - public void setCardView(CardView cardView) { + public void setCardView(CardViewWrapper cardView) { mCardView = cardView; } diff --git a/src/com/android/cards/internal/base/BaseCardArrayAdapter.java b/src/it/gmariotti/cardslib/library/internal/base/BaseCardArrayAdapter.java index 3bf07a1..fd72911 100644 --- a/src/com/android/cards/internal/base/BaseCardArrayAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/base/BaseCardArrayAdapter.java @@ -26,7 +26,7 @@ import java.util.List; import com.android.cards.R; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.listener.UndoBarController; /** @@ -101,7 +101,7 @@ public abstract class BaseCardArrayAdapter extends ArrayAdapter<Card> { * @param mCard * @param mCardView */ - protected void setupMultichoice(View view,Card mCard,CardView mCardView,long position){ + protected void setupMultichoice(View view,Card mCard,CardViewWrapper mCardView,long position){ //empty } diff --git a/src/com/android/cards/internal/base/BaseCardCursorAdapter.java b/src/it/gmariotti/cardslib/library/internal/base/BaseCardCursorAdapter.java index e967ff6..dd57cca 100644 --- a/src/com/android/cards/internal/base/BaseCardCursorAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/base/BaseCardCursorAdapter.java @@ -20,10 +20,12 @@ package com.android.cards.internal.base; import android.content.Context; import android.database.Cursor; +import android.view.View; import android.widget.CursorAdapter; import com.android.cards.R; import com.android.cards.internal.Card; +import com.android.cards.view.base.CardViewWrapper; /** * Base Cursor Adapter @@ -93,6 +95,15 @@ public abstract class BaseCardCursorAdapter extends CursorAdapter { return false; } + /** + * This method is used in with multichoice + * @param mCard + * @param mCardView + */ + protected void setupMultichoice(View view,Card mCard,CardViewWrapper mCardView,long position){ + //empty + } + @Override public Card getItem(int position) { Object obj = super.getItem(position); diff --git a/src/com/android/cards/internal/base/CardUIInterface.java b/src/it/gmariotti/cardslib/library/internal/base/CardUIInferface.java index 9d62e9f..734ffc7 100644 --- a/src/com/android/cards/internal/base/CardUIInterface.java +++ b/src/it/gmariotti/cardslib/library/internal/base/CardUIInferface.java @@ -27,7 +27,7 @@ import android.view.ViewGroup; * * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ -public interface CardUIInterface { +public interface CardUIInferface { /** * Implement this method to draw or inflate the Layout View of a Card or a Component Card. diff --git a/src/com/android/cards/internal/dismissanimation/BaseDismissAnimation.java b/src/it/gmariotti/cardslib/library/internal/dismissanimation/BaseDismissAnimation.java index f1e77b6..223864c 100644 --- a/src/com/android/cards/internal/dismissanimation/BaseDismissAnimation.java +++ b/src/it/gmariotti/cardslib/library/internal/dismissanimation/BaseDismissAnimation.java @@ -39,7 +39,7 @@ import com.android.cards.internal.Card; import com.android.cards.internal.CardArrayAdapter; import com.android.cards.internal.base.BaseCardArrayAdapter; import com.android.cards.view.CardListView; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.listener.SwipeDismissListViewTouchListener; /** @@ -114,9 +114,9 @@ public abstract class BaseDismissAnimation { prepareAnimation(); final List<Integer> positionsCopy = new ArrayList<Integer>(positions); - List<CardView> views = getVisibleViewsForPositions(positionsCopy); + List<CardViewWrapper> views = getVisibleViewsForPositions(positionsCopy); - for (CardView cardView:views){ + for (CardViewWrapper cardView:views){ dismissiCardWithAnimation(cardView); } } @@ -133,9 +133,9 @@ public abstract class BaseDismissAnimation { prepareAnimation(); final List<Card> cardsCopy = new ArrayList<Card>(cards); - List<CardView> views = getVisibleViewsForCards(cardsCopy); + List<CardViewWrapper> views = getVisibleViewsForCards(cardsCopy); - for (CardView cardView:views){ + for (CardViewWrapper cardView:views){ dismissiCardWithAnimation(cardView); } } @@ -151,7 +151,7 @@ public abstract class BaseDismissAnimation { } - public abstract void animate(Card card, CardView cardView);//, Card.OnDismissAnimationListener onDismissAnimationListener); + public abstract void animate(Card card, CardViewWrapper cardView);//, Card.OnDismissAnimationListener onDismissAnimationListener); /** @@ -177,12 +177,12 @@ public abstract class BaseDismissAnimation { * @param positions * @return */ - private List<CardView> getVisibleViewsForPositions(final Collection<Integer> positions) { - List<CardView> views = new ArrayList<CardView>(); + private List<CardViewWrapper> getVisibleViewsForPositions(final Collection<Integer> positions) { + List<CardViewWrapper> views = new ArrayList<CardViewWrapper>(); for (int i = 0; i < mCardListView.getChildCount(); i++) { View child = mCardListView.getChildAt(i); if (positions.contains(mCardListView.getPositionForView(child))) { - views.add((CardView) child); + views.add((CardViewWrapper) child); } } return views; @@ -194,8 +194,8 @@ public abstract class BaseDismissAnimation { * @param cardsCopy * @return */ - private List<CardView> getVisibleViewsForCards(List<Card> cardsCopy) { - List<CardView> originalViews = new ArrayList<CardView>(); + private List<CardViewWrapper> getVisibleViewsForCards(List<Card> cardsCopy) { + List<CardViewWrapper> originalViews = new ArrayList<CardViewWrapper>(); for (Card card:cardsCopy){ originalViews.add(card.getCardView()); } @@ -210,9 +210,9 @@ public abstract class BaseDismissAnimation { return originalViews; } - private void dismissiCardWithAnimation(final CardView cardView) { + private void dismissiCardWithAnimation(final CardViewWrapper cardView) { ++mDismissAnimationRefCount; - int mDownPosition = mCardListView.getPositionForView(cardView); + int mDownPosition = mCardListView.getPositionForView((View)cardView); animate(cardView.getCard(),cardView); } @@ -223,7 +223,7 @@ public abstract class BaseDismissAnimation { // all dismissed list item animations have completed. This triggers layout on each animation // frame; in the future we may want to do something smarter and more performant. - final int dismissPosition= mBaseAdapter.getPosition(((CardView) dismissView).getCard()); + final int dismissPosition= mBaseAdapter.getPosition(((CardViewWrapper) dismissView).getCard()); final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); final int originalHeight = dismissView.getHeight(); diff --git a/src/com/android/cards/internal/dismissanimation/SwipeDismissAnimation.java b/src/it/gmariotti/cardslib/library/internal/dismissanimation/SwipeDismissAnimation.java index 3b6705d..0332855 100644 --- a/src/com/android/cards/internal/dismissanimation/SwipeDismissAnimation.java +++ b/src/it/gmariotti/cardslib/library/internal/dismissanimation/SwipeDismissAnimation.java @@ -21,9 +21,10 @@ package com.android.cards.internal.dismissanimation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.view.View; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; /** * @author Gabriele Mariotti (gabri.mariotti@gmail.com) @@ -42,16 +43,16 @@ public class SwipeDismissAnimation extends BaseDismissAnimation { } @Override - public void animate(final Card card, final CardView cardView) { + public void animate(final Card card, final CardViewWrapper cardView) { - cardView.animate() + ((View)cardView).animate() .translationX(mDismissRight ? mListWidth : -mListWidth) .alpha(0) .setDuration(mAnimationTime) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - invokeCallbak(cardView); + invokeCallbak((View)cardView); } }); } diff --git a/src/com/android/cards/internal/multichoice/DefaultOptionMultiChoice.java b/src/it/gmariotti/cardslib/library/internal/multichoice/DefaultOptionMultiChoice.java index 5424693..5424693 100644 --- a/src/com/android/cards/internal/multichoice/DefaultOptionMultiChoice.java +++ b/src/it/gmariotti/cardslib/library/internal/multichoice/DefaultOptionMultiChoice.java diff --git a/src/com/android/cards/internal/multichoice/MultiChoiceAdapter.java b/src/it/gmariotti/cardslib/library/internal/multichoice/MultiChoiceAdapter.java index 9628d78..16422d0 100644 --- a/src/com/android/cards/internal/multichoice/MultiChoiceAdapter.java +++ b/src/it/gmariotti/cardslib/library/internal/multichoice/MultiChoiceAdapter.java @@ -21,7 +21,8 @@ package com.android.cards.internal.multichoice; import android.view.ActionMode; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * A base interface for multi choice adapter @@ -63,9 +64,7 @@ public interface MultiChoiceAdapter { * @param cardView * @param card */ - void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked, CardView cardView, Card card); - - + void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked, CardViewWrapper cardView, Card card); // ------------------------------------------------------------- // Default methods. You haven't to define it in your classes diff --git a/src/com/android/cards/internal/multichoice/MultiChoiceAdapterHelperBase.java b/src/it/gmariotti/cardslib/library/internal/multichoice/MultiChoiceAdapterHelperBase.java index 922f8eb..0f1d9d1 100644 --- a/src/com/android/cards/internal/multichoice/MultiChoiceAdapterHelperBase.java +++ b/src/it/gmariotti/cardslib/library/internal/multichoice/MultiChoiceAdapterHelperBase.java @@ -37,7 +37,7 @@ import java.util.ArrayList; import com.android.cards.R; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; public class MultiChoiceAdapterHelperBase implements AdapterView.OnItemLongClickListener,AdapterView.OnItemClickListener { @@ -84,14 +84,14 @@ public class MultiChoiceAdapterHelperBase implements AdapterView.OnItemLongClick * @param mCardView * @param position */ - public void setupMultichoice(View view, Card mCard, CardView mCardView, long position) { + public void setupMultichoice(View view, Card mCard, CardViewWrapper mCardView, long position) { final MultiChoiceAdapter adapter = (MultiChoiceAdapter) owner; View.OnClickListener advanceClickListener = new View.OnClickListener() { @Override public void onClick(View v) { - final CardView cardView = (CardView) v; + final CardViewWrapper cardView = (CardViewWrapper) v; int position = adapter.getPosition(cardView.getCard()); onItemClick(mAdapterView, v, position, adapter.getItemId(position)); } @@ -355,4 +355,4 @@ public class MultiChoiceAdapterHelperBase implements AdapterView.OnItemLongClick mMultiChoiceModeListener = multiChoiceModeListener; } -} +}
\ No newline at end of file diff --git a/src/com/android/cards/internal/multichoice/OptionMultiChoice.java b/src/it/gmariotti/cardslib/library/internal/multichoice/OptionMultiChoice.java index d6c9f33..d6c9f33 100644 --- a/src/com/android/cards/internal/multichoice/OptionMultiChoice.java +++ b/src/it/gmariotti/cardslib/library/internal/multichoice/OptionMultiChoice.java diff --git a/src/it/gmariotti/cardslib/library/prototypes/CardSection.java b/src/it/gmariotti/cardslib/library/prototypes/CardSection.java new file mode 100644 index 0000000..c305a70 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/prototypes/CardSection.java @@ -0,0 +1,47 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.prototypes; + +/** + * Default card section + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardSection { + + int firstPosition; + int sectionedPosition; + CharSequence title; + + // ------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------- + + public CardSection(int firstPosition, CharSequence title) { + this.firstPosition = firstPosition; + this.title = title; + } + + /** + * + * @return the title + */ + public CharSequence getTitle() { + return title; + } +}
\ No newline at end of file diff --git a/src/it/gmariotti/cardslib/library/prototypes/CardWithList.java b/src/it/gmariotti/cardslib/library/prototypes/CardWithList.java new file mode 100644 index 0000000..38f8013 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/prototypes/CardWithList.java @@ -0,0 +1,1027 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.prototypes; + +import android.content.Context; +import android.database.DataSetObserver; +import android.view.LayoutInflater; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStub; +import android.view.animation.AnimationUtils; +import android.widget.ArrayAdapter; + +import java.util.ArrayList; +import java.util.List; + +import com.android.cards.R; +import com.android.cards.internal.Card; +import com.android.cards.internal.CardHeader; + +/** + * A card with a LinearList inside. + * It is a particular Card that can display items inside the Card. + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +@SuppressWarnings({"JavaDoc", "UnusedDeclaration"}) +public abstract class CardWithList extends Card { + + /** + * The cardHeader + */ + protected CardHeader mCardHeader; + + /** + * The "listView" + */ + protected LinearListView mListView; + + /** + * The listAdapter + */ + protected LinearListAdapter mLinearListAdapter; + + /** + * Default layout used for each row + */ + protected int mChildLayoutId; + + /** + * Empty View + */ + private View mEmptyView; + + /** + * Progress View + */ + private View mProgressView; + + /** + * Resource Id used which identifies the empty view + */ + protected int emptyViewId = R.id.card_inner_base_empty_cardwithlist; + + /** + * An identifier for the layout resource to inflate when the ViewStub becomes visible + */ + protected int emptyViewViewStubLayoutId = R.layout.base_withlist_empty; + + /** + * Resource Id used which identifies the progressBar + */ + protected int progressBarId = R.id.card_inner_base_progressbar_cardwithlist; + + + /** + * An identifier for the layout resource to inflate when the ViewStub becomes visible + */ + protected int progressBarViewStubLayoutId = R.layout.base_withlist_progress; + + /** + * Indicates if the empty view feature is enabled + */ + protected boolean useEmptyView = true; + + /** + * Indicates if the progressBar feature is enabled + */ + protected boolean useProgressBar = false; + + /** + * Internal flag to indicate if the list is shown + */ + private boolean mListShown; + + /** + * Resource Id used which identifies the list + */ + protected int listViewId = R.id.card_inner_base_main_cardwithlist; + + /** + * Flag to set the observer as registered + */ + private boolean observerRegistered = false; + + private boolean couldUseNativeInnerLayout = false; + + + private DataSetObserver mDataObserver = new DataSetObserver() { + + @Override + public void onChanged() { + internalSetupChildren(); + } + + @Override + public void onInvalidated() { + internalSetupChildren(); + } + + }; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + /** + * Constructor with a base inner layout defined by R.layout.inner_base_main_cardwithlist + * + * @param context context + */ + public CardWithList(Context context) { + this(context, R.layout.inner_base_main_cardwithlist); + } + + /** + * Constructor with a custom inner layout. + * + * @param context context + * @param innerLayout resource ID for inner layout + */ + public CardWithList(Context context, int innerLayout) { + super(context, innerLayout); + + if (innerLayout == R.layout.inner_base_main_cardwithlist) { + couldUseNativeInnerLayout = true; + } + } + + // ------------------------------------------------------------- + // Init + // ------------------------------------------------------------- + + /** + * Init the card + */ + public void init() { + + //Init the CardHeader + mCardHeader = initCardHeader(); + if (mCardHeader != null) + addCardHeader(mCardHeader); + + //Init the Card + initCard(); + + //Init the children + List<ListObject> mChildren = initChildren(); + if (mChildren == null) + mChildren = new ArrayList<ListObject>(); + mLinearListAdapter = new LinearListAdapter(super.getContext(), mChildren); + + //Retrieve the layoutId use by children + mChildLayoutId = getChildLayoutId(); + + } + + // ------------------------------------------------------------- + // Abstract method to be implemented in your class + // ------------------------------------------------------------- + + /** + * Implement this method to initialize your CardHeader. + * <p/> + * An example: + * <pre><code> + * //Add Header + * CardHeader header = new CardHeader(getContext()); + * //Add a popup menu. This method set OverFlow button to visible + * header.setPopupMenu(R.menu.popupmain, new CardHeader.OnClickCardHeaderPopupMenuListener() { + * @Override + * public void onMenuItemClick(BaseCard card, MenuItem item) { + * Toast.makeText(getContext(), "Click on " + item.getTitle(), Toast.LENGTH_SHORT).show(); + * } + * }); + * header.setTitle("Weather"); //should use R.string. + * return header; + * <p/> + * </code></pre> + * + * @return the {@link CardHeader} + */ + protected abstract CardHeader initCardHeader(); + + /** + * Implement this method to initialize your Card. + * <p/> + * An example: + * <pre><code> + * setSwipeable(true); + * setOnSwipeListener(new OnSwipeListener() { + * @Override + * public void onSwipe(Card card) { + * Toast.makeText(getContext(), "Swipe on " + card.getCardHeader().getTitle(), Toast.LENGTH_SHORT).show(); + * } + * }); + * </code></pre> + */ + protected abstract void initCard(); + + /** + * Implement this method to initialize the list of objects + * <p/> + * An example: + * <pre><code> + * <p/> + * List<ListObject> mObjects = new ArrayList<ListObject>(); + * <p/> + * WeatherObject w1= new WeatherObject(); + * mObjects.add(w1); + * <p/> + * return mObjects; + * </code></pre> + * + * @return the List of ListObject. Return <code>null</code> if the list is empty. + */ + protected abstract List<ListObject> initChildren(); + + /** + * This method is called by the {@link com.android.cards.prototypes.CardWithList.LinearListAdapter} for each row. + * You can provide your layout and setup your ui elements. + * + * @param childPosition position inside the list of objects + * @param object {@link com.android.cards.prototypes.CardWithList.ListObject} + * @param convertView view used by row + * @param parent parent view + * @return + */ + public abstract View setupChildView(int childPosition, ListObject object, View convertView, ViewGroup parent); + + /** + * Implement this method to specify the layoutId used for each row in the list + * + * @return the layoutId + */ + public abstract int getChildLayoutId(); + + + // ------------------------------------------------------------- + // View + // ------------------------------------------------------------- + + /** + * Returns the id that identifies the {@link LinearListView}. + * + * @return + */ + protected int getListViewId() { + return listViewId; + } + + @Override + protected void setupInnerLayout() { + //Check if the default inner layout could be the native layout + if (couldUseNativeInnerLayout && isNative()) + mInnerLayout = R.layout.native_inner_base_main_cardwithlist; + } + + /** + * Setup the listAdapter. + * + * @param parent parent view (Inner Frame) + * @param view Inner View + */ + @Override + public void setupInnerViewElements(ViewGroup parent, View view) { + + mListView = (LinearListView) view.findViewById(getListViewId()); + if (mListView != null) { + + internalSetupProgressBar(parent, view); + + if (mLinearListAdapter != null) { + internalSetupChildren(); + mLinearListAdapter.registerDataSetObserver(mDataObserver); + } + } + + internalSetupEmptyView(parent, view); + + } + + /** + * Setup the children + */ + private void internalSetupChildren() { + if (mListView != null) { + + mListView.removeAllViews(); + + updateEmptyStatus((mLinearListAdapter == null) || mLinearListAdapter.isEmpty()); + + if (mLinearListAdapter == null) { + return; + } + mListView.setAdapter(mLinearListAdapter); + } + } + + /** + * Setup the empty view. + * + * @param parent mainContentLayout + * @param view innerView + */ + @SuppressWarnings("UnusedParameters") + private void internalSetupEmptyView(ViewGroup parent, View view) { + if (useEmptyView) { + mEmptyView = (View) parent.findViewById(getEmptyViewId()); + if (mEmptyView != null) { + if (mEmptyView instanceof ViewStub) + ((ViewStub) mEmptyView).setLayoutResource(getEmptyViewViewStubLayoutId()); + setEmptyView(mEmptyView); + } + } + } + + /** + * Setup the Progress Bar view. + * + * @param parent mainContentLayout + * @param view innerView + */ + @SuppressWarnings("UnusedParameters") + private void internalSetupProgressBar(ViewGroup parent, View view) { + if (useProgressBar) { + mProgressView = (View) parent.findViewById(getProgressBarId()); + mListShown=true; + if (mProgressView != null) { + if (mProgressView instanceof ViewStub) + ((ViewStub) mProgressView).setLayoutResource(getProgressBarViewStubLayoutId()); + setProgressView(mProgressView); + } + } + } + + /** + * Use this method to unregister the observer + */ + public void unregisterDataSetObserver(){ + if (mLinearListAdapter!=null) + mLinearListAdapter.unregisterDataSetObserver(mDataObserver); + } + + // ------------------------------------------------------------- + // Interface to be used by children + // ------------------------------------------------------------- + + + /** + * Children have to implement this interface + */ + public interface ListObject { + + /** + * Returns the object id + * + * @return + */ + public String getObjectId(); + + /** + * Returns the parent card + */ + public Card getParentCard(); + + /** + * Register a callback to be invoked when an item in this LinearListView has + * been clicked. + * + * @return The callback to be invoked with an item in this LinearListView has + * been clicked, or null id no callback has been set. + */ + public void setOnItemClickListener(OnItemClickListener onItemClickListener); + + /** + * @return The callback to be invoked with an item in this LinearListView has + * been clicked, or null id no callback has been set. + */ + public OnItemClickListener getOnItemClickListener(); + + /** + * Indicates if the item is swipeable + */ + public boolean isSwipeable(); + + /** + * Set the item as swipeable + * + * @param isSwipeable + */ + public void setSwipeable(boolean isSwipeable); + + /** + * Returns the callback to be invoked when item has been swiped + * + * @return listener + */ + public OnItemSwipeListener getOnItemSwipeListener(); + + /** + * Register a callback to be invoked when an item in this LinearListView has + * been swiped. + * + * @param onSwipeListener listener + */ + public void setOnItemSwipeListener(OnItemSwipeListener onSwipeListener); + + } + + + // ------------------------------------------------------------- + // Interface definition for a callback to be invoked when an item in this + // LinearListView has been clicked. + // ------------------------------------------------------------- + + /** + * Interface definition for a callback to be invoked when an item in this + * LinearListView has been clicked. + */ + public interface OnItemClickListener { + + /** + * Callback method to be invoked when an item in this LinearListView has + * been clicked. + * <p/> + * Implementers can call getItemAtPosition(position) if they need to + * access the data associated with the selected item. + * + * @param parent The LinearListView where the click happened. + * @param view The view within the LinearListView that was clicked (this + * will be a view provided by the adapter) + * @param position The position of the view in the adapter. + * @param object The object that was clicked. + */ + void onItemClick(LinearListView parent, View view, int position, ListObject object); + } + + // ------------------------------------------------------------- + // On Item Swipe Interface + // ------------------------------------------------------------- + + /** + * Interface definition for a callback to be invoked when an item in this + * LinearListView has been swiped + */ + public interface OnItemSwipeListener { + + /** + * Callback method to be invoked when an item in this LinearListView has + * been swiped. + * + * @param object The object that was clicked. + */ + public void onItemSwipe(ListObject object,boolean dismissRight); + + } + + + /** + * Returns listener invoked when card is swiped + * + * @return listener + */ + public OnSwipeListener getOnSwipeListener() { + return mOnSwipeListener; + } + + /** + * Sets listener invoked when card is swiped. + * If listener is <code>null</code> the card is not swipeable. + * + * @param onSwipeListener listener + */ + public void setOnSwipeListener(OnSwipeListener onSwipeListener) { + if (onSwipeListener != null) + mIsSwipeable = true; + else + mIsSwipeable = false; + this.mOnSwipeListener = onSwipeListener; + } + + // ------------------------------------------------------------- + // Default Implementation for ListObject + // ------------------------------------------------------------- + + /** + * Default ListObject + */ + public class DefaultListObject implements ListObject { + + /** + * Object Id + */ + protected String mObjectId; + + /** + * Parent Card + */ + protected Card mParentCard; + + /* + * Item click listener + */ + protected OnItemClickListener mOnItemClickListener; + + /** + * + */ + protected boolean mItemSwipeable = false; + + /** + * Item swipe listener + */ + protected OnItemSwipeListener mOnItemSwipeListener; + + // ------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------- + public DefaultListObject(Card parentCard) { + this.mParentCard = parentCard; + } + + // ------------------------------------------------------------- + // Default Implementation for getters and setters + // ------------------------------------------------------------- + + @Override + public String getObjectId() { + return mObjectId; + } + + @Override + public Card getParentCard() { + return null; + } + + public void setObjectId(String objectId) { + mObjectId = objectId; + } + + + @Override + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + mOnItemClickListener = onItemClickListener; + } + + @Override + public OnItemClickListener getOnItemClickListener() { + return mOnItemClickListener; + } + + @Override + public boolean isSwipeable() { + return mItemSwipeable; + } + + @Override + public void setSwipeable(boolean isSwipeable) { + mItemSwipeable = isSwipeable; + } + + @Override + public OnItemSwipeListener getOnItemSwipeListener() { + return mOnItemSwipeListener; + } + + @Override + public void setOnItemSwipeListener(OnItemSwipeListener onItemSwipeListener) { + if (onItemSwipeListener != null) + mItemSwipeable = true; + else + mItemSwipeable = false; + this.mOnItemSwipeListener = onItemSwipeListener; + } + + + } + + // ------------------------------------------------------------- + // Empty View + // ------------------------------------------------------------- + + /** + * Sets the view to show if the adapter is empty + */ + public void setEmptyView(View emptyView) { + mEmptyView = emptyView; + + useEmptyView = emptyView != null ? true : false; + + final LinearListAdapter adapter = getLinearListAdapter(); + final boolean empty = ((adapter == null) || adapter.isEmpty()); + updateEmptyStatus(empty); + } + + /** + * When the current adapter is empty, the LinearListView can display a special + * view call the empty view. The empty view is used to provide feedback to + * the user that no data is available in this LinearListView. + * + * @return The view to show if the adapter is empty. + */ + public View getEmptyView() { + return mEmptyView; + } + + /** + * Update the status of the list based on the empty parameter. If empty is + * true and we have an empty view, display it. In all the other cases, make + * sure that the layout is VISIBLE and that the empty view is GONE (if + * it's not null). + */ + private void updateEmptyStatus(boolean empty) { + if (isUseEmptyView()) { + if (empty) { + if (mEmptyView != null) { + mEmptyView.setVisibility(View.VISIBLE); + mListView.setVisibility(View.GONE); + } else { + // If the caller just removed our empty view, make sure the list + // view is visible + mListView.setVisibility(View.VISIBLE); + } + } else { + if (mEmptyView != null) + mEmptyView.setVisibility(View.GONE); + mListView.setVisibility(View.VISIBLE); + } + } + } + + + + // ------------------------------------------------------------- + // Progress bar + // ------------------------------------------------------------- + + /** + * When the current adapter is loading data, the LinearListView can display a special + * progress Bar. + * + * @return The view to show if the adapter is the progress bar is enabled. + */ + public View getProgressView() { + return mProgressView; + } + + /** + * Sets the view to show as progress bar + */ + public void setProgressView(View progressView) { + mProgressView = progressView; + useProgressBar = progressView != null; + } + + /** + * Updates the status of the list and the progress bar. + * + * @param shownList indicates if the list has to be shown + * @param animate indicates to use an animation between view transition + */ + public void updateProgressBar(boolean shownList, boolean animate) { + if (isUseProgressBar()) { + if (mListShown == shownList) { + return; + } + mListShown = shownList; + + if (shownList) { + if (animate) { + mProgressView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_out)); + mListView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_in)); + if (useEmptyView && mEmptyView!=null){ + mEmptyView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_in)); + } + } + mProgressView.setVisibility(View.GONE); + + final LinearListAdapter adapter = getLinearListAdapter(); + final boolean empty = ((adapter == null) || adapter.isEmpty()); + updateEmptyStatus(empty); + + + } else { + if (animate) { + mProgressView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_in)); + mListView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_out)); + if (useEmptyView && mEmptyView!=null){ + mEmptyView.startAnimation(AnimationUtils.loadAnimation( + getContext(), android.R.anim.fade_out)); + } + } + mProgressView.setVisibility(View.VISIBLE); + mListView.setVisibility(View.INVISIBLE); + if (useEmptyView && mEmptyView!=null){ + mEmptyView.setVisibility(View.INVISIBLE); + } + } + } + } + + // ------------------------------------------------------------- + // Internal Adapter + // ------------------------------------------------------------- + + /** + * ListAdapter used to populate the LinearLayout inside the Card. + */ + @SuppressWarnings("JavaDoc") + protected class LinearListAdapter extends ArrayAdapter<ListObject> { + + LayoutInflater mLayoutInflater; + + /** + * Listener invoked when a card is swiped + */ + protected SwipeDismissListItemViewTouchListener mOnTouchListener; + + /** + * Constructor + * + * @param context context + * @param objects objects + */ + public LinearListAdapter(Context context, List<ListObject> objects) { + super(context, 0, objects); + //mObjects = objects; + mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + + //Object + final ListObject object = getItem(position); + + View view = convertView; + if (view == null) { + view = mLayoutInflater.inflate(getChildLayoutId(), parent, false); + } + + //Setup the elements + final View viewChild = setupChildView(position, object, view, parent); + + //ClickItem Listener + if (viewChild != null && object.getOnItemClickListener() != null) { + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListView.playSoundEffect(SoundEffectConstants.CLICK); + object.getOnItemClickListener().onItemClick(mListView, viewChild, position, object); + } + }); + } + + //SwipeItem Listener + setupItemSwipeAnimation(object, viewChild); + + return viewChild; + } + + + @Override + public int getViewTypeCount() { + return 1; + } + + /** + * Sets SwipeAnimation on items List + * + * @param item {@link ListObject} + * @param itemView itemView + */ + protected void setupItemSwipeAnimation(final ListObject item, View itemView) { + + if (item.isSwipeable()) { + if (mOnTouchListener == null) { + mOnTouchListener = new SwipeDismissListItemViewTouchListener(mListView, mCallback); + } + itemView.setOnTouchListener(mOnTouchListener); + } + } + + + /** + * Returns the Object Id + * + * @param position position in the list + * @return the object Id + */ + @SuppressWarnings("UnusedDeclaration") + public String getChildId(int position) { + //Object + ListObject object = getItem(position); + return object.getObjectId(); + } + + + // ------------------------------------------------------------- + // SwipeListener and undo action + // ------------------------------------------------------------- + /** + * Listener invoked when a card is swiped + */ + SwipeDismissListItemViewTouchListener.DismissCallbacks mCallback = new SwipeDismissListItemViewTouchListener.DismissCallbacks() { + @Override + public boolean canDismiss(int position, Card card, ListObject listObject) { + return listObject.isSwipeable(); + } + + @Override + public void onDismiss(LinearListView listView, int position, boolean dismissRight) { + + int i = 0; + + //Remove cards and notifyDataSetChanged + ListObject object = getItem(position); + remove(object); + if (object.getOnItemSwipeListener() != null) { + object.getOnItemSwipeListener().onItemSwipe(object,dismissRight); + } + } + }; + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + if (!observerRegistered) { + super.registerDataSetObserver(observer); + } + observerRegistered = true; + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + if (observer == null) { + observerRegistered = false; + return; + } + super.unregisterDataSetObserver(observer); + observerRegistered = false; + } + } + + // ------------------------------------------------------------- + // Getter and setter + // ------------------------------------------------------------- + + /** + * Returns the adapter + * + * @return the adapter + */ + public LinearListAdapter getLinearListAdapter() { + return mLinearListAdapter; + } + + /** + * Sets the adapter + * + * @param linearListAdapter adapter + */ + public void setLinearListAdapter(LinearListAdapter linearListAdapter) { + mLinearListAdapter = linearListAdapter; + } + + /** + * Returns the resource Id used which identifies the empty view + * + * @return the resource Id used which identifies the empty view + */ + public int getEmptyViewId() { + return emptyViewId; + } + + /** + * Sets the resource Id used which identifies the empty view + * + * @param emptyViewId resource Id used which identifies the empty view + */ + public void setEmptyViewId(int emptyViewId) { + this.emptyViewId = emptyViewId; + } + + /** + * Returns the resource Id used which identifies the ProgressBar + * @return the resource Id used which identifies the ProgressBar + */ + public int getProgressBarId() { + return progressBarId; + } + + /** + * Sets the resource Id used which identifies the ProgressBar + * @param progressBarId resource Id used which identifies the ProgressBar + */ + public void setProgressBarId(int progressBarId) { + this.progressBarId = progressBarId; + } + + /** + * Return if the card uses the empty view built-in feature + * + * @return true if the card uses the empty view + */ + private boolean isUseEmptyView() { + if (mEmptyView != null) + return useEmptyView; + else return false; + } + + /** + * Sets the flag to enable and disable the empty view feature + * + * @param useEmptyView flag + */ + public void setUseEmptyView(boolean useEmptyView) { + this.useEmptyView = useEmptyView; + } + + private boolean isUseProgressBar() { + if (mProgressView != null) + return useProgressBar; + else + return false; + } + + /** + * Sets the flag to enable and disable the progress bar + * @param useProgressBar + */ + public void setUseProgressBar(boolean useProgressBar) { + this.useProgressBar = useProgressBar; + } + + + /** + * Sets the resource Id used which identifies the list + * + * @param listViewId resourceId which identifies the list + */ + public void setListViewId(int listViewId) { + this.listViewId = listViewId; + } + + /** + * Returns the identifier for the layout resource to inflate when the ViewStub becomes visible + * It is used only if the {@see useEmptyView) is setted to true and the {@see mEmptyView} is a {@link android.view.ViewStub}. + * + * @return + */ + public int getEmptyViewViewStubLayoutId() { + return emptyViewViewStubLayoutId; + } + + /** + * Sets the identifier for the layout resource to inflate when the ViewStub becomes visible + * + * @param emptyViewViewStubLayoutId + */ + public void setEmptyViewViewStubLayoutId(int emptyViewViewStubLayoutId) { + this.emptyViewViewStubLayoutId = emptyViewViewStubLayoutId; + } + + /** + * Returns the identifier for the layout resource to inflate when the ViewStub used by the ProgressBar becomes visible + * It is used only if the {@see useProgressBar) is setted to true and the {@see mProgressView} is a {@link android.view.ViewStub}. + * + * @return + */ + public int getProgressBarViewStubLayoutId() { + return progressBarViewStubLayoutId; + } + + /** + * Sets the identifier for the layout resource to inflate when the ViewStub used by the ProgressBar becomes visible + * + * @param progressBarViewStubLayoutId + */ + public void setProgressBarViewStubLayoutId(int progressBarViewStubLayoutId) { + this.progressBarViewStubLayoutId = progressBarViewStubLayoutId; + } +} + + diff --git a/src/it/gmariotti/cardslib/library/prototypes/LinearListView.java b/src/it/gmariotti/cardslib/library/prototypes/LinearListView.java new file mode 100644 index 0000000..3244135 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/prototypes/LinearListView.java @@ -0,0 +1,383 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.prototypes; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import com.android.cards.R; + +/** + * An extension of a linear layout that supports the divider API of Android + * 4.0+. You can populate this layout with data that comes from a + * {@link android.widget.ListAdapter} + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class LinearListView extends LinearLayout { + + /** + * Adapter + */ + private CardWithList.LinearListAdapter mListAdapter; + + /** + * Represents an invalid position. All valid positions are in the range 0 to 1 less than the + * number of items in the current adapter. + */ + public static final int INVALID_POSITION = -1; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + public LinearListView(Context context) { + super(context); + initAttrs(null, 0); + } + + public LinearListView(Context context, AttributeSet attrs) { + super(context, attrs); + initAttrs(attrs, 0); + } + + public LinearListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initAttrs(attrs, defStyle); + } + + // ------------------------------------------------------------- + // Init + // ------------------------------------------------------------- + + /** + * Init custom attrs. + * + * @param attrs + * @param defStyle + */ + protected void initAttrs(AttributeSet attrs, int defStyle) { + + TypedArray a = getContext().getTheme().obtainStyledAttributes( + attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout, defStyle, defStyle); + + try { + final Drawable d = a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider); + if (d != null) { + // If a divider is specified use its intrinsic height for divider height + setDividerDrawable(d); + } + + } finally { + a.recycle(); + } + + a = getContext().getTheme().obtainStyledAttributes( + attrs, R.styleable.card_listItem, defStyle, defStyle); + try { + // Use the height specified, zero being the default + final int dividerHeight = a.getDimensionPixelSize( + R.styleable.card_listItem_card_list_item_dividerHeight, 0); + if (dividerHeight != 0) { + setDividerHeight(dividerHeight); + } + } finally { + a.recycle(); + } + } + + // ------------------------------------------------------------- + // Divider + // ------------------------------------------------------------- + + private static final int[] R_styleable_LinearLayout = new int[]{ + /* 0 */ android.R.attr.divider, + /* 1 */ android.R.attr.measureWithLargestChild, + /* 2 */ android.R.attr.showDividers, + /* 3 */ android.R.attr.dividerPadding, + }; + private static final int LinearLayout_divider = 0; + + private Drawable mDivider; + protected int mDividerWidth; + protected int mDividerHeight; + + /** + * Set a drawable to be used as a divider between items. + * + * @param divider Drawable that will divide each item. + * @see #setShowDividers(int) + */ + public void setDividerDrawable(Drawable divider) { + if (divider == mDivider) { + return; + } + mDivider = divider; + + if (divider != null) { + mDividerWidth = divider.getIntrinsicWidth(); + mDividerHeight = divider.getIntrinsicHeight(); + } else { + mDividerWidth = 0; + mDividerHeight = 0; + } + setWillNotDraw(divider == null); + requestLayout(); + } + + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { + final int index = indexOfChild(child); + final int orientation = getOrientation(); + final LayoutParams params = (LayoutParams) child.getLayoutParams(); + if (hasDividerBeforeChildAt(index)) { + if (orientation == VERTICAL) { + //Account for the divider by pushing everything up + params.topMargin = mDividerHeight; + } else { + //Account for the divider by pushing everything left + params.leftMargin = mDividerWidth; + } + } + + final int count = getChildCount(); + if (index == count - 1) { + if (hasDividerBeforeChildAt(count)) { + if (orientation == VERTICAL) { + params.bottomMargin = mDividerHeight; + } else { + params.rightMargin = mDividerWidth; + } + } + } + super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mDivider != null) { + if (getOrientation() == VERTICAL) { + drawDividersVertical(canvas); + } else { + drawDividersHorizontal(canvas); + } + } + super.onDraw(canvas); + } + + void drawDividersVertical(Canvas canvas) { + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + + if (child != null && child.getVisibility() != GONE) { + if (hasDividerBeforeChildAt(i)) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/; + drawHorizontalDivider(canvas, top); + } + } + } + + if (hasDividerBeforeChildAt(count)) { + final View child = getChildAt(count - 1); + int bottom = 0; + if (child == null) { + bottom = getHeight() - getPaddingBottom() - mDividerHeight; + } else { + //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + bottom = child.getBottom()/* + lp.bottomMargin*/; + } + drawHorizontalDivider(canvas, bottom); + } + } + + void drawDividersHorizontal(Canvas canvas) { + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + + if (child != null && child.getVisibility() != GONE) { + if (hasDividerBeforeChildAt(i)) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/; + drawVerticalDivider(canvas, left); + } + } + } + + if (hasDividerBeforeChildAt(count)) { + final View child = getChildAt(count - 1); + int right = 0; + if (child == null) { + right = getWidth() - getPaddingRight() - mDividerWidth; + } else { + //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + right = child.getRight()/* + lp.rightMargin*/; + } + drawVerticalDivider(canvas, right); + } + } + + void drawHorizontalDivider(Canvas canvas, int top) { + mDivider.setBounds(getPaddingLeft() + getDividerPadding(), top, + getWidth() - getPaddingRight() - getDividerPadding(), top + mDividerHeight); + mDivider.draw(canvas); + + } + + void drawVerticalDivider(Canvas canvas, int left) { + mDivider.setBounds(left, getPaddingTop() + getDividerPadding(), + left + mDividerWidth, getHeight() - getPaddingBottom() - getDividerPadding()); + mDivider.draw(canvas); + } + + /** + * Determines where to position dividers between children. + * + * @param childIndex Index of child to check for preceding divider + * @return true if there should be a divider before the child at childIndex + * @hide Pending API consideration. Currently only used internally by the system. + */ + protected boolean hasDividerBeforeChildAt(int childIndex) { + if (childIndex == 0) { + return (getShowDividers() & SHOW_DIVIDER_BEGINNING) != 0; + } else if (childIndex == getChildCount()) { + return (getShowDividers() & SHOW_DIVIDER_END) != 0; + } else if ((getShowDividers() & SHOW_DIVIDER_MIDDLE) != 0) { + boolean hasVisibleViewBefore = false; + for (int i = childIndex - 1; i >= 0; i--) { + if (getChildAt(i).getVisibility() != GONE) { + hasVisibleViewBefore = true; + break; + } + } + return hasVisibleViewBefore; + } + return false; + } + + /** + * @return Returns the height of the divider that will be drawn between each item in the list. + */ + public int getDividerHeight() { + return mDividerHeight; + } + + /** + * Sets the height of the divider that will be drawn between each item in the list. Calling + * this will override the intrinsic height as set by {@link #setDividerDrawable(Drawable)} + * + * @param height The new height of the divider in pixels. + */ + public void setDividerHeight(int height) { + if (getOrientation() == VERTICAL) { + mDividerHeight = height; + } else { + mDividerWidth = height; + } + + requestLayout(); + } + + @Override + public void setOrientation(int orientation) { + if (orientation != getOrientation()) { + int tmp = mDividerHeight; + mDividerHeight = mDividerWidth; + mDividerWidth = tmp; + } + super.setOrientation(orientation); + } + + // ------------------------------------------------------------- + // Adapter + // ------------------------------------------------------------- + + /** + * Sets the adapter. + * Sets the data behind this LinearListView. + * + * @param listAdapter The ListAdapter which is responsible for maintaining the data + * backing this list and for producing a view to represent an + * item in that data set. + */ + public void setAdapter(CardWithList.LinearListAdapter listAdapter) { + this.mListAdapter = listAdapter; + setOrientation(VERTICAL); + + //Populate the list + if (mListAdapter != null) { + for (int i = 0; i < mListAdapter.getCount(); i++) { + View itemView = mListAdapter.getView(i, null, null); + if (itemView != null) + this.addView(itemView); + } + } + } + + /** + * Returns the adapter + * + * @return + */ + public CardWithList.LinearListAdapter getAdapter() { + return mListAdapter; + } + + + /** + * Get the position within the adapter's data set for the view, where view is a an adapter item + * or a descendant of an adapter item. + * + * @param view an adapter item, or a descendant of an adapter item. This must be visible in this + * AdapterView at the time of the call. + * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION} + * if the view does not correspond to a list item (or it is not currently visible). + */ + public int getPositionForView(View view) { + View listItem = view; + try { + View v; + while (!(v = (View) listItem.getParent()).equals(this)) { + listItem = v; + } + } catch (ClassCastException e) { + // We made it up to the window without find this list itemView + return INVALID_POSITION; + } + + // Search the children for the list item + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + if (getChildAt(i).equals(listItem)) { + return i; + } + } + + // Child not found! + return INVALID_POSITION; + } +}
\ No newline at end of file diff --git a/src/it/gmariotti/cardslib/library/prototypes/SectionedCardAdapter.java b/src/it/gmariotti/cardslib/library/prototypes/SectionedCardAdapter.java new file mode 100644 index 0000000..0bf5df5 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/prototypes/SectionedCardAdapter.java @@ -0,0 +1,346 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.prototypes; + +import android.content.Context; +import android.database.DataSetObserver; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.Arrays; +import java.util.Comparator; + +import com.android.cards.R; +import com.android.cards.internal.CardArrayAdapter; + +/** + * An adapter to build a CardList with sections. + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class SectionedCardAdapter extends BaseAdapter { + + private boolean mValid = true; + + /** + * Default layout used for sections + */ + private int mSectionResourceId = R.layout.base_section_layout; + + /** + * Inflater + */ + private LayoutInflater mLayoutInflater; + + /** + * Adapter with cards. + */ + private BaseAdapter mBaseAdapter; + + /** + * Array with Card Sections + */ + private SparseArray<CardSection> mCardSections = new SparseArray<CardSection>(); + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + /** + * Constructor + * + * @param context + * @param cardArrayAdapter + */ + public SectionedCardAdapter(Context context, CardArrayAdapter cardArrayAdapter) { + this(context,R.layout.base_section_layout,cardArrayAdapter); + } + + /** + * + * @param context context + * @param sectionResourceId layout used by sections + * @param cardArrayAdapter cardArrayAdapter + */ + public SectionedCardAdapter(Context context, int sectionResourceId, CardArrayAdapter cardArrayAdapter) { + mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mBaseAdapter = cardArrayAdapter; + mSectionResourceId = sectionResourceId; + + mBaseAdapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + mValid = !mBaseAdapter.isEmpty(); + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + mValid = false; + notifyDataSetInvalidated(); + } + }); + } + + // ------------------------------------------------------------- + // Section + // ------------------------------------------------------------- + + /** + * Sets the Card sections + * + * @param cardSections + */ + public void setCardSections(CardSection[] cardSections) { + mCardSections.clear(); + + Arrays.sort(cardSections, new Comparator<CardSection>() { + @Override + public int compare(CardSection o, CardSection o1) { + return (o.firstPosition == o1.firstPosition) + ? 0 + : ((o.firstPosition < o1.firstPosition) ? -1 : 1); + } + }); + + int offset = 0; // offset positions for the headers we're adding + for (CardSection cardSection : cardSections) { + cardSection.sectionedPosition = cardSection.firstPosition + offset; + mCardSections.append(cardSection.sectionedPosition, cardSection); + ++offset; + } + + notifyDataSetChanged(); + } + + // ------------------------------------------------------------- + // Adapter's methods + // ------------------------------------------------------------- + + /** + * Returns the sectioned position from the position + * + * @param position + * @return + */ + public int positionToSectionedPosition(int position) { + int offset = 0; + for (int i = 0; i < mCardSections.size(); i++) { + if (mCardSections.valueAt(i).firstPosition > position) { + break; + } + ++offset; + } + return position + offset; + } + + /** + * Returns the position from the sectioned position + * @param sectionedPosition + * @return + */ + public int sectionedPositionToPosition(int sectionedPosition) { + if (isSectionHeaderPosition(sectionedPosition)) { + return ListView.INVALID_POSITION; + } + + int offset = 0; + for (int i = 0; i < mCardSections.size(); i++) { + if (mCardSections.valueAt(i).sectionedPosition > sectionedPosition) { + break; + } + --offset; + } + return sectionedPosition + offset; + } + + /** + * Returns true if the position is a Section + * + * @param position + * @return + */ + public boolean isSectionHeaderPosition(int position) { + return mCardSections.get(position) != null; + } + + @Override + public int getCount() { + return (mValid ? mBaseAdapter.getCount() + mCardSections.size() : 0); + } + + @Override + public Object getItem(int position) { + return isSectionHeaderPosition(position) + ? mCardSections.get(position) + : mBaseAdapter.getItem(sectionedPositionToPosition(position)); + } + + @Override + public long getItemId(int position) { + return isSectionHeaderPosition(position) + ? Integer.MAX_VALUE - mCardSections.indexOfKey(position) + : mBaseAdapter.getItemId(sectionedPositionToPosition(position)); + } + + @Override + public int getItemViewType(int position) { + return isSectionHeaderPosition(position) + ? getViewTypeCount() - 1 + : mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)); + } + + @Override + public boolean isEnabled(int position) { + //noinspection SimplifiableConditionalExpression + return isSectionHeaderPosition(position) + ? false + : mBaseAdapter.isEnabled(sectionedPositionToPosition(position)); + } + + @Override + public int getViewTypeCount() { + return mBaseAdapter.getViewTypeCount() + 1; // the section headings + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean hasStableIds() { + return mBaseAdapter.hasStableIds(); + } + + @Override + public boolean isEmpty() { + return mBaseAdapter.isEmpty(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (isSectionHeaderPosition(position)) { + return internalSectionView(position, convertView, parent); + } else { + return mBaseAdapter.getView(sectionedPositionToPosition(position), convertView, parent); + } + } + + /** + * Returns the view used by the section at position + * + * @param position section's position + * @param convertView + * @param parent + * @return + */ + protected View internalSectionView(int position, View convertView, ViewGroup parent) { + View view = convertView; + if (view == null) { + view = mLayoutInflater.inflate(mSectionResourceId, parent, false); + } + getSectionView(position,view,parent); + return view; + } + + /** + * Returns the view used by the section at position. + * Override this method to set your ui elements. + * + * @param position section's position + * @param view + * @param parent + * @return + */ + protected View getSectionView(int position, View view, ViewGroup parent) { + + TextView textView = (TextView) view.findViewById(R.id.card_section_simple_title); + if (textView != null) + textView.setText(mCardSections.get(position).title); + + return view; + } + + /** + * Use this method to add a single {@link CardSection}.</p> + * If you want to add more CardSections use the method {@link #addCardSections(CardSection[])} + * + * @param cardSection to be added + */ + public void addCardSection(CardSection cardSection) { + + if (cardSection != null) { + int oldSize = mCardSections.size(); + CardSection[] newCardSections = new CardSection[oldSize + 1]; + + //Get current sections + for (int i = 0; i < mCardSections.size(); i++) { + newCardSections[i] = mCardSections.valueAt(i); + } + //Add new section + newCardSections[oldSize] = cardSection; + setCardSections(newCardSections); + } + } + + /** + * Adds card Sections + * + * @param cardSections card sections to be added + */ + public void addCardSections(CardSection[] cardSections) { + + if (cardSections != null && cardSections.length>0) { + + int oldSize = mCardSections.size(); + + //Get current sections + CardSection[] newCardSections = new CardSection[oldSize + cardSections.length]; + for (int i = 0; i < mCardSections.size(); i++) { + newCardSections[i] = mCardSections.valueAt(i); + } + + //Add new sections + for (int i = 0; i < cardSections.length; i++){ + newCardSections[i + oldSize] = cardSections[i]; + } + + setCardSections(newCardSections); + } + } + + // ------------------------------------------------------------- + // Getters and setters + // ------------------------------------------------------------- + + /** + * Returns the sections + * @return + */ + public SparseArray<CardSection> getCardSections() { + return mCardSections; + } + +} diff --git a/src/it/gmariotti/cardslib/library/prototypes/SwipeDismissListItemViewTouchListener.java b/src/it/gmariotti/cardslib/library/prototypes/SwipeDismissListItemViewTouchListener.java new file mode 100644 index 0000000..1c28f1a --- /dev/null +++ b/src/it/gmariotti/cardslib/library/prototypes/SwipeDismissListItemViewTouchListener.java @@ -0,0 +1,289 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.prototypes; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.graphics.Rect; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.ListView; + +import com.android.cards.R; +import com.android.cards.internal.Card; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class SwipeDismissListItemViewTouchListener implements View.OnTouchListener { + + + /** + * The callback interface used by {@link SwipeDismissListItemViewTouchListener} to inform its client + * about a successful dismissal of one or more list item positions. + */ + public interface DismissCallbacks { + + /** + * Called to determine whether the given position can be dismissed. + */ + boolean canDismiss(int position, Card card, CardWithList.ListObject listObject); + + /** + * Called when the user has indicated they she would like to dismiss one or more list item + * positions. + * + * @param listView The originating {@link ListView}. + * @param position position to dismiss + */ + void onDismiss(LinearListView listView, int position,boolean dismissRight); + } + + + private CardWithList.ListObject mListObject; + private LinearListView mListView; + + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + + // Fixed properties + private View itemView; + private DismissCallbacks mCallbacks; + private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero + + // Transient properties + private float mDownX; + private float mDownY; + //private Card mToken; + private boolean mSwiping; + private VelocityTracker mVelocityTracker; + private boolean mPaused; + private float mTranslationX; + private int mSwipingSlop; + private int mDownPosition; + private View mDownView; + + private int swipeDistanceDivisor = 2; + + public SwipeDismissListItemViewTouchListener(LinearListView listView, DismissCallbacks callbacks) { + ViewConfiguration vc = ViewConfiguration.get(listView.getContext()); + mSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = listView.getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + + this.mListView = listView; + mCallbacks = callbacks; + swipeDistanceDivisor = listView.getContext().getResources().getInteger(R.integer.list_card_swipe_distance_divisor); + } + + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + + if (mViewWidth < 2) { + mViewWidth = mListView.getWidth(); + } + + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + + if (mPaused) { + return false; + } + + if (mSwiping) { + return true; + } + + // Find the child view that was touched (perform a hit test) + Rect rect = new Rect(); + int childCount = mListView.getChildCount(); + int headerCount = 0; + int footerCount = 0; + int[] listViewCoords = new int[2]; + mListView.getLocationOnScreen(listViewCoords); + int x = (int) motionEvent.getRawX() - listViewCoords[0]; + int y = (int) motionEvent.getRawY() - listViewCoords[1]; + View child=null; + for (int i = headerCount; i < (childCount - footerCount); i++) { + child = mListView.getChildAt(i); + child.getHitRect(rect); + if (rect.contains(x, y)) { + mDownView = child; + break; + } + } + + if (mDownView != null) { + mDownX = motionEvent.getRawX(); + mDownY = motionEvent.getRawY(); + mDownPosition = mListView.getPositionForView(mDownView); + CardWithList.ListObject object = mListView.getAdapter().getItem(mDownPosition); + if (mCallbacks.canDismiss(mDownPosition, object.getParentCard(), object)) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(motionEvent); + } else { + mDownView = null; + } + } + view.onTouchEvent(motionEvent); + return false; + + } + case MotionEvent.ACTION_MOVE: { + if (mVelocityTracker == null || mPaused) { + break; + } + + mVelocityTracker.addMovement(motionEvent); + float deltaX = motionEvent.getRawX() - mDownX; + float deltaY = motionEvent.getRawY() - mDownY; + if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { + mSwiping = true; + mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); + mDownView.getParent().requestDisallowInterceptTouchEvent(true); + mListView.requestDisallowInterceptTouchEvent(true); + + // Cancel ListView's touch (un-highlighting the item) + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent + .setAction(MotionEvent.ACTION_CANCEL + | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); + mDownView.onTouchEvent(cancelEvent); + cancelEvent.recycle(); + } + + if (mSwiping) { + mDownView.setTranslationX(deltaX - mSwipingSlop); + + /* mDownView.setAlpha(Math.max(0f, Math.min(1f, + 1f - 2f * Math.abs(deltaX) / mViewWidth))); + */ + return true; + } + break; + } + + case MotionEvent.ACTION_UP: { + if (mVelocityTracker == null) { + break; + } + + float deltaX = motionEvent.getRawX() - mDownX; + mVelocityTracker.addMovement(motionEvent); + mVelocityTracker.computeCurrentVelocity(1000); + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + boolean dismiss = false; + boolean dismissRight = false; + if (Math.abs(deltaX) > mViewWidth / swipeDistanceDivisor) { + dismiss = true; + dismissRight = deltaX > 0; + } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX && mSwiping) { + // dismiss only if flinging in the same direction as dragging + dismiss = (velocityX < 0) == (deltaX < 0); + dismissRight = mVelocityTracker.getXVelocity() > 0; + } + if (dismiss && mDownPosition != ListView.INVALID_POSITION) { + + // dismiss + dismiss(mDownView, mDownPosition, dismissRight); + + } else { + // cancel + mDownView.animate().translationX(0).alpha(1) + .setDuration(mAnimationTime).setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mDownX = 0; + mDownY = 0; + mDownView = null; + mDownPosition = ListView.INVALID_POSITION; + if (mSwiping){ + //To prevent onClick event with a fast swipe + mSwiping = false; + return true; + } + mSwiping = false; + break; + } + + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + if (mDownView != null) { + // cancel + mDownView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mDownX = 0; + mDownY = 0; + mDownView = null; + mDownPosition = ListView.INVALID_POSITION; + mSwiping = false; + break; + } + + } + + return false; + } + + private void dismiss(final View view, final int position, final boolean dismissRight) { + if (view == null) { + // No view, shortcut to calling onDismiss to let it deal with adapter + // updates and all that. + mCallbacks.onDismiss(mListView, position, dismissRight ); + return; + } + + view.animate() + .translationX(dismissRight ? mViewWidth : -mViewWidth) + .alpha(0) + .setDuration(mAnimationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + finalizeDismiss(view, position,dismissRight); + } + }); + } + + private void finalizeDismiss(final View dismissView, final int dismissPosition,final boolean dismissRight ) { + mCallbacks.onDismiss(mListView, dismissPosition,dismissRight); + } + +} diff --git a/src/com/android/cards/utils/BitmapUtils.java b/src/it/gmariotti/cardslib/library/utils/BitmapUtils.java index 0e1b0df..0e1b0df 100644 --- a/src/com/android/cards/utils/BitmapUtils.java +++ b/src/it/gmariotti/cardslib/library/utils/BitmapUtils.java diff --git a/src/com/android/cards/utils/CacheUtil.java b/src/it/gmariotti/cardslib/library/utils/CacheUtil.java index 27e1044..27e1044 100644 --- a/src/com/android/cards/utils/CacheUtil.java +++ b/src/it/gmariotti/cardslib/library/utils/CacheUtil.java diff --git a/src/com/android/cards/view/BaseCardView.java b/src/it/gmariotti/cardslib/library/view/BaseCardView.java index ec601e3..af1e66c 100644 --- a/src/com/android/cards/view/BaseCardView.java +++ b/src/it/gmariotti/cardslib/library/view/BaseCardView.java @@ -32,6 +32,8 @@ import com.android.cards.view.base.CardViewInterface; import com.android.cards.view.component.CardHeaderView; import com.android.cards.view.component.CardShadowView; import com.android.cards.view.component.CardThumbnailView; +import com.android.cards.view.helper.CardViewHelper; +import com.android.cards.view.helper.CardViewHelperUtil; /** * BaseView for Card @@ -87,23 +89,25 @@ public class BaseCardView extends LinearLayout implements CardViewInterface { */ protected boolean mForceReplaceInnerLayout =false; + protected CardViewHelper mHelperImpl; + //-------------------------------------------------------------------------- // Constructor //-------------------------------------------------------------------------- public BaseCardView(Context context) { - super(context); - init(null, 0); + this(context, null, 0); } public BaseCardView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs, 0); + this(context, attrs, 0); } public BaseCardView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); + + mHelperImpl = CardViewHelperUtil.getInstance(context); } //-------------------------------------------------------------------------- diff --git a/src/it/gmariotti/cardslib/library/view/CardExpandableListView.java b/src/it/gmariotti/cardslib/library/view/CardExpandableListView.java new file mode 100644 index 0000000..e33aab2 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/CardExpandableListView.java @@ -0,0 +1,149 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.ExpandableListView; +import android.widget.ListAdapter; + +import com.android.cards.R; +import com.android.cards.internal.CardExpandableListAdapter; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardExpandableListView extends ExpandableListView{ + + protected static String TAG = "CardExpandableListView"; + + /** + * CardExpandableListAdapter + */ + protected CardExpandableListAdapter mAdapter; + + //-------------------------------------------------------------------------- + // Custom Attrs + //-------------------------------------------------------------------------- + + /** + * Default layout to apply to card + */ + protected int list_card_layout_resourceID = R.layout.list_card_layout; + + //-------------------------------------------------------------------------- + // Constructors + //-------------------------------------------------------------------------- + + public CardExpandableListView(Context context) { + super(context); + init(null, 0); + } + + public CardExpandableListView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public CardExpandableListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + //-------------------------------------------------------------------------- + // Init + //-------------------------------------------------------------------------- + + /** + * Initialize + * + * @param attrs + * @param defStyle + */ + protected void init(AttributeSet attrs, int defStyle){ + + //Init attrs + initAttrs(attrs,defStyle); + + //Set divider to 0dp + setDividerHeight(0); + + } + + + /** + * Init custom attrs. + * + * @param attrs + * @param defStyle + */ + protected void initAttrs(AttributeSet attrs, int defStyle) { + + list_card_layout_resourceID = R.layout.list_card_layout; + + TypedArray a = getContext().getTheme().obtainStyledAttributes( + attrs, R.styleable.card_options, defStyle, defStyle); + + try { + list_card_layout_resourceID = a.getResourceId(R.styleable.card_options_list_card_layout_resourceID, this.list_card_layout_resourceID); + } finally { + a.recycle(); + } + } + + //-------------------------------------------------------------------------- + // Adapter + //-------------------------------------------------------------------------- + + /** + * Set the adapter. You can provide a {@link com.android.cards.internal.CardArrayAdapter}, or a {@link com.android.cards.internal.CardCursorAdapter} + * or a generic adapter. + * Pay attention: your generic adapter has to call {@link com.android.cards.internal.CardArrayAdapter#getView} method + * + * @param adapter + */ + @Override + public void setAdapter(ListAdapter adapter) { + if (adapter instanceof CardExpandableListAdapter){ + setAdapter(adapter); + }else { + Log.w(TAG, "You are using a generic adapter. Pay attention: your adapter has to call cardArrayAdapter#getView method"); + super.setAdapter(adapter); + } + } + + /** + * Set {@link CardExpandableListAdapter} and layout used by items in ListView + * + * @param adapter {@link CardExpandableListAdapter} + */ + public void setAdapter(CardExpandableListAdapter adapter) { + super.setAdapter(adapter); + + //Set Layout used by items + adapter.setGroupLayoutId(list_card_layout_resourceID); + + adapter.setCardListView(this); + mAdapter=adapter; + } + + +} diff --git a/src/com/android/cards/view/CardGridView.java b/src/it/gmariotti/cardslib/library/view/CardGridView.java index 3b44261..9b49fb7 100644 --- a/src/com/android/cards/view/CardGridView.java +++ b/src/it/gmariotti/cardslib/library/view/CardGridView.java @@ -29,6 +29,7 @@ import android.widget.ListAdapter; import com.android.cards.R; import com.android.cards.internal.CardGridArrayAdapter; import com.android.cards.internal.CardGridCursorAdapter; +import com.android.cards.view.base.CardViewWrapper; /** * Card Grid View. @@ -61,7 +62,7 @@ import com.android.cards.internal.CardGridCursorAdapter; * </p> * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ -public class CardGridView extends GridView implements CardView.OnExpandListAnimatorListener { +public class CardGridView extends GridView implements CardViewWrapper.OnExpandListAnimatorListener { protected static String TAG = "CardGridView"; @@ -232,13 +233,13 @@ public class CardGridView extends GridView implements CardView.OnExpandListAnima //-------------------------------------------------------------------------- @Override - public void onExpandStart(CardView viewCard,View expandingLayout) { + public void onExpandStart(CardViewWrapper viewCard,View expandingLayout) { //do nothing. Don't use this kind of animation in a grid Log.w(TAG,"Don't use this kind of animation in a grid"); } @Override - public void onCollapseStart(CardView viewCard,View expandingLayout) { + public void onCollapseStart(CardViewWrapper viewCard,View expandingLayout) { //do nothing. Don't use this kind of animation in a grid Log.w(TAG,"Don't use this kind of animation in a grid"); } diff --git a/src/com/android/cards/view/CardListView.java b/src/it/gmariotti/cardslib/library/view/CardListView.java index e61ffcc..784b5cf 100644 --- a/src/com/android/cards/view/CardListView.java +++ b/src/it/gmariotti/cardslib/library/view/CardListView.java @@ -25,7 +25,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Log; -import android.view.ScaleGestureDetector; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; @@ -36,6 +35,7 @@ import com.android.cards.R; import com.android.cards.internal.Card; import com.android.cards.internal.CardArrayAdapter; import com.android.cards.internal.CardCursorAdapter; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.listener.SwipeOnScrollListener; /** @@ -63,7 +63,7 @@ import com.android.cards.view.listener.SwipeOnScrollListener; * </p> * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ -public class CardListView extends ListView implements CardView.OnExpandListAnimatorListener { +public class CardListView extends ListView implements CardViewWrapper.OnExpandListAnimatorListener { protected static String TAG = "CardListView"; @@ -82,11 +82,6 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima */ protected SwipeOnScrollListener mOnScrollListener; - /** - * Custom gesture listener to be used with a CardListView and cards with swipe action - */ - protected ScaleGestureDetector mGestureDetector; - //-------------------------------------------------------------------------- // Custom Attrs @@ -260,26 +255,12 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima this.mOnScrollListener = (SwipeOnScrollListener)mOnScrollListener; } - /** - * Set external custom gesture detector - */ - public void setGestureDetector(ScaleGestureDetector gestureDetector) { - this.mGestureDetector = gestureDetector; - } - - /** - * Get external custom gesture detector - */ - public ScaleGestureDetector getGestureDetector() { - return this.mGestureDetector; - } - //-------------------------------------------------------------------------- // Expand and Collapse animator //-------------------------------------------------------------------------- @Override - public void onExpandStart(CardView viewCard,View expandingLayout) { + public void onExpandStart(CardViewWrapper viewCard,View expandingLayout) { boolean expandable = true; if (mCursorAdapter!=null){ @@ -296,7 +277,7 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima } @Override - public void onCollapseStart(CardView viewCard,View expandingLayout) { + public void onCollapseStart(CardViewWrapper viewCard,View expandingLayout) { boolean collapsible = true; if (mCursorAdapter!=null){ collapsible = mCursorAdapter.onCollapseStart(viewCard); @@ -322,8 +303,7 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima * @param cardView cardView * @param listView listView */ - public static void animateCollapsing(final View expandingLayout, - final CardView cardView,final AbsListView listView) { + public static void animateCollapsing(final View expandingLayout, final CardViewWrapper cardView,final AbsListView listView) { int origHeight = expandingLayout.getHeight(); ValueAnimator animator = createHeightAnimator(expandingLayout, origHeight, 0); @@ -352,19 +332,16 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima * @param cardView cardView * @param listView listView */ - public static void animateExpanding(final View expandingLayout, - final CardView cardView,final AbsListView listView) { + public static void animateExpanding(final View expandingLayout, final CardViewWrapper cardView,final AbsListView listView) { /* Update the layout so the extra content becomes visible.*/ expandingLayout.setVisibility(View.VISIBLE); View parent = (View) expandingLayout.getParent(); - final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth() - - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); + final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); expandingLayout.measure(widthSpec, heightSpec); - ValueAnimator animator = - createHeightAnimator(expandingLayout, 0, expandingLayout.getMeasuredHeight()); + ValueAnimator animator = createHeightAnimator(expandingLayout, 0, expandingLayout.getMeasuredHeight()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { final int listViewHeight = listView.getHeight(); final int listViewBottomPadding = listView.getPaddingBottom(); @@ -376,8 +353,7 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima if (bottom > listViewHeight) { final int top = v.getTop(); if (top > 0) { - listView.smoothScrollBy(Math.min( - bottom - listViewHeight + listViewBottomPadding, top), 0); + listView.smoothScrollBy(Math.min(bottom - listViewHeight + listViewBottomPadding, top), 0); } } } @@ -409,8 +385,7 @@ public class CardListView extends ListView implements CardView.OnExpandListAnima return result; } - public static ValueAnimator createHeightAnimator( - final View view, final int start, final int end) { + public static ValueAnimator createHeightAnimator(final View view, final int start, final int end) { ValueAnimator animator = ValueAnimator.ofInt(start, end); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { diff --git a/src/com/android/cards/view/CardView.java b/src/it/gmariotti/cardslib/library/view/CardView.java index a6f1353..6d748e9 100644 --- a/src/com/android/cards/view/CardView.java +++ b/src/it/gmariotti/cardslib/library/view/CardView.java @@ -22,6 +22,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -29,6 +30,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -41,6 +43,7 @@ import com.android.cards.internal.CardExpand; import com.android.cards.internal.CardHeader; import com.android.cards.internal.CardThumbnail; import com.android.cards.internal.ViewToClickToExpand; +import com.android.cards.view.base.CardViewWrapper; import com.android.cards.view.component.CardHeaderView; import com.android.cards.view.component.CardThumbnailView; import com.android.cards.view.listener.SwipeDismissViewTouchListener; @@ -98,7 +101,7 @@ import com.android.cards.view.listener.SwipeDismissViewTouchListener; * </p> * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ -public class CardView extends BaseCardView { +public class CardView extends BaseCardView implements CardViewWrapper { //-------------------------------------------------------------------------- // @@ -201,6 +204,15 @@ public class CardView extends BaseCardView { } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); + if (mInternalMainCardLayout != null && mInternalMainCardLayout instanceof ForegroundLinearLayout) { + mInternalMainCardLayout.drawableHotspotChanged(x,y); + } + } + //-------------------------------------------------------------------------- // Card //-------------------------------------------------------------------------- @@ -275,6 +287,9 @@ public class CardView extends BaseCardView { //Setup Expand View setupExpandView(); + //Setup Supplemental Actions + setupSupplementalActions(); + //Setup Listeners setupListeners(); @@ -403,6 +418,11 @@ public class CardView extends BaseCardView { } } + protected void setupSupplementalActions() { + if (mCard != null) + mCard.setupSupplementalActions(); + } + //-------------------------------------------------------------------------- // Listeners //-------------------------------------------------------------------------- @@ -410,8 +430,8 @@ public class CardView extends BaseCardView { protected void setupExpandAction(){ //Config ExpandLayout and its animation - if ( (mInternalExpandLayout !=null && mCardHeader!=null && mCardHeader.isButtonExpandVisible() ) || - mCard.getViewToClickToExpand()!=null ){ + if ( mInternalExpandLayout !=null && ( (mCardHeader!=null && mCardHeader.isButtonExpandVisible()) || + mCard.getViewToClickToExpand()!=null) ){ //Create the expand/collapse animator mInternalExpandLayout.getViewTreeObserver().addOnPreDrawListener( @@ -420,22 +440,13 @@ public class CardView extends BaseCardView { @Override public boolean onPreDraw() { mInternalExpandLayout.getViewTreeObserver().removeOnPreDrawListener(this); - //mInternalExpandLayout.setVisibility(View.GONE); View parent = (View) mInternalExpandLayout.getParent(); - final int widthSpec = View.MeasureSpec.makeMeasureSpec( - parent.getMeasuredWidth() - parent.getPaddingLeft() - - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); - final int heightSpec = View.MeasureSpec.makeMeasureSpec( - LayoutParams.WRAP_CONTENT, View.MeasureSpec.AT_MOST); - + final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); + final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); mInternalExpandLayout.measure(widthSpec, heightSpec); - final int widthSpecCard = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - final int heightSpecCard = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - mCollapsedHeight = getMeasuredHeight(); - - mExpandAnimator = createSlideAnimator(0, mInternalExpandLayout.getMeasuredHeight()); + mExpandAnimator = ExpandCollapseHelper.createSlideAnimator((CardView)mCard.getCardView(), 0, mInternalExpandLayout.getMeasuredHeight()); return true; } }); @@ -452,21 +463,43 @@ public class CardView extends BaseCardView { @SuppressLint("NewApi") protected void setupListeners(){ - // Swipe listener - addGlobalSwipeListener(this); + //Swipe listener + if (mCard.isSwipeable()){ + this.setOnTouchListener(new SwipeDismissViewTouchListener(this, mCard,new SwipeDismissViewTouchListener.DismissCallbacks() { + @Override + public boolean canDismiss(Card card) { + return card.isSwipeable(); + } - // OnClick listeners and partial listener + @Override + public void onDismiss(CardViewWrapper cardView, Card card) { + final ViewGroup vg = (ViewGroup)(((View)cardView).getParent()); + if (vg!=null){ + vg.removeView((View)cardView); + card.onSwipeCard(); + } + } + })); + }else{ + this.setOnTouchListener(null); + } - // Reset Partial Listeners - resetPartialListeners(); + //OnClick listeners and partial listener - boolean hasPartialClickListener = false; + //Reset Partial Listeners + resetPartialListeners(); if (mCard.isClickable()){ //Set the onClickListener if(!mCard.isMultiChoiceEnabled()){ if (mCard.getOnClickListener() != null) { - addGlobalClickListener(this); + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mCard.getOnClickListener()!=null) + mCard.getOnClickListener().onClick(mCard,v); + } + }); //Prevent multiple events //if (!mCard.isSwipeable() && mCard.getOnSwipeListener() == null) { @@ -476,7 +509,7 @@ public class CardView extends BaseCardView { }else{ HashMap<Integer,Card.OnCardClickListener> mMultipleOnClickListner=mCard.getMultipleOnClickListener(); if (mMultipleOnClickListner!=null && !mMultipleOnClickListner.isEmpty()){ - hasPartialClickListener = true; + for (int key:mMultipleOnClickListner.keySet()){ View viewClickable= decodeAreaOnClickListener(key); final Card.OnCardClickListener mListener=mMultipleOnClickListner.get(key); @@ -493,11 +526,8 @@ public class CardView extends BaseCardView { //Add Selector to this view if (key > Card.CLICK_LISTENER_ALL_VIEW) { - if (Build.VERSION.SDK_INT >= 16){ - viewClickable.setBackground(getResources().getDrawable(R.drawable.card_selector)); - } else { - viewClickable.setBackgroundDrawable(getResources().getDrawable(R.drawable.card_selector)); - } + mHelperImpl.setCardSelector(viewClickable, getResources().getDrawable(R.drawable.card_selector)); + } } } @@ -511,87 +541,18 @@ public class CardView extends BaseCardView { this.setClickable(false); } - // LongClick and partial listeners. - if (mCard.isLongClickable()) { - if (mCard.getOnLongClickListener() != null) { - this.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mCard.getOnLongClickListener()!=null) - return mCard.getOnLongClickListener().onLongClick(mCard, v); - return false; - } - }); - } - HashMap<Integer, Card.OnLongCardClickListener> multipleOnLongClickListner = - mCard.getMultipleOnLongClickListener(); - if (multipleOnLongClickListner != null && !multipleOnLongClickListner.isEmpty()) { - - for (int key : multipleOnLongClickListner.keySet()) { - View viewLongClickable = decodeAreaOnClickListener(key); - final Card.OnLongCardClickListener listener = - multipleOnLongClickListner.get(key); - if (viewLongClickable != null) { - //Add listener to this view - viewLongClickable.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - //Callback to card listener - if (listener != null) { - return listener.onLongClick(mCard, v); - } - return false; - } - }); - // We may have only partial longclicklistener. So add for each view - // in this case the global click listener. - // As well add for this view if available the swipe listener. - addGlobalSwipeListener(viewLongClickable); - if (!hasPartialClickListener) { - addGlobalClickListener(viewLongClickable); - } - } - } - } - } else { - this.setLongClickable(false); - } - } - - private void addGlobalClickListener(View view) { - if (mCard.isClickable() && !mCard.isMultiChoiceEnabled() - && mCard.getOnClickListener() != null) { - view.setOnClickListener(new OnClickListener() { + //LongClick listener + if(mCard.isLongClickable()){ + this.setOnLongClickListener(new OnLongClickListener() { @Override - public void onClick(View v) { - if (mCard.getOnClickListener() != null) { - mCard.getOnClickListener().onClick(mCard, v); - } + public boolean onLongClick(View v) { + if (mCard.getOnLongClickListener()!=null) + return mCard.getOnLongClickListener().onLongClick(mCard,v); + return false; } }); - } - } - - private void addGlobalSwipeListener(View view) { - if (mCard.isSwipeable()) { - view.setOnTouchListener(new SwipeDismissViewTouchListener(this, - mCard, new SwipeDismissViewTouchListener.DismissCallbacks() { - @Override - public boolean canDismiss(Card card) { - return card.isSwipeable(); - } - - @Override - public void onDismiss(CardView cardView, Card card) { - final ViewGroup vg = (ViewGroup)(cardView.getParent()); - if (vg!=null){ - vg.removeView(cardView); - card.onSwipeCard(); - } - } - })); - } else { - view.setOnTouchListener(null); + }else{ + this.setLongClickable(false); } } @@ -599,26 +560,17 @@ public class CardView extends BaseCardView { * Reset all partial listeners */ protected void resetPartialListeners() { - View viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW); - if (viewClickable != null) { - viewClickable.setClickable(false); - viewClickable.setLongClickable(false); - } - viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW); - if (viewClickable != null) { + View viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW); + if (viewClickable!=null) viewClickable.setClickable(false); - viewClickable.setLongClickable(false); - } - viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW); - if (viewClickable != null) { + + viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW); + if (viewClickable!=null) viewClickable.setClickable(false); - viewClickable.setLongClickable(false); - } - viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_EXPAND_VIEW); - if (viewClickable != null) { + + viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW); + if (viewClickable!=null) viewClickable.setClickable(false); - viewClickable.setLongClickable(false); - } } /** @@ -626,29 +578,25 @@ public class CardView extends BaseCardView { * @param area * @return */ - public View decodeAreaOnClickListener(int area){ + protected View decodeAreaOnClickListener(int area){ - if (area < Card.CLICK_LISTENER_ALL_VIEW && area > Card.CLICK_LISTENER_CONTENT_VIEW) { + if (area<Card.CLICK_LISTENER_ALL_VIEW && area>Card.CLICK_LISTENER_CONTENT_VIEW) return null; - } View view = null; switch (area){ - case Card.CLICK_LISTENER_ALL_VIEW: - view = this; + case Card.CLICK_LISTENER_ALL_VIEW : + view=this; break; - case Card.CLICK_LISTENER_HEADER_VIEW: - view = mInternalHeaderLayout; + case Card.CLICK_LISTENER_HEADER_VIEW : + view=mInternalHeaderLayout; break; case Card.CLICK_LISTENER_THUMBNAIL_VIEW: - view = mInternalThumbnailLayout; + view=mInternalThumbnailLayout; break; case Card.CLICK_LISTENER_CONTENT_VIEW: - view = mInternalContentLayout; - break; - case Card.CLICK_LISTENER_EXPAND_VIEW: - view = mInternalExpandLayout; + view=mInternalContentLayout; break; default: break; @@ -660,9 +608,6 @@ public class CardView extends BaseCardView { // Expandable Actions and Listeners //-------------------------------------------------------------------------- - protected int mCollapsedHeight; - protected int mExpandedHeight=-1; - /** * Add ClickListener to expand and collapse hidden view */ @@ -670,14 +615,16 @@ public class CardView extends BaseCardView { if (mInternalExpandLayout != null) { mInternalExpandLayout.setVisibility(View.GONE); - + boolean internal_blockForLongClickOnImageButtonExpand = false; ViewToClickToExpand viewToClickToExpand = null; + //ButtonExpandVisible has a priority to viewClickToExpand if (mCardHeader != null && mCardHeader.isButtonExpandVisible()) { viewToClickToExpand = ViewToClickToExpand.builder() .setupView(mInternalHeaderLayout.getImageButtonExpand()) .highlightView(true); + internal_blockForLongClickOnImageButtonExpand = true; } else if (mCard.getViewToClickToExpand() != null) { @@ -688,13 +635,23 @@ public class CardView extends BaseCardView { TitleViewOnClickListener titleViewOnClickListener = new TitleViewOnClickListener(mInternalExpandLayout, mCard, viewToClickToExpand.isViewToSelect()); - if (mCardHeader!=null && mCardHeader.isButtonExpandVisible() && mInternalHeaderLayout != null) { + /*if (mCardHeader!=null && mCardHeader.isButtonExpandVisible() && mInternalHeaderLayout != null) { mInternalHeaderLayout.setOnClickExpandCollapseActionListener(titleViewOnClickListener); - } + }*/ View viewToClick = viewToClickToExpand.getViewToClick(); if (viewToClick != null) { - viewToClick.setOnClickListener(titleViewOnClickListener); + + if (internal_blockForLongClickOnImageButtonExpand) { + //The long click on Header button is now allowed + viewToClick.setOnClickListener(titleViewOnClickListener); + }else{ + if (viewToClickToExpand.isUseLongClick()){ + viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener)); + }else{ + viewToClick.setOnClickListener(titleViewOnClickListener); + } + } }else{ ViewToClickToExpand.CardElementUI cardElementUI=viewToClickToExpand.getCardElementUIToClick(); if (cardElementUI!=null){ @@ -713,7 +670,11 @@ public class CardView extends BaseCardView { break; } if (viewToClick != null) { - viewToClick.setOnClickListener(titleViewOnClickListener); + if (viewToClickToExpand.isUseLongClick()){ + viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener)); + }else{ + viewToClick.setOnClickListener(titleViewOnClickListener); + } } } } @@ -765,6 +726,43 @@ public class CardView extends BaseCardView { } } + public void doToggleExpand() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (isVisible) { + ExpandCollapseHelper.animateCollapsing(helper); + } else { + ExpandCollapseHelper.animateExpanding(helper); + } + } + } + + public void doExpand() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (!isVisible) { + ExpandCollapseHelper.animateExpanding(helper); + } + } + } + + public void doCollapse() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (isVisible) { + ExpandCollapseHelper.animateCollapsing(helper); + } + } + } /** * Listener to expand/collapse hidden Expand Layout @@ -772,71 +770,130 @@ public class CardView extends BaseCardView { */ protected class TitleViewOnClickListener implements View.OnClickListener { - private View mContentParent; - private Card mCard; - private boolean viewToSelect=true; + ExpandContainerHelper mExpandContainerHelper; private TitleViewOnClickListener(View contentParent,Card card) { this (contentParent, card,true); } private TitleViewOnClickListener(View contentParent,Card card,boolean viewToSelect) { - this.mContentParent = contentParent; - this.mCard=card; - this.viewToSelect=viewToSelect; + mExpandContainerHelper = new ExpandContainerHelper(contentParent, card, viewToSelect); } @Override public void onClick(View view) { - boolean isVisible = mContentParent.getVisibility() == View.VISIBLE; + boolean isVisible = mExpandContainerHelper.contentParent.getVisibility() == View.VISIBLE; if (isVisible) { - animateCollapsing(); - if (viewToSelect) + ExpandCollapseHelper.animateCollapsing(mExpandContainerHelper); + if (mExpandContainerHelper.viewToSelect) view.setSelected(false); } else { - animateExpanding(); - if (viewToSelect) + ExpandCollapseHelper.animateExpanding(mExpandContainerHelper); + if (mExpandContainerHelper.viewToSelect) view.setSelected(true); } } + } + + /** + * Listener to expand/collapse hidden Expand Layout + * It starts animation + */ + protected class TitleViewOnLongClickListener implements OnLongClickListener { + + TitleViewOnClickListener mOnClickListener; + + private TitleViewOnLongClickListener(TitleViewOnClickListener onClickListener) { + mOnClickListener = onClickListener; + } + + @Override + public boolean onLongClick(View view) { + if (mOnClickListener != null){ + mOnClickListener.onClick(view); + return true; + } + return false; + } + } + + private class ExpandContainerHelper{ + + private View contentParent; + private Card card; + private boolean viewToSelect=true; + + private ExpandContainerHelper(View contentParent, Card card, boolean viewToSelect) { + this.contentParent = contentParent; + this.card = card; + this.viewToSelect = viewToSelect; + } + + public CardView getCardView() { + return (CardView) card.getCardView(); + } + } + + @Override + protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) + { + super.onSizeChanged(xNew, yNew, xOld, yOld); + } + + + private static class ExpandCollapseHelper { /** * Expanding animator. */ - private void animateExpanding() { + private static void animateExpanding(final ExpandContainerHelper helper) { - if (getOnExpandListAnimatorListener()!=null){ + //Callback + if (helper.card.getOnExpandAnimatorStartListener() != null) + helper.card.getOnExpandAnimatorStartListener().onExpandStart(helper.card); + + if (helper.getCardView().getOnExpandListAnimatorListener()!=null){ //List Animator - getOnExpandListAnimatorListener().onExpandStart(mCard.getCardView(), mContentParent); + helper.getCardView().getOnExpandListAnimatorListener().onExpandStart(helper.getCardView(), helper.contentParent); }else{ //Std animator - mContentParent.setVisibility(View.VISIBLE); - mExpandAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mCard.setExpanded(true); - //Callback - if (mCard.getOnExpandAnimatorEndListener()!=null) - mCard.getOnExpandAnimatorEndListener().onExpandEnd(mCard); - } - }); - mExpandAnimator.start(); + helper.contentParent.setVisibility(View.VISIBLE); + if (helper.getCardView().mExpandAnimator != null) { + helper.getCardView().mExpandAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + helper.card.setExpanded(true); + //Callback + if (helper.card.getOnExpandAnimatorEndListener() != null) + helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card); + } + }); + helper.getCardView().mExpandAnimator.start(); + }else{ + if (helper.card.getOnExpandAnimatorEndListener() != null) + helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card); + Log.w(TAG,"Does the card have the ViewToClickToExpand?"); + } } } /** * Collapse animator */ - private void animateCollapsing() { + private static void animateCollapsing(final ExpandContainerHelper helper) { + + //Callback + if (helper.card.getOnCollapseAnimatorStartListener() != null) + helper.card.getOnCollapseAnimatorStartListener().onCollapseStart(helper.card); - if (getOnExpandListAnimatorListener()!=null){ + if (helper.getCardView().getOnExpandListAnimatorListener() != null) { //There is a List Animator. - getOnExpandListAnimatorListener().onCollapseStart(mCard.getCardView(), mContentParent); + helper.getCardView().getOnExpandListAnimatorListener().onCollapseStart(helper.getCardView(), helper.contentParent); }else{ //Std animator - int origHeight = mContentParent.getHeight(); + int origHeight = helper.contentParent.getHeight(); - ValueAnimator animator = createSlideAnimator(origHeight, 0); + ValueAnimator animator = createSlideAnimator(helper.getCardView(),origHeight, 0); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { @@ -844,11 +901,11 @@ public class CardView extends BaseCardView { @Override public void onAnimationEnd(Animator animator) { - mContentParent.setVisibility(View.GONE); - mCard.setExpanded(false); + helper.contentParent.setVisibility(View.GONE); + helper.card.setExpanded(false); //Callback - if (mCard.getOnCollapseAnimatorEndListener()!=null) - mCard.getOnCollapseAnimatorEndListener().onCollapseEnd(mCard); + if (helper.card.getOnCollapseAnimatorEndListener() != null) + helper.card.getOnCollapseAnimatorEndListener().onCollapseEnd(helper.card); } @Override @@ -862,32 +919,27 @@ public class CardView extends BaseCardView { animator.start(); } } - } - /** - * Create the Slide Animator invoked when the expand/collapse button is clicked - */ - protected ValueAnimator createSlideAnimator(int start, int end) { - ValueAnimator animator = ValueAnimator.ofInt(start, end); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - int value = (Integer) valueAnimator.getAnimatedValue(); + /** + * Create the Slide Animator invoked when the expand/collapse button is clicked + */ + protected static ValueAnimator createSlideAnimator(final CardView cardView,int start, int end) { + ValueAnimator animator = ValueAnimator.ofInt(start, end); - ViewGroup.LayoutParams layoutParams = mInternalExpandLayout.getLayoutParams(); - layoutParams.height = value; - mInternalExpandLayout.setLayoutParams(layoutParams); - } - }); - return animator; - } + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + int value = (Integer) valueAnimator.getAnimatedValue(); + + ViewGroup.LayoutParams layoutParams = cardView.mInternalExpandLayout.getLayoutParams(); + layoutParams.height = value; + cardView.mInternalExpandLayout.setLayoutParams(layoutParams); + } + }); + return animator; + } - @Override - protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) - { - super.onSizeChanged(xNew, yNew, xOld, yOld); - mExpandedHeight = yNew; } // ------------------------------------------------------------- @@ -895,14 +947,6 @@ public class CardView extends BaseCardView { // ------------------------------------------------------------- /** - * Interface to listen any callbacks when expand/collapse animation starts - */ - public interface OnExpandListAnimatorListener { - public void onExpandStart(CardView viewCard,View expandingLayout); - public void onCollapseStart(CardView viewCard,View expandingLayout); - } - - /** * Returns the listener invoked when expand/collpase animation starts * It is used internally * @@ -973,22 +1017,6 @@ public class CardView extends BaseCardView { return mInternalInnerView; } - public int getCollapsedHeight() { - return mCollapsedHeight; - } - - public void setCollapsedHeight(int collapsedHeight) { - mCollapsedHeight = collapsedHeight; - } - - public int getExpandedHeight() { - return mExpandedHeight; - } - - public void setExpandedHeight(int expandedHeight) { - mExpandedHeight = expandedHeight; - } - /** * Indicates if the card is expanded or collapsed * @@ -1012,6 +1040,11 @@ public class CardView extends BaseCardView { } } + @Override + public boolean isNative() { + return false; + } + /** * Retrieves the InternalMainCardGlobalLayout. * Background style is applied here. @@ -1027,6 +1060,7 @@ public class CardView extends BaseCardView { * * @param drawableResourceId drawable resource Id */ + @Override public void changeBackgroundResourceId(int drawableResourceId) { if (drawableResourceId!=0){ if (mInternalMainCardLayout!=null){ @@ -1040,14 +1074,17 @@ public class CardView extends BaseCardView { * * @param drawableResource drawable resource */ + @Override public void changeBackgroundResource(Drawable drawableResource) { if (drawableResource!=null){ if (mInternalMainCardLayout!=null){ - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - mInternalMainCardLayout.setBackground(drawableResource); - else - mInternalMainCardLayout.setBackgroundDrawable(drawableResource); + mHelperImpl.setBackground(mInternalMainCardLayout, drawableResource); } } } + + @Override + public void changeBackgroundColorResourceId(int colorResourceId) { + //TODO : do nothing for now + } } diff --git a/src/it/gmariotti/cardslib/library/view/CardViewNative.java b/src/it/gmariotti/cardslib/library/view/CardViewNative.java new file mode 100644 index 0000000..b241886 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/CardViewNative.java @@ -0,0 +1,1271 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.LayoutRes; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.LinearLayout; + +import java.util.HashMap; + +import com.android.cards.R; +import com.android.cards.internal.Card; +import com.android.cards.internal.CardExpand; +import com.android.cards.internal.CardHeader; +import com.android.cards.internal.CardThumbnail; +import com.android.cards.internal.ViewToClickToExpand; +import com.android.cards.view.base.CardViewWrapper; +import com.android.cards.view.component.CardHeaderView; +import com.android.cards.view.component.CardThumbnailView; +import com.android.cards.view.helper.CardViewHelper; +import com.android.cards.view.helper.CardViewHelperUtil; +import com.android.cards.view.listener.SwipeDismissViewTouchListener; + +/** +* Card view +* </p> +* Use an XML layout file to display it. +* </p> +* First, you need an XML layout that will display the Card. +* <pre><code> +* <com.android.cards.view.CardViewNative +* android:id="@+id/carddemo_example_card3" +* android:layout_width="match_parent" +* android:layout_height="wrap_content" +* android:layout_marginLeft="12dp" +* android:layout_marginRight="12dp" +* android:layout_marginTop="12dp"/> +* </code></pre> +* Then create a model: +* <pre><code> +* +* //Create a Card +* Card card = new Card(getContext()); +* +* //Create a CardHeader +* CardHeader header = new CardHeader(getContext()); +* +* //Add Header to card +* card.addCardHeader(header); +* +* </code></pre> +* Last get a reference to the `CardViewNative` from your code, and set your `Card. +* <pre><code> +* //Set card in the cardView +* CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo); +* +* cardView.setCard(card); +* </code></pre> +* You can easily build your layout. +* </p> +* The quickest way to start with this would be to copy one of this files and create your layout. +* Then you can inflate your layout in the `CardViewNative` using the attr: `card:card_layout_resourceID="@layout/my_layout` +* Example: +* <pre><code> +* <com.android.cards.view.CardViewNative +* android:id="@+id/carddemo_thumb_url" +* android:layout_width="match_parent" +* android:layout_height="wrap_content" +* android:layout_marginLeft="12dp" +* android:layout_marginRight="12dp" +* card:card_layout_resourceID="@layout/card_thumbnail_layout" +* android:layout_marginTop="12dp"/> +* </code></pre> +* </p> +* @author Gabriele Mariotti (gabri.mariotti@gmail.com) +*/ +public class CardViewNative extends android.support.v7.widget.CardView implements CardViewWrapper { + + protected static String TAG = "CardViewNative"; + + //-------------------------------------------------------------------------- + // BaseCardView attribute + //-------------------------------------------------------------------------- + + /** + * Card Model + */ + protected Card mCard; + + /** + * Default layout to apply to card + */ + protected @LayoutRes + int card_layout_resourceID = R.layout.native_card_layout; + + /** + * Global View for this Component + */ + protected View mInternalOuterView; + + + /** + * Header Compound View + */ + protected CardHeaderView mInternalHeaderLayout; + + + /** + * Thumbnail Compound View + */ + protected CardThumbnailView mInternalThumbnailLayout; + + /** + * Used to recycle ui elements. + */ + protected boolean mIsRecycle=false; + + /** + * Used to replace inner layout elements. + */ + protected boolean mForceReplaceInnerLayout =false; + + + protected CardViewHelper mHelperImpl; + + //-------------------------------------------------------------------------- + // CardView attribute + //-------------------------------------------------------------------------- + + /** + * {@link com.android.cards.internal.CardHeader} model + */ + protected CardHeader mCardHeader; + + /** + * {@link com.android.cards.internal.CardThumbnail} model + */ + protected CardThumbnail mCardThumbnail; + + /** + * {@link com.android.cards.internal.CardExpand} model + */ + protected CardExpand mCardExpand; + + + //-------------------------------------------------------------------------- + // Layout + //-------------------------------------------------------------------------- + + + /** + * Main Layout + */ + protected View mInternalMainCardLayout; + + /** + * Content Layout + */ + protected View mInternalContentLayout; + + /** + * Inner View. + */ + protected View mInternalInnerView; + + /** + * Hidden layout used by expand/collapse action + */ + protected View mInternalExpandLayout; + + /** + * Expand Inner view + */ + protected View mInternalExpandInnerView; + + + /** Animator to expand/collapse */ + protected Animator mExpandAnimator; + + /** + * Listener invoked when Expand Animator starts + * It is used internally + */ + protected OnExpandListAnimatorListener mOnExpandListAnimatorListener; + + //-------------------------------------------------------------------------- + // Constructor + //-------------------------------------------------------------------------- + + + public CardViewNative(Context context) { + this(context, null, 0); + } + + public CardViewNative(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CardViewNative(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + + mHelperImpl = CardViewHelperUtil.getInstance(context); + } + + //-------------------------------------------------------------------------- + // Init + //-------------------------------------------------------------------------- + + /** + * Initialize + * + * @param attrs + * @param defStyle + */ + protected void init(AttributeSet attrs, int defStyle) { + //Init attrs + initAttrs(attrs, defStyle); + + //Init view + if (!isInEditMode()) + initView(); + } + + /** + * Init custom attrs. + * + * @param attrs + * @param defStyle + */ + protected void initAttrs(AttributeSet attrs, int defStyle) { + + card_layout_resourceID = R.layout.native_card_layout; + + TypedArray a = getContext().getTheme().obtainStyledAttributes( + attrs, R.styleable.card_options, defStyle, defStyle); + + try { + card_layout_resourceID = a.getResourceId(R.styleable.card_options_card_layout_resourceID, this.card_layout_resourceID); + } finally { + a.recycle(); + } + } + + /** + * Init View + */ + protected void initView() { + + //Inflate outer view + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mInternalOuterView = inflater.inflate(card_layout_resourceID, this, true); + + //Radius + setRadius(getResources().getDimension(R.dimen.card_background_default_radius)); + + + + } + + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); + if (mInternalMainCardLayout != null && mInternalMainCardLayout instanceof ForegroundLinearLayout) { + mInternalMainCardLayout.drawableHotspotChanged(x,y); + } + } + + //-------------------------------------------------------------------------- + // Card + //-------------------------------------------------------------------------- + + /** + * Add a {@link Card}. + * It is very important to set all values and all components before launch this method. + * + * @param card {@link Card} model + */ + @Override + public void setCard(Card card){ + + mCard = card; + + if (card!=null){ + mCardHeader=card.getCardHeader(); + mCardThumbnail=card.getCardThumbnail(); + mCardExpand=card.getCardExpand(); + } + + //Retrieve all IDs + if (!isRecycle()){ + retrieveLayoutIDs(); + } + + //Build UI + buildUI(); + } + + /** + * Refreshes the card content (it doesn't inflate layouts again) + * + * @param card + */ + public void refreshCard(Card card) { + mIsRecycle=true; + setCard(card); + mIsRecycle=false; + } + + /** + * Refreshes the card content and replaces the inner layout elements (it inflates layouts again!) + * + * @param card + */ + public void replaceCard(Card card) { + mForceReplaceInnerLayout=true; + refreshCard(card); + mForceReplaceInnerLayout=false; + } + + //-------------------------------------------------------------------------- + // Setup methods + //-------------------------------------------------------------------------- + + protected void buildUI() { + + if (mCard == null) { + Log.e(TAG, "No card model found. Please use setCard(card) to set all values."); + return; + } + mCard.setCardView(this); + + //Shadow + setupShadowView(); + + //Setup Header view + setupHeaderView(); + + //Setup Main View + setupMainView(); + + //setup Thumbnail + setupThumbnailView(); + + //Setup Expand View + setupExpandView(); + + //Setup Supplemental Actions + setupSupplementalActions(); + + //Setup Listeners + setupListeners(); + + //Setup Expand Action + setupExpandAction(); + + //Setup Drawable Resources + setupDrawableResources(); + } + + + /** + * Retrieve all Layouts IDs + */ + protected void retrieveLayoutIDs(){ + + //Main Layout + mInternalMainCardLayout = (View) findViewById(R.id.card_main_layout); + + //Get HeaderLayout + mInternalHeaderLayout = (CardHeaderView) findViewById(R.id.card_header_layout); + + //Get ExpandHiddenView + mInternalExpandLayout = (View) findViewById(R.id.card_content_expand_layout); + + //Get ContentLayout + mInternalContentLayout = (View) findViewById(R.id.card_main_content_layout); + + //Get ThumbnailLayout + mInternalThumbnailLayout = (CardThumbnailView) findViewById(R.id.card_thumbnail_layout); + } + + /** + * Sets up Shadow visibility + * + * @return + */ + protected void setupShadowView() { + if (mCard != null && mCard.getCardElevation() != null) { + this.setCardElevation(mCard.getCardElevation()); + } + } + + /** + * Setup Header View + */ + protected void setupHeaderView(){ + + if (mCardHeader!=null){ + + if (mInternalHeaderLayout !=null){ + mInternalHeaderLayout.setVisibility(VISIBLE); + + //Set recycle value (very important in a ListView) + mInternalHeaderLayout.setRecycle(isRecycle()); + mInternalHeaderLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout()); + //Add Header View + mInternalHeaderLayout.addCardHeader(mCardHeader); + + } + }else{ + //No header. Hide layouts + if (mInternalHeaderLayout !=null){ + mInternalHeaderLayout.setVisibility(GONE); + //mInternalExpandLayout.setVisibility(View.GONE); + + if (isForceReplaceInnerLayout()){ + mInternalHeaderLayout.addCardHeader(null); + //mInternalHeaderLayout.removeAllViews(); + } + } + } + } + + /** + * Setup the Main View + */ + protected void setupMainView(){ + if (mInternalContentLayout !=null){ + + ViewGroup mParentGroup=null; + try{ + mParentGroup = (ViewGroup) mInternalContentLayout; + }catch (Exception e){ + setRecycle(false); + } + + //Check if view can be recycled + //It can happen in a listView, and improves performances + if (!isRecycle() || isForceReplaceInnerLayout()){ + + if (isForceReplaceInnerLayout() && mInternalContentLayout!=null && mInternalInnerView!=null) + ((ViewGroup)mInternalContentLayout).removeView(mInternalInnerView); + + mInternalInnerView=mCard.getInnerView(getContext(), (ViewGroup) mInternalContentLayout); + }else{ + //View can be recycled. + //Only setup Inner Elements + if (mCard.getInnerLayout()>-1) + mCard.setupInnerViewElements(mParentGroup,mInternalInnerView); + } + } + } + + + /** + * Setup the Thumbnail View + */ + protected void setupThumbnailView() { + if (mInternalThumbnailLayout!=null){ + if (mCardThumbnail!=null){ + mInternalThumbnailLayout.setVisibility(VISIBLE); + mInternalThumbnailLayout.setRecycle(isRecycle()); + mInternalThumbnailLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout()); + mInternalThumbnailLayout.addCardThumbnail(mCardThumbnail); + }else{ + mInternalThumbnailLayout.setVisibility(GONE); + } + } + } + + /** + * Setup Drawable Resources + */ + protected void setupDrawableResources() { + + //Card + if (mCard!=null){ + if (mCard.getBackgroundResourceId()!= Card.DEFAULT_COLOR){ + changeBackgroundResourceId(mCard.getBackgroundResourceId()); + }else if (mCard.getBackgroundResource()!=null){ + changeBackgroundResource(mCard.getBackgroundResource()); + } + + if (mCard.getBackgroundColorResourceId() != Card.DEFAULT_COLOR){ + changeBackgroundColorResourceId(mCard.getBackgroundColorResourceId()); + } + } + } + + protected void setupSupplementalActions() { + if (mCard != null) + mCard.setupSupplementalActions(); + } + + //-------------------------------------------------------------------------- + // Listeners + //-------------------------------------------------------------------------- + + protected void setupExpandAction(){ + + //Config ExpandLayout and its animation + if ( mInternalExpandLayout !=null && ( (mCardHeader!=null && mCardHeader.isButtonExpandVisible()) || + mCard.getViewToClickToExpand()!=null) ){ + + //Create the expand/collapse animator + mInternalExpandLayout.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + + @Override + public boolean onPreDraw() { + mInternalExpandLayout.getViewTreeObserver().removeOnPreDrawListener(this); + + View parent = (View) mInternalExpandLayout.getParent(); + final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); + final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + mInternalExpandLayout.measure(widthSpec, heightSpec); + + mExpandAnimator = ExpandCollapseHelper.createSlideAnimator((CardViewNative)mCard.getCardView(),0, mInternalExpandLayout.getMeasuredHeight()); + return true; + } + }); + } + + //Setup action and callback + setupExpandCollapseActionListener(); + } + + /** + * Setup All listeners + */ + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + protected void setupListeners(){ + + //Swipe listener + if (mCard.isSwipeable()){ + this.setOnTouchListener(new SwipeDismissViewTouchListener(this, mCard,new SwipeDismissViewTouchListener.DismissCallbacks() { + @Override + public boolean canDismiss(Card card) { + return card.isSwipeable(); + } + + @Override + public void onDismiss(CardViewWrapper cardView, Card card) { + final ViewGroup vg = (ViewGroup)(((View)cardView).getParent()); + if (vg!=null){ + vg.removeView((View)cardView); + card.onSwipeCard(); + } + } + })); + }else{ + this.setOnTouchListener(null); + } + + //OnClick listeners and partial listener + + //Reset Partial Listeners + resetPartialListeners(); + + if (mCard.isClickable()){ + //Set the onClickListener + if(!mCard.isMultiChoiceEnabled()){ + if (mCard.getOnClickListener() != null) { + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mCard.getOnClickListener()!=null) + mCard.getOnClickListener().onClick(mCard,v); + } + }); + + //Prevent multiple events + //if (!mCard.isSwipeable() && mCard.getOnSwipeListener() == null) { + // this.setClickable(true); + //} + + }else{ + HashMap<Integer,Card.OnCardClickListener> mMultipleOnClickListner=mCard.getMultipleOnClickListener(); + if (mMultipleOnClickListner!=null && !mMultipleOnClickListner.isEmpty()){ + + for (int key:mMultipleOnClickListner.keySet()){ + View viewClickable= decodeAreaOnClickListener(key); + final Card.OnCardClickListener mListener=mMultipleOnClickListner.get(key); + if (viewClickable!=null){ + //Add listener to this view + viewClickable.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + //Callback to card listener + if (mListener!=null) + mListener.onClick(mCard,v); + } + }); + + //Add Selector to this view + if (key > Card.CLICK_LISTENER_ALL_VIEW) { + mHelperImpl.setBackground(viewClickable, mHelperImpl.getResourceFromAttrs(getContext(),android.R.attr.selectableItemBackground)); + } + } + } + }else{ + //There aren't listners + this.setClickable(false); + } + } + } + }else{ + this.setClickable(false); + } + + //LongClick listener + if(mCard.isLongClickable()){ + this.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mCard.getOnLongClickListener()!=null) + return mCard.getOnLongClickListener().onLongClick(mCard,v); + return false; + } + }); + }else{ + this.setLongClickable(false); + } + } + + /** + * Reset all partial listeners + */ + protected void resetPartialListeners() { + View viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW); + if (viewClickable!=null) + viewClickable.setClickable(false); + + viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW); + if (viewClickable!=null) + viewClickable.setClickable(false); + + viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW); + if (viewClickable!=null) + viewClickable.setClickable(false); + + viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_ACTIONAREA1_VIEW); + if (viewClickable!=null) + viewClickable.setClickable(false); + } + + /** + * + * @param area + * @return + */ + protected View decodeAreaOnClickListener(int area){ + + if (area<Card.CLICK_LISTENER_ALL_VIEW && area>Card.CLICK_LISTENER_CONTENT_VIEW) + return null; + + View view = null; + + switch (area){ + case Card.CLICK_LISTENER_ALL_VIEW : + view=this; + break; + case Card.CLICK_LISTENER_HEADER_VIEW : + view=mInternalHeaderLayout; + break; + case Card.CLICK_LISTENER_THUMBNAIL_VIEW: + view=mInternalThumbnailLayout; + break; + case Card.CLICK_LISTENER_CONTENT_VIEW: + view=mInternalContentLayout; + break; + case Card.CLICK_LISTENER_ACTIONAREA1_VIEW: + view=mInternalMainCardLayout; + break; + default: + break; + } + return view; + } + + //-------------------------------------------------------------------------- + // Expandable Actions and Listeners + //-------------------------------------------------------------------------- + + /** + * Add ClickListener to expand and collapse hidden view + */ + protected void setupExpandCollapseActionListener() { + if (mInternalExpandLayout != null) { + mInternalExpandLayout.setVisibility(View.GONE); + + boolean internal_blockForLongClickOnImageButtonExpand = false; + ViewToClickToExpand viewToClickToExpand = null; + + //ButtonExpandVisible has a priority to viewClickToExpand + if (mCardHeader != null && mCardHeader.isButtonExpandVisible()) { + + viewToClickToExpand = ViewToClickToExpand.builder() + .setupView(mInternalHeaderLayout.getImageButtonExpand()) + .highlightView(true); + internal_blockForLongClickOnImageButtonExpand = true; + + } else if (mCard.getViewToClickToExpand() != null) { + + viewToClickToExpand = mCard.getViewToClickToExpand(); + } + + if (viewToClickToExpand != null) { + + TitleViewOnClickListener titleViewOnClickListener = new TitleViewOnClickListener(mInternalExpandLayout, mCard, viewToClickToExpand.isViewToSelect()); + + /*if (mCardHeader!=null && mCardHeader.isButtonExpandVisible() && mInternalHeaderLayout != null) { + mInternalHeaderLayout.setOnClickExpandCollapseActionListener(titleViewOnClickListener); + }*/ + + View viewToClick = viewToClickToExpand.getViewToClick(); + if (viewToClick != null) { + + if (internal_blockForLongClickOnImageButtonExpand) { + //The long click on Header button is now allowed + viewToClick.setOnClickListener(titleViewOnClickListener); + }else{ + if (viewToClickToExpand.isUseLongClick()){ + viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener)); + }else{ + viewToClick.setOnClickListener(titleViewOnClickListener); + } + } + }else{ + ViewToClickToExpand.CardElementUI cardElementUI=viewToClickToExpand.getCardElementUIToClick(); + if (cardElementUI!=null){ + switch (cardElementUI){ + case CARD: + viewToClick = this; + break; + case HEADER: + viewToClick = getInternalHeaderLayout(); + break; + case THUMBNAIL: + viewToClick = getInternalThumbnailLayout(); + break; + case MAIN_CONTENT: + viewToClick = getInternalContentLayout(); + break; + } + if (viewToClick != null) { + if (viewToClickToExpand.isUseLongClick()){ + viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener)); + }else{ + viewToClick.setOnClickListener(titleViewOnClickListener); + } + } + } + } + + if (isExpanded()) { + //Make layout visible and button selected + mInternalExpandLayout.setVisibility(View.VISIBLE); + if (viewToClick != null) { + if (viewToClickToExpand.isViewToSelect()) + viewToClick.setSelected(true); + } + } else { + //Make layout hidden and button not selected + mInternalExpandLayout.setVisibility(View.GONE); + if (viewToClick != null) { + if (viewToClickToExpand.isViewToSelect()) + viewToClick.setSelected(false); + } + } + } + + } + } + + /** + * Listener to expand/collapse hidden Expand Layout + * It starts animation + */ + protected class TitleViewOnLongClickListener implements OnLongClickListener { + + TitleViewOnClickListener mOnClickListener; + + private TitleViewOnLongClickListener(TitleViewOnClickListener onClickListener) { + mOnClickListener = onClickListener; + } + + @Override + public boolean onLongClick(View view) { + if (mOnClickListener != null){ + mOnClickListener.onClick(view); + return true; + } + return false; + } + } + + private class ExpandContainerHelper{ + + private View contentParent; + private Card card; + private boolean viewToSelect=true; + + private ExpandContainerHelper(View contentParent, Card card, boolean viewToSelect) { + this.contentParent = contentParent; + this.card = card; + this.viewToSelect = viewToSelect; + } + + public CardViewNative getCardView() { + return (CardViewNative) card.getCardView(); + } + } + + private static class ExpandCollapseHelper { + + /** + * Expanding animator. + */ + private static void animateExpanding(final ExpandContainerHelper helper) { + + if (helper.getCardView().getOnExpandListAnimatorListener()!=null){ + //List Animator + helper.getCardView().getOnExpandListAnimatorListener().onExpandStart(helper.getCardView(), helper.contentParent); + }else{ + //Std animator + helper.contentParent.setVisibility(View.VISIBLE); + if (helper.getCardView().mExpandAnimator != null) { + helper.getCardView().mExpandAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + helper.card.setExpanded(true); + //Callback + if (helper.card.getOnExpandAnimatorEndListener() != null) + helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card); + } + }); + helper.getCardView().mExpandAnimator.start(); + }else{ + if (helper.card.getOnExpandAnimatorEndListener() != null) + helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card); + Log.w(TAG,"Does the card have the ViewToClickToExpand?"); + } + } + } + + /** + * Collapse animator + */ + private static void animateCollapsing(final ExpandContainerHelper helper) { + + if (helper.getCardView().getOnExpandListAnimatorListener()!=null){ + //There is a List Animator. + helper.getCardView().getOnExpandListAnimatorListener().onCollapseStart(helper.getCardView(), helper.contentParent); + }else{ + //Std animator + int origHeight = helper.contentParent.getHeight(); + + ValueAnimator animator = createSlideAnimator(helper.getCardView(),origHeight, 0); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + } + + @Override + public void onAnimationEnd(Animator animator) { + helper.contentParent.setVisibility(View.GONE); + helper.card.setExpanded(false); + //Callback + if (helper.card.getOnCollapseAnimatorEndListener()!=null) + helper.card.getOnCollapseAnimatorEndListener().onCollapseEnd(helper.card); + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }); + animator.start(); + } + } + + + /** + * Create the Slide Animator invoked when the expand/collapse button is clicked + */ + protected static ValueAnimator createSlideAnimator(final CardViewNative cardView,int start, int end) { + ValueAnimator animator = ValueAnimator.ofInt(start, end); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + int value = (Integer) valueAnimator.getAnimatedValue(); + + ViewGroup.LayoutParams layoutParams = cardView.mInternalExpandLayout.getLayoutParams(); + layoutParams.height = value; + cardView.mInternalExpandLayout.setLayoutParams(layoutParams); + } + }); + return animator; + } + + } + + /** + * Setup Expand View + */ + protected void setupExpandView(){ + if (mInternalExpandLayout!=null && mCardExpand!=null){ + + //Check if view can be recycled + //It can happen in a listView, and improves performances + if (!isRecycle() || isForceReplaceInnerLayout()){ + + if (isForceReplaceInnerLayout() && mInternalExpandLayout!=null && mInternalExpandInnerView!=null) + ((ViewGroup)mInternalExpandLayout).removeView(mInternalExpandInnerView); + + mInternalExpandInnerView=mCardExpand.getInnerView(getContext(),(ViewGroup) mInternalExpandLayout); + }else{ + //View can be recycled. + //Only setup Inner Elements + if (mCardExpand.getInnerLayout()>-1) + mCardExpand.setupInnerViewElements((ViewGroup)mInternalExpandLayout,mInternalExpandInnerView); + } + + ViewGroup.LayoutParams layoutParams = mInternalExpandLayout.getLayoutParams(); + layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + mInternalExpandLayout.setLayoutParams(layoutParams); + } + } + + public void doToggleExpand() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (isVisible) { + ExpandCollapseHelper.animateCollapsing(helper); + } else { + ExpandCollapseHelper.animateExpanding(helper); + } + } + } + + public void doExpand() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (!isVisible) { + ExpandCollapseHelper.animateExpanding(helper); + } + } + } + + public void doCollapse() { + + if (mInternalExpandLayout != null) { + ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false); + + boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE; + if (isVisible) { + ExpandCollapseHelper.animateCollapsing(helper); + } + } + } + + /** + * Listener to expand/collapse hidden Expand Layout + * It starts animation + */ + protected class TitleViewOnClickListener implements View.OnClickListener { + + ExpandContainerHelper mExpandContainerHelper; + + private TitleViewOnClickListener(View contentParent,Card card) { + this (contentParent, card,true); + } + + private TitleViewOnClickListener(View contentParent,Card card,boolean viewToSelect) { + mExpandContainerHelper = new ExpandContainerHelper(contentParent, card, viewToSelect); + } + + @Override + public void onClick(View view) { + boolean isVisible = mExpandContainerHelper.contentParent.getVisibility() == View.VISIBLE; + if (isVisible) { + ExpandCollapseHelper.animateCollapsing(mExpandContainerHelper); + if (mExpandContainerHelper.viewToSelect) + view.setSelected(false); + } else { + ExpandCollapseHelper.animateExpanding(mExpandContainerHelper); + if (mExpandContainerHelper.viewToSelect) + view.setSelected(true); + } + } + } + + @Override + protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) + { + super.onSizeChanged(xNew, yNew, xOld, yOld); + } + + // ------------------------------------------------------------- + // OnExpandListAnimator Interface and Listener + // ------------------------------------------------------------- + + /** + * Returns the listener invoked when expand/collpase animation starts + * It is used internally + * + * @return listener + */ + public OnExpandListAnimatorListener getOnExpandListAnimatorListener() { + return mOnExpandListAnimatorListener; + } + + /** + * Sets the listener invoked when expand/collapse animation starts + * It is used internally. Don't override it. + * + * @param onExpandListAnimatorListener listener + */ + @Override + public void setOnExpandListAnimatorListener(CardViewWrapper.OnExpandListAnimatorListener onExpandListAnimatorListener) { + this.mOnExpandListAnimatorListener = onExpandListAnimatorListener; + } + + // ------------------------------------------------------------- + // ChangeBackground + // ------------------------------------------------------------- + + /** + * Changes dynamically the drawable resource to override the style of MainLayout. + * + * @param drawableResourceId drawable resource Id + */ + @Override + public void changeBackgroundResourceId(int drawableResourceId) { + if (drawableResourceId!=Card.DEFAULT_COLOR){ + changeBackgroundResource(getResources().getDrawable(drawableResourceId)); + } + } + + /** + * Changes dynamically the drawable resource to override the style of MainLayout. + * + * @param drawableResource drawable resource + */ + @Override + public void changeBackgroundResource(Drawable drawableResource) { + if (drawableResource!=null){ + if (mInternalMainCardLayout!=null){ + mHelperImpl.setBackground(mInternalMainCardLayout, drawableResource); + } + } + } + + /** + * Changes dynamically the color of the background card + * + * @param colorResourceId color resource Id + */ + @Override + public void changeBackgroundColorResourceId(int colorResourceId) { + if (colorResourceId!=Card.DEFAULT_COLOR){ + //this.setBackgroundDrawable(mHelperImpl.getResourceFromAttrs(getContext(),R.attr.cardBackgroundColor)); + mInternalMainCardLayout.setBackgroundColor(getResources().getColor(colorResourceId)); + } + } + + // ------------------------------------------------------------- + // Bitmap export + // ------------------------------------------------------------- + + /** + * Create a {@link android.graphics.Bitmap} from CardView + * @return + */ + public Bitmap createBitmap(){ + + if (getWidth()<=0 && getHeight()<=0){ + int spec = MeasureSpec.makeMeasureSpec( 0,MeasureSpec.UNSPECIFIED); + measure(spec,spec); + layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } + + Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + draw(c); + return b; + } + + //-------------------------------------------------------------------------- + // Getters and Setters + //-------------------------------------------------------------------------- + + public View getInternalOuterView() { + return mInternalOuterView; + } + + /** + * Returns {@link Card} model + * + * @return {@link Card} model + */ + public Card getCard() { + return mCard; + } + + /** + * Returns the view used for Header + * + * @return {@link CardHeaderView} + */ + public CardHeaderView getInternalHeaderLayout() { + return mInternalHeaderLayout; + } + + /** + * Returns the view used by Thumbnail + * + * @return {@link CardThumbnailView} + */ + @Override + public CardThumbnailView getInternalThumbnailLayout() { + return mInternalThumbnailLayout; + } + + /** + * Indicates if view can recycle ui elements. + * + * @return <code>true</code> if views can recycle ui elements + */ + public boolean isRecycle() { + return mIsRecycle; + } + + /** + * Sets if view can recycle ui elements + * + * @param isRecycle <code>true</code> to recycle + */ + @Override + public void setRecycle(boolean isRecycle) { + this.mIsRecycle = isRecycle; + } + + /** + * Indicates if inner layout have to be replaced + * + * @return <code>true</code> if inner layout can be recycled + */ + public boolean isForceReplaceInnerLayout() { + return mForceReplaceInnerLayout; + } + + /** + * Sets if inner layout have to be replaced + * + * @param forceReplaceInnerLayout <code>true</code> to recycle + */ + @Override + public void setForceReplaceInnerLayout(boolean forceReplaceInnerLayout) { + this.mForceReplaceInnerLayout = forceReplaceInnerLayout; + } + + /** + * Returns the view used by Expand Layout + * + * @return {@link View} used by Expand Layout + */ + public View getInternalExpandLayout() { + return mInternalExpandLayout; + } + + /** + * FIXME + * @return + */ + public View getInternalContentLayout() { + return mInternalContentLayout; + } + + /** + * FIXME + * @return + */ + public View getInternalInnerView() { + return mInternalInnerView; + } + + /** + * Indicates if the card is expanded or collapsed + * + * @return <code>true</code> if the card is expanded + */ + public boolean isExpanded() { + if (mCard!=null){ + return mCard.isExpanded(); + }else + return false; + } + + /** + * Sets the card as expanded or collapsed + * + * @param expanded <code>true</code> if the card is expanded + */ + public void setExpanded(boolean expanded) { + if (mCard!=null){ + mCard.setExpanded(expanded); + } + } + + @Override + public boolean isNative() { + return true; + } + + /** + * Retrieves the InternalMainCardGlobalLayout. + * + * @return + */ + public View getInternalMainCardLayout() { + return mInternalMainCardLayout; + } +} diff --git a/src/com/android/cards/view/ForegroundLinearLayout.java b/src/it/gmariotti/cardslib/library/view/ForegroundLinearLayout.java index c5e769e..a991a70 100644 --- a/src/com/android/cards/view/ForegroundLinearLayout.java +++ b/src/it/gmariotti/cardslib/library/view/ForegroundLinearLayout.java @@ -19,11 +19,13 @@ package com.android.cards.view; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.AttributeSet; import android.view.Gravity; import android.widget.LinearLayout; @@ -220,4 +222,16 @@ public class ForegroundLinearLayout extends LinearLayout { foreground.draw(canvas); } } -} + + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (mForeground != null) { + mForeground.setHotspot(x, y); + } + } + } +}
\ No newline at end of file diff --git a/src/com/android/cards/view/base/CardViewInterface.java b/src/it/gmariotti/cardslib/library/view/base/CardViewInterface.java index 7f7d926..7f7d926 100644 --- a/src/com/android/cards/view/base/CardViewInterface.java +++ b/src/it/gmariotti/cardslib/library/view/base/CardViewInterface.java diff --git a/src/it/gmariotti/cardslib/library/view/base/CardViewWrapper.java b/src/it/gmariotti/cardslib/library/view/base/CardViewWrapper.java new file mode 100644 index 0000000..09e6ecc --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/base/CardViewWrapper.java @@ -0,0 +1,162 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.base; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; + +import com.android.cards.internal.Card; +import com.android.cards.view.component.CardThumbnailView; + +/** + * Common interface for CardView. + * <p> + * Necessary to merge the native cardview and the library cardview. + * <p> + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public interface CardViewWrapper { + + /** + * Return the context + * + * @return context + */ + Context getContext(); + + /** + * Expand the card + */ + void doExpand(); + + /** + * Collapse the card + */ + void doCollapse(); + + /** + * Toggle the card + */ + void doToggleExpand(); + + /** Support for longClickable**/ + void setLongClickable(boolean b); + + /** + * Interface to listen any callbacks when expand/collapse animation starts + */ + public interface OnExpandListAnimatorListener { + public void onExpandStart(CardViewWrapper viewCard,View expandingLayout); + public void onCollapseStart(CardViewWrapper viewCard,View expandingLayout); + } + + /** + * Changes dynamically the drawable resource to override the style of MainLayout. + * + * @param drawableResourceId drawable resource Id + */ + void changeBackgroundResourceId(int drawableResourceId); + + /** + * Changes dynamically the drawable resource to override the style of MainLayout. + * + * @param drawableResource drawable resource + */ + void changeBackgroundResource(Drawable drawableResource); + + /** + * Changes dynamically the background color + * + * @param colorResourceId + */ + void changeBackgroundColorResourceId(int colorResourceId); + + /** + * Returns {@link Card} model + * + * @return {@link Card} model + */ + Card getCard(); + + /** + * Add a {@link Card}. + * It is very important to set all values and all components before launch this method. + * + * @param card {@link Card} model + */ + void setCard(Card card); + + /** + * Indicates if inner layout have to be replaced + * + * + */ + void setForceReplaceInnerLayout(boolean forceReplaceInnerLayout); + + /** + * Sets if view can recycle ui elements + * + * @param recycle <code>true</code> to recycle + */ + void setRecycle(boolean recycle); + + /** + * Implement to refresh the card content (it doesn't inflate layouts again) + * + * @param card + */ + void refreshCard(Card card); + + /** Returns the view used by Thumbnail + * + * @return {@link CardThumbnailView} + */ + CardThumbnailView getInternalThumbnailLayout(); + + + void setOnTouchListener(View.OnTouchListener onTouchListener); + + /** + * Sets the listener invoked when expand/collapse animation starts + * It is used internally. Don't override it. + * + * @param onExpandListAnimatorListener listener + */ + void setOnExpandListAnimatorListener(OnExpandListAnimatorListener onExpandListAnimatorListener); + + void setOnClickListener(View.OnClickListener advanceClickListener); + + /** + * Sets the card as expanded or collapsed + * + * @param expanded <code>true</code> if the card is expanded + */ + void setExpanded(boolean expanded); + + + boolean isNative(); + + /** + * Retrieves the InternalMainCardGlobalLayout. + * + * @return + */ + View getInternalMainCardLayout(); +} diff --git a/src/com/android/cards/view/component/CardHeaderView.java b/src/it/gmariotti/cardslib/library/view/component/CardHeaderView.java index f409063..06cdf8f 100644 --- a/src/com/android/cards/view/component/CardHeaderView.java +++ b/src/it/gmariotti/cardslib/library/view/component/CardHeaderView.java @@ -21,7 +21,6 @@ package com.android.cards.view.component; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; -import android.os.Build; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MenuInflater; @@ -35,6 +34,8 @@ import android.widget.PopupMenu; import com.android.cards.R; import com.android.cards.internal.CardHeader; import com.android.cards.view.base.CardViewInterface; +import com.android.cards.view.helper.CardViewHelper; +import com.android.cards.view.helper.CardViewHelperUtil; /** * Compound View for Header Component. @@ -123,7 +124,7 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { /** * Listener invoked when expand/collapse button is clicked */ - protected OnClickListener mOnClickExpandCollapseActionListener; + //protected OnClickListener mOnClickExpandCollapseActionListener; /** * Used to recycle ui elements. @@ -140,23 +141,25 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { */ protected PopupMenu mPopupMenu; + protected CardViewHelper mHelperImpl; + //-------------------------------------------------------------------------- // Constructors //-------------------------------------------------------------------------- public CardHeaderView(Context context) { - super(context); - init(null, 0); + this(context, null, 0); } public CardHeaderView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs, 0); + this(context, attrs, 0); } public CardHeaderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); + + mHelperImpl = CardViewHelperUtil.getInstance(context); } //-------------------------------------------------------------------------- @@ -259,12 +262,11 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { if (mCardHeader.isButtonOverflowVisible()) { visibilityButtonHelper(VISIBLE, GONE, GONE); - if (mCardHeader.getPopupMenu() != CardHeader.NO_POPUP_MENU) { - //Add popup - addPopup(); - } else if (mCardHeader.getCustomOverflowAnimation() != null) { + addPopup(); + if (mPopupMenu==null && mCardHeader.getCustomOverflowAnimation() != null) { addCustomOverflowAnimation(); } + } else { if (mCardHeader.isButtonExpandVisible()) { @@ -276,11 +278,7 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { //Check if button is not null if (mImageButtonOther != null) { if (mCardHeader.getOtherButtonDrawable() > 0) { - if (Build.VERSION.SDK_INT >= 16) { - mImageButtonOther.setBackground(getResources().getDrawable(mCardHeader.getOtherButtonDrawable())); - } else { - mImageButtonOther.setBackgroundDrawable(getResources().getDrawable(mCardHeader.getOtherButtonDrawable())); - } + mHelperImpl.setButtonBackground(mImageButtonOther, mCardHeader.getOtherButtonDrawable() ); } addOtherListener(); } @@ -370,16 +368,19 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { protected void visibilityButtonHelper(int overflowButtonVisibility, int expandButtonVisibility, int otherButtonVisibility) { if (overflowButtonVisibility == VISIBLE || overflowButtonVisibility == GONE) { - if (mImageButtonOverflow != null) + if (mImageButtonOverflow != null) { mImageButtonOverflow.setVisibility(overflowButtonVisibility); + } } if (expandButtonVisibility == VISIBLE || expandButtonVisibility == GONE) { - if (mImageButtonExpand != null) + if (mImageButtonExpand != null) { mImageButtonExpand.setVisibility(expandButtonVisibility); + } } if (otherButtonVisibility == VISIBLE || otherButtonVisibility == GONE) { - if (mImageButtonOther != null) + if (mImageButtonOther != null) { mImageButtonOther.setVisibility(otherButtonVisibility); + } } } @@ -391,10 +392,10 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { //To prevent recycle mPopupMenu = null; - if (mCardHeader.getPopupMenu() > -1 && mImageButtonOverflow != null) { + if (mImageButtonOverflow != null) { // allow dynamic customization on popup menu - boolean prepareMenu = true; + boolean prepareMenu = mCardHeader.getPopupMenu() > CardHeader.NO_POPUP_MENU ? true : false; if (mCardHeader.getPopupMenuPrepareListener() != null) { //Build the popupMenu @@ -418,8 +419,10 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { //PopupMenu is built inside onClick() method to avoid building the menu when it is not necessary mPopupMenu = _buildPopupMenu(); } - if (mPopupMenu!=null) + if (mPopupMenu!=null) { mPopupMenu.show(); + mImageButtonOverflow.setSelected(true); + } } }); } else { @@ -442,12 +445,14 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { private PopupMenu _buildPopupMenu(){ PopupMenu popup = new PopupMenu(getContext(), mImageButtonOverflow); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(mCardHeader.getPopupMenu(), popup.getMenu()); + if (mCardHeader.getPopupMenu()> CardHeader.NO_POPUP_MENU){ + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(mCardHeader.getPopupMenu(), popup.getMenu()); + } popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - if (mCardHeader.getPopupMenu() > 0 && mCardHeader.getPopupMenuListener() != null) { + if (mCardHeader.getPopupMenuListener() != null) { // This individual card has it unique menu mCardHeader.getPopupMenuListener().onMenuItemClick(mCardHeader.getParentCard(), item); } @@ -455,6 +460,15 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { } }); + popup.setOnDismissListener(new PopupMenu.OnDismissListener() { + @Override + public void onDismiss(PopupMenu menu) { + if (mImageButtonOverflow != null) + mImageButtonOverflow.setSelected(false); + } + }); + + return popup; } @@ -465,23 +479,23 @@ public class CardHeaderView extends FrameLayout implements CardViewInterface { /** * Returns Listener invoked when expand/collpse button is clicked * + * @deprecated * @return listener */ - public OnClickListener getOnClickExpandCollapseActionListener() { + /*public OnClickListener getOnClickExpandCollapseActionListener() { return mOnClickExpandCollapseActionListener; - } + }*/ /** * Attaches Listener to expand/collapse button * + * @deprecated * @param onClickExpandCollapseActionListener listener */ - public void setOnClickExpandCollapseActionListener(OnClickListener onClickExpandCollapseActionListener) { + /*public void setOnClickExpandCollapseActionListener(OnClickListener onClickExpandCollapseActionListener) { this.mOnClickExpandCollapseActionListener = onClickExpandCollapseActionListener; - /*if (mImageButtonExpand != null) - mImageButtonExpand.setOnClickListener(onClickExpandCollapseActionListener); - */ - } + + }*/ /** * Indicates if view can recycle ui elements. diff --git a/src/com/android/cards/view/component/CardShadowView.java b/src/it/gmariotti/cardslib/library/view/component/CardShadowView.java index e367d33..e367d33 100644 --- a/src/com/android/cards/view/component/CardShadowView.java +++ b/src/it/gmariotti/cardslib/library/view/component/CardShadowView.java diff --git a/src/com/android/cards/view/component/CardThumbnailView.java b/src/it/gmariotti/cardslib/library/view/component/CardThumbnailView.java index 43984c1..6f5b47b 100644 --- a/src/com/android/cards/view/component/CardThumbnailView.java +++ b/src/it/gmariotti/cardslib/library/view/component/CardThumbnailView.java @@ -174,13 +174,7 @@ public class CardThumbnailView extends FrameLayout implements CardViewInterface //Get ImageVIew mImageView= (ImageView) findViewById(R.id.card_thumbnail_image); - } - private void initLruCache() { - mMemoryCache = CacheUtil.getMemoryCache(); - if (mMemoryCache != null) { - return; - } // Get max available VM memory, exceeding this amount will throw an // OutOfMemory exception. Stored in kilobytes as LruCache takes an // int in its constructor. @@ -189,20 +183,23 @@ public class CardThumbnailView extends FrameLayout implements CardViewInterface // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; - mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { - - @Override - protected int sizeOf(String key, Bitmap bitmap) { - // The cache size will be measured in kilobytes rather than - // number of items. - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { - return bitmap.getByteCount() / 1024; - } else { - return bitmap.getRowBytes() * bitmap.getHeight() / 1024; + mMemoryCache = CacheUtil.getMemoryCache(); + if (mMemoryCache==null){ + mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { + + @Override + protected int sizeOf(String key, Bitmap bitmap) { + // The cache size will be measured in kilobytes rather than + // number of items. + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { + return bitmap.getByteCount() / 1024; + } else { + return bitmap.getRowBytes() * bitmap.getHeight() / 1024; + } } - } - }; - CacheUtil.putMemoryCache(mMemoryCache); + }; + CacheUtil.putMemoryCache(mMemoryCache); + } } //-------------------------------------------------------------------------- @@ -245,15 +242,13 @@ public class CardThumbnailView extends FrameLayout implements CardViewInterface mCardThumbnail.setupInnerViewElements((ViewGroup)mInternalOuterView,mImageView); //Load bitmap - if (!mCardThumbnail.isExternalUsage()) { - initLruCache(); - if (mCardThumbnail.getCustomSource() != null) { + if (!mCardThumbnail.isExternalUsage()){ + if (mCardThumbnail.getCustomSource() != null) loadBitmap(mCardThumbnail.getCustomSource(), mImageView); - } else if (mCardThumbnail.getDrawableResource() > 0) { + else if(mCardThumbnail.getDrawableResource()>0) loadBitmap(mCardThumbnail.getDrawableResource(), mImageView); - } else { + else loadBitmap(mCardThumbnail.getUrlResource(), mImageView); - } } } @@ -436,24 +431,19 @@ public class CardThumbnailView extends FrameLayout implements CardViewInterface return true; } - public static boolean cancelPotentialWork( - CardThumbnail.CustomSource customSource, ImageView imageView) { + public static boolean cancelPotentialWork(CardThumbnail.CustomSource customSource, ImageView imageView) { + final BitmapWorkerCustomSourceTask bitmapWorkerTask = getBitmapWorkerCustomSourceTask(imageView); - final BitmapWorkerCustomSourceTask bitmapWorkerTask = - getBitmapWorkerCustomSourceTask(imageView); - - if (bitmapWorkerTask != null) { - - final CardThumbnail.CustomSource bitmapWorkerTaskCustomSource = - bitmapWorkerTask.customSource; - - if (bitmapWorkerTaskCustomSource != null - && !bitmapWorkerTaskCustomSource.getTag().equals(customSource.getTag())) { - // Cancel previous task - bitmapWorkerTask.cancel(true); - } else { - // The same work is already in progress - return false; + if (bitmapWorkerTask != null && bitmapWorkerTask.customSource != null) { + final CardThumbnail.CustomSource bitmapWorkerTaskCustomSource = bitmapWorkerTask.customSource; + if (bitmapWorkerTaskCustomSource.getTag() != null) { + if (!bitmapWorkerTaskCustomSource.getTag().equals(customSource.getTag())) { + // Cancel previous task + bitmapWorkerTask.cancel(true); + } else { + // The same work is already in progress + return false; + } } } // No task associated with the ImageView, or an existing task was cancelled diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelper.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelper.java new file mode 100644 index 0000000..7d7c215 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelper.java @@ -0,0 +1,54 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.ImageButton; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public interface CardViewHelper { + + /** + * Sets the background of the wiew + * @param view + * @param drawable + */ + void setBackground(View view,Drawable drawable); + + /** + * This method sets the button background for android api <L, while set the image source for android api >= L + * @param imageButton + * @param buttonDrawableResource + */ + void setButtonBackground(ImageButton imageButton, int buttonDrawableResource); + + + void setCardSelector(View viewClickable, Drawable defaultDrawable); + + + void setElevation(View view,float elevation); + + + Drawable getResourceFromAttrs(Context themedContext, int attr); + +} diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplBase.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplBase.java new file mode 100644 index 0000000..c592878 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplBase.java @@ -0,0 +1,82 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.support.v4.view.ViewCompat; +import android.view.View; +import android.widget.ImageButton; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardViewHelperImplBase implements CardViewHelper { + + protected Context mContext; + + public CardViewHelperImplBase(Context context){ + mContext = context; + } + + @Override + public void setBackground(View view, Drawable drawable) { + if (view != null) + view.setBackgroundDrawable(drawable); + } + + @Override + public void setButtonBackground(ImageButton imageButton, int buttonDrawableResource) { + setBackground(imageButton, mContext.getResources().getDrawable(buttonDrawableResource)); + } + + @Override + public void setCardSelector(View viewClickable, Drawable defaultDrawable) { + setBackground(viewClickable, defaultDrawable); + } + + @Override + public void setElevation(View view, float elevation) { + ViewCompat.setElevation(view, elevation); + } + + @Override + public Drawable getResourceFromAttrs(Context themedContext, int attr){ + // Create an array of the attributes we want to resolve + // using values from a theme + int[] attrs = new int[] { attr /* index 0 */}; + + // Obtain the styled attributes. 'themedContext' is a context with a + // theme, typically the current Activity (i.e. 'this') + TypedArray ta = themedContext.obtainStyledAttributes(attrs); + + // To get the value of the 'listItemBackground' attribute that was + // set in the theme used in 'themedContext'. The parameter is the index + // of the attribute in the 'attrs' array. The returned Drawable + // is what you are after + Drawable drawableFromTheme = ta.getDrawable(0 /* index */); + + // Finally, free the resources used by TypedArray + ta.recycle(); + + return drawableFromTheme; + } + +} diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplJB.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplJB.java new file mode 100644 index 0000000..e339c8e --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplJB.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.View; +import android.widget.ImageButton; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class CardViewHelperImplJB extends CardViewHelperImplBase { + + public CardViewHelperImplJB(Context context) { + super(context); + } + + @Override + public void setBackground(View view, Drawable drawable) { + if (view != null) + view.setBackground(drawable); + } + + @Override + public void setButtonBackground(ImageButton imageButton, int buttonDrawableResource) { + setBackground(imageButton, mContext.getResources().getDrawable(buttonDrawableResource)); + } + +} diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplKK.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplKK.java new file mode 100644 index 0000000..8be05b7 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplKK.java @@ -0,0 +1,36 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +@TargetApi(Build.VERSION_CODES.KITKAT) +public class CardViewHelperImplKK extends CardViewHelperImplJB{ + + public CardViewHelperImplKK(Context context) { + super(context); + } + + +} diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplL.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplL.java new file mode 100644 index 0000000..3267439 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperImplL.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.widget.ImageButton; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class CardViewHelperImplL extends CardViewHelperImplKK { + + public CardViewHelperImplL(Context context) { + super(context); + } + + @Override + public void setButtonBackground(ImageButton imageButton, int buttonDrawableResource) { + imageButton.setImageResource(buttonDrawableResource); + } + + @Override + public void setElevation(View view, float elevation) { + if (view != null){ + view.setElevation(elevation); + } + } + +} diff --git a/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperUtil.java b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperUtil.java new file mode 100644 index 0000000..bceb1ec --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/helper/CardViewHelperUtil.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.helper; + +import android.content.Context; +import android.os.Build; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardViewHelperUtil { + + + public static CardViewHelper getInstance(Context context){ + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + return new CardViewHelperImplL(context); + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + return new CardViewHelperImplKK(context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return new CardViewHelperImplJB(context); + } else { + return new CardViewHelperImplBase(context); + } + } + + +} diff --git a/src/com/android/cards/view/listener/SwipeDismissListViewTouchListener.java b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissListViewTouchListener.java index 7a7b83b..d39f53f 100644 --- a/src/com/android/cards/view/listener/SwipeDismissListViewTouchListener.java +++ b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissListViewTouchListener.java @@ -24,7 +24,6 @@ import android.animation.ValueAnimator; import android.graphics.Rect; import android.os.SystemClock; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -37,8 +36,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.android.cards.R; import com.android.cards.internal.Card; -import com.android.cards.view.CardListView; +import com.android.cards.view.listener.dismiss.Dismissable; /** * It is based on Roman Nurik code. @@ -54,7 +54,7 @@ import com.android.cards.view.CardListView; * * <p>After creating the listener, the caller should also call * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, using a - * {@link it.gmariotti.cardslib.library.view.listener.SwipeOnScrollListener}. + * {@link com.android.cards.view.listener.SwipeOnScrollListener}. * * If a scroll listener is already assigned, the caller should still pass scroll changes through to this listener. This will * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view @@ -100,7 +100,6 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { private float mDownX; private float mDownY; private boolean mSwiping; - private boolean mSwipeInitialized; private int mSwipingSlop; private VelocityTracker mVelocityTracker; private int mDownPosition; @@ -108,9 +107,12 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { private boolean mPaused; /** - * Custom gesture listener + * Dismissable Manager */ - protected ScaleGestureDetector mGestureDetector; + protected Dismissable mDismissable; + + + private int swipeDistanceDivisor = 2; /** * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client @@ -120,7 +122,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { /** * Called to determine whether the given position can be dismissed. */ - boolean canDismiss(int position, Card card); + boolean canDismiss(int position,Card card); /** * Called when the user has indicated they she would like to dismiss one or more list item @@ -149,9 +151,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { android.R.integer.config_shortAnimTime); mListView = listView; mCallbacks = callbacks; - if (mListView instanceof CardListView) { - mGestureDetector = ((CardListView) mListView).getGestureDetector(); - } + swipeDistanceDivisor = listView.getContext().getResources().getInteger(R.integer.list_card_swipe_distance_divisor); } /** @@ -191,9 +191,6 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { if (mViewWidth < 2) { mViewWidth = mListView.getWidth(); } - if (mGestureDetector != null && !mSwiping) { - mGestureDetector.onTouchEvent(motionEvent); - } switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: { @@ -210,12 +207,14 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { // Find the child view that was touched (perform a hit test) Rect rect = new Rect(); int childCount = mListView.getChildCount(); + int headerCount = mListView.getHeaderViewsCount(); + int footerCount = mListView.getFooterViewsCount(); int[] listViewCoords = new int[2]; mListView.getLocationOnScreen(listViewCoords); int x = (int) motionEvent.getRawX() - listViewCoords[0]; int y = (int) motionEvent.getRawY() - listViewCoords[1]; - View child; - for (int i = 0; i < childCount; i++) { + View child=null; + for (int i = headerCount; i < (childCount - footerCount); i++) { child = mListView.getChildAt(i); child.getHitRect(rect); if (rect.contains(x, y)) { @@ -225,42 +224,28 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { } if (mDownView != null) { + mDownX = motionEvent.getRawX(); mDownY = motionEvent.getRawY(); mDownPosition = mListView.getPositionForView(mDownView); - if (mCallbacks.canDismiss(mDownPosition, - (Card) mListView.getAdapter().getItem(mDownPosition))) { - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.addMovement(motionEvent); - } else { + if (mDownPosition != ListView.INVALID_POSITION && mDownPosition < mListView.getAdapter().getCount()) { + if (mListView.getAdapter().getItem(mDownPosition) instanceof Card) { + if (mCallbacks.canDismiss(mDownPosition, (Card) mListView.getAdapter().getItem(mDownPosition))) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(motionEvent); + } else { + mDownView = null; + } + } else { + mDownView = null; + } + }else{ mDownView = null; } } - return false; - } - - case MotionEvent.ACTION_CANCEL: { - if (mVelocityTracker == null) { - break; - } - - if (mDownView != null && mSwiping) { - // cancel - mDownView.animate() - .translationX(0) - .alpha(1) - .setDuration(mAnimationTime) - .setListener(null); - } - mVelocityTracker.recycle(); - mVelocityTracker = null; - mDownX = 0; - mDownY = 0; - mDownView = null; - mDownPosition = ListView.INVALID_POSITION; - mSwipeInitialized = false; - mSwiping = false; - break; + view.onTouchEvent(motionEvent); + return true; + //return false; } case MotionEvent.ACTION_UP: { @@ -276,7 +261,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); boolean dismiss = false; boolean dismissRight = false; - if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { + if (Math.abs(deltaX) > mViewWidth / swipeDistanceDivisor && mSwiping) { dismiss = true; dismissRight = deltaX > 0; } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity @@ -287,19 +272,8 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { } if (dismiss && mDownPosition != ListView.INVALID_POSITION) { // dismiss - final View downView = mDownView; // mDownView gets null'd before animation ends - final int downPosition = mDownPosition; - ++mDismissAnimationRefCount; - mDownView.animate() - .translationX(dismissRight ? mViewWidth : -mViewWidth) - .alpha(0) - .setDuration(mAnimationTime) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - performDismiss(downView, downPosition); - } - }); + dismiss(mDownView, mDownPosition - mListView.getHeaderViewsCount(), dismissRight); + } else { // cancel mDownView.animate() @@ -308,15 +282,15 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { .setDuration(mAnimationTime) .setListener(null); } + mVelocityTracker.recycle(); mVelocityTracker = null; mDownX = 0; mDownY = 0; mDownView = null; mDownPosition = ListView.INVALID_POSITION; - mSwipeInitialized = false; - if (mSwiping) { - // To prevent onClick event with a fast swipe + if (mSwiping){ + //To prevent onClick event with a fast swipe mSwiping = false; return true; } @@ -324,6 +298,29 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { break; } + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + if (mDownView != null) { + // cancel + mDownView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mDownX = 0; + mDownY = 0; + mDownView = null; + mDownPosition = ListView.INVALID_POSITION; + mSwiping = false; + break; + } + case MotionEvent.ACTION_MOVE: { if (mVelocityTracker == null || mPaused) { break; @@ -332,12 +329,10 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { mVelocityTracker.addMovement(motionEvent); float deltaX = motionEvent.getRawX() - mDownX; float deltaY = motionEvent.getRawY() - mDownY; - if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { + boolean movementAllowed = isSwipeMovementAllowed(deltaX); + if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2 && movementAllowed) { mSwiping = true; - if (!mSwipeInitialized) { - mSwipeInitialized = true; - mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); - } + mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); mListView.requestDisallowInterceptTouchEvent(true); // Cancel ListView's touch (un-highlighting the item) @@ -362,6 +357,28 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { return false; } + private void dismiss(final View view, final int position, boolean dismissRight) { + ++mDismissAnimationRefCount; + if (view == null) { + // No view, shortcut to calling onDismiss to let it deal with adapter + // updates and all that. + mCallbacks.onDismiss(mListView, new int[] { position }); + return; + } + + view.animate() + .translationX(dismissRight ? mViewWidth : -mViewWidth) + .alpha(0) + .setDuration(mAnimationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + performDismiss(view, position); + } + }); + } + + class PendingDismissData implements Comparable<PendingDismissData> { public int position; public View view; @@ -439,4 +456,25 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener { mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView)); animator.start(); } -} + + private boolean isSwipeMovementAllowed(float deltaX) { + switch (mDismissable.getSwipeDirectionAllowed()) { + case BOTH: + return Math.abs(deltaX) > 0; + case RIGHT: + return deltaX > 0; + case LEFT: + return deltaX < 0; + default: + return false; + } + } + + /** + * Sets a custom DismissableManager + * @param dismissable + */ + public void setDismissable(Dismissable dismissable) { + mDismissable = dismissable; + } +}
\ No newline at end of file diff --git a/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTopBottomTouchListener.java b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTopBottomTouchListener.java new file mode 100644 index 0000000..4335684 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTopBottomTouchListener.java @@ -0,0 +1,285 @@ +/* + * ****************************************************************************** + * Copyright (c) 2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; + +/** + * A {@link android.view.View.OnTouchListener} that makes any {@link android.view.View} dismissable when the + * user swipes (drags her finger) horizontally across the view. + * + * <p><em>For {@link android.widget.ListView} list items that don't manage their own touch events + * (i.e. you're using + * {@link android.widget.ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)} + * or an equivalent listener on {@link android.app.ListActivity} or + * {@link android.app.ListFragment}, use {@link com.android.cards.view.listener.SwipeDismissListViewTouchListener} instead.</em></p> + * + * <p>Example usage:</p> + * + * <pre> + * view.setOnTouchListener(new SwipeDismissTouchListener( + * view, + * null, // Optional token/cookie object + * new SwipeDismissTouchListener.OnDismissCallback() { + * public void onDismiss(View view, Object token) { + * parent.removeView(view); + * } + * })); + * </pre> + * + * <p>This class Requires API level 12 or later due to use of {@link + * android.view.ViewPropertyAnimator}.</p> + * + * @see com.android.cards.view.listener.SwipeDismissListViewTouchListener + */ +public class SwipeDismissTopBottomTouchListener implements View.OnTouchListener { + + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + + // Fixed properties + private View mView; + private DismissCallbacks mCallbacks; + private int mViewHeight = 1; // 1 and not 0 to prevent dividing by zero + + // Transient properties + private float mDownX; + private float mDownY; + private boolean mSwiping; + private int mSwipingSlop; + private Object mToken; + private VelocityTracker mVelocityTracker; + private float mTranslationY; + private float mOriginalY; + + /** + * The callback interface used by {@link com.android.cards.view.listener.SwipeDismissTopBottomTouchListener} to inform its client + * about a successful dismissal of the view for which it was created. + */ + public interface DismissCallbacks { + /** + * Called to determine whether the view can be dismissed. + */ + boolean canDismiss(Object token); + + /** + * Called when the user has indicated they she would like to dismiss the view. + * + * @param view The originating {@link android.view.View} to be dismissed. + * @param token The optional token passed to this object's constructor. + */ + void onDismiss(View view, Object token); + } + + /** + * Constructs a new swipe-to-dismiss touch listener for the given view. + * + * @param view The view to make dismissable. + * @param token An optional token/cookie object to be passed through to the callback. + * @param callbacks The callback to trigger when the user has indicated that she would like to + * dismiss this view. + */ + public SwipeDismissTopBottomTouchListener(View view, Object token, DismissCallbacks callbacks) { + ViewConfiguration vc = ViewConfiguration.get(view.getContext()); + mSlop = 0; + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = view.getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + mView = view; + mToken = token; + mCallbacks = callbacks; + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + // offset because the view is translated during swipe + motionEvent.offsetLocation(0, mTranslationY); + + if (mViewHeight < 2) { + mViewHeight = mView.getHeight(); + } + + if (mOriginalY == 0){ + mOriginalY = mView.getY(); + } + + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // TODO: ensure this is a finger, and set a flag + mDownX = motionEvent.getRawX(); + mDownY = motionEvent.getRawY(); + if (mCallbacks.canDismiss(mToken)) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(motionEvent); + } + view.onTouchEvent(motionEvent); + return true; + } + + case MotionEvent.ACTION_UP: { + if (mVelocityTracker == null) { + break; + } + + float deltaY = motionEvent.getRawY() - mDownY; + mVelocityTracker.addMovement(motionEvent); + mVelocityTracker.computeCurrentVelocity(1000); + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + boolean dismiss = false; + boolean dismissRight = false; + if (Math.abs(deltaY) > 2 * mViewHeight / 3 && mSwiping) { + dismiss = true; + dismissRight = deltaY > 0; + } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX + && mSwiping) { + // dismiss only if flinging in the same direction as dragging + dismiss = (velocityX < 0) == (deltaY < 0); + dismissRight = mVelocityTracker.getXVelocity() > 0; + } + if (dismiss) { + // dismiss + mView.animate() + .translationY(dismissRight ? mViewHeight : -mViewHeight) + .alpha(0) + .setDuration(mAnimationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + performDismiss(); + } + }); + } else if (mSwiping) { + // cancel + mView.animate() + .translationY(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mTranslationY = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + mView.animate() + .translationY(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + mVelocityTracker.recycle(); + mVelocityTracker = null; + mTranslationY = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_MOVE: { + if (mVelocityTracker == null) { + break; + } + + mVelocityTracker.addMovement(motionEvent); + float deltaX = motionEvent.getRawX() - mDownX; + float deltaY = motionEvent.getRawY() - mDownY; + if (Math.abs(deltaY) > mSlop && Math.abs(deltaX) < Math.abs(deltaY) / 2 & deltaY > 0) { + mSwiping = true; + mSwipingSlop = (deltaY > 0 ? mSlop : 0); + mView.getParent().requestDisallowInterceptTouchEvent(true); + + // Cancel listview's touch + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | + (motionEvent.getActionIndex() << + MotionEvent.ACTION_POINTER_INDEX_SHIFT)); + mView.onTouchEvent(cancelEvent); + cancelEvent.recycle(); + } + + if (mSwiping) { + mTranslationY = deltaY >=0 ? deltaY : 0; + mView.setTranslationY( deltaY >=0 ? deltaY - mSwipingSlop: 0); + // TODO: use an ease-out interpolator or such + mView.setAlpha(deltaY >=0 ? Math.max(0f, Math.min(1f, + 1f - 1.5f * Math.abs(deltaY) / mViewHeight)): 1f); + return true; + } + break; + } + } + return false; + } + + private void performDismiss() { + // Animate the dismissed view to zero-height and then fire the dismiss callback. + // This triggers layout on each animation frame; in the future we may want to do something + // smarter and more performant. + + final ViewGroup.LayoutParams lp = mView.getLayoutParams(); + final int originalHeight = mView.getHeight(); + + ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCallbacks.onDismiss(mView, mToken); + // Reset view presentation + mView.setAlpha(1f); + mView.setTranslationY(0); + lp.height = originalHeight; + mView.setLayoutParams(lp); + } + }); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + lp.height = (Integer) valueAnimator.getAnimatedValue(); + mView.setLayoutParams(lp); + } + }); + + animator.start(); + } +}
\ No newline at end of file diff --git a/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTouchListener.java b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTouchListener.java new file mode 100644 index 0000000..f9ebdd7 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissTouchListener.java @@ -0,0 +1,284 @@ +/* + * ****************************************************************************** + * Copyright (c) 2014 Google Inc., Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ListActivity; +import android.app.ListFragment; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +/** + * A {@link View.OnTouchListener} that makes any {@link View} dismissable when the + * user swipes (drags her finger) horizontally across the view. + * + * <p><em>For {@link ListView} list items that don't manage their own touch events + * (i.e. you're using + * {@link ListView#setOnItemClickListener(AdapterView.OnItemClickListener)} + * or an equivalent listener on {@link ListActivity} or + * {@link ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.</em></p> + * + * <p>Example usage:</p> + * + * <pre> + * view.setOnTouchListener(new SwipeDismissTouchListener( + * view, + * null, // Optional token/cookie object + * new SwipeDismissTouchListener.OnDismissCallback() { + * public void onDismiss(View view, Object token) { + * parent.removeView(view); + * } + * })); + * </pre> + * + * <p>This class Requires API level 12 or later due to use of {@link + * android.view.ViewPropertyAnimator}.</p> + * + * @see SwipeDismissListViewTouchListener + */ +public class SwipeDismissTouchListener implements View.OnTouchListener { + + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + + // Fixed properties + private View mView; + private DismissCallbacks mCallbacks; + private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero + + // Transient properties + private float mDownX; + private float mDownY; + private boolean mSwiping; + private int mSwipingSlop; + private Object mToken; + private VelocityTracker mVelocityTracker; + private float mTranslationX; + + /** + * The callback interface used by {@link SwipeDismissTouchListener} to inform its client + * about a successful dismissal of the view for which it was created. + */ + public interface DismissCallbacks { + /** + * Called to determine whether the view can be dismissed. + */ + boolean canDismiss(Object token); + + /** + * Called when the user has indicated they she would like to dismiss the view. + * + * @param view The originating {@link View} to be dismissed. + * @param token The optional token passed to this object's constructor. + */ + void onDismiss(View view, Object token); + } + + /** + * Constructs a new swipe-to-dismiss touch listener for the given view. + * + * @param view The view to make dismissable. + * @param token An optional token/cookie object to be passed through to the callback. + * @param callbacks The callback to trigger when the user has indicated that she would like to + * dismiss this view. + */ + public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) { + ViewConfiguration vc = ViewConfiguration.get(view.getContext()); + mSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = view.getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + mView = view; + mToken = token; + mCallbacks = callbacks; + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + // offset because the view is translated during swipe + motionEvent.offsetLocation(mTranslationX, 0); + + if (mViewWidth < 2) { + mViewWidth = mView.getWidth(); + } + + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // TODO: ensure this is a finger, and set a flag + mDownX = motionEvent.getRawX(); + mDownY = motionEvent.getRawY(); + if (mCallbacks.canDismiss(mToken)) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(motionEvent); + } + view.onTouchEvent(motionEvent); + return true; + } + + case MotionEvent.ACTION_UP: { + if (mVelocityTracker == null) { + break; + } + + float deltaX = motionEvent.getRawX() - mDownX; + mVelocityTracker.addMovement(motionEvent); + mVelocityTracker.computeCurrentVelocity(1000); + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + boolean dismiss = false; + boolean dismissRight = false; + if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { + dismiss = true; + dismissRight = deltaX > 0; + } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX + && mSwiping) { + // dismiss only if flinging in the same direction as dragging + dismiss = (velocityX < 0) == (deltaX < 0); + dismissRight = mVelocityTracker.getXVelocity() > 0; + } + if (dismiss) { + // dismiss + mView.animate() + .translationX(dismissRight ? mViewWidth : -mViewWidth) + .alpha(0) + .setDuration(mAnimationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + performDismiss(); + } + }); + } else if (mSwiping) { + // cancel + mView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mTranslationX = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + mView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + mVelocityTracker.recycle(); + mVelocityTracker = null; + mTranslationX = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_MOVE: { + if (mVelocityTracker == null) { + break; + } + + mVelocityTracker.addMovement(motionEvent); + float deltaX = motionEvent.getRawX() - mDownX; + float deltaY = motionEvent.getRawY() - mDownY; + if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { + mSwiping = true; + mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); + mView.getParent().requestDisallowInterceptTouchEvent(true); + + // Cancel listview's touch + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | + (motionEvent.getActionIndex() << + MotionEvent.ACTION_POINTER_INDEX_SHIFT)); + mView.onTouchEvent(cancelEvent); + cancelEvent.recycle(); + } + + if (mSwiping) { + mTranslationX = deltaX; + mView.setTranslationX(deltaX - mSwipingSlop); + // TODO: use an ease-out interpolator or such + mView.setAlpha(Math.max(0f, Math.min(1f, + 1f - 2f * Math.abs(deltaX) / mViewWidth))); + return true; + } + break; + } + } + return false; + } + + private void performDismiss() { + // Animate the dismissed view to zero-height and then fire the dismiss callback. + // This triggers layout on each animation frame; in the future we may want to do something + // smarter and more performant. + + final ViewGroup.LayoutParams lp = mView.getLayoutParams(); + final int originalHeight = mView.getHeight(); + + ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCallbacks.onDismiss(mView, mToken); + // Reset view presentation + mView.setAlpha(1f); + mView.setTranslationX(0); + lp.height = originalHeight; + mView.setLayoutParams(lp); + } + }); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + lp.height = (Integer) valueAnimator.getAnimatedValue(); + mView.setLayoutParams(lp); + } + }); + + animator.start(); + } +}
\ No newline at end of file diff --git a/src/com/android/cards/view/listener/SwipeDismissViewTouchListener.java b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissViewTouchListener.java index 16fb6ec..2768a25 100644 --- a/src/com/android/cards/view/listener/SwipeDismissViewTouchListener.java +++ b/src/it/gmariotti/cardslib/library/view/listener/SwipeDismissViewTouchListener.java @@ -28,8 +28,10 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import com.android.cards.R; import com.android.cards.internal.Card; -import com.android.cards.view.CardView; +import com.android.cards.view.base.CardViewWrapper; + /** * It is based on Roman Nurik code. @@ -51,18 +53,22 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { private long mAnimationTime; // Fixed properties - private CardView mCardView; + private CardViewWrapper mCardView; private DismissCallbacks mCallbacks; private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero // Transient properties private float mDownX; + private float mDownY; private Card mToken; private boolean mSwiping; + private int mSwipingSlop; private VelocityTracker mVelocityTracker; private boolean mPaused; private float mTranslationX; + private int swipeDistanceDivisor = 2; + /** * The callback interface used by {@link SwipeDismissViewTouchListener} * to inform its client about a successful dismissal of the view for which it was created. @@ -79,7 +85,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { * @param cardView The originating {@link com.android.cards.view.CardView}. * @parma card Card */ - void onDismiss(CardView cardView,Card card); + void onDismiss(CardViewWrapper cardView,Card card); } /** @@ -88,7 +94,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { * @param cardView The card view which should be dismissable. * @param callbacks The callback to trigger when the user has indicated that she */ - public SwipeDismissViewTouchListener(CardView cardView, + public SwipeDismissViewTouchListener(CardViewWrapper cardView, Card card, DismissCallbacks callbacks) { ViewConfiguration vc = ViewConfiguration.get(cardView.getContext()); @@ -100,6 +106,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { mCardView = cardView; mToken= card; mCallbacks = callbacks; + swipeDistanceDivisor = cardView.getContext().getResources().getInteger(R.integer.list_card_swipe_distance_divisor); } /** @@ -119,7 +126,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { motionEvent.offsetLocation(mTranslationX, 0); if (mViewWidth < 2) { - mViewWidth = mCardView.getWidth(); + mViewWidth = ((View)mCardView).getWidth(); } switch (motionEvent.getActionMasked()) { @@ -131,6 +138,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { // TODO: ensure this is a finger, and set a flag mDownX = motionEvent.getRawX(); + mDownY = motionEvent.getRawY(); if (mCallbacks.canDismiss(mToken)) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(motionEvent); @@ -153,12 +161,12 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); boolean dismiss = false; boolean dismissRight = false; - if (Math.abs(deltaX) > mViewWidth / 2) { + if (Math.abs(deltaX) > mViewWidth / swipeDistanceDivisor && mSwiping) { dismiss = true; dismissRight = deltaX > 0; } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity - && absVelocityY < absVelocityX) { + && absVelocityY < absVelocityX && mSwiping) { // dismiss only if flinging in the same direction as dragging dismiss = (velocityX < 0) == (deltaX < 0); dismissRight = mVelocityTracker.getXVelocity() > 0; @@ -166,7 +174,7 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { if (dismiss) { // dismiss - mCardView.animate() + ((View)mCardView).animate() .translationX(dismissRight ? mViewWidth : -mViewWidth) .alpha(0).setDuration(mAnimationTime) .setListener(new AnimatorListenerAdapter() { @@ -175,14 +183,35 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { performDismiss(); } }); - } else { + } else if (mSwiping) { // cancel - mCardView.animate().translationX(0).alpha(1) + ((View)mCardView).animate().translationX(0).alpha(1) .setDuration(mAnimationTime).setListener(null); } mVelocityTracker.recycle(); mVelocityTracker = null; + mTranslationX = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + ((View)mCardView).animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + mVelocityTracker.recycle(); + mVelocityTracker = null; + mTranslationX = 0; mDownX = 0; + mDownY = 0; mSwiping = false; break; } @@ -194,23 +223,26 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { mVelocityTracker.addMovement(motionEvent); float deltaX = motionEvent.getRawX() - mDownX; - if (Math.abs(deltaX) > mSlop) { + float deltaY = motionEvent.getRawY() - mDownY; + if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { mSwiping = true; - mCardView.getParent().requestDisallowInterceptTouchEvent(true); - + ((View)mCardView).getParent().requestDisallowInterceptTouchEvent(true); + mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); + // Cancel ListView's touch (un-highlighting the item) MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); cancelEvent .setAction(MotionEvent.ACTION_CANCEL | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); - mCardView.onTouchEvent(cancelEvent); + ((View)mCardView).onTouchEvent(cancelEvent); cancelEvent.recycle(); } if (mSwiping) { mTranslationX = deltaX; - mCardView.setTranslationX(deltaX); - mCardView.setAlpha(Math.max(0f, + //((View)mCardView).setTranslationX(deltaX); + ((View)mCardView).setTranslationX(deltaX - mSwipingSlop); + ((View)mCardView).setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth))); return true; } @@ -226,8 +258,8 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { // This triggers layout on each animation frame; in the future we may want to do something // smarter and more performant. - final ViewGroup.LayoutParams lp = mCardView.getLayoutParams(); - final int originalHeight = mCardView.getHeight(); + final ViewGroup.LayoutParams lp = ((View)mCardView).getLayoutParams(); + final int originalHeight = ((View)mCardView).getHeight(); ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1) .setDuration(mAnimationTime); @@ -238,11 +270,11 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { mCallbacks.onDismiss(mCardView,mToken); // Reset view presentation - mCardView.setAlpha(1f); - mCardView.setTranslationX(0); + ((View)mCardView).setAlpha(1f); + ((View)mCardView).setTranslationX(0); //ViewGroup.LayoutParams lp = mCardView.getLayoutParams(); lp.height = originalHeight; - mCardView.setLayoutParams(lp); + ((View) mCardView).setLayoutParams(lp); } }); @@ -250,10 +282,10 @@ public class SwipeDismissViewTouchListener implements View.OnTouchListener { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { lp.height = (Integer) valueAnimator.getAnimatedValue(); - mCardView.setLayoutParams(lp); + ((View)mCardView).setLayoutParams(lp); } }); animator.start(); } -} +}
\ No newline at end of file diff --git a/src/com/android/cards/view/listener/SwipeOnScrollListener.java b/src/it/gmariotti/cardslib/library/view/listener/SwipeOnScrollListener.java index efcc193..be37336 100644 --- a/src/com/android/cards/view/listener/SwipeOnScrollListener.java +++ b/src/it/gmariotti/cardslib/library/view/listener/SwipeOnScrollListener.java @@ -48,4 +48,4 @@ public class SwipeOnScrollListener implements AbsListView.OnScrollListener { @Override public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { } -} +}
\ No newline at end of file diff --git a/src/it/gmariotti/cardslib/library/view/listener/UndoBarController.java b/src/it/gmariotti/cardslib/library/view/listener/UndoBarController.java new file mode 100644 index 0000000..4513c8a --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/UndoBarController.java @@ -0,0 +1,478 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013 Roman Nurik, 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcelable; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.widget.TextView; + +import com.android.cards.R; +import com.android.cards.internal.CardArrayAdapter; + +/** + * It is based on Roman Nurik code. + * See this link for original code: + * https://code.google.com/p/romannurik-code/source/browse/#git%2Fmisc%2Fundobar + * + * + */ +public class UndoBarController { + + private View mBarView; + private TextView mMessageView; + private ViewPropertyAnimator mBarAnimator; + private Handler mHideHandler = new Handler(); + + private UndoListener mUndoListener; + private UndoBarHideListener mUndoBarHideListener; + + // State objects + private Parcelable mUndoToken; + private CharSequence mUndoMessage; + + private UndoBarUIElements mUndoBarUIElements; + + /** + * Interface to listen the undo controller actions + */ + public interface UndoListener { + /* + * Called when you undo the action + */ + void onUndo(Parcelable undoToken); + } + + /** + * Interface to listen for when the Undo controller hides the Undo Bar, + * after it times out from being shown for the currently dismissed mUndoToken. + */ + public interface UndoBarHideListener { + /** + * Called when the UndoBar is hidden after being shown. + * @param undoOccurred true if the user pressed the undo button + * for the current mUndoToken. + */ + void onUndoBarHide(boolean undoOccurred); + } + + public UndoBarController(View undoBarView, UndoListener undoListener) { + this (undoBarView,undoListener,null); + } + + public UndoBarController(View undoBarView, UndoListener undoListener,UndoBarUIElements undoBarUIElements) { + mBarView = undoBarView; + mBarAnimator = mBarView.animate(); + mUndoListener = undoListener; + + if (undoBarUIElements==null) + undoBarUIElements = new DefaultUndoBarUIElements(); + mUndoBarUIElements = undoBarUIElements; + + mMessageView = (TextView) mBarView.findViewById(mUndoBarUIElements.getUndoBarMessageId()); + mBarView.findViewById(mUndoBarUIElements.getUndoBarButtonId()) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + hideUndoBar(false); + mUndoListener.onUndo(mUndoToken); + // Remove the reference to the undo token, since the undo has occurred. + mUndoToken = null; + } + }); + + setupAnimation(); + + if (mUndoBarUIElements.isEnabledUndoBarSwipeAction() != UndoBarUIElements.SwipeDirectionEnabled.NONE){ + setupSwipeActionOnUndoBar(); + } + + hideUndoBar(true); + } + + + + public void showUndoBar(boolean immediate, CharSequence message, + Parcelable undoToken, UndoBarHideListener undoBarHideListener) { + + // We're replacing the existing UndoBarHideListener, meaning that + // the original object removal was not undone. So, execute + // onUndoBarHide for the previous listener. + if (mUndoBarHideListener != null) { + mUndoBarHideListener.onUndoBarHide(mUndoToken == null); + } + + + mUndoToken = undoToken; + mUndoMessage = message; + mUndoBarHideListener = undoBarHideListener; + mMessageView.setText(mUndoMessage); + + mHideHandler.removeCallbacks(mHideRunnable); + mHideHandler.postDelayed(mHideRunnable, + mBarView.getResources().getInteger(R.integer.list_card_undobar_hide_delay)); + + mBarView.setVisibility(View.VISIBLE); + if (immediate) { + mBarView.setAlpha(1); + } else { + + if (mUndoBarUIElements.getAnimationType() == UndoBarUIElements.AnimationType.ALPHA) { + + mBarAnimator.cancel(); + mBarAnimator + .alpha(1) + .setDuration( + mBarView.getResources() + .getInteger(android.R.integer.config_shortAnimTime)) + .setListener(null); + } else if (mUndoBarUIElements.getAnimationType() == UndoBarUIElements.AnimationType.TOPBOTTOM){ + + mBarAnimator.cancel(); + mBarAnimator + .alpha(1) + .translationY(0) + .setDuration( + mBarView.getResources() + .getInteger(android.R.integer.config_mediumAnimTime)) + .setListener(null); + + } + } + } + + public void hideUndoBar(boolean immediate) { + mHideHandler.removeCallbacks(mHideRunnable); + if (immediate) { + mBarView.setVisibility(View.GONE); + mBarView.setAlpha(0); + mUndoMessage = null; + if (mUndoBarHideListener != null) { + // The undo has occurred only if mUndoToken was set to null. + mUndoBarHideListener.onUndoBarHide(mUndoToken == null); + } + mUndoBarHideListener = null; + mUndoToken = null; + } else { + mBarAnimator.cancel(); + + if (mUndoBarUIElements.getAnimationType() == UndoBarUIElements.AnimationType.ALPHA) { + mBarAnimator + .alpha(0) + .setDuration(mBarView.getResources() + .getInteger(android.R.integer.config_shortAnimTime)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBarView.setVisibility(View.GONE); + mUndoMessage = null; + if (mUndoBarHideListener != null) { + // The undo has occurred only if mUndoToken was set to null. + mUndoBarHideListener.onUndoBarHide(mUndoToken == null); + } + mUndoBarHideListener = null; + mUndoToken = null; + } + }); + } else if (mUndoBarUIElements.getAnimationType() == UndoBarUIElements.AnimationType.TOPBOTTOM){ + mBarAnimator + .alpha(0) + .translationY(+mBarView.getHeight()) + .setDuration(mBarView.getResources() + .getInteger(android.R.integer.config_shortAnimTime)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBarView.setVisibility(View.GONE); + mUndoMessage = null; + if (mUndoBarHideListener != null) { + // The undo has occurred only if mUndoToken was set to null. + mUndoBarHideListener.onUndoBarHide(mUndoToken == null); + } + mUndoBarHideListener = null; + mUndoToken = null; + } + }); + } + } + } + + public void onSaveInstanceState(Bundle outState) { + outState.putCharSequence("undo_message", mUndoMessage); + outState.putParcelable("undo_token", mUndoToken); + } + + public void onRestoreInstanceState(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mUndoMessage = savedInstanceState.getCharSequence("undo_message"); + mUndoToken = savedInstanceState.getParcelable("undo_token"); + + if (mUndoToken != null || !TextUtils.isEmpty(mUndoMessage)) { + showUndoBar(true, mUndoMessage, mUndoToken, mUndoBarHideListener); + } + } + } + + private Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hideUndoBar(false); + } + }; + + public Parcelable getUndoToken(){ + return mUndoToken; + } + + private void setupAnimation(){ + if (mUndoBarUIElements.getAnimationType() == UndoBarUIElements.AnimationType.TOPBOTTOM) { + mBarView.setTranslationY(mBarView.getHeight()); + } + } + + private void setupSwipeActionOnUndoBar() { + if (mBarView != null) { + + if (mUndoBarUIElements.isEnabledUndoBarSwipeAction() == UndoBarUIElements.SwipeDirectionEnabled.LEFTRIGHT) { + + mBarView.setOnTouchListener(new SwipeDismissTouchListener(mBarView, null, + new SwipeDismissTouchListener.DismissCallbacks() { + @Override + public boolean canDismiss(Object token) { + return mUndoBarUIElements.isEnabledUndoBarSwipeAction() != UndoBarUIElements.SwipeDirectionEnabled.NONE; + } + + @Override + public void onDismiss(View view, Object token) { + hideUndoBar(true); + mUndoListener.onUndo(mUndoToken); + // Remove the reference to the undo token, since the undo has occurred. + mUndoToken = null; + } + })); + } else if (mUndoBarUIElements.isEnabledUndoBarSwipeAction() == UndoBarUIElements.SwipeDirectionEnabled.TOPBOTTOM) { + + mBarView.setOnTouchListener(new SwipeDismissTopBottomTouchListener(mBarView, null, + new SwipeDismissTopBottomTouchListener.DismissCallbacks() { + @Override + public boolean canDismiss(Object token) { + return mUndoBarUIElements.isEnabledUndoBarSwipeAction() != UndoBarUIElements.SwipeDirectionEnabled.NONE; + } + + @Override + public void onDismiss(View view, Object token) { + hideUndoBar(true); + mUndoListener.onUndo(mUndoToken); + // Remove the reference to the undo token, since the undo has occurred. + mUndoToken = null; + } + })); + } + } + + } + + // ------------------------------------------------------------- + // Undo Custom Bar + // ------------------------------------------------------------- + + /** + * Interface to set the ui elements in undo bar + */ + public interface UndoBarUIElements{ + + /** + * UndoBar id + * @return + */ + public int getUndoBarId(); + + /** + * TextView Id which displays message + * + * @return + */ + public int getUndoBarMessageId(); + + /** + * UndoButton Id + * + * @return + */ + public int getUndoBarButtonId(); + + /** + * UndoMessage. + * Implement this method to customize the undo message dynamically. + * </p> + * You can't find the cards with these positions in your arrayAdapter because the cards are removed. + * You have to/can use your id itemIds, to identify your cards. + * + * @param cardArrayAdapter array Adapter + * @param itemIds ids of items + * @param itemPositions position of removed items + * @return + */ + public String getMessageUndo(CardArrayAdapter cardArrayAdapter,String[] itemIds,int[] itemPositions); + + /** + * Define the swipe action to remove the undobar. + * @return + */ + public SwipeDirectionEnabled isEnabledUndoBarSwipeAction(); + + /** + * Define the animation type for the undobar when it appears. + * @return + */ + public AnimationType getAnimationType(); + + + /** + * Enum to define the animation type of the undobar.<p/> + * Use the {@link AnimationType#ALPHA} for an alpha animation, or {@link AnimationType#TOPBOTTOM} for a translation from bottom to top. + */ + public enum AnimationType { + ALPHA(0), + TOPBOTTOM(1); + + private final int mValue; + + private AnimationType(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + } + + /** + * Enum to define the direction of the swipe action. + * <p/> + * You can use {@link SwipeDirectionEnabled#NONE} to disable the swipe action or {@link SwipeDirectionEnabled#LEFTRIGHT} to enable an action in left-right direction + * or {@link SwipeDirectionEnabled#TOPBOTTOM} to define a swipe action from top to bottom. + */ + public enum SwipeDirectionEnabled { + NONE(0), + LEFTRIGHT(1), + TOPBOTTOM(2); + + private final int mValue; + + private SwipeDirectionEnabled(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + } + + } + + /** + * Default UndoBar + * + * You can provide a custom UndoBar. + * This UndoBar has to contains these elements: + * <ul> + * <li>A TextView</li> + * <li>A Button</li> + * <li>A root element with an id attribute </li> + * </ul> + * + * You should use the same Ids provided in the default layout list_card_undo_message, + * but if you have to use different ids you can use the CardArrayAdapter.setUndoBarUIElements. + * + * Example: + * <code> + * mCardArrayAdapter.setUndoBarUIElements(new UndoBarController.DefaultUndoBarUIElements(){ + * + * //Override methods to customize the elements + * } + * </code> + * It is very important to set the UndoBarUIElements before to call the setEnableUndo(true); + * + */ + public static class DefaultUndoBarUIElements implements UndoBarUIElements { + + public DefaultUndoBarUIElements(){}; + + @Override + public int getUndoBarId() { + return R.id.list_card_undobar; + } + + @Override + public int getUndoBarMessageId() { + return R.id.list_card_undobar_message; + } + + @Override + public int getUndoBarButtonId() { + return R.id.list_card_undobar_button; + } + + @Override + public String getMessageUndo(CardArrayAdapter cardArrayAdapter, String[] itemIds, int[] itemPositions) { + if (cardArrayAdapter!=null && cardArrayAdapter.getContext()!=null) { + Resources res = cardArrayAdapter.getContext().getResources(); + if (res!=null) + return res.getQuantityString(R.plurals.list_card_undo_items, itemPositions.length, itemPositions.length); + } + return null; + } + + @Override + public SwipeDirectionEnabled isEnabledUndoBarSwipeAction() { + return SwipeDirectionEnabled.NONE; + } + + @Override + public AnimationType getAnimationType() { + return AnimationType.ALPHA; + } + + }; + + + /** + * Sets UndoBar UI Elements + * + * @return + */ + public UndoBarUIElements getUndoBarUIElements() { + return mUndoBarUIElements; + } + + /** + * Returns UndoBar UI Elements + * @param undoBarUIElements + */ + public void setUndoBarUIElements(UndoBarUIElements undoBarUIElements) { + this.mUndoBarUIElements = undoBarUIElements; + } +} diff --git a/src/com/android/cards/view/listener/UndoCard.java b/src/it/gmariotti/cardslib/library/view/listener/UndoCard.java index 10f8b3a..10f8b3a 100644 --- a/src/com/android/cards/view/listener/UndoCard.java +++ b/src/it/gmariotti/cardslib/library/view/listener/UndoCard.java diff --git a/src/it/gmariotti/cardslib/library/view/listener/dismiss/AbstractDismissableManager.java b/src/it/gmariotti/cardslib/library/view/listener/dismiss/AbstractDismissableManager.java new file mode 100644 index 0000000..7d8f4eb --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/dismiss/AbstractDismissableManager.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener.dismiss; + +import android.widget.Adapter; + +import com.android.cards.internal.Card; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public abstract class AbstractDismissableManager implements Dismissable { + + protected Adapter mAdapter; + + @Override + public final boolean isDismissable(int position, Card card) { + return card.isSwipeable(); + } + + @Override + public abstract SwipeDirection getSwipeDirectionAllowed(); + + + public void setAdapter(Adapter adapter) { + mAdapter = adapter; + } +} diff --git a/src/it/gmariotti/cardslib/library/view/listener/dismiss/DefaultDismissableManager.java b/src/it/gmariotti/cardslib/library/view/listener/dismiss/DefaultDismissableManager.java new file mode 100644 index 0000000..3a07b02 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/dismiss/DefaultDismissableManager.java @@ -0,0 +1,35 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener.dismiss; + +/** + * Default DismissableManager.<p/> + * Extend this class to implement you favorite logic. + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class DefaultDismissableManager extends AbstractDismissableManager { + + + @Override + public SwipeDirection getSwipeDirectionAllowed() { + return SwipeDirection.BOTH; + } + +} diff --git a/src/it/gmariotti/cardslib/library/view/listener/dismiss/Dismissable.java b/src/it/gmariotti/cardslib/library/view/listener/dismiss/Dismissable.java new file mode 100644 index 0000000..1b5bfe9 --- /dev/null +++ b/src/it/gmariotti/cardslib/library/view/listener/dismiss/Dismissable.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.view.listener.dismiss; + +import android.widget.Adapter; + +import com.android.cards.internal.Card; + +/** + * An interface to specify if items can or cannot be dismissed and the SwipeDirection + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public interface Dismissable { + + /** + * Returns whether the item for given id and position can be dismissed. + * @param position the position of the item. + * @param card card + * @return true if the item can be dismissed, false otherwise. + */ + boolean isDismissable(int position, Card card); + + SwipeDirection getSwipeDirectionAllowed(); + + void setAdapter(Adapter adapter); + + + public enum SwipeDirection { + BOTH(0), + LEFT(1), + RIGHT(2); + + private final int mValue; + + private SwipeDirection(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + } +} |
