Lightning browser with I2P configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

415 lines
16 KiB

package acr.browser.lightning.fragment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.SimpleItemAnimator;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.squareup.otto.Bus;
import javax.inject.Inject;
import acr.browser.lightning.R;
import acr.browser.lightning.activity.TabsManager;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.browser.TabsView;
import acr.browser.lightning.bus.NavigationEvents;
import acr.browser.lightning.bus.TabEvents;
import acr.browser.lightning.controller.UIController;
import acr.browser.lightning.fragment.anim.HorizontalItemAnimator;
import acr.browser.lightning.fragment.anim.VerticalItemAnimator;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.view.LightningView;
/**
* A fragment that holds and manages the tabs and interaction with the tabs.
* It is reliant on the BrowserController in order to get the current UI state
* of the browser. It also uses the BrowserController to signal that the UI needs
* to change. This class contains the adapter used by both the drawer tabs and
* the desktop tabs. It delegates touch events for the tab UI appropriately.
*/
public class TabsFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener, TabsView {
private static final String TAG = TabsFragment.class.getSimpleName();
/**
* Arguments boolean to tell the fragment it is displayed in the drawner or on the tab strip
* If true, the fragment is in the left drawner in the strip otherwise.
*/
public static final String VERTICAL_MODE = TAG + ".VERTICAL_MODE";
public static final String IS_INCOGNITO = TAG + ".IS_INCOGNITO";
private boolean mIsIncognito, mDarkTheme;
private int mIconColor;
private boolean mColorMode = true;
private boolean mShowInNavigationDrawer;
@Nullable private LightningViewAdapter mTabsAdapter;
private UIController mUiController;
private RecyclerView mRecyclerView;
private TabsManager mTabsManager;
@Inject Bus mBus;
@Inject PreferenceManager mPreferences;
public TabsFragment() {
BrowserApp.getAppComponent().inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle arguments = getArguments();
final Context context = getContext();
mUiController = (UIController) getActivity();
mTabsManager = mUiController.getTabModel();
mIsIncognito = arguments.getBoolean(IS_INCOGNITO, false);
mShowInNavigationDrawer = arguments.getBoolean(VERTICAL_MODE, true);
mDarkTheme = mPreferences.getUseTheme() != 0 || mIsIncognito;
mColorMode = mPreferences.getColorModeEnabled();
mColorMode &= !mDarkTheme;
mIconColor = mDarkTheme ?
ThemeUtils.getIconDarkThemeColor(context) :
ThemeUtils.getIconLightThemeColor(context);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view;
final LayoutManager layoutManager;
if (mShowInNavigationDrawer) {
view = inflater.inflate(R.layout.tab_drawer, container, false);
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
setupFrameLayoutButton(view, R.id.tab_header_button, R.id.plusIcon);
setupFrameLayoutButton(view, R.id.new_tab_button, R.id.icon_plus);
setupFrameLayoutButton(view, R.id.action_back, R.id.icon_back);
setupFrameLayoutButton(view, R.id.action_forward, R.id.icon_forward);
setupFrameLayoutButton(view, R.id.action_home, R.id.icon_home);
} else {
view = inflater.inflate(R.layout.tab_strip, container, false);
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
ImageView newTab = (ImageView) view.findViewById(R.id.new_tab_button);
newTab.setColorFilter(ThemeUtils.getIconDarkThemeColor(getActivity()));
newTab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUiController.newTabClicked();
}
});
}
mRecyclerView = (RecyclerView) view.findViewById(R.id.tabs_list);
SimpleItemAnimator animator;
if (mShowInNavigationDrawer) {
animator = new VerticalItemAnimator();
} else {
animator = new HorizontalItemAnimator();
}
animator.setSupportsChangeAnimations(false);
animator.setAddDuration(200);
animator.setChangeDuration(0);
animator.setRemoveDuration(200);
animator.setMoveDuration(200);
mRecyclerView.setLayerType(View.LAYER_TYPE_NONE, null);
mRecyclerView.setItemAnimator(animator);
mRecyclerView.setLayoutManager(layoutManager);
mTabsAdapter = new LightningViewAdapter(mShowInNavigationDrawer);
mRecyclerView.setAdapter(mTabsAdapter);
mRecyclerView.setHasFixedSize(true);
return view;
}
private void setupFrameLayoutButton(@NonNull final View root, @IdRes final int buttonId,
@IdRes final int imageId) {
final View frameButton = root.findViewById(buttonId);
final ImageView buttonImage = (ImageView) root.findViewById(imageId);
frameButton.setOnClickListener(this);
frameButton.setOnLongClickListener(this);
buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mTabsAdapter = null;
}
@Override
public void onStart() {
super.onStart();
mBus.register(this);
}
@Override
public void onResume() {
super.onResume();
// Force adapter refresh
if (mTabsAdapter != null) {
mTabsAdapter.notifyDataSetChanged();
}
}
@Override
public void onStop() {
super.onStop();
mBus.unregister(this);
}
public void reinitializePreferences() {
Activity activity = getActivity();
if (activity == null) {
return;
}
mDarkTheme = mPreferences.getUseTheme() != 0 || mIsIncognito;
mColorMode = mPreferences.getColorModeEnabled();
mColorMode &= !mDarkTheme;
mIconColor = mDarkTheme ?
ThemeUtils.getIconDarkThemeColor(activity) :
ThemeUtils.getIconLightThemeColor(activity);
if (mTabsAdapter != null) {
mTabsAdapter.notifyDataSetChanged();
}
}
@Override
public void onClick(@NonNull View v) {
switch (v.getId()) {
case R.id.tab_header_button:
mUiController.showCloseDialog(mTabsManager.indexOfCurrentTab());
break;
case R.id.new_tab_button:
mBus.post(new TabEvents.NewTab());
break;
case R.id.action_back:
mBus.post(new NavigationEvents.GoBack());
break;
case R.id.action_forward:
mBus.post(new NavigationEvents.GoForward());
break;
case R.id.action_home:
mBus.post(new NavigationEvents.GoHome());
default:
break;
}
}
@Override
public boolean onLongClick(@NonNull View v) {
switch (v.getId()) {
case R.id.action_new_tab:
mBus.post(new TabEvents.NewTabLongPress());
break;
default:
break;
}
return true;
}
@Override
public void tabAdded() {
if (mTabsAdapter != null) {
mTabsAdapter.notifyItemInserted(mTabsManager.last());
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
mRecyclerView.smoothScrollToPosition(mTabsAdapter.getItemCount() - 1);
}
}, 500);
}
}
@Override
public void tabRemoved(int position) {
if (mTabsAdapter != null) {
mTabsAdapter.notifyItemRemoved(position);
}
}
@Override
public void tabChanged(int position) {
if (mTabsAdapter != null) {
mTabsAdapter.notifyItemChanged(position);
}
}
private class LightningViewAdapter extends RecyclerView.Adapter<LightningViewAdapter.LightningViewHolder> {
private final int mLayoutResourceId;
@Nullable private final Drawable mBackgroundTabDrawable;
@Nullable private final Drawable mForegroundTabDrawable;
@Nullable private final Bitmap mForegroundTabBitmap;
private ColorMatrix mColorMatrix;
private Paint mPaint;
private ColorFilter mFilter;
private static final float DESATURATED = 0.5f;
private final boolean mDrawerTabs;
public LightningViewAdapter(final boolean vertical) {
this.mLayoutResourceId = vertical ? R.layout.tab_list_item : R.layout.tab_list_item_horizontal;
this.mDrawerTabs = vertical;
if (vertical) {
mBackgroundTabDrawable = null;
mForegroundTabBitmap = null;
mForegroundTabDrawable = ThemeUtils.getSelectedBackground(getContext(), mDarkTheme);
} else {
int backgroundColor = Utils.mixTwoColors(ThemeUtils.getPrimaryColor(getContext()), Color.BLACK, 0.75f);
Bitmap backgroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888);
Utils.drawTrapezoid(new Canvas(backgroundTabBitmap), backgroundColor, true);
mBackgroundTabDrawable = new BitmapDrawable(getResources(), backgroundTabBitmap);
int foregroundColor = ThemeUtils.getPrimaryColor(getContext());
mForegroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888);
Utils.drawTrapezoid(new Canvas(mForegroundTabBitmap), foregroundColor, false);
mForegroundTabDrawable = null;
}
}
@NonNull
@Override
public LightningViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
View view = inflater.inflate(mLayoutResourceId, viewGroup, false);
return new LightningViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final LightningViewHolder holder, int position) {
holder.exitButton.setTag(position);
ViewCompat.jumpDrawablesToCurrentState(holder.exitButton);
LightningView web = mTabsManager.getTabAtPosition(position);
if (web == null) {
return;
}
holder.txtTitle.setText(web.getTitle());
final Bitmap favicon = web.getFavicon();
if (web.isForegroundTab()) {
TextViewCompat.setTextAppearance(holder.txtTitle, R.style.boldText);
Drawable foregroundDrawable;
if (!mDrawerTabs) {
foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap);
if (!mIsIncognito && mColorMode) {
foregroundDrawable.setColorFilter(mUiController.getUiColor(), PorterDuff.Mode.SRC_IN);
}
} else {
foregroundDrawable = mForegroundTabDrawable;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
holder.layout.setBackground(foregroundDrawable);
} else {
//noinspection deprecation
holder.layout.setBackgroundDrawable(foregroundDrawable);
}
if (!mIsIncognito && mColorMode) {
mUiController.changeToolbarBackground(favicon, foregroundDrawable);
}
holder.favicon.setImageBitmap(favicon);
} else {
TextViewCompat.setTextAppearance(holder.txtTitle, R.style.normalText);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
holder.layout.setBackground(mBackgroundTabDrawable);
} else {
//noinspection deprecation
holder.layout.setBackgroundDrawable(mBackgroundTabDrawable);
}
holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon));
}
}
@Override
public int getItemCount() {
return mTabsManager.size();
}
public Bitmap getDesaturatedBitmap(@NonNull Bitmap favicon) {
Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(),
favicon.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(grayscaleBitmap);
if (mColorMatrix == null || mFilter == null || mPaint == null) {
mPaint = new Paint();
mColorMatrix = new ColorMatrix();
mColorMatrix.setSaturation(DESATURATED);
mFilter = new ColorMatrixColorFilter(mColorMatrix);
mPaint.setColorFilter(mFilter);
}
c.drawBitmap(favicon, 0, 0, mPaint);
return grayscaleBitmap;
}
public class LightningViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public LightningViewHolder(@NonNull View view) {
super(view);
txtTitle = (TextView) view.findViewById(R.id.textTab);
favicon = (ImageView) view.findViewById(R.id.faviconTab);
exit = (ImageView) view.findViewById(R.id.deleteButton);
layout = (LinearLayout) view.findViewById(R.id.tab_item_background);
exitButton = (FrameLayout) view.findViewById(R.id.deleteAction);
exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
exitButton.setOnClickListener(this);
layout.setOnClickListener(this);
layout.setOnLongClickListener(this);
}
@NonNull final TextView txtTitle;
@NonNull final ImageView favicon;
@NonNull final ImageView exit;
@NonNull final FrameLayout exitButton;
@NonNull final LinearLayout layout;
@Override
public void onClick(View v) {
if (v == exitButton) {
// Close tab
mBus.post(new TabEvents.CloseTab(getAdapterPosition()));
}
if (v == layout) {
mBus.post(new TabEvents.ShowTab(getAdapterPosition()));
}
}
@Override
public boolean onLongClick(View v) {
// Show close dialog
mBus.post(new TabEvents.ShowCloseDialog(getAdapterPosition()));
return true;
}
}
}
}