Converting adapters and retrofit services to kotlin.
This commit is contained in:
parent
97cee06ebe
commit
9b95404500
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<!-- For firebase only -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MyApp"
|
android:name=".MyApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -104,7 +104,7 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
if (title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()) {
|
if (title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()) {
|
||||||
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
api.createSource(title, url, mSpoutsValue, mTags.text.toString(), "").enqueue(object : Callback<SuccessResponse> {
|
api.createSource(title, url, mSpoutsValue!!, mTags.text.toString(), "").enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
if (response.body() != null && response.body().isSuccess) {
|
if (response.body() != null && response.body().isSuccess) {
|
||||||
finish()
|
finish()
|
||||||
|
@ -55,7 +55,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
private val REQUEST_INVITE_BYMAIL = 13232
|
private val REQUEST_INVITE_BYMAIL = 13232
|
||||||
private var mRecyclerView: RecyclerView? = null
|
private var mRecyclerView: RecyclerView? = null
|
||||||
private var api: SelfossApi? = null
|
private var api: SelfossApi? = null
|
||||||
private var items: List<Item>? = null
|
private var items: ArrayList<Item> = ArrayList()
|
||||||
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null
|
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null
|
||||||
|
|
||||||
private var clickBehavior = false
|
private var clickBehavior = false
|
||||||
@ -177,15 +177,15 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
|
||||||
try {
|
try {
|
||||||
val i = items!![viewHolder.adapterPosition]
|
val i = items[viewHolder.adapterPosition]
|
||||||
val position = items!!.indexOf(i)
|
val position = items.indexOf(i)
|
||||||
|
|
||||||
if (shouldBeCardView) {
|
if (shouldBeCardView) {
|
||||||
(mRecyclerView!!.adapter as ItemCardAdapter).removeItemAtIndex(position)
|
(mRecyclerView!!.adapter as ItemCardAdapter).removeItemAtIndex(position)
|
||||||
} else {
|
} else {
|
||||||
(mRecyclerView!!.adapter as ItemListAdapter).removeItemAtIndex(position)
|
(mRecyclerView!!.adapter as ItemListAdapter).removeItemAtIndex(position)
|
||||||
}
|
}
|
||||||
tabNew!!.setBadgeCount(items!!.size - 1)
|
tabNew!!.setBadgeCount(items.size - 1)
|
||||||
|
|
||||||
} catch (e: IndexOutOfBoundsException) {
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
FirebaseCrash.logcat(Log.ERROR, "SWIPE ERROR", "Swipe index out of bound")
|
FirebaseCrash.logcat(Log.ERROR, "SWIPE ERROR", "Swipe index out of bound")
|
||||||
@ -248,7 +248,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
api!!.unreadItems.enqueue(object : Callback<List<Item>> {
|
api!!.unreadItems.enqueue(object : Callback<List<Item>> {
|
||||||
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
||||||
if (response.body() != null && response.body().isNotEmpty()) {
|
if (response.body() != null && response.body().isNotEmpty()) {
|
||||||
items = response.body()
|
items = response.body() as ArrayList<Item>
|
||||||
} else {
|
} else {
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
api!!.readItems.enqueue(object : Callback<List<Item>> {
|
api!!.readItems.enqueue(object : Callback<List<Item>> {
|
||||||
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
||||||
if (response.body() != null && response.body().isNotEmpty()) {
|
if (response.body() != null && response.body().isNotEmpty()) {
|
||||||
items = response.body()
|
items = response.body() as ArrayList<Item>
|
||||||
} else {
|
} else {
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
}
|
}
|
||||||
@ -288,7 +288,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
api!!.starredItems.enqueue(object : Callback<List<Item>> {
|
api!!.starredItems.enqueue(object : Callback<List<Item>> {
|
||||||
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
|
||||||
if (response.body() != null && response.body().isNotEmpty()) {
|
if (response.body() != null && response.body().isNotEmpty()) {
|
||||||
items = response.body()
|
items = response.body() as ArrayList<Item>
|
||||||
} else {
|
} else {
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
}
|
}
|
||||||
@ -308,14 +308,14 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val mAdapter: RecyclerView.Adapter<*>
|
val mAdapter: RecyclerView.Adapter<*>
|
||||||
if (shouldBeCardView) {
|
if (shouldBeCardView) {
|
||||||
mAdapter = ItemCardAdapter(this, items, api, mCustomTabActivityHelper, internalBrowser, articleViewer, fullHeightCards)
|
mAdapter = ItemCardAdapter(this, items, api!!, mCustomTabActivityHelper!!, internalBrowser, articleViewer, fullHeightCards)
|
||||||
} else {
|
} else {
|
||||||
mAdapter = ItemListAdapter(this, items, api, mCustomTabActivityHelper, clickBehavior, internalBrowser, articleViewer)
|
mAdapter = ItemListAdapter(this, items, api!!, mCustomTabActivityHelper!!, clickBehavior, internalBrowser, articleViewer)
|
||||||
}
|
}
|
||||||
mRecyclerView!!.adapter = mAdapter
|
mRecyclerView!!.adapter = mAdapter
|
||||||
mAdapter.notifyDataSetChanged()
|
mAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
if (items!!.isEmpty()) Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show()
|
if (items.isEmpty()) Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
}
|
}
|
||||||
@ -356,7 +356,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
R.id.readAll -> {
|
R.id.readAll -> {
|
||||||
if (elementsShown == UNREAD_SHOWN) {
|
if (elementsShown == UNREAD_SHOWN) {
|
||||||
mSwipeRefreshLayout!!.isRefreshing = false
|
mSwipeRefreshLayout!!.isRefreshing = false
|
||||||
val ids = items!!.map { it.id }
|
val ids = items.map { it.id }
|
||||||
|
|
||||||
api!!.readAll(ids).enqueue(object : Callback<SuccessResponse> {
|
api!!.readAll(ids).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
|
@ -23,6 +23,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
|||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.checkAndDisplayStoreApk
|
import apps.amine.bou.readerforselfoss.utils.checkAndDisplayStoreApk
|
||||||
import apps.amine.bou.readerforselfoss.utils.isUrlValid
|
import apps.amine.bou.readerforselfoss.utils.isUrlValid
|
||||||
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
@ -43,6 +44,8 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
private var isWithLogin = false
|
private var isWithLogin = false
|
||||||
private var isWithHTTPLogin = false
|
private var isWithHTTPLogin = false
|
||||||
private var mLoginFormView: View? = null
|
private var mLoginFormView: View? = null
|
||||||
|
private var mFirebaseAnalytics: FirebaseAnalytics? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -60,6 +63,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
isWithHTTPLogin = false
|
isWithHTTPLogin = false
|
||||||
inValidCount = 0
|
inValidCount = 0
|
||||||
|
|
||||||
|
mFirebaseAnalytics = FirebaseAnalytics.getInstance(this)
|
||||||
mUrlView = findViewById(R.id.url) as EditText
|
mUrlView = findViewById(R.id.url) as EditText
|
||||||
mLoginView = findViewById(R.id.login) as TextView
|
mLoginView = findViewById(R.id.login) as TextView
|
||||||
mHTTPLoginView = findViewById(R.id.httpLogin) as TextView
|
mHTTPLoginView = findViewById(R.id.httpLogin) as TextView
|
||||||
@ -200,7 +204,8 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
if (response.body() != null && response.body().isSuccess) {
|
if (response.body() != null && response.body().isSuccess) {
|
||||||
goToMain()
|
mFirebaseAnalytics!!.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle())
|
||||||
|
goToMain()
|
||||||
} else {
|
} else {
|
||||||
preferenceError()
|
preferenceError()
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
val mRecyclerView = findViewById(R.id.activity_sources) as RecyclerView
|
val mRecyclerView = findViewById(R.id.activity_sources) as RecyclerView
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
val api = SelfossApi(this)
|
val api = SelfossApi(this)
|
||||||
var items: List<Sources> = ArrayList()
|
var items: ArrayList<Sources> = ArrayList()
|
||||||
|
|
||||||
mFab.attachToRecyclerView(mRecyclerView)
|
mFab.attachToRecyclerView(mRecyclerView)
|
||||||
mRecyclerView.setHasFixedSize(true)
|
mRecyclerView.setHasFixedSize(true)
|
||||||
@ -37,7 +37,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
api.sources.enqueue(object : Callback<List<Sources>> {
|
api.sources.enqueue(object : Callback<List<Sources>> {
|
||||||
override fun onResponse(call: Call<List<Sources>>, response: Response<List<Sources>>) {
|
override fun onResponse(call: Call<List<Sources>>, response: Response<List<Sources>>) {
|
||||||
if (response.body() != null && response.body().isNotEmpty()) {
|
if (response.body() != null && response.body().isNotEmpty()) {
|
||||||
items = response.body()
|
items = response.body() as ArrayList<Sources>
|
||||||
}
|
}
|
||||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||||
mRecyclerView.adapter = mAdapter
|
mRecyclerView.adapter = mAdapter
|
||||||
|
@ -1,310 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.adapters;
|
|
||||||
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.constraint.ConstraintLayout;
|
|
||||||
import android.support.customtabs.CustomTabsIntent;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.Html;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ImageView.ScaleType;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.R;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse;
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper;
|
|
||||||
import com.amulyakhare.textdrawable.TextDrawable;
|
|
||||||
import com.amulyakhare.textdrawable.TextDrawable.IBuilder;
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator;
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.request.target.BitmapImageViewTarget;
|
|
||||||
import com.like.LikeButton;
|
|
||||||
import com.like.OnLikeListener;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Callback;
|
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
import static apps.amine.bou.readerforselfoss.utils.LinksUtilsKt.buildCustomTabsIntent;
|
|
||||||
import static apps.amine.bou.readerforselfoss.utils.LinksUtilsKt.openItemUrl;
|
|
||||||
|
|
||||||
|
|
||||||
public class ItemCardAdapter extends RecyclerView.Adapter<ItemCardAdapter.ViewHolder> {
|
|
||||||
private final List<Item> items;
|
|
||||||
private final SelfossApi api;
|
|
||||||
private final CustomTabActivityHelper helper;
|
|
||||||
private final Context c;
|
|
||||||
private final boolean internalBrowser;
|
|
||||||
private final boolean articleViewer;
|
|
||||||
private final Activity app;
|
|
||||||
private final ColorGenerator generator;
|
|
||||||
private final boolean fullHeightCards;
|
|
||||||
|
|
||||||
public ItemCardAdapter(Activity a, List<Item> myObject, SelfossApi selfossApi,
|
|
||||||
CustomTabActivityHelper mCustomTabActivityHelper, boolean internalBrowser,
|
|
||||||
boolean articleViewer, boolean fullHeightCards) {
|
|
||||||
app = a;
|
|
||||||
items = myObject;
|
|
||||||
api = selfossApi;
|
|
||||||
helper = mCustomTabActivityHelper;
|
|
||||||
c = app.getApplicationContext();
|
|
||||||
this.internalBrowser = internalBrowser;
|
|
||||||
this.articleViewer = articleViewer;
|
|
||||||
generator = ColorGenerator.MATERIAL;
|
|
||||||
this.fullHeightCards = fullHeightCards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
ConstraintLayout v = (ConstraintLayout) LayoutInflater.from(c).inflate(R.layout.card_item, parent, false);
|
|
||||||
return new ViewHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
|
||||||
Item itm = items.get(position);
|
|
||||||
|
|
||||||
|
|
||||||
holder.saveBtn.setLiked((itm.getStarred()));
|
|
||||||
holder.title.setText(Html.fromHtml(itm.getTitle()));
|
|
||||||
|
|
||||||
String sourceAndDate = itm.getSourcetitle();
|
|
||||||
long d;
|
|
||||||
try {
|
|
||||||
d = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(itm.getDatetime()).getTime();
|
|
||||||
sourceAndDate += " " +
|
|
||||||
DateUtils.getRelativeTimeSpanString(
|
|
||||||
d,
|
|
||||||
new Date().getTime(),
|
|
||||||
DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
|
||||||
);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
holder.sourceTitleAndDate.setText(sourceAndDate);
|
|
||||||
|
|
||||||
if (itm.getThumbnail(c).isEmpty()) {
|
|
||||||
Glide.clear(holder.itemImage);
|
|
||||||
holder.itemImage.setImageDrawable(null);
|
|
||||||
} else {
|
|
||||||
if (fullHeightCards) {
|
|
||||||
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().fitCenter().into(holder.itemImage);
|
|
||||||
} else {
|
|
||||||
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().centerCrop().into(holder.itemImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ViewHolder fHolder = holder;
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
|
||||||
int color = generator.getColor(itm.getSourcetitle());
|
|
||||||
StringBuilder textDrawable = new StringBuilder();
|
|
||||||
for(String s : itm.getSourcetitle().split(" "))
|
|
||||||
{
|
|
||||||
textDrawable.append(s.charAt(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
IBuilder builder = TextDrawable.builder().round();
|
|
||||||
|
|
||||||
TextDrawable drawable = builder.build(textDrawable.toString(), color);
|
|
||||||
holder.sourceImage.setImageDrawable(drawable);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(new BitmapImageViewTarget(holder.sourceImage) {
|
|
||||||
@Override
|
|
||||||
protected void setResource(Bitmap resource) {
|
|
||||||
RoundedBitmapDrawable circularBitmapDrawable =
|
|
||||||
RoundedBitmapDrawableFactory.create(c.getResources(), resource);
|
|
||||||
circularBitmapDrawable.setCircular(true);
|
|
||||||
fHolder.sourceImage.setImageDrawable(circularBitmapDrawable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.saveBtn.setLiked(itm.getStarred());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doUnmark(final Item i, final int position) {
|
|
||||||
Snackbar s = Snackbar
|
|
||||||
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(R.string.undo_string, new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
items.add(position, i);
|
|
||||||
notifyItemInserted(position);
|
|
||||||
|
|
||||||
api.unmarkItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
items.remove(i);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
doUnmark(i, position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
View view = s.getView();
|
|
||||||
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
|
|
||||||
tv.setTextColor(Color.WHITE);
|
|
||||||
s.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeItemAtIndex(final int position) {
|
|
||||||
|
|
||||||
final Item i = items.get(position);
|
|
||||||
|
|
||||||
items.remove(i);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
|
|
||||||
api.markItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {
|
|
||||||
|
|
||||||
doUnmark(i, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show();
|
|
||||||
items.add(i);
|
|
||||||
notifyItemInserted(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
LikeButton saveBtn;
|
|
||||||
ImageButton browserBtn;
|
|
||||||
ImageButton shareBtn;
|
|
||||||
ImageView itemImage;
|
|
||||||
ImageView sourceImage;
|
|
||||||
TextView title;
|
|
||||||
TextView sourceTitleAndDate;
|
|
||||||
public ConstraintLayout mView;
|
|
||||||
|
|
||||||
public ViewHolder(ConstraintLayout itemView) {
|
|
||||||
super(itemView);
|
|
||||||
mView = itemView;
|
|
||||||
handleClickListeners();
|
|
||||||
handleCustomTabActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleClickListeners() {
|
|
||||||
sourceImage = (ImageView) mView.findViewById( R.id.sourceImage);
|
|
||||||
itemImage = (ImageView) mView.findViewById( R.id.itemImage);
|
|
||||||
title = (TextView) mView.findViewById( R.id.title);
|
|
||||||
sourceTitleAndDate = (TextView) mView.findViewById( R.id.sourceTitleAndDate);
|
|
||||||
saveBtn = (LikeButton) mView.findViewById( R.id.favButton);
|
|
||||||
shareBtn = (ImageButton) mView.findViewById( R.id.shareBtn);
|
|
||||||
browserBtn = (ImageButton) mView.findViewById( R.id.browserBtn);
|
|
||||||
|
|
||||||
if (!fullHeightCards) {
|
|
||||||
itemImage.setMaxHeight((int) c.getResources().getDimension(R.dimen.card_image_max_height));
|
|
||||||
itemImage.setScaleType(ScaleType.CENTER_CROP);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveBtn.setOnLikeListener(new OnLikeListener() {
|
|
||||||
@Override
|
|
||||||
public void liked(LikeButton likeButton) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
api.starrItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
saveBtn.setLiked(false);
|
|
||||||
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unLiked(LikeButton likeButton) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
api.unstarrItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
saveBtn.setLiked(true);
|
|
||||||
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
shareBtn.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
Intent sendIntent = new Intent();
|
|
||||||
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, i.getLinkDecoded());
|
|
||||||
sendIntent.setType("text/plain");
|
|
||||||
c.startActivity(Intent.createChooser(sendIntent, c.getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
browserBtn.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.setData(Uri.parse(i.getLinkDecoded()));
|
|
||||||
c.startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCustomTabActions() {
|
|
||||||
final CustomTabsIntent customTabsIntent = buildCustomTabsIntent(c);
|
|
||||||
helper.bindCustomTabsService(app);
|
|
||||||
|
|
||||||
mView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
openItemUrl(items.get(getAdapterPosition()),
|
|
||||||
customTabsIntent,
|
|
||||||
internalBrowser,
|
|
||||||
articleViewer,
|
|
||||||
app,
|
|
||||||
c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,252 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.adapters
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
|
import android.support.constraint.ConstraintLayout
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.format.DateUtils
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.ImageView.ScaleType
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import apps.amine.bou.readerforselfoss.R
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||||
|
import com.like.LikeButton
|
||||||
|
import com.like.OnLikeListener
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class ItemCardAdapter(private val app: Activity, private val items: ArrayList<Item>, private val api: SelfossApi,
|
||||||
|
private val helper: CustomTabActivityHelper, private val internalBrowser: Boolean,
|
||||||
|
private val articleViewer: Boolean, private val fullHeightCards: Boolean) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() {
|
||||||
|
private val c: Context = app.applicationContext
|
||||||
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as ConstraintLayout
|
||||||
|
return ViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val itm = items[position]
|
||||||
|
|
||||||
|
|
||||||
|
holder.saveBtn!!.isLiked = itm.starred
|
||||||
|
holder.title!!.text = Html.fromHtml(itm.title)
|
||||||
|
|
||||||
|
var sourceAndDate = itm.sourcetitle
|
||||||
|
val d: Long
|
||||||
|
try {
|
||||||
|
d = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(itm.datetime).time
|
||||||
|
sourceAndDate += " " + DateUtils.getRelativeTimeSpanString(
|
||||||
|
d,
|
||||||
|
Date().time,
|
||||||
|
DateUtils.MINUTE_IN_MILLIS,
|
||||||
|
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||||
|
)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.sourceTitleAndDate!!.text = sourceAndDate
|
||||||
|
|
||||||
|
if (itm.getThumbnail(c).isEmpty()) {
|
||||||
|
Glide.clear(holder.itemImage)
|
||||||
|
holder.itemImage!!.setImageDrawable(null)
|
||||||
|
} else {
|
||||||
|
if (fullHeightCards) {
|
||||||
|
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().fitCenter().into(holder.itemImage)
|
||||||
|
} else {
|
||||||
|
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().centerCrop().into(holder.itemImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val fHolder = holder
|
||||||
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
|
val color = generator.getColor(itm.sourcetitle)
|
||||||
|
val textDrawable = StringBuilder()
|
||||||
|
for (s in itm.sourcetitle.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||||
|
textDrawable.append(s[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = TextDrawable.builder().round()
|
||||||
|
|
||||||
|
val drawable = builder.build(textDrawable.toString(), color)
|
||||||
|
holder.sourceImage!!.setImageDrawable(drawable)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(object : BitmapImageViewTarget(holder.sourceImage) {
|
||||||
|
override fun setResource(resource: Bitmap) {
|
||||||
|
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(c.resources, resource)
|
||||||
|
circularBitmapDrawable.isCircular = true
|
||||||
|
fHolder.sourceImage!!.setImageDrawable(circularBitmapDrawable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.saveBtn!!.isLiked = itm.starred
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doUnmark(i: Item, position: Int) {
|
||||||
|
val s = Snackbar
|
||||||
|
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.undo_string) {
|
||||||
|
items.add(position, i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
|
||||||
|
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
items.remove(i)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
doUnmark(i, position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = s.view
|
||||||
|
val tv = view.findViewById(android.support.design.R.id.snackbar_text) as TextView
|
||||||
|
tv.setTextColor(Color.WHITE)
|
||||||
|
s.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItemAtIndex(position: Int) {
|
||||||
|
|
||||||
|
val i = items[position]
|
||||||
|
|
||||||
|
items.remove(i)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
|
||||||
|
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
|
|
||||||
|
doUnmark(i, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show()
|
||||||
|
items.add(i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
|
var saveBtn: LikeButton? = null
|
||||||
|
var browserBtn: ImageButton? = null
|
||||||
|
var shareBtn: ImageButton? = null
|
||||||
|
var itemImage: ImageView? = null
|
||||||
|
var sourceImage: ImageView? = null
|
||||||
|
var title: TextView? = null
|
||||||
|
var sourceTitleAndDate: TextView? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
handleClickListeners()
|
||||||
|
handleCustomTabActions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleClickListeners() {
|
||||||
|
sourceImage = mView.findViewById(R.id.sourceImage) as ImageView
|
||||||
|
itemImage = mView.findViewById(R.id.itemImage) as ImageView
|
||||||
|
title = mView.findViewById(R.id.title) as TextView
|
||||||
|
sourceTitleAndDate = mView.findViewById(R.id.sourceTitleAndDate) as TextView
|
||||||
|
saveBtn = mView.findViewById(R.id.favButton) as LikeButton
|
||||||
|
shareBtn = mView.findViewById(R.id.shareBtn) as ImageButton
|
||||||
|
browserBtn = mView.findViewById(R.id.browserBtn) as ImageButton
|
||||||
|
|
||||||
|
if (!fullHeightCards) {
|
||||||
|
itemImage!!.maxHeight = c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||||
|
itemImage!!.scaleType = ScaleType.CENTER_CROP
|
||||||
|
}
|
||||||
|
|
||||||
|
saveBtn!!.setOnLikeListener(object : OnLikeListener {
|
||||||
|
override fun liked(likeButton: LikeButton) {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
saveBtn!!.isLiked = false
|
||||||
|
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unLiked(likeButton: LikeButton) {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
saveBtn!!.isLiked = true
|
||||||
|
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
shareBtn!!.setOnClickListener {
|
||||||
|
val i = items[adapterPosition]
|
||||||
|
val sendIntent = Intent()
|
||||||
|
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, i.getLinkDecoded())
|
||||||
|
sendIntent.type = "text/plain"
|
||||||
|
c.startActivity(Intent.createChooser(sendIntent, c.getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||||
|
}
|
||||||
|
|
||||||
|
browserBtn!!.setOnClickListener {
|
||||||
|
val i = items[adapterPosition]
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
intent.data = Uri.parse(i.getLinkDecoded())
|
||||||
|
c.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCustomTabActions() {
|
||||||
|
val customTabsIntent = buildCustomTabsIntent(c)
|
||||||
|
helper.bindCustomTabsService(app)
|
||||||
|
|
||||||
|
mView.setOnClickListener {
|
||||||
|
openItemUrl(items[adapterPosition],
|
||||||
|
customTabsIntent,
|
||||||
|
internalBrowser,
|
||||||
|
articleViewer,
|
||||||
|
app,
|
||||||
|
c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,363 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.adapters;
|
|
||||||
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.constraint.ConstraintLayout;
|
|
||||||
import android.support.customtabs.CustomTabsIntent;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.Html;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.R;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse;
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper;
|
|
||||||
import com.amulyakhare.textdrawable.TextDrawable;
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator;
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.request.target.BitmapImageViewTarget;
|
|
||||||
import com.like.LikeButton;
|
|
||||||
import com.like.OnLikeListener;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Callback;
|
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
import static apps.amine.bou.readerforselfoss.utils.LinksUtilsKt.buildCustomTabsIntent;
|
|
||||||
import static apps.amine.bou.readerforselfoss.utils.LinksUtilsKt.openItemUrl;
|
|
||||||
|
|
||||||
|
|
||||||
public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ViewHolder> {
|
|
||||||
private final boolean clickBehavior;
|
|
||||||
private final boolean articleViewer;
|
|
||||||
private final boolean internalBrowser;
|
|
||||||
private final ColorGenerator generator;
|
|
||||||
private SelfossApi api;
|
|
||||||
private Context c;
|
|
||||||
private List<Item> items;
|
|
||||||
private List<Boolean> bars;
|
|
||||||
private Activity app;
|
|
||||||
private CustomTabActivityHelper helper;
|
|
||||||
|
|
||||||
public ItemListAdapter(Activity a, List<Item> myObject, SelfossApi selfossApi,
|
|
||||||
CustomTabActivityHelper mCustomTabActivityHelper, boolean clickBehavior,
|
|
||||||
boolean internalBrowser, boolean articleViewer) {
|
|
||||||
app = a;
|
|
||||||
items = myObject;
|
|
||||||
api = selfossApi;
|
|
||||||
helper = mCustomTabActivityHelper;
|
|
||||||
c = app.getApplicationContext();
|
|
||||||
this.clickBehavior = clickBehavior;
|
|
||||||
this.internalBrowser = internalBrowser;
|
|
||||||
this.articleViewer = articleViewer;
|
|
||||||
bars = new ArrayList<>(Collections.nCopies(items.size() + 1, false));
|
|
||||||
generator = ColorGenerator.MATERIAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
ConstraintLayout v = (ConstraintLayout) LayoutInflater.from(c).inflate(R.layout.list_item, parent, false);
|
|
||||||
return new ViewHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
|
||||||
Item itm = items.get(position);
|
|
||||||
|
|
||||||
|
|
||||||
holder.saveBtn.setLiked((itm.getStarred()));
|
|
||||||
holder.title.setText(Html.fromHtml(itm.getTitle()));
|
|
||||||
|
|
||||||
String sourceAndDate = itm.getSourcetitle();
|
|
||||||
long d;
|
|
||||||
try {
|
|
||||||
d = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(itm.getDatetime()).getTime();
|
|
||||||
sourceAndDate += " " +
|
|
||||||
DateUtils.getRelativeTimeSpanString(
|
|
||||||
d,
|
|
||||||
new Date().getTime(),
|
|
||||||
DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
|
||||||
);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
holder.sourceTitleAndDate.setText(sourceAndDate);
|
|
||||||
|
|
||||||
if (itm.getThumbnail(c).isEmpty()) {
|
|
||||||
int sizeInInt = 46;
|
|
||||||
int sizeInDp = (int) TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP, sizeInInt, c.getResources()
|
|
||||||
.getDisplayMetrics());
|
|
||||||
|
|
||||||
int marginInInt = 16;
|
|
||||||
int marginInDp = (int) TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP, marginInInt, c.getResources()
|
|
||||||
.getDisplayMetrics());
|
|
||||||
|
|
||||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) holder.sourceImage.getLayoutParams();
|
|
||||||
params.height = sizeInDp;
|
|
||||||
params.width = sizeInDp;
|
|
||||||
params.setMargins(marginInDp, 0, 0, 0);
|
|
||||||
holder.sourceImage.setLayoutParams(params);
|
|
||||||
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
|
||||||
int color = generator.getColor(itm.getSourcetitle());
|
|
||||||
StringBuilder textDrawable = new StringBuilder();
|
|
||||||
for(String s : itm.getSourcetitle().split(" "))
|
|
||||||
{
|
|
||||||
textDrawable.append(s.charAt(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TextDrawable.IBuilder builder = TextDrawable.builder().round();
|
|
||||||
|
|
||||||
TextDrawable drawable = builder.build(textDrawable.toString(), color);
|
|
||||||
holder.sourceImage.setImageDrawable(drawable);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
final ViewHolder fHolder = holder;
|
|
||||||
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(new BitmapImageViewTarget(holder.sourceImage) {
|
|
||||||
@Override
|
|
||||||
protected void setResource(Bitmap resource) {
|
|
||||||
RoundedBitmapDrawable circularBitmapDrawable =
|
|
||||||
RoundedBitmapDrawableFactory.create(c.getResources(), resource);
|
|
||||||
circularBitmapDrawable.setCircular(true);
|
|
||||||
fHolder.sourceImage.setImageDrawable(circularBitmapDrawable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().centerCrop().into(holder.sourceImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bars.get(position)) {
|
|
||||||
holder.actionBar.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.actionBar.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.saveBtn.setLiked(itm.getStarred());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void doUnmark(final Item i, final int position) {
|
|
||||||
Snackbar s = Snackbar
|
|
||||||
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(R.string.undo_string, new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
items.add(position, i);
|
|
||||||
notifyItemInserted(position);
|
|
||||||
|
|
||||||
api.unmarkItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
items.remove(i);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
doUnmark(i, position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
View view = s.getView();
|
|
||||||
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
|
|
||||||
tv.setTextColor(Color.WHITE);
|
|
||||||
s.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeItemAtIndex(final int position) {
|
|
||||||
|
|
||||||
final Item i = items.get(position);
|
|
||||||
|
|
||||||
items.remove(i);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
|
|
||||||
api.markItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {
|
|
||||||
doUnmark(i, position);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show();
|
|
||||||
items.add(i);
|
|
||||||
notifyItemInserted(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
LikeButton saveBtn;
|
|
||||||
ImageButton browserBtn;
|
|
||||||
ImageButton shareBtn;
|
|
||||||
public RelativeLayout actionBar;
|
|
||||||
ImageView sourceImage;
|
|
||||||
TextView title;
|
|
||||||
TextView sourceTitleAndDate;
|
|
||||||
public ConstraintLayout mView;
|
|
||||||
|
|
||||||
public ViewHolder(ConstraintLayout itemView) {
|
|
||||||
super(itemView);
|
|
||||||
mView = itemView;
|
|
||||||
handleClickListeners();
|
|
||||||
handleCustomTabActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleClickListeners() {
|
|
||||||
actionBar = (RelativeLayout) mView.findViewById(R.id.actionBar);
|
|
||||||
sourceImage = (ImageView) mView.findViewById( R.id.itemImage);
|
|
||||||
title = (TextView) mView.findViewById( R.id.title);
|
|
||||||
sourceTitleAndDate = (TextView) mView.findViewById( R.id.sourceTitleAndDate);
|
|
||||||
saveBtn = (LikeButton) mView.findViewById( R.id.favButton);
|
|
||||||
shareBtn = (ImageButton) mView.findViewById( R.id.shareBtn);
|
|
||||||
browserBtn = (ImageButton) mView.findViewById( R.id.browserBtn);
|
|
||||||
|
|
||||||
|
|
||||||
saveBtn.setOnLikeListener(new OnLikeListener() {
|
|
||||||
@Override
|
|
||||||
public void liked(LikeButton likeButton) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
api.starrItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
saveBtn.setLiked(false);
|
|
||||||
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unLiked(LikeButton likeButton) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
api.unstarrItem(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
saveBtn.setLiked(true);
|
|
||||||
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
shareBtn.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View view) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
Intent sendIntent = new Intent();
|
|
||||||
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, i.getLinkDecoded());
|
|
||||||
sendIntent.setType("text/plain");
|
|
||||||
c.startActivity(Intent.createChooser(sendIntent, c.getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
browserBtn.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Item i = items.get(getAdapterPosition());
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.setData(Uri.parse(i.getLinkDecoded()));
|
|
||||||
c.startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void handleCustomTabActions() {
|
|
||||||
final CustomTabsIntent customTabsIntent = buildCustomTabsIntent(c);
|
|
||||||
helper.bindCustomTabsService(app);
|
|
||||||
|
|
||||||
|
|
||||||
if (!clickBehavior) {
|
|
||||||
mView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
openItemUrl(items.get(getAdapterPosition()),
|
|
||||||
customTabsIntent,
|
|
||||||
internalBrowser,
|
|
||||||
articleViewer,
|
|
||||||
app,
|
|
||||||
c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mView.setOnLongClickListener(new View.OnLongClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onLongClick(View view) {
|
|
||||||
actionBarShowHide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
mView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
actionBarShowHide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mView.setOnLongClickListener(new View.OnLongClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onLongClick(View view) {
|
|
||||||
openItemUrl(items.get(getAdapterPosition()),
|
|
||||||
customTabsIntent,
|
|
||||||
internalBrowser,
|
|
||||||
articleViewer,
|
|
||||||
app,
|
|
||||||
c);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void actionBarShowHide() {
|
|
||||||
bars.set(getAdapterPosition(), true);
|
|
||||||
if (actionBar.getVisibility() == View.GONE)
|
|
||||||
actionBar.setVisibility(View.VISIBLE);
|
|
||||||
else
|
|
||||||
actionBar.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,290 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.adapters
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
|
import android.support.constraint.ConstraintLayout
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.format.DateUtils
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.*
|
||||||
|
import apps.amine.bou.readerforselfoss.R
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||||
|
import com.like.LikeButton
|
||||||
|
import com.like.OnLikeListener
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class ItemListAdapter(private val app: Activity, private val items: ArrayList<Item>, private val api: SelfossApi,
|
||||||
|
private val helper: CustomTabActivityHelper, private val clickBehavior: Boolean,
|
||||||
|
private val internalBrowser: Boolean, private val articleViewer: Boolean) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
|
||||||
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
|
private val c: Context = app.applicationContext
|
||||||
|
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val v = LayoutInflater.from(c).inflate(R.layout.list_item, parent, false) as ConstraintLayout
|
||||||
|
return ViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val itm = items[position]
|
||||||
|
|
||||||
|
|
||||||
|
holder.saveBtn!!.isLiked = itm.starred
|
||||||
|
holder.title!!.text = Html.fromHtml(itm.title)
|
||||||
|
|
||||||
|
var sourceAndDate = itm.sourcetitle
|
||||||
|
val d: Long
|
||||||
|
try {
|
||||||
|
d = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(itm.datetime).time
|
||||||
|
sourceAndDate += " " + DateUtils.getRelativeTimeSpanString(
|
||||||
|
d,
|
||||||
|
Date().time,
|
||||||
|
DateUtils.MINUTE_IN_MILLIS,
|
||||||
|
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||||
|
)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.sourceTitleAndDate!!.text = sourceAndDate
|
||||||
|
|
||||||
|
if (itm.getThumbnail(c).isEmpty()) {
|
||||||
|
val sizeInInt = 46
|
||||||
|
val sizeInDp = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP, sizeInInt.toFloat(), c.resources
|
||||||
|
.displayMetrics).toInt()
|
||||||
|
|
||||||
|
val marginInInt = 16
|
||||||
|
val marginInDp = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP, marginInInt.toFloat(), c.resources
|
||||||
|
.displayMetrics).toInt()
|
||||||
|
|
||||||
|
val params = holder.sourceImage!!.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
params.height = sizeInDp
|
||||||
|
params.width = sizeInDp
|
||||||
|
params.setMargins(marginInDp, 0, 0, 0)
|
||||||
|
holder.sourceImage!!.layoutParams = params
|
||||||
|
|
||||||
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
|
val color = generator.getColor(itm.sourcetitle)
|
||||||
|
val textDrawable = StringBuilder()
|
||||||
|
for (s in itm.sourcetitle.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||||
|
textDrawable.append(s[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = TextDrawable.builder().round()
|
||||||
|
|
||||||
|
val drawable = builder.build(textDrawable.toString(), color)
|
||||||
|
holder.sourceImage!!.setImageDrawable(drawable)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val fHolder = holder
|
||||||
|
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(object : BitmapImageViewTarget(holder.sourceImage) {
|
||||||
|
override fun setResource(resource: Bitmap) {
|
||||||
|
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(c.resources, resource)
|
||||||
|
circularBitmapDrawable.isCircular = true
|
||||||
|
fHolder.sourceImage!!.setImageDrawable(circularBitmapDrawable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Glide.with(c).load(itm.getThumbnail(c)).asBitmap().centerCrop().into(holder.sourceImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bars[position]) {
|
||||||
|
holder.actionBar!!.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.actionBar!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.saveBtn!!.isLiked = itm.starred
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun doUnmark(i: Item, position: Int) {
|
||||||
|
val s = Snackbar
|
||||||
|
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.undo_string) {
|
||||||
|
items.add(position, i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
|
||||||
|
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
items.remove(i)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
doUnmark(i, position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = s.view
|
||||||
|
val tv = view.findViewById(android.support.design.R.id.snackbar_text) as TextView
|
||||||
|
tv.setTextColor(Color.WHITE)
|
||||||
|
s.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItemAtIndex(position: Int) {
|
||||||
|
|
||||||
|
val i = items[position]
|
||||||
|
|
||||||
|
items.remove(i)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
|
||||||
|
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
|
doUnmark(i, position)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show()
|
||||||
|
items.add(i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
|
var saveBtn: LikeButton? = null
|
||||||
|
var browserBtn: ImageButton? = null
|
||||||
|
var shareBtn: ImageButton? = null
|
||||||
|
var actionBar: RelativeLayout? = null
|
||||||
|
var sourceImage: ImageView? = null
|
||||||
|
var title: TextView? = null
|
||||||
|
var sourceTitleAndDate: TextView? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
handleClickListeners()
|
||||||
|
handleCustomTabActions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleClickListeners() {
|
||||||
|
actionBar = mView.findViewById(R.id.actionBar) as RelativeLayout
|
||||||
|
sourceImage = mView.findViewById(R.id.itemImage) as ImageView
|
||||||
|
title = mView.findViewById(R.id.title) as TextView
|
||||||
|
sourceTitleAndDate = mView.findViewById(R.id.sourceTitleAndDate) as TextView
|
||||||
|
saveBtn = mView.findViewById(R.id.favButton) as LikeButton
|
||||||
|
shareBtn = mView.findViewById(R.id.shareBtn) as ImageButton
|
||||||
|
browserBtn = mView.findViewById(R.id.browserBtn) as ImageButton
|
||||||
|
|
||||||
|
|
||||||
|
saveBtn!!.setOnLikeListener(object : OnLikeListener {
|
||||||
|
override fun liked(likeButton: LikeButton) {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
saveBtn!!.isLiked = false
|
||||||
|
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unLiked(likeButton: LikeButton) {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
saveBtn!!.isLiked = true
|
||||||
|
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
shareBtn!!.setOnClickListener {
|
||||||
|
val i = items[adapterPosition]
|
||||||
|
val sendIntent = Intent()
|
||||||
|
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, i.getLinkDecoded())
|
||||||
|
sendIntent.type = "text/plain"
|
||||||
|
c.startActivity(Intent.createChooser(sendIntent, c.getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||||
|
}
|
||||||
|
|
||||||
|
browserBtn!!.setOnClickListener {
|
||||||
|
val i = items[adapterPosition]
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
intent.data = Uri.parse(i.getLinkDecoded())
|
||||||
|
c.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun handleCustomTabActions() {
|
||||||
|
val customTabsIntent = buildCustomTabsIntent(c)
|
||||||
|
helper.bindCustomTabsService(app)
|
||||||
|
|
||||||
|
|
||||||
|
if (!clickBehavior) {
|
||||||
|
mView.setOnClickListener {
|
||||||
|
openItemUrl(items[adapterPosition],
|
||||||
|
customTabsIntent,
|
||||||
|
internalBrowser,
|
||||||
|
articleViewer,
|
||||||
|
app,
|
||||||
|
c)
|
||||||
|
}
|
||||||
|
mView.setOnLongClickListener {
|
||||||
|
actionBarShowHide()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mView.setOnClickListener { actionBarShowHide() }
|
||||||
|
mView.setOnLongClickListener {
|
||||||
|
openItemUrl(items[adapterPosition],
|
||||||
|
customTabsIntent,
|
||||||
|
internalBrowser,
|
||||||
|
articleViewer,
|
||||||
|
app,
|
||||||
|
c)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun actionBarShowHide() {
|
||||||
|
bars[adapterPosition] = true
|
||||||
|
if (actionBar!!.visibility == View.GONE)
|
||||||
|
actionBar!!.visibility = View.VISIBLE
|
||||||
|
else
|
||||||
|
actionBar!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,133 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.adapters;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.support.constraint.ConstraintLayout;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.*;
|
|
||||||
import apps.amine.bou.readerforselfoss.R;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources;
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse;
|
|
||||||
import com.amulyakhare.textdrawable.TextDrawable;
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator;
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.request.target.BitmapImageViewTarget;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Callback;
|
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SourcesListAdapter extends RecyclerView.Adapter<SourcesListAdapter.ViewHolder> {
|
|
||||||
private final List<Sources> items;
|
|
||||||
private final Activity app;
|
|
||||||
private final SelfossApi api;
|
|
||||||
private final Context c;
|
|
||||||
private final ColorGenerator generator;
|
|
||||||
|
|
||||||
public SourcesListAdapter(Activity activity, List<Sources> items, SelfossApi api) {
|
|
||||||
this.app = activity;
|
|
||||||
this.items = items;
|
|
||||||
this.api = api;
|
|
||||||
this.c = app.getBaseContext();
|
|
||||||
|
|
||||||
generator = ColorGenerator.MATERIAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
ConstraintLayout v = (ConstraintLayout) LayoutInflater.from(c).inflate(R.layout.source_list_item, parent, false);
|
|
||||||
return new ViewHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
|
||||||
Sources itm = items.get(position);
|
|
||||||
|
|
||||||
final ViewHolder fHolder = holder;
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
|
||||||
int color = generator.getColor(itm.getTitle());
|
|
||||||
StringBuilder textDrawable = new StringBuilder();
|
|
||||||
for(String s : itm.getTitle().split(" "))
|
|
||||||
{
|
|
||||||
textDrawable.append(s.charAt(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TextDrawable.IBuilder builder = TextDrawable.builder().round();
|
|
||||||
|
|
||||||
TextDrawable drawable = builder.build(textDrawable.toString(), color);
|
|
||||||
holder.sourceImage.setImageDrawable(drawable);
|
|
||||||
} else {
|
|
||||||
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(new BitmapImageViewTarget(holder.sourceImage) {
|
|
||||||
@Override
|
|
||||||
protected void setResource(Bitmap resource) {
|
|
||||||
RoundedBitmapDrawable circularBitmapDrawable =
|
|
||||||
RoundedBitmapDrawableFactory.create(c.getResources(), resource);
|
|
||||||
circularBitmapDrawable.setCircular(true);
|
|
||||||
fHolder.sourceImage.setImageDrawable(circularBitmapDrawable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.sourceTitle.setText(itm.getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
ConstraintLayout mView;
|
|
||||||
ImageView sourceImage;
|
|
||||||
TextView sourceTitle;
|
|
||||||
Button deleteBtn;
|
|
||||||
|
|
||||||
public ViewHolder(ConstraintLayout itemView) {
|
|
||||||
super(itemView);
|
|
||||||
mView = itemView;
|
|
||||||
|
|
||||||
handleClickListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleClickListeners() {
|
|
||||||
sourceImage = (ImageView) mView.findViewById(R.id.itemImage);
|
|
||||||
sourceTitle = (TextView) mView.findViewById(R.id.sourceTitle);
|
|
||||||
deleteBtn = (Button) mView.findViewById(R.id.deleteBtn);
|
|
||||||
|
|
||||||
deleteBtn.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Sources i = items.get(getAdapterPosition());
|
|
||||||
api.deleteSource(i.getId()).enqueue(new Callback<SuccessResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {
|
|
||||||
if (response.body() != null && response.body().isSuccess()) {
|
|
||||||
items.remove(getAdapterPosition());
|
|
||||||
notifyItemRemoved(getAdapterPosition());
|
|
||||||
notifyItemRangeChanged(getAdapterPosition(), getItemCount());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Toast.makeText(app, "Petit soucis lors de la suppression de la source.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<SuccessResponse> call, Throwable t) {
|
|
||||||
Toast.makeText(app, "Petit soucis lors de la suppression de la source.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,105 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.adapters
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.support.constraint.ConstraintLayout
|
||||||
|
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import apps.amine.bou.readerforselfoss.R
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
|
||||||
|
class SourcesListAdapter(private val app: Activity, private val items: ArrayList<Sources>, private val api: SelfossApi) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
|
||||||
|
private val c: Context = app.baseContext
|
||||||
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val v = LayoutInflater.from(c).inflate(R.layout.source_list_item, parent, false) as ConstraintLayout
|
||||||
|
return ViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val itm = items[position]
|
||||||
|
|
||||||
|
val fHolder = holder
|
||||||
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
|
val color = generator.getColor(itm.title)
|
||||||
|
val textDrawable = StringBuilder()
|
||||||
|
for (s in itm.title.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||||
|
textDrawable.append(s[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = TextDrawable.builder().round()
|
||||||
|
|
||||||
|
val drawable = builder.build(textDrawable.toString(), color)
|
||||||
|
holder.sourceImage!!.setImageDrawable(drawable)
|
||||||
|
} else {
|
||||||
|
Glide.with(c).load(itm.getIcon(c)).asBitmap().centerCrop().into(object : BitmapImageViewTarget(holder.sourceImage) {
|
||||||
|
override fun setResource(resource: Bitmap) {
|
||||||
|
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(c.resources, resource)
|
||||||
|
circularBitmapDrawable.isCircular = true
|
||||||
|
fHolder.sourceImage!!.setImageDrawable(circularBitmapDrawable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.sourceTitle!!.text = itm.title
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(internal val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
|
var sourceImage: ImageView? = null
|
||||||
|
var sourceTitle: TextView? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
handleClickListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleClickListeners() {
|
||||||
|
sourceImage = mView.findViewById(R.id.itemImage) as ImageView
|
||||||
|
sourceTitle = mView.findViewById(R.id.sourceTitle) as TextView
|
||||||
|
|
||||||
|
val deleteBtn = mView.findViewById(R.id.deleteBtn) as Button
|
||||||
|
|
||||||
|
deleteBtn.setOnClickListener {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||||
|
if (response.body() != null && response.body().isSuccess) {
|
||||||
|
items.removeAt(adapterPosition)
|
||||||
|
notifyItemRemoved(adapterPosition)
|
||||||
|
notifyItemRangeChanged(adapterPosition, itemCount)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(app, "Petit soucis lors de la suppression de la source.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
Toast.makeText(app, "Petit soucis lors de la suppression de la source.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.mercury;
|
|
||||||
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Retrofit;
|
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
|
||||||
|
|
||||||
|
|
||||||
public class MercuryApi {
|
|
||||||
private final MercuryService service;
|
|
||||||
private final String key;
|
|
||||||
|
|
||||||
public MercuryApi(String key) {
|
|
||||||
|
|
||||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
|
||||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
|
||||||
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
|
|
||||||
|
|
||||||
Gson gson = new GsonBuilder()
|
|
||||||
.setLenient()
|
|
||||||
.create();
|
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://mercury.postlight.com").client(client)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson)).build();
|
|
||||||
service = retrofit.create(MercuryService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<ParsedContent> parseUrl(String url) {
|
|
||||||
return service.parseUrl(url, this.key);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.api.mercury
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
|
||||||
|
class MercuryApi(private val key: String) {
|
||||||
|
private val service: MercuryService
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
val interceptor = HttpLoggingInterceptor()
|
||||||
|
interceptor.level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
|
val gson = GsonBuilder()
|
||||||
|
.setLenient()
|
||||||
|
.create()
|
||||||
|
val retrofit = Retrofit.Builder().baseUrl("https://mercury.postlight.com").client(client)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create(gson)).build()
|
||||||
|
service = retrofit.create(MercuryService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseUrl(url: String): Call<ParsedContent> {
|
||||||
|
return service.parseUrl(url, this.key)
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.mercury;
|
|
||||||
|
|
||||||
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.http.GET;
|
|
||||||
import retrofit2.http.Header;
|
|
||||||
import retrofit2.http.Query;
|
|
||||||
|
|
||||||
|
|
||||||
public interface MercuryService {
|
|
||||||
@GET("parser")
|
|
||||||
Call<ParsedContent> parseUrl(@Query("url") String url, @Header("x-api-key") String key);
|
|
||||||
}
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.api.mercury
|
||||||
|
|
||||||
|
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
|
||||||
|
interface MercuryService {
|
||||||
|
@GET("parser")
|
||||||
|
fun parseUrl(@Query("url") url: String, @Header("x-api-key") key: String): Call<ParsedContent>
|
||||||
|
}
|
@ -1,138 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.selfoss;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config;
|
|
||||||
import com.burgstaller.okhttp.AuthenticationCacheInterceptor;
|
|
||||||
import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
|
|
||||||
import com.burgstaller.okhttp.DispatchingAuthenticator;
|
|
||||||
import com.burgstaller.okhttp.basic.BasicAuthenticator;
|
|
||||||
import com.burgstaller.okhttp.digest.CachingAuthenticator;
|
|
||||||
import com.burgstaller.okhttp.digest.Credentials;
|
|
||||||
import com.burgstaller.okhttp.digest.DigestAuthenticator;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Retrofit;
|
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
|
||||||
|
|
||||||
|
|
||||||
public class SelfossApi {
|
|
||||||
|
|
||||||
private final SelfossService service;
|
|
||||||
private final Config config;
|
|
||||||
private final String userName;
|
|
||||||
private final String password;
|
|
||||||
|
|
||||||
public SelfossApi(Context c) {
|
|
||||||
this.config = new Config(c);
|
|
||||||
|
|
||||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
|
||||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
|
||||||
|
|
||||||
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
|
|
||||||
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
String httpUserName = config.getHttpUserLogin();
|
|
||||||
String httpPassword = config.getHttpUserPassword();
|
|
||||||
|
|
||||||
Credentials credentials = new Credentials(httpUserName, httpPassword);
|
|
||||||
final BasicAuthenticator basicAuthenticator = new BasicAuthenticator(credentials);
|
|
||||||
final DigestAuthenticator digestAuthenticator = new DigestAuthenticator(credentials);
|
|
||||||
|
|
||||||
// note that all auth schemes should be registered as lowercase!
|
|
||||||
DispatchingAuthenticator authenticator = new DispatchingAuthenticator.Builder()
|
|
||||||
.with("digest", digestAuthenticator)
|
|
||||||
.with("basic", basicAuthenticator)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
OkHttpClient client = httpBuilder
|
|
||||||
.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
|
|
||||||
.addInterceptor(new AuthenticationCacheInterceptor(authCache))
|
|
||||||
.addInterceptor(interceptor)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
|
|
||||||
GsonBuilder builder = new GsonBuilder();
|
|
||||||
builder.registerTypeAdapter(boolean.class, new BooleanTypeAdapter());
|
|
||||||
|
|
||||||
Gson gson = builder
|
|
||||||
.setLenient()
|
|
||||||
.create();
|
|
||||||
|
|
||||||
userName = config.getUserLogin();
|
|
||||||
password = config.getUserPassword();
|
|
||||||
Retrofit retrofit = new Retrofit.Builder().baseUrl(config.getBaseUrl()).client(client)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson)).build();
|
|
||||||
service = retrofit.create(SelfossService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<SuccessResponse> login() {
|
|
||||||
return service.loginToSelfoss(config.getUserLogin(), config.getUserPassword());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<List<Item>> getReadItems() {
|
|
||||||
return getItems("read");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<List<Item>> getUnreadItems() {
|
|
||||||
return getItems("unread");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<List<Item>> getStarredItems() {
|
|
||||||
return getItems("starred");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Call<List<Item>> getItems(String type) {
|
|
||||||
return service.getItems(type, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<SuccessResponse> markItem(String itemId) {
|
|
||||||
return service.markAsRead(itemId, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<SuccessResponse> unmarkItem(String itemId) {
|
|
||||||
return service.unmarkAsRead(itemId, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<SuccessResponse> readAll(List<String> ids) {
|
|
||||||
return service.markAllAsRead(ids, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<SuccessResponse> starrItem(String itemId) {
|
|
||||||
return service.starr(itemId, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Call<SuccessResponse> unstarrItem(String itemId) {
|
|
||||||
return service.unstarr(itemId, userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<Stats> getStats() {
|
|
||||||
return service.stats(userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<List<Tag>> getTags() {
|
|
||||||
return service.tags(userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<String> update() {
|
|
||||||
return service.update(userName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Call<List<Sources>> getSources() { return service.sources(userName, password); }
|
|
||||||
|
|
||||||
public Call<SuccessResponse> deleteSource(String id) { return service.deleteSource(id, userName, password);}
|
|
||||||
|
|
||||||
public Call<Map<String, Spout>> spouts() { return service.spouts(userName, password); }
|
|
||||||
|
|
||||||
public Call<SuccessResponse> createSource(String title, String url, String spout, String tags, String filter) {return service.createSource(title, url, spout, tags, filter, userName, password);}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,134 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.api.selfoss
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
|
import com.burgstaller.okhttp.AuthenticationCacheInterceptor
|
||||||
|
import com.burgstaller.okhttp.CachingAuthenticatorDecorator
|
||||||
|
import com.burgstaller.okhttp.DispatchingAuthenticator
|
||||||
|
import com.burgstaller.okhttp.basic.BasicAuthenticator
|
||||||
|
import com.burgstaller.okhttp.digest.CachingAuthenticator
|
||||||
|
import com.burgstaller.okhttp.digest.Credentials
|
||||||
|
import com.burgstaller.okhttp.digest.DigestAuthenticator
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
|
||||||
|
class SelfossApi(c: Context) {
|
||||||
|
|
||||||
|
private val service: SelfossService
|
||||||
|
private val config: Config = Config(c)
|
||||||
|
private val userName: String
|
||||||
|
private val password: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
val interceptor = HttpLoggingInterceptor()
|
||||||
|
interceptor.level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
|
||||||
|
val httpBuilder = OkHttpClient.Builder()
|
||||||
|
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
||||||
|
|
||||||
|
val httpUserName = config.httpUserLogin
|
||||||
|
val httpPassword = config.httpUserPassword
|
||||||
|
|
||||||
|
val credentials = Credentials(httpUserName, httpPassword)
|
||||||
|
val basicAuthenticator = BasicAuthenticator(credentials)
|
||||||
|
val digestAuthenticator = DigestAuthenticator(credentials)
|
||||||
|
|
||||||
|
// note that all auth schemes should be registered as lowercase!
|
||||||
|
val authenticator = DispatchingAuthenticator.Builder()
|
||||||
|
.with("digest", digestAuthenticator)
|
||||||
|
.with("basic", basicAuthenticator)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val client = httpBuilder
|
||||||
|
.authenticator(CachingAuthenticatorDecorator(authenticator, authCache))
|
||||||
|
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
||||||
|
.addInterceptor(interceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
|
||||||
|
val builder = GsonBuilder()
|
||||||
|
builder.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
|
||||||
|
|
||||||
|
val gson = builder
|
||||||
|
.setLenient()
|
||||||
|
.create()
|
||||||
|
|
||||||
|
userName = config.userLogin
|
||||||
|
password = config.userPassword
|
||||||
|
val retrofit = Retrofit.Builder().baseUrl(config.baseUrl).client(client)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create(gson)).build()
|
||||||
|
service = retrofit.create(SelfossService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun login(): Call<SuccessResponse> {
|
||||||
|
return service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
val readItems: Call<List<Item>>
|
||||||
|
get() = getItems("read")
|
||||||
|
|
||||||
|
val unreadItems: Call<List<Item>>
|
||||||
|
get() = getItems("unread")
|
||||||
|
|
||||||
|
val starredItems: Call<List<Item>>
|
||||||
|
get() = getItems("starred")
|
||||||
|
|
||||||
|
private fun getItems(type: String): Call<List<Item>> {
|
||||||
|
return service.getItems(type, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markItem(itemId: String): Call<SuccessResponse> {
|
||||||
|
return service.markAsRead(itemId, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unmarkItem(itemId: String): Call<SuccessResponse> {
|
||||||
|
return service.unmarkAsRead(itemId, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readAll(ids: List<String>): Call<SuccessResponse> {
|
||||||
|
return service.markAllAsRead(ids, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun starrItem(itemId: String): Call<SuccessResponse> {
|
||||||
|
return service.starr(itemId, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun unstarrItem(itemId: String): Call<SuccessResponse> {
|
||||||
|
return service.unstarr(itemId, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
val stats: Call<Stats>
|
||||||
|
get() = service.stats(userName, password)
|
||||||
|
|
||||||
|
val tags: Call<List<Tag>>
|
||||||
|
get() = service.tags(userName, password)
|
||||||
|
|
||||||
|
fun update(): Call<String> {
|
||||||
|
return service.update(userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sources: Call<List<Sources>>
|
||||||
|
get() = service.sources(userName, password)
|
||||||
|
|
||||||
|
fun deleteSource(id: String): Call<SuccessResponse> {
|
||||||
|
return service.deleteSource(id, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun spouts(): Call<Map<String, Spout>> {
|
||||||
|
return service.spouts(userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSource(title: String, url: String, spout: String, tags: String, filter: String): Call<SuccessResponse> {
|
||||||
|
return service.createSource(title, url, spout, tags, filter, userName, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -68,8 +68,8 @@ data class Item(val id: String,
|
|||||||
id = source.readString(),
|
id = source.readString(),
|
||||||
datetime = source.readString(),
|
datetime = source.readString(),
|
||||||
title = source.readString(),
|
title = source.readString(),
|
||||||
unread = source.readByte() != 0,
|
unread = 0.toByte() != source.readByte(),
|
||||||
starred = source.readByte() != 0,
|
starred = 0.toByte() != source.readByte(),
|
||||||
thumbnail = source.readString(),
|
thumbnail = source.readString(),
|
||||||
icon = source.readString(),
|
icon = source.readString(),
|
||||||
link = source.readString(),
|
link = source.readString(),
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.selfoss;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.http.DELETE;
|
|
||||||
import retrofit2.http.Field;
|
|
||||||
import retrofit2.http.FormUrlEncoded;
|
|
||||||
import retrofit2.http.GET;
|
|
||||||
import retrofit2.http.POST;
|
|
||||||
import retrofit2.http.Path;
|
|
||||||
import retrofit2.http.Query;
|
|
||||||
|
|
||||||
|
|
||||||
interface SelfossService {
|
|
||||||
@GET("login")
|
|
||||||
Call<SuccessResponse> loginToSelfoss(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@GET("items")
|
|
||||||
Call<List<Item>> getItems(@Query("type") String type, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@POST("mark/{id}")
|
|
||||||
Call<SuccessResponse> markAsRead(@Path("id") String id, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@POST("unmark/{id}")
|
|
||||||
Call<SuccessResponse> unmarkAsRead(@Path("id") String id, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("mark")
|
|
||||||
Call<SuccessResponse> markAllAsRead(@Field("ids[]") List<String> ids, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@POST("starr/{id}")
|
|
||||||
Call<SuccessResponse> starr(@Path("id") String id, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@POST("unstarr/{id}")
|
|
||||||
Call<SuccessResponse> unstarr(@Path("id") String id, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@GET("stats")
|
|
||||||
Call<Stats> stats(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@GET("tags")
|
|
||||||
Call<List<Tag>> tags(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@GET("update")
|
|
||||||
Call<String> update(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@GET("sources/spouts")
|
|
||||||
Call<Map<String, Spout>> spouts(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@GET("sources/list")
|
|
||||||
Call<List<Sources>> sources(@Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
|
|
||||||
@DELETE("source/{id}")
|
|
||||||
Call<SuccessResponse> deleteSource(@Path("id") String id, @Query("username") String username, @Query("password") String password);
|
|
||||||
|
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("source")
|
|
||||||
Call<SuccessResponse> createSource(@Field("title") String title, @Field("url") String url, @Field("spout") String spout, @Field("tags") String tags, @Field("filter") String filter, @Query("username") String username, @Query("password") String password);
|
|
||||||
}
|
|
@ -0,0 +1,65 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.api.selfoss
|
||||||
|
|
||||||
|
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.DELETE
|
||||||
|
import retrofit2.http.Field
|
||||||
|
import retrofit2.http.FormUrlEncoded
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
|
||||||
|
internal interface SelfossService {
|
||||||
|
@GET("login")
|
||||||
|
fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
@GET("items")
|
||||||
|
fun getItems(@Query("type") type: String, @Query("username") username: String, @Query("password") password: String): Call<List<Item>>
|
||||||
|
|
||||||
|
@POST("mark/{id}")
|
||||||
|
fun markAsRead(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
|
||||||
|
@POST("unmark/{id}")
|
||||||
|
fun unmarkAsRead(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("mark")
|
||||||
|
fun markAllAsRead(@Field("ids[]") ids: List<String>, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
|
||||||
|
@POST("starr/{id}")
|
||||||
|
fun starr(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
|
||||||
|
@POST("unstarr/{id}")
|
||||||
|
fun unstarr(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
|
||||||
|
@GET("stats")
|
||||||
|
fun stats(@Query("username") username: String, @Query("password") password: String): Call<Stats>
|
||||||
|
|
||||||
|
|
||||||
|
@GET("tags")
|
||||||
|
fun tags(@Query("username") username: String, @Query("password") password: String): Call<List<Tag>>
|
||||||
|
|
||||||
|
|
||||||
|
@GET("update")
|
||||||
|
fun update(@Query("username") username: String, @Query("password") password: String): Call<String>
|
||||||
|
|
||||||
|
@GET("sources/spouts")
|
||||||
|
fun spouts(@Query("username") username: String, @Query("password") password: String): Call<Map<String, Spout>>
|
||||||
|
|
||||||
|
@GET("sources/list")
|
||||||
|
fun sources(@Query("username") username: String, @Query("password") password: String): Call<List<Sources>>
|
||||||
|
|
||||||
|
|
||||||
|
@DELETE("source/{id}")
|
||||||
|
fun deleteSource(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("source")
|
||||||
|
fun createSource(@Field("title") title: String, @Field("url") url: String, @Field("spout") spout: String, @Field("tags") tags: String, @Field("filter") filter: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils
|
package apps.amine.bou.readerforselfoss.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="withLoginSwitch">"Login required ?"</string>
|
<string name="withLoginSwitch">"Login required ?"</string>
|
||||||
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string>
|
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string>
|
||||||
<string name="login_url_problem">"Oups. You may need to add a \"/\" at the end of the url."</string>
|
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
||||||
<string name="prompt_login">"Username"</string>
|
<string name="prompt_login">"Username"</string>
|
||||||
<string name="prompt_http_login">"HTTP Username"</string>
|
<string name="prompt_http_login">"HTTP Username"</string>
|
||||||
<string name="label_share">"Share"</string>
|
<string name="label_share">"Share"</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user