package com.duckduckgo.mobile.android.views; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.transition.Fade; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.PopupWindow; import android.widget.TextView; import com.duckduckgo.mobile.android.R; import com.duckduckgo.mobile.android.activity.DuckDuckGo; import com.duckduckgo.mobile.android.bus.BusProvider; import com.duckduckgo.mobile.android.events.WebViewEvents.WebViewUpdateMenuNavigationEvent; import com.duckduckgo.mobile.android.events.WebViewEvents.WebViewItemMenuClickEvent; import com.duckduckgo.mobile.android.objects.FeedObject; import com.squareup.otto.Subscribe; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class DDGOverflowMenu extends PopupWindow implements View.OnClickListener, AdapterView.OnItemClickListener, AdapterView.OnItemSelectedListener { private Context context; private ListView menuListView = null; private DDGOverflowAdapter overflowAdapter; private LinearLayout header = null; private HashMap<Integer, MenuItem> headerItems = null; private boolean isBusRegistered = false; private FeedObject feed = null; public DDGOverflowMenu(Context context) { super(context, null, R.attr.popUp); this.context = context; init(); } public DDGOverflowMenu(Context context, AttributeSet attrs) { super(context, attrs, R.attr.popUp); this.context = context; init(); } public void init() { setFocusable(true); setOutsideTouchable(true); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View container = inflater.inflate(R.layout.overflow_menu, null); setContentView(container); setHeight(WindowManager.LayoutParams.WRAP_CONTENT); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { setTransitions(); } menuListView = (ListView) container.findViewById(R.id.menu_listview); menuListView.setOnItemClickListener(this); menuListView.setOnItemSelectedListener(this); header = (LinearLayout) container.findViewById(R.id.header_container); } public void registerBus() { isBusRegistered = true; BusProvider.getInstance().register(this); } public void unregisterBus() { if(isBusRegistered) { isBusRegistered = false; BusProvider.getInstance().unregister(this); } } public void setMenu(Menu menu) { overflowAdapter = new DDGOverflowAdapter(context, R.layout.item_overflow_menu); menuListView.setAdapter(overflowAdapter); setMenu(menu, false); } public void setMenu(Menu menu, boolean newSection) { menuListView.setVisibility(View.VISIBLE); List<MenuItem> newMenuItems = new ArrayList<MenuItem>(); for(int i=0; i<menu.size(); i++) { if(menu.getItem(i).isVisible()) { newMenuItems.add(menu.getItem(i)); } } overflowAdapter.addItems(newMenuItems, newSection); overflowAdapter.notifyDataSetChanged(); } public void setHeaderMenu(Menu menu) { registerBus(); header.setVisibility(View.VISIBLE); headerItems = new HashMap<Integer, MenuItem>(); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); for(int i=0; i<menu.size(); i++) { ImageButton imageButton = (ImageButton) inflater.inflate(R.layout.web_navigation_button, header, false); final String title = ""+menu.getItem(i).getTitle(); final int actionId = menu.getItem(i).getItemId(); imageButton.setId(actionId); imageButton.setEnabled(menu.getItem(i).isEnabled()); imageButton.setImageDrawable(menu.getItem(i).getIcon()); if(menu.getItem(i).getIcon()==null) { imageButton.setEnabled(false); } else { imageButton.setOnClickListener(this); } headerItems.put(imageButton.getId(), menu.getItem(i)); header.addView(imageButton); } } public void setFeed(FeedObject feed) { this.feed = feed; } public void show(View anchor) { show(anchor, true, true); } public void showFeedMenu(View anchor) { show(anchor, false, true); } public void showBelowAnchor(View anchor) { show(anchor, false, false); } private void show(View anchor, boolean withMarginOnAnchor, boolean coverAnchor) { DisplayMetrics displayMetrics = new DisplayMetrics(); ((DuckDuckGo)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); setWidth(getMaxWidth(context, overflowAdapter)); int itemCount = overflowAdapter.getItemCount(); int height = ((int) context.getResources().getDimension(R.dimen.listview_item_height)) * (overflowAdapter.getItemCount()); int divider = (int) context.getResources().getDimension(R.dimen.simple_divider_height); height += divider; Rect rect = new Rect(); anchor.getGlobalVisibleRect(rect); boolean reverseMenu = false; if(displayMetrics.heightPixels>height) { menuListView.getLayoutParams().height = height; if((displayMetrics.heightPixels-rect.top)<=height) { reverseMenu = true; } } else { setHeight(WindowManager.LayoutParams.MATCH_PARENT); menuListView.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; } int xOffset = 0; int yOffset = 0; if(coverAnchor) { xOffset = anchor.getMeasuredWidth() - getWidth(); yOffset = reverseMenu ? height : anchor.getMeasuredHeight(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (withMarginOnAnchor) { int screenMargin = (int) context.getResources().getDimension(R.dimen.menu_outer_margin); xOffset -= screenMargin; yOffset -= screenMargin; } } } if(coverAnchor) { showAsDropDown(anchor, xOffset, yOffset * -1); } } public void showCentered(View anchor) { setWidth(getMaxWidth(context, overflowAdapter)); showAtLocation(anchor, Gravity.CENTER, 0, 0); } @Override public void dismiss() { unregisterBus(); super.dismiss(); } @Override public void onClick(View view) { BusProvider.getInstance().post(new WebViewItemMenuClickEvent(headerItems.get(view.getId()))); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if(feed==null) { BusProvider.getInstance().post(new WebViewItemMenuClickEvent(overflowAdapter.getMenuItem(position))); } else { BusProvider.getInstance().post(new WebViewItemMenuClickEvent(overflowAdapter.getMenuItem(position), feed)); } dismiss(); } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { dismiss(); } @Override public void onNothingSelected(AdapterView<?> parent) { dismiss(); } @TargetApi(Build.VERSION_CODES.M) private void setTransitions() { Fade fadeIn = new Fade(Fade.IN); fadeIn.setDuration(100); Fade fadeOut = new Fade(Fade.OUT); fadeOut.setDuration(100); setEnterTransition(fadeIn); setExitTransition(fadeOut); } public static int getMaxWidth(Context context, DDGOverflowAdapter adapter) { int maxLength = 0; for(int i=0; i<adapter.getCount(); i++) { int newLength = adapter.getMenuItemTitle(i).length(); maxLength = newLength>maxLength ? newLength : maxLength; } int width = (int) context.getResources().getDimension(R.dimen.menu_letterspace) * (maxLength+2); int menuPadding = (int) context.getResources().getDimension(R.dimen.menu_padding) * 2; return width + menuPadding; } public static int getWidestView(Context context, Adapter adapter) { int maxWidth = 0; View view = null; FrameLayout fakeParent = new FrameLayout(context); for (int i=0, count=adapter.getCount(); i<count; i++) { view = adapter.getView(i, view, fakeParent); view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); int width = view.getMeasuredWidth(); if (width > maxWidth) { maxWidth = width; } } return maxWidth; } private int getStatusBarHeight() { Rect rect = new Rect(); Window window = ((DuckDuckGo)context).getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rect); return rect.top; } @Subscribe public void onWebViewDisableMenuNavigationButtonEvent(WebViewUpdateMenuNavigationEvent event) { for(HashMap.Entry<Integer, Boolean> entry : event.newStates.entrySet()) { ImageButton imageButton = (ImageButton) header.findViewById(entry.getKey()); if(imageButton!=null) { imageButton.setEnabled(entry.getValue() ); } } } public class DDGOverflowAdapter extends ArrayAdapter<MenuItem> { private static final int TYPE_ITEM = 0; private static final int TYPE_DIVIDER = 1; private Context context; private int layoutResId; private List<Item> items; public DDGOverflowAdapter(Context context, int layoutResId) { super(context, layoutResId); this.context = context; this.layoutResId = layoutResId; items = new ArrayList<Item>(); } public void addItems(List<MenuItem> menuItems, boolean newSection) { if(newSection) { items.add(new Item(true)); } for(MenuItem menuItem : menuItems) { items.add(new Item(menuItem)); } } @Override public View getView(int position, View convertView, ViewGroup parent) { View root = convertView; Holder holder = null; int itemType = getItemViewType(position); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if(root==null) { if(itemType==TYPE_ITEM) { root = inflater.inflate(layoutResId, parent, false); holder = new Holder(); holder.text = (TextView) root.findViewById(R.id.text1); root.setTag(holder); } else { root = inflater.inflate(R.layout.overflowmenu_divier, parent, false); } } else { if(itemType==TYPE_ITEM) { holder = (Holder) root.getTag(); } } if(itemType==TYPE_ITEM) { MenuItem item = items.get(position).item; holder.text.setText(item.getTitle()); holder.text.setEnabled(item.isEnabled()); } return root; } public int getItemCount() { int out = 0; for(Item item : items) { if(!item.isDivider) { out++; } } return out; } @Override public int getCount() { return items.size(); } @Override public int getItemViewType(int position) { Item item = items.get(position); if(item.isDivider) { return TYPE_DIVIDER; } return TYPE_ITEM; } @Override public boolean isEnabled(int position) { return !items.get(position).isDivider; } public MenuItem getMenuItem(int position) { Item item = items.get(position); if(item.isDivider) { return null; } return item.item; } public String getMenuItemTitle(int position) { Item item = items.get(position); if(item.isDivider) { return ""; } return item.item.getTitle().toString(); } class Holder { TextView text; } class Item { public boolean isDivider = false; public MenuItem item; public Item(boolean isDivider) { this.isDivider = isDivider; } public Item(MenuItem item) { this.item = item; } } } }