package crysalis.example.xyzreader.ui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.os.Bundle;
import android.text.Html;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.example.xyzreader.R;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import crysalis.example.xyzreader.data.ArticleLoader;
import crysalis.example.xyzreader.data.ItemsContract;
import crysalis.example.xyzreader.data.UpdaterService;
/**
* An activity representing a list of Articles. This activity has different presentations for
* handset and tablet-size devices. On handsets, the activity presents a list of items, which when
* touched, lead to a {@link ArticleDetailActivity} representing item details. On tablets, the
* activity presents a grid of items as cards.
*/
public class ArticleListActivity extends AppCompatActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = ArticleListActivity.class.toString();
private Toolbar mToolbar;
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mRecyclerView;
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss");
// Use default locale format
private SimpleDateFormat outputFormat = new SimpleDateFormat();
// Most time functions can only handle 1902 - 2037
private GregorianCalendar START_OF_EPOCH = new GregorianCalendar(2,1,1);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_article_list);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
final View toolbarContainerView = findViewById(R.id.toolbar_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
getSupportLoaderManager().initLoader(0, null, this);
//getLoaderManager().initLoader(0, null, this);
if (savedInstanceState == null) {
refresh();
}
}
private void refresh() {
startService(new Intent(this, UpdaterService.class));
}
@Override
protected void onStart() {
super.onStart();
registerReceiver(mRefreshingReceiver,
new IntentFilter(UpdaterService.BROADCAST_ACTION_STATE_CHANGE));
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(mRefreshingReceiver);
}
private boolean mIsRefreshing = false;
private BroadcastReceiver mRefreshingReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (UpdaterService.BROADCAST_ACTION_STATE_CHANGE.equals(intent.getAction())) {
mIsRefreshing = intent.getBooleanExtra(UpdaterService.EXTRA_REFRESHING, false);
updateRefreshingUI();
}
}
};
private void updateRefreshingUI() {
mSwipeRefreshLayout.setRefreshing(mIsRefreshing);
}
@NonNull
@Override
public Loader<Cursor> onCreateLoader(int id, @Nullable Bundle args) {
// return new ArticleLoader(getApplicationContext());
return ArticleLoader.newAllArticlesInstance(this);
}
@Override
public void onLoadFinished(@NonNull androidx.loader.content.Loader<Cursor> loader, Cursor data) {
Adapter adapter = new Adapter(data);
adapter.setHasStableIds(true);
mRecyclerView.setAdapter(adapter);
int columnCount = getResources().getInteger(R.integer.list_column_count);
StaggeredGridLayoutManager sglm =
new StaggeredGridLayoutManager(columnCount, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(sglm);
}
@Override
public void onLoaderReset(@NonNull androidx.loader.content.Loader<Cursor> loader) {
mRecyclerView.setAdapter(null);
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
private Cursor mCursor;
public Adapter(Cursor cursor) {
mCursor = cursor;
}
@Override
public long getItemId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(ArticleLoader.Query._ID);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = getLayoutInflater().inflate(R.layout.list_item_article, parent, false);
final ViewHolder vh = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW,
ItemsContract.Items.buildItemUri(getItemId(vh.getAdapterPosition()))));
}
});
return vh;
}
private Date parsePublishedDate() {
try {
String date = mCursor.getString(ArticleLoader.Query.PUBLISHED_DATE);
return dateFormat.parse(date);
} catch (ParseException ex) {
Log.e(TAG, ex.getMessage());
Log.i(TAG, "passing today's date");
return new Date();
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
mCursor.moveToPosition(position);
holder.titleView.setText(mCursor.getString(ArticleLoader.Query.TITLE));
Date publishedDate = parsePublishedDate();
if (!publishedDate.before(START_OF_EPOCH.getTime())) {
holder.subtitleView.setText(Html.fromHtml(
DateUtils.getRelativeTimeSpanString(
publishedDate.getTime(),
System.currentTimeMillis(), DateUtils.HOUR_IN_MILLIS,
DateUtils.FORMAT_ABBREV_ALL).toString()
+ "<br/>" + " by "
+ mCursor.getString(ArticleLoader.Query.AUTHOR)));
} else {
holder.subtitleView.setText(Html.fromHtml(
outputFormat.format(publishedDate)
+ "<br/>" + " by "
+ mCursor.getString(ArticleLoader.Query.AUTHOR)));
}
holder.thumbnailView.setImageUrl(
mCursor.getString(ArticleLoader.Query.THUMB_URL),
ImageLoaderHelper.getInstance(ArticleListActivity.this).getImageLoader());
holder.thumbnailView.setAspectRatio(mCursor.getFloat(ArticleLoader.Query.ASPECT_RATIO));
}
@Override
public int getItemCount() {
return mCursor.getCount();
}
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public DynamicHeightNetworkImageView thumbnailView;
public TextView titleView;
public TextView subtitleView;
public ViewHolder(View view) {
super(view);
thumbnailView = (DynamicHeightNetworkImageView) view.findViewById(R.id.thumbnail);
titleView = (TextView) view.findViewById(R.id.article_title);
subtitleView = (TextView) view.findViewById(R.id.article_subtitle);
}
}
}
The logcat output
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.xyzreader, PID: 13019
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.xyzreader/crysalis.example.xyzreader.ui.ArticleListActivity}: android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class androidx.core.widget.SwipeRefreshLayout
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class androidx.core.widget.SwipeRefreshLayout
Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class androidx.core.widget.SwipeRefreshLayout
Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.core.widget.SwipeRefreshLayout" on path: DexPathList[[zip file "/data/app/com.example.xyzreader-jVcseTSIIyMtEDk_Wlm8eg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.xyzreader-jVcseTSIIyMtEDk_Wlm8eg==/lib/x86, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.view.LayoutInflater.createView(LayoutInflater.java:606)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at crysalis.example.xyzreader.ui.ArticleListActivity.onCreate(ArticleListActivity.java:60)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)