Dbrant has uploaded a new change for review. https://gerrit.wikimedia.org/r/214117
Change subject: [WIP] Tabbed browsing. ...................................................................... [WIP] Tabbed browsing. Bug: T69251 Change-Id: I00967dc2256fc2cb8c251cc5a6a0ebb83e144a6e --- A icon-svgs/24/ic_add_tab.noflip.svg A icon-svgs/24/ic_close_gray.noflip.svg A icon-svgs/24/ic_tab_list.noflip.svg M wikipedia/AndroidManifest.xml A wikipedia/res/anim/tab_item_press.xml A wikipedia/res/anim/tab_list_items_enter.xml A wikipedia/res/anim/tab_list_items_exit.xml A wikipedia/res/anim/tab_list_zoom_enter.xml A wikipedia/res/anim/tab_list_zoom_exit.xml A wikipedia/res/drawable-hdpi/ic_add_tab.png A wikipedia/res/drawable-hdpi/ic_close_gray.png A wikipedia/res/drawable-hdpi/ic_tab_list.png A wikipedia/res/drawable-ldpi/ic_add_tab.png A wikipedia/res/drawable-ldpi/ic_close_gray.png A wikipedia/res/drawable-ldpi/ic_tab_list.png A wikipedia/res/drawable-mdpi/ic_add_tab.png A wikipedia/res/drawable-mdpi/ic_close_gray.png A wikipedia/res/drawable-mdpi/ic_tab_list.png A wikipedia/res/drawable-xhdpi/ic_add_tab.png A wikipedia/res/drawable-xhdpi/ic_close_gray.png A wikipedia/res/drawable-xhdpi/ic_tab_list.png A wikipedia/res/drawable-xxhdpi/ic_add_tab.png A wikipedia/res/drawable-xxhdpi/ic_close_gray.png A wikipedia/res/drawable-xxhdpi/ic_tab_list.png A wikipedia/res/drawable/tab_item_bottom_gradient.xml A wikipedia/res/drawable/tab_item_selector.xml A wikipedia/res/drawable/tab_item_shape.xml A wikipedia/res/drawable/tab_item_shape_selected.xml M wikipedia/res/layout/activity_page.xml M wikipedia/res/layout/dialog_page_info.xml A wikipedia/res/layout/item_tab_entry.xml M wikipedia/res/menu/menu_page_actions.xml A wikipedia/res/menu/menu_page_long_press.xml A wikipedia/res/menu/menu_tabs.xml M wikipedia/res/values/strings.xml M wikipedia/src/main/java/org/wikipedia/page/PageActivity.java A wikipedia/src/main/java/org/wikipedia/page/PageLongPressHandler.java M wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java A wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java A wikipedia/src/main/java/org/wikipedia/page/tabs/TabsProvider.java 40 files changed, 1,052 insertions(+), 20 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia refs/changes/17/214117/1 diff --git a/icon-svgs/24/ic_add_tab.noflip.svg b/icon-svgs/24/ic_add_tab.noflip.svg new file mode 100644 index 0000000..0e7f37e --- /dev/null +++ b/icon-svgs/24/ic_add_tab.noflip.svg @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="ic_add_tab.noflip.svg"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1156" + inkscape:window-height="725" + id="namedview8" + showgrid="false" + inkscape:zoom="27.812867" + inkscape:cx="6.8946906" + inkscape:cy="10.24447" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="svg2" /> + <path + d="M0 0h24v24h-24z" + fill="none" + id="path4" /> + <path + d="M 4,6 2,6 2,20 c 0,1.1 0.9,2 2,2 l 14,0 0,-2 -14,0 z M 20,2 8,2 C 6.9,2 6,2.9 6,4 l 0,12 c 0,1.1 0.9,2 2,2 l 12,0 c 1.1,0 2,-0.9 2,-2 L 22,4 C 22,2.9 21.1,2 20,2 Z m -1,9 -4,0 0,4 -2,0 0,-4 -4,0 0,-2 4,0 0,-4 2,0 0,4 4,0 z" + id="path6" + style="fill:#ffffff" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccssccccsssssssssccccccccccccc" /> +</svg> diff --git a/icon-svgs/24/ic_close_gray.noflip.svg b/icon-svgs/24/ic_close_gray.noflip.svg new file mode 100644 index 0000000..2cbe099 --- /dev/null +++ b/icon-svgs/24/ic_close_gray.noflip.svg @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48" + height="48" + viewBox="0 0 48 48" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="ic_close_gray.noflip.svg"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1194" + inkscape:window-height="920" + id="namedview8" + showgrid="false" + inkscape:zoom="4.9166667" + inkscape:cx="-5.5932198" + inkscape:cy="24" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="svg2" /> + <path + d="M38 12.83l-2.83-2.83-11.17 11.17-11.17-11.17-2.83 2.83 11.17 11.17-11.17 11.17 2.83 2.83 11.17-11.17 11.17 11.17 2.83-2.83-11.17-11.17z" + id="path4" + style="fill:#808080;fill-opacity:1" /> + <path + d="M0 0h48v48h-48z" + fill="none" + id="path6" /> +</svg> diff --git a/icon-svgs/24/ic_tab_list.noflip.svg b/icon-svgs/24/ic_tab_list.noflip.svg new file mode 100644 index 0000000..54e0465 --- /dev/null +++ b/icon-svgs/24/ic_tab_list.noflip.svg @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="ic_tab_list.noflip.svg"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1310" + inkscape:window-height="880" + id="namedview8" + showgrid="false" +<<<<<<< HEAD + inkscape:zoom="55.625733" + inkscape:cx="3.3728809" + inkscape:cy="5.1296812" +======= + inkscape:zoom="29.86" + inkscape:cx="11.971591" + inkscape:cy="9.8484366" +>>>>>>> Tabbed browsing prototype. + inkscape:window-x="254" + inkscape:window-y="57" + inkscape:window-maximized="0" + inkscape:current-layer="svg2" /> + <path +<<<<<<< HEAD + d="M0 0h24v24h-24z" + fill="none" + id="path4" /> + <path + d="M 3,5 1,5 1,21 c 0,1.1 0.9,2 2,2 l 16,0 0,-2 -16,0 z M 21,1 7,1 C 5.9,1 5,1.9 5,3 l 0,14 c 0,1.1 0.9,2 2,2 l 14,0 c 1.1,0 2,-0.9 2,-2 L 23,3 C 23,1.9 22.1,1 21,1 Z M 21,17 7,17 7,3 21,3 Z" + id="path6" + style="fill:#ffffff" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccssccccsssssssssccccc" /> +======= + style="fill:#ffffff" + d="m 21.041392,7.0765443 -14.2735549,0 0,9.1439967 c 0,0.872273 0.7136778,1.58595 1.5859507,1.58595 l 11.1016532,0 c 0.872273,0 1.585951,-0.713677 1.585951,-1.58595 l 0,-9.1439967 z" + id="path4162" + inkscape:connector-curvature="0" /> + <path + style="fill:#ffffff" + d="m 8.3537878,3.3908514 c -0.8722729,0 -1.5859507,0.7136778 -1.5859507,1.5859507 l 0,1.090341 14.2735549,0 0,-1.090341 c 0,-0.8722729 -0.713678,-1.5859507 -1.585951,-1.5859507 l -11.1016532,0 z" + id="path4153" + inkscape:connector-curvature="0" /> + <path + style="fill:#ffffff" + d="m 4.9493418,10.280636 -1.7841944,0 0,9.143997 c 0,0.872273 0.7136777,1.58595 1.5859507,1.58595 l 11.1016539,0 c 0.872273,0 1.585951,-0.713677 1.585951,-1.58595 l -12.4893612,0 0,-9.143997 z" + id="path4204" + inkscape:connector-curvature="0" /> + <path + style="fill:#ffffff" + d="m 4.9493418,6.6186236 -0.1982437,0 c -0.872273,0 -1.5859507,0.7136778 -1.5859507,1.5859506 l 0,1.0903411 1.7841944,0 0,-2.6762917 z" + id="path4195" + inkscape:connector-curvature="0" /> +>>>>>>> Tabbed browsing prototype. +</svg> diff --git a/wikipedia/AndroidManifest.xml b/wikipedia/AndroidManifest.xml index 21597ad..7447191 100644 --- a/wikipedia/AndroidManifest.xml +++ b/wikipedia/AndroidManifest.xml @@ -13,6 +13,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.VIBRATE" /> <!-- For Nearby feature --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> diff --git a/wikipedia/res/anim/tab_item_press.xml b/wikipedia/res/anim/tab_item_press.xml new file mode 100644 index 0000000..93a33ec --- /dev/null +++ b/wikipedia/res/anim/tab_item_press.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:duration="200" + android:fromYDelta="0" + android:toYDelta="-10" + android:repeatCount="1" + android:repeatMode="reverse"/> +</set> \ No newline at end of file diff --git a/wikipedia/res/anim/tab_list_items_enter.xml b/wikipedia/res/anim/tab_list_items_enter.xml new file mode 100644 index 0000000..45bf0cb --- /dev/null +++ b/wikipedia/res/anim/tab_list_items_enter.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <translate + android:duration="300" + android:fromYDelta="-100%" + android:toYDelta="0"/> + <scale + android:duration="300" + android:fromXScale="1.33" + android:fromYScale="1.33" + android:pivotX="50%" + android:pivotY="50%" + android:toXScale="1" + android:toYScale="1"/> +</set> \ No newline at end of file diff --git a/wikipedia/res/anim/tab_list_items_exit.xml b/wikipedia/res/anim/tab_list_items_exit.xml new file mode 100644 index 0000000..05aed09 --- /dev/null +++ b/wikipedia/res/anim/tab_list_items_exit.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <translate + android:duration="300" + android:fromYDelta="0" + android:toYDelta="-100%"/> + <scale + android:duration="300" + android:fromXScale="1" + android:fromYScale="1" + android:pivotX="50%" + android:pivotY="50%" + android:toXScale="1.33" + android:toYScale="1.33"/> +</set> \ No newline at end of file diff --git a/wikipedia/res/anim/tab_list_zoom_enter.xml b/wikipedia/res/anim/tab_list_zoom_enter.xml new file mode 100644 index 0000000..d8b138a --- /dev/null +++ b/wikipedia/res/anim/tab_list_zoom_enter.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <translate + android:duration="300" + android:fromYDelta="0" + android:toYDelta="30%"/> + <scale + android:duration="300" + android:fromXScale="1" + android:fromYScale="1" + android:pivotX="50%" + android:pivotY="100%" + android:toXScale="0.75" + android:toYScale="0.75"/> +</set> \ No newline at end of file diff --git a/wikipedia/res/anim/tab_list_zoom_exit.xml b/wikipedia/res/anim/tab_list_zoom_exit.xml new file mode 100644 index 0000000..08d9446 --- /dev/null +++ b/wikipedia/res/anim/tab_list_zoom_exit.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <translate + android:duration="300" + android:fromYDelta="30%" + android:toYDelta="0%"/> + <scale + android:duration="300" + android:fromXScale="0.75" + android:fromYScale="0.75" + android:pivotX="50%" + android:pivotY="100%" + android:toXScale="1" + android:toYScale="1"/> +</set> \ No newline at end of file diff --git a/wikipedia/res/drawable-hdpi/ic_add_tab.png b/wikipedia/res/drawable-hdpi/ic_add_tab.png new file mode 100644 index 0000000..3950fda --- /dev/null +++ b/wikipedia/res/drawable-hdpi/ic_add_tab.png Binary files differ diff --git a/wikipedia/res/drawable-hdpi/ic_close_gray.png b/wikipedia/res/drawable-hdpi/ic_close_gray.png new file mode 100644 index 0000000..cd33738 --- /dev/null +++ b/wikipedia/res/drawable-hdpi/ic_close_gray.png Binary files differ diff --git a/wikipedia/res/drawable-hdpi/ic_tab_list.png b/wikipedia/res/drawable-hdpi/ic_tab_list.png new file mode 100644 index 0000000..85a763a --- /dev/null +++ b/wikipedia/res/drawable-hdpi/ic_tab_list.png Binary files differ diff --git a/wikipedia/res/drawable-ldpi/ic_add_tab.png b/wikipedia/res/drawable-ldpi/ic_add_tab.png new file mode 100644 index 0000000..c681198 --- /dev/null +++ b/wikipedia/res/drawable-ldpi/ic_add_tab.png Binary files differ diff --git a/wikipedia/res/drawable-ldpi/ic_close_gray.png b/wikipedia/res/drawable-ldpi/ic_close_gray.png new file mode 100644 index 0000000..a0c4d03 --- /dev/null +++ b/wikipedia/res/drawable-ldpi/ic_close_gray.png Binary files differ diff --git a/wikipedia/res/drawable-ldpi/ic_tab_list.png b/wikipedia/res/drawable-ldpi/ic_tab_list.png new file mode 100644 index 0000000..262730c --- /dev/null +++ b/wikipedia/res/drawable-ldpi/ic_tab_list.png Binary files differ diff --git a/wikipedia/res/drawable-mdpi/ic_add_tab.png b/wikipedia/res/drawable-mdpi/ic_add_tab.png new file mode 100644 index 0000000..db34796 --- /dev/null +++ b/wikipedia/res/drawable-mdpi/ic_add_tab.png Binary files differ diff --git a/wikipedia/res/drawable-mdpi/ic_close_gray.png b/wikipedia/res/drawable-mdpi/ic_close_gray.png new file mode 100644 index 0000000..79fd948 --- /dev/null +++ b/wikipedia/res/drawable-mdpi/ic_close_gray.png Binary files differ diff --git a/wikipedia/res/drawable-mdpi/ic_tab_list.png b/wikipedia/res/drawable-mdpi/ic_tab_list.png new file mode 100644 index 0000000..003c3b7 --- /dev/null +++ b/wikipedia/res/drawable-mdpi/ic_tab_list.png Binary files differ diff --git a/wikipedia/res/drawable-xhdpi/ic_add_tab.png b/wikipedia/res/drawable-xhdpi/ic_add_tab.png new file mode 100644 index 0000000..f44b86d --- /dev/null +++ b/wikipedia/res/drawable-xhdpi/ic_add_tab.png Binary files differ diff --git a/wikipedia/res/drawable-xhdpi/ic_close_gray.png b/wikipedia/res/drawable-xhdpi/ic_close_gray.png new file mode 100644 index 0000000..4fb8455 --- /dev/null +++ b/wikipedia/res/drawable-xhdpi/ic_close_gray.png Binary files differ diff --git a/wikipedia/res/drawable-xhdpi/ic_tab_list.png b/wikipedia/res/drawable-xhdpi/ic_tab_list.png new file mode 100644 index 0000000..d00300d --- /dev/null +++ b/wikipedia/res/drawable-xhdpi/ic_tab_list.png Binary files differ diff --git a/wikipedia/res/drawable-xxhdpi/ic_add_tab.png b/wikipedia/res/drawable-xxhdpi/ic_add_tab.png new file mode 100644 index 0000000..77a2d35 --- /dev/null +++ b/wikipedia/res/drawable-xxhdpi/ic_add_tab.png Binary files differ diff --git a/wikipedia/res/drawable-xxhdpi/ic_close_gray.png b/wikipedia/res/drawable-xxhdpi/ic_close_gray.png new file mode 100644 index 0000000..bf19e3c --- /dev/null +++ b/wikipedia/res/drawable-xxhdpi/ic_close_gray.png Binary files differ diff --git a/wikipedia/res/drawable-xxhdpi/ic_tab_list.png b/wikipedia/res/drawable-xxhdpi/ic_tab_list.png new file mode 100644 index 0000000..413ea21 --- /dev/null +++ b/wikipedia/res/drawable-xxhdpi/ic_tab_list.png Binary files differ diff --git a/wikipedia/res/drawable/tab_item_bottom_gradient.xml b/wikipedia/res/drawable/tab_item_bottom_gradient.xml new file mode 100644 index 0000000..ebfe12a --- /dev/null +++ b/wikipedia/res/drawable/tab_item_bottom_gradient.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:startColor="@color/gallery_background" + android:endColor="#00000000" + android:angle="90"/> +</shape> \ No newline at end of file diff --git a/wikipedia/res/drawable/tab_item_selector.xml b/wikipedia/res/drawable/tab_item_selector.xml new file mode 100644 index 0000000..e1fad34 --- /dev/null +++ b/wikipedia/res/drawable/tab_item_selector.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:drawable="@drawable/tab_item_shape_selected" /> + <item android:state_pressed="true" android:drawable="@drawable/tab_item_shape_selected" /> + <item android:drawable="@drawable/tab_item_shape" /> +</selector> \ No newline at end of file diff --git a/wikipedia/res/drawable/tab_item_shape.xml b/wikipedia/res/drawable/tab_item_shape.xml new file mode 100644 index 0000000..f450050 --- /dev/null +++ b/wikipedia/res/drawable/tab_item_shape.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/window_background_light"/> + <corners android:topLeftRadius="8dp" android:topRightRadius="8dp"/> +</shape> \ No newline at end of file diff --git a/wikipedia/res/drawable/tab_item_shape_selected.xml b/wikipedia/res/drawable/tab_item_shape_selected.xml new file mode 100644 index 0000000..65b7c39 --- /dev/null +++ b/wikipedia/res/drawable/tab_item_shape_selected.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/gray_highlight"/> + <corners android:topLeftRadius="8dp" android:topRightRadius="8dp"/> +</shape> \ No newline at end of file diff --git a/wikipedia/res/layout/activity_page.xml b/wikipedia/res/layout/activity_page.xml index 5f8d4f5..162ff73 100644 --- a/wikipedia/res/layout/activity_page.xml +++ b/wikipedia/res/layout/activity_page.xml @@ -9,7 +9,8 @@ <FrameLayout android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="@color/gallery_background"> <!-- The main content view --> <FrameLayout @@ -18,6 +19,23 @@ android:layout_height="match_parent"> </FrameLayout> + <!-- The tabs container --> + <FrameLayout + android:id="@+id/tabs_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone"> + <ListView + android:id="@+id/tabs_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:divider="@null" + android:dividerHeight="0dp" + android:stackFromBottom="true" + android:transcriptMode="alwaysScroll" + /> + </FrameLayout> + <!-- The search container --> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/search_fragment" diff --git a/wikipedia/res/layout/dialog_page_info.xml b/wikipedia/res/layout/dialog_page_info.xml index 577fe9d..caa2a55 100644 --- a/wikipedia/res/layout/dialog_page_info.xml +++ b/wikipedia/res/layout/dialog_page_info.xml @@ -69,7 +69,7 @@ android:padding="8dp" android:layout_gravity="center_vertical" android:gravity="center" - android:src="@drawable/close" + android:src="@drawable/ic_close_gray" android:background="@drawable/button_selector_transparent" android:contentDescription="@string/dialog_close_description" /> </LinearLayout> diff --git a/wikipedia/res/layout/item_tab_entry.xml b/wikipedia/res/layout/item_tab_entry.xml new file mode 100644 index 0000000..8998f35 --- /dev/null +++ b/wikipedia/res/layout/item_tab_entry.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:descendantFocusability="blocksDescendants" + android:background="@drawable/tab_item_shape"> + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <ImageView + android:id="@+id/tab_item_thumbnail" + android:layout_height="32dp" + android:layout_width="32dp" + android:layout_margin="8dp" + android:layout_gravity="center_vertical" + android:scaleType="centerCrop" + android:src="@drawable/ic_pageimage_placeholder" + android:background="@android:color/transparent" + android:contentDescription="@null" + /> + + <TextView + android:id="@+id/tab_item_title" + android:layout_height="wrap_content" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:maxLines="1" + android:ellipsize="end" + tools:text="Sample tab title" + style="?android:textAppearanceMedium" + /> + + <ImageView + android:id="@+id/tab_item_close" + android:layout_height="32dp" + android:layout_width="32dp" + android:layout_margin="8dp" + android:padding="4dp" + android:layout_gravity="center_vertical" + android:src="@drawable/ic_close_gray" + android:contentDescription="@null" + android:clickable="true" + android:background="@drawable/button_selector_transparent" + /> + </LinearLayout> + <View + android:id="@+id/tab_item_bottom_gradient" + android:layout_width="match_parent" + android:layout_height="8dp" + android:layout_gravity="bottom" + android:background="@drawable/tab_item_bottom_gradient"/> + </FrameLayout> +</LinearLayout> \ No newline at end of file diff --git a/wikipedia/res/menu/menu_page_actions.xml b/wikipedia/res/menu/menu_page_actions.xml index ba0bda1..6a3177b 100644 --- a/wikipedia/res/menu/menu_page_actions.xml +++ b/wikipedia/res/menu/menu_page_actions.xml @@ -3,6 +3,11 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> + <item android:id="@+id/menu_show_tabs" + android:title="@string/menu_show_tabs" + android:icon="@drawable/ic_tab_list" + app:showAsAction="always" + /> <item android:id="@+id/menu_toc" android:title="@string/menu_show_toc" android:icon="@drawable/ic_toc" diff --git a/wikipedia/res/menu/menu_page_long_press.xml b/wikipedia/res/menu/menu_page_long_press.xml new file mode 100644 index 0000000..7720162 --- /dev/null +++ b/wikipedia/res/menu/menu_page_long_press.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + > + <item android:id="@+id/menu_open_link" + android:title="@string/menu_open_link" + app:showAsAction="ifRoom" /> + + <item android:id="@+id/menu_open_in_new_tab" + android:title="@string/menu_open_in_new_tab" + app:showAsAction="ifRoom" /> + + <item android:id="@+id/menu_save_for_later" + android:title="@string/menu_save_page_popup" + app:showAsAction="ifRoom" /> + +</menu> diff --git a/wikipedia/res/menu/menu_tabs.xml b/wikipedia/res/menu/menu_tabs.xml new file mode 100644 index 0000000..0571cca --- /dev/null +++ b/wikipedia/res/menu/menu_tabs.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + > + <item android:id="@+id/menu_new_tab" + android:icon="@drawable/ic_add_tab" + android:title="@string/menu_new_tab" + app:showAsAction="ifRoom" /> + +</menu> diff --git a/wikipedia/res/values/strings.xml b/wikipedia/res/values/strings.xml index fa1ec40..a3c3f3d 100644 --- a/wikipedia/res/values/strings.xml +++ b/wikipedia/res/values/strings.xml @@ -282,4 +282,9 @@ <string name="gallery_fair_use_license">Fair use</string> <string name="gallery_uploader_unknown">Uploader unknown</string> <string name="edit_save_unknown_error">Could not save your edit: %s</string> + <string name="menu_show_tabs">Show tabs</string> + <string name="menu_new_tab">New tab</string> + <string name="menu_open_link">Open link</string> + <string name="menu_open_in_new_tab">Open in new tab</string> + <string name="menu_save_page_popup">Save for later</string> </resources> diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java index 0a94a77..00b12e6 100644 --- a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java +++ b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java @@ -78,6 +78,15 @@ private WikipediaApp app; private View fragmentContainerView; + public View getContentView() { + return fragmentContainerView; + } + + private View tabsContainerView; + public View getTabsContainerView() { + return tabsContainerView; + } + private WikiDrawerLayout drawerLayout; private NavDrawerFragment fragmentNavdrawer; private SearchArticlesFragment searchFragment; @@ -88,6 +97,10 @@ private View toolbarContainer; private ActionMode currentActionMode; + private View toolbarView; + public View getToolbarView() { + return toolbarView; + } private ActionBarDrawerToggle mDrawerToggle; public ActionBarDrawerToggle getDrawerToggle() { @@ -139,8 +152,8 @@ PreferenceManager.setDefaultValues(this, R.xml.preferences, false); setContentView(R.layout.activity_page); - final Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar); - setSupportActionBar(toolbar); + toolbarView = findViewById(R.id.main_toolbar); + setSupportActionBar((Toolbar) toolbarView); getSupportActionBar().setDisplayHomeAsUpEnabled(true); toolbarContainer = findViewById(R.id.main_toolbar_container); @@ -155,6 +168,7 @@ searchHintText = (TextView) findViewById(R.id.main_search_bar_text); fragmentContainerView = findViewById(R.id.content_fragment_container); + tabsContainerView = findViewById(R.id.tabs_container); progressBar = (ProgressBar)findViewById(R.id.main_progressbar); progressBar.setMax(PROGRESS_BAR_MAX_VALUE); updateProgressBar(false, true, 0); diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageLongPressHandler.java b/wikipedia/src/main/java/org/wikipedia/page/PageLongPressHandler.java new file mode 100644 index 0000000..91ce103 --- /dev/null +++ b/wikipedia/src/main/java/org/wikipedia/page/PageLongPressHandler.java @@ -0,0 +1,69 @@ +package org.wikipedia.page; + +import org.wikipedia.R; +import org.wikipedia.history.HistoryEntry; +import android.content.Context; +import android.os.Vibrator; +import android.support.v7.widget.PopupMenu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +public class PageLongPressHandler { + private final PageViewFragmentInternal fragment; + private final ViewGroup containerView; + private PageActivity activity; + private View anchorView; + + private Vibrator vibrator; + + PageLongPressHandler(PageViewFragmentInternal fragment, ViewGroup containerView) { + this.fragment = fragment; + this.containerView = containerView; + this.activity = (PageActivity) fragment.getActivity(); + vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + } + + public void onLongPress(int x, int y, final PageTitle title, final HistoryEntry entry) { + // create a temporary view at the location of the click event + anchorView = new View(fragment.getActivity()); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1, 1); + params.leftMargin = x; + params.topMargin = y; + anchorView.setLayoutParams(params); + containerView.addView(anchorView); + // create a popup menu and anchor it to the temporary view + PopupMenu popupMenu = new PopupMenu(activity, anchorView); + popupMenu.inflate(R.menu.menu_page_long_press); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_open_link: + activity.displayNewPage(title, entry); + return true; + case R.id.menu_open_in_new_tab: + fragment.openInNewTab(title, entry); + return true; + default: + break; + } + return false; + } + }); + popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() { + @Override + public void onDismiss(PopupMenu menu) { + if (anchorView != null) { + containerView.removeView(anchorView); + anchorView = null; + } + } + }); + popupMenu.show(); + final int vibrateMillis = 50; + vibrator.vibrate(vibrateMillis); + } + +} diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java index 71367cc..2d8a9e9 100755 --- a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java +++ b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java @@ -25,6 +25,8 @@ import org.wikipedia.page.linkpreview.LinkPreviewVersion; import org.wikipedia.page.snippet.NoTextSelectedShareAdapter; import org.wikipedia.page.snippet.TextSelectedShareAdapter; +import org.wikipedia.page.tabs.Tab; +import org.wikipedia.page.tabs.TabsProvider; import org.wikipedia.pageimages.PageImage; import org.wikipedia.pageimages.PageImagesTask; import org.wikipedia.savedpages.ImageUrlMap; @@ -32,6 +34,7 @@ import org.wikipedia.savedpages.LoadSavedPageUrlMapTask; import org.wikipedia.savedpages.SavePageTask; import org.wikipedia.search.SearchBarHideHandler; +import org.wikipedia.staticdata.MainPageNameData; import org.wikipedia.util.ApiUtil; import org.wikipedia.util.DimenUtil; import org.wikipedia.util.NetworkUtils; @@ -47,6 +50,7 @@ import android.app.AlertDialog; import android.content.Intent; import android.graphics.Bitmap; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -54,6 +58,7 @@ import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBarActivity; import android.support.v7.view.ActionMode; +import android.support.v7.widget.PopupMenu; import android.text.Html; import android.util.Log; import android.util.TypedValue; @@ -64,6 +69,9 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; import javax.net.ssl.SSLException; @@ -91,11 +99,11 @@ private int subState = SUBSTATE_NONE; /** - * List of lightweight history items to serve as the backstack for this fragment. + * List of tabs, each of which contains a backstack of page titles. * Since the list consists of Parcelable objects, it can be saved and restored from the * savedInstanceState of the fragment. */ - private ArrayList<PageBackStackItem> backStack; + private ArrayList<Tab> tabList; /** * Whether to save the full page content as soon as it's loaded. @@ -143,6 +151,7 @@ private ReferenceDialog referenceDialog; private EditHandler editHandler; private ActionMode findInPageActionMode; + private PageLongPressHandler longPressHandler; private WikipediaApp app; @@ -157,6 +166,8 @@ private TextSelectedShareAdapter textSelectedShareAdapter; private NoTextSelectedShareAdapter noTextSelectedShareAdapter; + + private TabsProvider tabsProvider; public ObservableWebView getWebView() { return webView; @@ -175,7 +186,7 @@ } public PageViewFragmentInternal() { - backStack = new ArrayList<>(); + tabList = new ArrayList<>(); } private void displayLeadSection() { @@ -227,6 +238,12 @@ refreshView.setRefreshing(false); ((PageActivity) getActivity()).updateProgressBar(true, true, 0); + + // update tab display, in case it's showing + getCurrentTab().getBackStack().get(getCurrentTab().getBackStack().size() - 1).getTitle().setThumbUrl(title.getThumbUrl()); + getCurrentTab().getBackStack().get(getCurrentTab().getBackStack().size() - 1).getTitle().setDescription( + title.getDescription()); + tabsProvider.invalidate(); } private void displayNonLeadSection(int index) { @@ -317,7 +334,10 @@ connectionIssueFunnel = new ConnectionIssueFunnel(app); if (savedInstanceState != null) { - backStack = savedInstanceState.getParcelableArrayList("backStack"); + tabList = savedInstanceState.getParcelableArrayList("tabList"); + } else if (tabList.size() == 0) { + // fresh launch, so initialize with a single tab + tabList.add(new Tab()); } updateFontSize(); @@ -427,10 +447,64 @@ pageSequenceNum = 0; + tabsProvider = new TabsProvider((PageActivity) getActivity(), tabList); + tabsProvider.setTabsProviderListener(new TabsProvider.TabsProviderListener() { + @Override + public void onCancelTabView() { + tabsProvider.exitTabMode(); + } + + @Override + public void onTabSelected(int position) { + // move the selected tab to the bottom of the list, and navigate to it! + // (but only if it's a different tab than the one currently in view! + if (position != tabList.size() - 1) { + Tab tab = tabList.remove(position); + tabList.add(tab); + loadPageFromBackStack(); + } + tabsProvider.exitTabMode(); + } + + @Override + public void onNewTabRequested() { + // just load the main page into a new tab... + PageTitle newTitle = new PageTitle(MainPageNameData.valueFor(app.getLanguage()), app.getPrimarySite()); + HistoryEntry newEntry = new HistoryEntry(newTitle, HistoryEntry.SOURCE_INTERNAL_LINK); + openInNewTab(newTitle, newEntry); + } + + @Override + public void onCloseTabRequested(int position) { + tabList.remove(position); + if (position < tabList.size()) { + // if it's not the topmost tab, then just delete it and update the tab list... + tabsProvider.invalidate(); + } else if (tabList.size() > 0) { + // but if it's the topmost tab, then load the topmost page in the next tab. + loadPageFromBackStack(); + } else { + // and if the last tab was closed, then finish the activity! + getActivity().finish(); + } + } + }); + + longPressHandler = new PageLongPressHandler(this, (ViewGroup) ((PageActivity) getActivity()).getContentView()); + webView.addOnLongPressListener(new ObservableWebView.OnLongPressListener() { + @Override + public boolean onLongPress(float x, float y, final String linkTitle) { + PageTitle newTitle = titleOriginal.getSite().titleForInternalLink(linkTitle); + HistoryEntry newEntry = new HistoryEntry(newTitle, HistoryEntry.SOURCE_INTERNAL_LINK); + longPressHandler.onLongPress((int) x, (int) y, newTitle, newEntry); + return true; + } + }); + // if we already have pages in the backstack (whether it's from savedInstanceState, or // from being stored in the activity's fragment backstack), then load the topmost page // on the backstack. - if (backStack.size() > 0) { + if (getCurrentTab().getBackStack().size() > 0) { loadPageFromBackStack(); } } @@ -460,7 +534,7 @@ super.onSaveInstanceState(outState); // update the topmost entry in the backstack updateBackStackItem(); - outState.putParcelableArrayList("backStack", backStack); + outState.putParcelableArrayList("tabList", tabList); } @Override @@ -469,15 +543,19 @@ ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(""); } + public Tab getCurrentTab() { + return tabList.get(tabList.size() - 1); + } + /** * Pop the topmost entry from the backstack. * Does NOT automatically load the next topmost page on the backstack. */ private void popBackStack() { - if (backStack.size() == 0) { + if (getCurrentTab().getBackStack().size() == 0) { return; } - backStack.remove(backStack.size() - 1); + getCurrentTab().getBackStack().remove(getCurrentTab().getBackStack().size() - 1); } /** @@ -485,7 +563,7 @@ */ private void pushBackStack() { PageBackStackItem item = new PageBackStackItem(titleOriginal, curEntry); - backStack.add(item); + getCurrentTab().getBackStack().add(item); } /** @@ -494,21 +572,37 @@ * Should be done right before loading a new page. */ private void updateBackStackItem() { - if (backStack.size() == 0) { + if (getCurrentTab().getBackStack().size() == 0) { return; } - PageBackStackItem item = backStack.get(backStack.size() - 1); + PageBackStackItem item = getCurrentTab().getBackStack().get( + getCurrentTab().getBackStack().size() - 1); item.setScrollY(webView.getScrollY()); } private void loadPageFromBackStack() { - if (backStack.size() == 0) { + if (getCurrentTab().getBackStack().size() == 0) { return; } - PageBackStackItem item = backStack.get(backStack.size() - 1); + PageBackStackItem item = getCurrentTab().getBackStack().get( + getCurrentTab().getBackStack().size() - 1); // display the page based on the backstack item, stage the scrollY position based on // the backstack item. displayNewPage(item.getTitle(), item.getHistoryEntry(), true, false, item.getScrollY()); + } + + public void openInNewTab(PageTitle title, HistoryEntry entry) { + // create a new tab + Tab tab = new Tab(); + // make this tab current + tabList.add(tab); + // clear out our current title + this.titleOriginal = null; + this.title = null; + this.curEntry = null; + // and... that should be it. + ((PageActivity) getActivity()).displayNewPage(title, entry); + tabsProvider.showAndHideTabs(); } /** @@ -561,6 +655,8 @@ if (pushBackStack) { pushBackStack(); } + + tabsProvider.invalidate(); // increment our sequence number, so that any async tasks that depend on the sequence // will invalidate themselves upon completion. @@ -650,7 +746,7 @@ } private boolean isFirstPage() { - return backStack.size() <= 1 && !webView.canGoBack(); + return getCurrentTab().getBackStack().size() <= 1 && !webView.canGoBack(); } public Bitmap getLeadImageBitmap() { @@ -954,13 +1050,17 @@ langIntent.setClass(getActivity(), LangLinksActivity.class); langIntent.setAction(LangLinksActivity.ACTION_LANGLINKS_FOR_TITLE); langIntent.putExtra(LangLinksActivity.EXTRA_PAGETITLE, title); - getActivity().startActivityForResult(langIntent, PageActivity.ACTIVITY_REQUEST_LANGLINKS); + getActivity().startActivityForResult(langIntent, + PageActivity.ACTIVITY_REQUEST_LANGLINKS); return true; case R.id.menu_find_in_page: showFindInPage(); return true; case R.id.menu_themechooser: ((PageActivity) getActivity()).showThemeChooser(); + return true; + case R.id.menu_show_tabs: + tabsProvider.enterTabMode(); return true; default: return super.onOptionsItemSelected(item); @@ -1098,6 +1198,9 @@ if (mobileView.has("description")) { title.setDescription(Utils.capitalizeFirstChar(mobileView.getString("description"))); } + // update the title and description in the original title... + titleOriginal.setThumbUrl(title.getThumbUrl()); + titleOriginal.setDescription(title.getDescription()); } return super.processResult(result); } @@ -1434,10 +1537,20 @@ if (closeFindInPage()) { return true; } - if (backStack.size() > 1) { + if (tabsProvider.onBackPressed()) { + return true; + } + if (getCurrentTab().getBackStack().size() > 1) { popBackStack(); loadPageFromBackStack(); return true; + } else if (tabList.size() > 1) { + // if we're at the end of the current tab's backstack, and we have additional + // tabs available, then pop the current tab, and load the topmost page in that tab. + tabList.remove(tabList.size() - 1); + loadPageFromBackStack(); + tabsProvider.enterTabMode(); + return true; } return false; } diff --git a/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java b/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java new file mode 100644 index 0000000..451f576 --- /dev/null +++ b/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java @@ -0,0 +1,43 @@ +package org.wikipedia.page.tabs; + +import org.wikipedia.page.PageBackStackItem; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + +public class Tab implements Parcelable { + private final List<PageBackStackItem> backStack; + public List<PageBackStackItem> getBackStack() { + return backStack; + } + + public Tab() { + backStack = new ArrayList<>(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeList(backStack); + } + + private Tab(Parcel in) { + backStack = (ArrayList<PageBackStackItem>) in.readArrayList(Tab.class.getClassLoader()); + } + + public static final Parcelable.Creator<Tab> CREATOR + = new Parcelable.Creator<Tab>() { + public Tab createFromParcel(Parcel in) { + return new Tab(in); + } + + public Tab[] newArray(int size) { + return new Tab[size]; + } + }; +} diff --git a/wikipedia/src/main/java/org/wikipedia/page/tabs/TabsProvider.java b/wikipedia/src/main/java/org/wikipedia/page/tabs/TabsProvider.java new file mode 100644 index 0000000..867fbad --- /dev/null +++ b/wikipedia/src/main/java/org/wikipedia/page/tabs/TabsProvider.java @@ -0,0 +1,366 @@ +package org.wikipedia.page.tabs; + +import org.wikipedia.R; +import org.wikipedia.Utils; +import org.wikipedia.WikipediaApp; +import org.wikipedia.page.PageActivity; +import org.wikipedia.page.PageBackStackItem; +import org.wikipedia.page.PageTitle; + +import com.squareup.picasso.Picasso; +import android.support.v7.view.ActionMode; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import java.util.List; + +public class TabsProvider { + + private PageActivity parentActivity; + private float displayDensity; + + private View pageContentView; + private View tabContainerView; + private ListView tabListView; + private TabListAdapter tabListAdapter; + + private ActionMode tabActionMode; + + List<Tab> tabList; + + public interface TabsProviderListener { + void onCancelTabView(); + void onTabSelected(int position); + void onNewTabRequested(); + void onCloseTabRequested(int position); + } + private TabsProviderListener providerListener; + public void setTabsProviderListener(TabsProviderListener listener) { + providerListener = listener; + } + + public TabsProvider(PageActivity parentActivity, List<Tab> tabList) { + this.parentActivity = parentActivity; + this.tabList = tabList; + displayDensity = parentActivity.getResources().getDisplayMetrics().density; + + pageContentView = parentActivity.getContentView(); + tabContainerView = parentActivity.getTabsContainerView(); + tabListView = (ListView) tabContainerView.findViewById(R.id.tabs_list); + tabListAdapter = new TabListAdapter(parentActivity.getLayoutInflater()); + tabListView.setAdapter(tabListAdapter); + + tabContainerView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (providerListener != null) { + providerListener.onCancelTabView(); + } + } + }); + + tabListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (providerListener != null) { + providerListener.onTabSelected(position); + } + } + }); + + } + + public boolean onBackPressed() { + if (tabActionMode != null) { + exitTabMode(); + return true; + } + return false; + } + + public void enterTabMode() { + enterTabMode(null); + } + + private void enterTabMode(final Runnable onTabModeEntered) { + if (tabActionMode != null) { + // already inside action mode... + // but make sure to update the list of tabs. + tabListAdapter.notifyDataSetInvalidated(); + tabListView.smoothScrollToPosition(tabList.size() - 1); + onTabModeEntered.run(); + return; + } + parentActivity.startSupportActionMode(new ActionMode.Callback() { + private final String actionModeTag = "actionModeTabList"; + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + tabActionMode = mode; + mode.getMenuInflater().inflate(R.menu.menu_tabs, menu); + Animation anim = AnimationUtils.loadAnimation(parentActivity, + R.anim.tab_list_zoom_enter); + parentActivity.getContentView().startAnimation(anim); + layoutTabList(onTabModeEntered); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + mode.setTag(actionModeTag); + // find the action mode base view, and give it an empty click listener + View v = parentActivity.findViewById(R.id.action_mode_bar); + v.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + } + }); + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_new_tab: + if (providerListener != null) { + providerListener.onNewTabRequested(); + } + return true; + default: + break; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + Animation anim = AnimationUtils.loadAnimation(parentActivity, R.anim.tab_list_zoom_exit); + parentActivity.getContentView().startAnimation(anim); + hideTabList(); + tabActionMode = null; + parentActivity.showToolbar(); + } + }); + } + + public void exitTabMode() { + if (tabActionMode != null) { + tabActionMode.finish(); + } + } + + public void showAndHideTabs() { + enterTabMode(new Runnable() { + final int animDelay = 500; + @Override + public void run() { + tabContainerView.postDelayed(new Runnable() { + @Override + public void run() { + exitTabMode(); + } + }, animDelay); + } + }); + } + + private void layoutTabList(final Runnable onTabModeEntered) { + tabContainerView.setVisibility(View.VISIBLE); + tabListAdapter.notifyDataSetInvalidated(); + + // size the listview to be the same width as the scaled-down webview... + final float proportionHorz = 0.25f; + final float proportionVert = 0.3f; + final int heightOffset = 16; + int toolbarHeight = Utils.getActionBarSize(parentActivity); + int maxHeight = (int) (pageContentView.getHeight() * proportionVert + pageContentView.getHeight() * proportionHorz - toolbarHeight - heightOffset * displayDensity); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, maxHeight); + float margin = pageContentView.getWidth() * proportionHorz / 2f; + params.leftMargin = (int) margin; + params.rightMargin = (int) margin; + params.topMargin = toolbarHeight; + tabListView.setLayoutParams(params); + + Animation anim = AnimationUtils.loadAnimation(parentActivity, + R.anim.tab_list_items_enter); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + if (onTabModeEntered != null) { + onTabModeEntered.run(); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + tabListView.startAnimation(anim); + // scroll to the bottom of the tab list + tabListView.smoothScrollToPosition(tabList.size() - 1); + } + + private void hideTabList() { + Animation anim = AnimationUtils.loadAnimation(parentActivity, + R.anim.tab_list_items_exit); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + tabContainerView.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + tabListView.startAnimation(anim); + } + + public void invalidate() { + tabListAdapter.notifyDataSetInvalidated(); + } + + + private View.OnTouchListener onItemTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + Animation anim = AnimationUtils.loadAnimation(parentActivity, R.anim.tab_item_press); + v.startAnimation(anim); + break; + default: + break; + } + return false; + } + }; + + private View.OnClickListener onItemCloseButtonListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + ViewHolder holder = (ViewHolder) v.getTag(); + final int position = holder.position; + Animation anim = AnimationUtils.loadAnimation(parentActivity, R.anim.slide_out_right); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + if (providerListener != null) { + providerListener.onCloseTabRequested(position); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + holder.container.startAnimation(anim); + } + }; + + private class ViewHolder { + private View container; + private TextView title; + private ImageView thumbnail; + private View gradient; + private View closeButton; + int position; + } + + private final class TabListAdapter extends BaseAdapter { + private final LayoutInflater inflater; + + private TabListAdapter(LayoutInflater inflater) { + this.inflater = inflater; + } + + @Override + public int getCount() { + return tabList.size(); + } + + @Override + public Object getItem(int position) { + return tabList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + convertView = inflater.inflate(R.layout.item_tab_entry, parent, false); + convertView.setTag(viewHolder); + viewHolder.container = convertView; + viewHolder.title = (TextView) convertView.findViewById(R.id.tab_item_title); + viewHolder.thumbnail = (ImageView) convertView.findViewById(R.id.tab_item_thumbnail); + viewHolder.gradient = convertView.findViewById(R.id.tab_item_bottom_gradient); + viewHolder.closeButton = convertView.findViewById(R.id.tab_item_close); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + viewHolder.position = position; + viewHolder.container.setOnTouchListener( + position == tabList.size() - 1 ? null : onItemTouchListener); + viewHolder.closeButton.setTag(viewHolder); + viewHolder.closeButton.setOnClickListener(onItemCloseButtonListener); + // hide the shadow if this is the topmost tab + viewHolder.gradient.setVisibility( + position == tabList.size() - 1 ? View.GONE : View.VISIBLE); + + List<PageBackStackItem> backstack = tabList.get(position).getBackStack(); + if (backstack.size() > 0) { + PageTitle title = backstack.get(backstack.size() - 1).getTitle(); + viewHolder.title.setText(title.getDisplayText()); + String thumbnail = title.getThumbUrl(); + if (WikipediaApp.getInstance().showImages() && thumbnail != null) { + Picasso.with(parentActivity) + .load(thumbnail) + .placeholder(R.drawable.ic_pageimage_placeholder) + .error(R.drawable.ic_pageimage_placeholder) + .into(viewHolder.thumbnail); + } else { + Picasso.with(parentActivity) + .load(R.drawable.ic_pageimage_placeholder) + .into(viewHolder.thumbnail); + } + } + return convertView; + } + + } + +} -- To view, visit https://gerrit.wikimedia.org/r/214117 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I00967dc2256fc2cb8c251cc5a6a0ebb83e144a6e Gerrit-PatchSet: 1 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Dbrant <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
