/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.linkbubble.ui; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import com.linkbubble.R; import com.linkbubble.Settings; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Set; import butterknife.Bind; import butterknife.ButterKnife; public class SettingsDomainsActivity extends AppCompatActivity { Adapter adapter; @Bind(R.id.recycler_view) RecyclerView recyclerView; @Bind(R.id.fab) FloatingActionButton addButton; @Bind(R.id.root_view) View rootView; LinearLayoutManager linearLayoutManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings_domains); ButterKnife.bind(this); adapter = new Adapter(this); recyclerView.setAdapter(adapter); linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitle(R.string.preference_domains_title); setSupportActionBar(toolbar); toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); addButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showAddRedirectDialog(); } }); } void showAddRedirectDialog() { View layout = LayoutInflater.from(this).inflate(R.layout.view_add_domain, null); final EditText editText = (EditText)layout.findViewById(R.id.edit_text); new AlertDialog.Builder(this) .setTitle(R.string.preference_add_domain_title) .setView(layout) .setPositiveButton(R.string.action_add, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String host = editText.getText().toString(); boolean added = false; if (!TextUtils.isEmpty(host)) { host = host.replace("\"", ""); int protocolIndex = host.indexOf("://"); if (protocolIndex > -1) { host = host.substring(protocolIndex + "://".length()); } if (host.contains(".") && !host.contains(" ")) { int slashIndex = host.indexOf("/"); if (slashIndex > -1) { host = host.substring(0, slashIndex); } try { URL url = new URL("http", host, "/"); adapter.addDomain(url.getHost()); Settings.get().addFallbackRedirectHost(url.getHost()); added = true; } catch(MalformedURLException e){ e.printStackTrace(); } } } if (!added) { showSnackbar(String.format(getString(R.string.add_domain_error), editText.getText().toString())); } } }) .create() .show(); mHandler.postDelayed(new Runnable() { @Override public void run() { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); } }, 300); } private Handler mHandler = new Handler(); private Snackbar currentSnackbar; void showSnackbar(String message) { showSnackbar(message, null, null); } void showSnackbar(String message, String action, View.OnClickListener onActionClickListener) { if (currentSnackbar != null) { currentSnackbar.dismiss(); currentSnackbar = null; } currentSnackbar = Snackbar.make(rootView, message, action != null ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT) .setAction(action, onActionClickListener); currentSnackbar.show(); } static abstract class BaseItem { Context context; String title; int width; int height; BaseItem(Context context, String title) { this.context = context; this.title = title; width = ViewGroup.LayoutParams.MATCH_PARENT; height = context.getResources().getDimensionPixelSize(R.dimen.settings_item_height); } static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); } void setTag(Object tag) { itemView.setTag(tag); } void bind(BaseItem baseItem) { setTag(baseItem); ViewGroup.LayoutParams lp = itemView.getLayoutParams(); if (lp == null) { lp = new ViewGroup.LayoutParams(baseItem.width, baseItem.height); } else { lp.width = baseItem.width; lp.height = baseItem.height; } if (baseItem.height == ViewGroup.LayoutParams.WRAP_CONTENT) { // wrap content based on text container rather than root layout to ensure // selectableItemBackground extends correctly to visible bounds. View textContainer = itemView.findViewById(R.id.settings_text_container); if (textContainer != null) { int vPadding = baseItem.context.getResources().getDimensionPixelSize(R.dimen.default_margin_half); textContainer.setPadding(itemView.getPaddingLeft(), vPadding, itemView.getPaddingRight(), vPadding); } } itemView.setLayoutParams(lp); } } } static class HeadingItem extends BaseItem { HeadingItem(Context context, String title) { super(context, title); height = context.getResources().getDimensionPixelSize(R.dimen.settings_group_title_item_height); } static class ViewHolder extends BaseItem.ViewHolder { TextView titleView; public ViewHolder(View itemView) { super(itemView); titleView = (TextView) itemView.findViewById(R.id.settings_title); } void bind(HeadingItem headingItem) { super.bind(headingItem); titleView.setText(headingItem.title); } } } static class DomainItem extends BaseItem { DomainItem(Context context, String title) { super(context, title); height = context.getResources().getDimensionPixelSize(R.dimen.settings_domain_item_height); } static class ViewHolder extends BaseItem.ViewHolder { @Bind(R.id.settings_title) TextView titleView; @Bind(R.id.settings_divider) View divider; @Bind(R.id.remove_icon) ImageView removeIcon; public ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, this.itemView); itemView.findViewById(R.id.settings_summary).setVisibility(View.GONE); itemView.findViewById(R.id.app_icon).setVisibility(View.GONE); } void bind(DomainItem domainItem, boolean showDivider, View.OnClickListener onRemoveClickListener) { super.bind(domainItem); titleView.setText(domainItem.title); divider.setVisibility(showDivider ? View.VISIBLE : View.INVISIBLE); removeIcon.setOnClickListener(onRemoveClickListener); } } } static final int VIEW_TYPE_HEADING = 0; static final int VIEW_TYPE_DOMAIN = 1; public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { Context context; ArrayList<BaseItem> items = new ArrayList<>(); Adapter(Context context) { this.context = context; items.add(new HeadingItem(context, getString(R.string.preference_redirects_title))); Set<String> redirectHosts = Settings.get().getFallbackRedirectHosts(); if (redirectHosts.size() > 0) { ArrayList<String> strings = new ArrayList<>(redirectHosts); Collections.sort(strings); for (String string : strings) { items.add(new DomainItem(context, string)); } } } void addDomain(String host) { DomainItem item = new DomainItem(context, host); addItem(item); } void addItem(BaseItem item) { items.add(item); notifyItemInserted(items.indexOf(item)); } void removeItem(BaseItem baseItem) { int index = items.indexOf(baseItem); items.remove(index); notifyItemRemoved(index); } @Override public int getItemViewType(int position) { BaseItem baseItem = items.get(position); if (baseItem instanceof HeadingItem) { return VIEW_TYPE_HEADING; } return VIEW_TYPE_DOMAIN; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case VIEW_TYPE_HEADING: return new HeadingItem.ViewHolder(LayoutInflater.from(context) .inflate(R.layout.view_settings_group_title, null)); case VIEW_TYPE_DOMAIN: return new DomainItem.ViewHolder(LayoutInflater.from(context) .inflate(R.layout.view_settings_item, null)); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof DomainItem.ViewHolder) { final DomainItem item = (DomainItem) items.get(position); ((DomainItem.ViewHolder) holder).bind(item, !(items.get(position - 1) instanceof HeadingItem), new View.OnClickListener() { @Override public void onClick(View v) { removeItem(item); Settings.get().removeFallbackRedirectHost(item.title); showSnackbar("Removed " + item.title + ".", getString(R.string.action_undo), new View.OnClickListener() { @Override public void onClick(View v) { addItem(item); Settings.get().addFallbackRedirectHost(item.title); } }); } }); } else if (holder instanceof HeadingItem.ViewHolder) { ((HeadingItem.ViewHolder) holder).bind((HeadingItem) items.get(position)); holder.itemView.setOnClickListener(null); } } @Override public int getItemCount() { return items.size(); } } }