Stefano Pacifici
9 years ago
10 changed files with 447 additions and 237 deletions
@ -0,0 +1,49 @@ |
|||||||
|
package acr.browser.lightning.bus; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Stefano Pacifici |
||||||
|
* @date 2015/09/14 |
||||||
|
*/ |
||||||
|
public final class TabEvents { |
||||||
|
|
||||||
|
private TabEvents() { |
||||||
|
// No instances
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the |
||||||
|
* tab exit button |
||||||
|
*/ |
||||||
|
public static class CloseTab { |
||||||
|
public final int position; |
||||||
|
|
||||||
|
public CloseTab(int position) { |
||||||
|
this.position = position; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the |
||||||
|
* tab itself. |
||||||
|
*/ |
||||||
|
public static class ShowTab { |
||||||
|
public final int position; |
||||||
|
|
||||||
|
public ShowTab(int position) { |
||||||
|
this.position = position; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long press on the |
||||||
|
* tab itself. |
||||||
|
*/ |
||||||
|
public static class ShowCloseDialog { |
||||||
|
public final int position; |
||||||
|
|
||||||
|
public ShowCloseDialog(int position) { |
||||||
|
this.position = position; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,281 @@ |
|||||||
|
package acr.browser.lightning.fragment; |
||||||
|
|
||||||
|
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.Nullable; |
||||||
|
import android.support.v4.app.Fragment; |
||||||
|
import android.support.v4.view.ViewCompat; |
||||||
|
import android.support.v7.widget.LinearLayoutManager; |
||||||
|
import android.support.v7.widget.RecyclerView; |
||||||
|
import android.support.v7.widget.RecyclerView.LayoutManager; |
||||||
|
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 com.squareup.otto.Subscribe; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
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.bus.BrowserEvents; |
||||||
|
import acr.browser.lightning.bus.TabEvents; |
||||||
|
import acr.browser.lightning.utils.ThemeUtils; |
||||||
|
import acr.browser.lightning.utils.Utils; |
||||||
|
import acr.browser.lightning.view.LightningView; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Stefano Pacifici based on Anthony C. Restaino's code |
||||||
|
* @date 2015/09/14 |
||||||
|
*/ |
||||||
|
public class TabsFragment extends Fragment { |
||||||
|
|
||||||
|
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"; |
||||||
|
|
||||||
|
private boolean mDarkTheme = false; // TODO Only temporary
|
||||||
|
private int mIconColor = 0; // TODO Only temporary
|
||||||
|
private boolean mColorMode = true; // TODO Only temporary
|
||||||
|
private boolean isIncognito = false; // TODO Only temporary
|
||||||
|
private int mCurrentUiColor = 0; // TODO Only temporary
|
||||||
|
|
||||||
|
private RecyclerView mRecyclerView; |
||||||
|
private LightningViewAdapter mTabsAdapter; |
||||||
|
|
||||||
|
@Inject |
||||||
|
TabsManager tabsManager; |
||||||
|
|
||||||
|
@Inject |
||||||
|
Bus bus; |
||||||
|
|
||||||
|
public TabsFragment() { |
||||||
|
BrowserApp.getAppComponent().inject(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
||||||
|
final Bundle arguments = getArguments(); |
||||||
|
final boolean vertical = arguments.getBoolean(VERTICAL_MODE, true); |
||||||
|
final View view; |
||||||
|
final LayoutManager layoutManager; |
||||||
|
if (vertical) { |
||||||
|
view = inflater.inflate(R.layout.tab_drawer, container, false); |
||||||
|
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); |
||||||
|
} else { |
||||||
|
view = inflater.inflate(R.layout.tab_strip, container, false); |
||||||
|
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); |
||||||
|
} |
||||||
|
mRecyclerView = (RecyclerView) view.findViewById(R.id.tabs_list); |
||||||
|
mRecyclerView.setLayoutManager(layoutManager); |
||||||
|
mTabsAdapter = new LightningViewAdapter(vertical); |
||||||
|
mRecyclerView.setAdapter(mTabsAdapter); |
||||||
|
mRecyclerView.setHasFixedSize(true); |
||||||
|
return view; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroyView() { |
||||||
|
mRecyclerView = null; |
||||||
|
mTabsAdapter = null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStart() { |
||||||
|
super.onStart(); |
||||||
|
bus.register(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStop() { |
||||||
|
super.onStop(); |
||||||
|
bus.unregister(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Subscribe |
||||||
|
public void tabsChanged(final BrowserEvents.TabsChanged event) { |
||||||
|
if (mTabsAdapter != null) { |
||||||
|
mTabsAdapter.notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class LightningViewAdapter extends RecyclerView.Adapter<LightningViewAdapter.LightningViewHolder> { |
||||||
|
|
||||||
|
private final int layoutResourceId; |
||||||
|
private final Drawable mBackgroundTabDrawable; |
||||||
|
private final Drawable mForegroundTabDrawable; |
||||||
|
private final Bitmap mForegroundTabBitmap; |
||||||
|
private ColorMatrix mColorMatrix; |
||||||
|
private Paint mPaint; |
||||||
|
private ColorFilter mFilter; |
||||||
|
private static final float DESATURATED = 0.5f; |
||||||
|
|
||||||
|
private final boolean vertical; |
||||||
|
|
||||||
|
public LightningViewAdapter(final boolean vertical) { |
||||||
|
this.layoutResourceId = vertical ? R.layout.tab_list_item : R.layout.tab_list_item_horizontal; |
||||||
|
this.vertical = 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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public LightningViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { |
||||||
|
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); |
||||||
|
View view = inflater.inflate(layoutResourceId, viewGroup, false); |
||||||
|
return new LightningViewHolder(view); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBindViewHolder(final LightningViewHolder holder, int position) { |
||||||
|
holder.exitButton.setTag(position); |
||||||
|
|
||||||
|
ViewCompat.jumpDrawablesToCurrentState(holder.exitButton); |
||||||
|
|
||||||
|
LightningView web = tabsManager.getTabAtPosition(position); |
||||||
|
holder.txtTitle.setText(web.getTitle()); |
||||||
|
|
||||||
|
final Bitmap favicon = web.getFavicon(); |
||||||
|
if (web.isForegroundTab()) { |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
||||||
|
holder.txtTitle.setTextAppearance(R.style.boldText); |
||||||
|
} else { |
||||||
|
holder.txtTitle.setTextAppearance(getContext(), R.style.boldText); |
||||||
|
} |
||||||
|
Drawable foregroundDrawable; |
||||||
|
if (!vertical) { |
||||||
|
foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap); |
||||||
|
if (!isIncognito && mColorMode) { |
||||||
|
foregroundDrawable.setColorFilter(mCurrentUiColor, PorterDuff.Mode.SRC_IN); |
||||||
|
} |
||||||
|
} else { |
||||||
|
foregroundDrawable = mForegroundTabDrawable; |
||||||
|
} |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
||||||
|
holder.layout.setBackground(foregroundDrawable); |
||||||
|
} else { |
||||||
|
holder.layout.setBackgroundDrawable(foregroundDrawable); |
||||||
|
} |
||||||
|
if (!isIncognito && mColorMode) { |
||||||
|
// TODO Restore this
|
||||||
|
// changeToolbarBackground(favicon, foregroundDrawable);
|
||||||
|
} |
||||||
|
holder.favicon.setImageBitmap(favicon); |
||||||
|
} else { |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
||||||
|
holder.txtTitle.setTextAppearance(R.style.normalText); |
||||||
|
} else { |
||||||
|
holder.txtTitle.setTextAppearance(getContext(), R.style.normalText); |
||||||
|
} |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
||||||
|
holder.layout.setBackground(mBackgroundTabDrawable); |
||||||
|
} else { |
||||||
|
holder.layout.setBackgroundDrawable(mBackgroundTabDrawable); |
||||||
|
} |
||||||
|
holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getItemCount() { |
||||||
|
return tabsManager.size(); |
||||||
|
} |
||||||
|
|
||||||
|
public Bitmap getDesaturatedBitmap(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(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); |
||||||
|
} |
||||||
|
|
||||||
|
final TextView txtTitle; |
||||||
|
final ImageView favicon; |
||||||
|
final ImageView exit; |
||||||
|
final FrameLayout exitButton; |
||||||
|
final LinearLayout layout; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onClick(View v) { |
||||||
|
if (v == exitButton) { |
||||||
|
// Close tab
|
||||||
|
bus.post(new TabEvents.CloseTab(getAdapterPosition())); |
||||||
|
} |
||||||
|
if (v == layout) { |
||||||
|
bus.post(new TabEvents.ShowTab(getAdapterPosition())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onLongClick(View v) { |
||||||
|
// Show close dialog
|
||||||
|
bus.post(new TabEvents.ShowCloseDialog(getAdapterPosition())); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="30dp" |
||||||
|
android:background="@color/black" |
||||||
|
android:overScrollMode="never" |
||||||
|
android:scrollbars="none" |
||||||
|
android:id="@+id/tabs_list" /> |
||||||
|
|
Loading…
Reference in new issue