Browse Source

Downloads init, profile and download fixes.

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
c8e68f4351
  1. 2
      app/src/main/AndroidManifest.xml
  2. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  3. 274
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
  4. 181
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
  5. 36
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
  6. 11
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  7. 19
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  8. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  9. 121
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  10. 58
      app/src/main/java/gr/thmmy/mthmmy/model/Download.java
  11. 34
      app/src/main/java/gr/thmmy/mthmmy/model/LinkTarget.java
  12. 1
      app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java
  13. 11
      app/src/main/res/drawable/ic_file_upload.xml
  14. 62
      app/src/main/res/layout/activity_downloads.xml
  15. 65
      app/src/main/res/layout/activity_downloads_row.xml
  16. 1
      app/src/main/res/values/strings.xml

2
app/src/main/AndroidManifest.xml

@ -63,6 +63,8 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity"/>
</activity>
<activity android:name=".activities.downloads.DownloadsActivity">
</activity>
</application>
</manifest>

4
app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java

@ -186,6 +186,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@SuppressWarnings("unused")
private static final String TAG = "BoardTask"; //Separate tag for AsyncTask
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(false);
@ -207,6 +208,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
return false;
}
@Override
protected void onPostExecute(Boolean result) {
if (!result) { //Parse failed!
Report.d(TAG, "Parse failed!");
@ -214,6 +216,8 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
finish();
}
if (boardTitle == null || Objects.equals(boardTitle, "")) toolbar.setTitle(boardTitle);
//Parse was successful
++pagesLoaded;
if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(true);

274
app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java

@ -0,0 +1,274 @@
package gr.thmmy.mthmmy.activities.downloads;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import javax.net.ssl.SSLHandshakeException;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Download;
import gr.thmmy.mthmmy.model.LinkTarget;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import mthmmy.utils.Report;
import okhttp3.Request;
import okhttp3.Response;
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
/**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused")
private static final String TAG = "DownloadsActivity";
/**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle.
*/
public static final String BUNDLE_DOWNLOADS_URL = "DOWNLOADS_URL";
/**
* The key to use when putting download's title String to {@link DownloadsActivity}'s Bundle.
*/
public static final String BUNDLE_DOWNLOADS_TITLE = "DOWNLOADS_TITLE";
private static final String downloadsIndexUrl = "https://www.thmmy.gr/smf/index.php?action=tpmod;dl;";
private String downloadsUrl;
String downloadsTitle;
private ArrayList<Download> parsedDownloads = new ArrayList<>();
private MaterialProgressBar progressBar;
private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter;
private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1;
private int pagesLoaded = 0;
private boolean isLoadingMore;
private static final int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_downloads);
Bundle extras = getIntent().getExtras();
downloadsTitle = extras.getString(BUNDLE_DOWNLOADS_TITLE);
if (downloadsTitle == null || Objects.equals(downloadsTitle, ""))
downloadsTitle = "Downloads";
downloadsUrl = extras.getString(BUNDLE_DOWNLOADS_URL);
if (downloadsUrl != null && !Objects.equals(downloadsUrl, "")) {
LinkTarget.Target target = LinkTarget.resolveLinkTarget(Uri.parse(downloadsUrl));
if (!target.is(LinkTarget.Target.DOWNLOADS)) {
Report.e(TAG, "Bundle came with a non board url!\nUrl:\n" + downloadsUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish();
}
} else downloadsUrl = downloadsIndexUrl;
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(downloadsTitle);
/*setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}*/
createDrawer();
progressBar = (MaterialProgressBar) findViewById(R.id.progressBar);
recyclerView = (RecyclerView) findViewById(R.id.downloads_recycler_view);
recyclerView.setHasFixedSize(true);
final LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
downloadsAdapter = new DownloadsAdapter(getApplicationContext(), parsedDownloads);
recyclerView.setAdapter(downloadsAdapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = layoutManager.getItemCount();
lastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (!isLoadingMore && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
isLoadingMore = true;
onLoadMore();
}
}
});
uploadFAB = (FloatingActionButton) findViewById(R.id.download_fab);
uploadFAB.setEnabled(false);
parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.execute(downloadsUrl);
}
@Override
public void onLoadMore() {
if (pagesLoaded < numberOfPages) {
parsedDownloads.add(null);
downloadsAdapter.notifyItemInserted(parsedDownloads.size());
//Load data
parseDownloadPageTask = new ParseDownloadPageTask();
if (downloadsUrl.contains("tpstart"))
parseDownloadPageTask.execute(downloadsUrl.substring(0
, downloadsUrl.lastIndexOf(";tpstart=")) + ";tpstart=" + pagesLoaded * 10);
else parseDownloadPageTask.execute(downloadsUrl + ";tpstart=" + pagesLoaded * 10);
}
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen()) {
drawer.closeDrawer();
return;
}
super.onBackPressed();
}
@Override
protected void onResume() {
drawer.setSelection(-1);
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
recyclerView.setAdapter(null);
if (parseDownloadPageTask != null && parseDownloadPageTask.getStatus() != AsyncTask.Status.RUNNING)
parseDownloadPageTask.cancel(true);
}
/**
* An {@link AsyncTask} that handles asynchronous fetching of a downloads page and parsing it's
* data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link RecyclerView#swapAdapter}
* to build graphics.
* <p>
* <p>Calling TopicTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!</p>
*/
class ParseDownloadPageTask extends AsyncTask<String, Void, Void> {
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "ParseDownloadPageTask"; //Separate tag for AsyncTask
private String thisPageUrl;
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
}
@Override
protected Void doInBackground(String... downloadsUrl) {
thisPageUrl = downloadsUrl[0];
Request request = new Request.Builder()
.url(downloadsUrl[0])
.build();
try {
Response response = BaseActivity.getClient().newCall(request).execute();
parseDownloads(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) {
Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
Report.e("TAG", "ERROR", e);
}
return null;
}
@Override
protected void onPostExecute(Void voids) {
if (downloadsTitle == null || Objects.equals(downloadsTitle, ""))
toolbar.setTitle(downloadsTitle);
++pagesLoaded;
if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE);
downloadsAdapter.notifyDataSetChanged();
isLoadingMore = false;
}
private void parseDownloads(Document downloadPage) {
if (downloadsTitle == null || Objects.equals(downloadsTitle, ""))
downloadsTitle = downloadPage.select("div.nav>b>a.nav").last().text();
//Removes loading item
if (isLoadingMore) {
if (parsedDownloads.size() > 0) parsedDownloads.remove(parsedDownloads.size() - 1);
}
Download.DownloadItemType type;
if (LinkTarget.resolveLinkTarget(Uri.parse(thisPageUrl)).is(LinkTarget.
Target.DOWNLOADS_CATEGORY)) type = Download.DownloadItemType.DOWNLOADS_CATEGORY;
else type = Download.DownloadItemType.DOWNLOADS_FILE;
Elements pages = downloadPage.select("a.navPages");
if (pages != null) {
for (Element page : pages) {
int pageNumber = Integer.parseInt(page.text());
if (pageNumber > numberOfPages) numberOfPages = pageNumber;
}
} else numberOfPages = 1;
Elements rows = downloadPage.select("table.tborder>tbody>tr");
if (type == Download.DownloadItemType.DOWNLOADS_CATEGORY) {
Elements navigationLinks = downloadPage.select("div.nav>b");
for (Element row : rows) {
if (row.select("td").size() == 1) continue;
String url = row.select("b>a").first().attr("href"),
title = row.select("b>a").first().text(),
subtitle = row.select("div.smalltext:not(:has(a))").text();
if (!row.select("td").last().hasClass("windowbg2")) {
if (navigationLinks.size() < 4) {
parsedDownloads.add(new Download(type, url, title, subtitle, null,
true, null));
} else {
String stats = row.text();
stats = stats.replace(title, "").replace(subtitle, "").trim();
parsedDownloads.add(new Download(type, url, title, subtitle, stats,
false, null));
}
} else {
String stats = row.text();
stats = stats.replace(title, "").replace(subtitle, "").trim();
parsedDownloads.add(new Download(type, url, title, subtitle, stats,
false, null));
}
}
} else {
parsedDownloads.add(new Download(type,
rows.select("b>a").first().attr("href"),
rows.select("b>a").first().text(),
rows.select("div.smalltext:not(:has(a))").text(),
rows.select("span:not(:has(a))").first().text(),
false,
rows.select("span:has(a)").first().html()));
}
}
}
}

181
app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java

@ -0,0 +1,181 @@
package gr.thmmy.mthmmy.activities.downloads;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Download;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_TITLE;
import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_URL;
class DownloadsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused")
private static final String TAG = "DownloadsAdapter";
private final int VIEW_TYPE_DOWNLOAD = 0;
private final int VIEW_TYPE_LOADING = 1;
private final Context context;
private ArrayList<Download> parsedDownloads = new ArrayList<>();
private final ArrayList<Boolean> downloadExpandableVisibility = new ArrayList<>();
DownloadsAdapter(Context context, ArrayList<Download> parsedDownloads) {
this.context = context;
this.parsedDownloads = parsedDownloads;
}
interface OnLoadMoreListener {
void onLoadMore();
}
@Override
public int getItemViewType(int position) {
return (parsedDownloads.get(position) == null) ? VIEW_TYPE_LOADING : VIEW_TYPE_DOWNLOAD;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_DOWNLOAD) {
View download = LayoutInflater.from(parent.getContext()).
inflate(R.layout.activity_downloads_row, parent, false);
return new DownloadViewHolder(download);
} else if (viewType == VIEW_TYPE_LOADING) {
View loading = LayoutInflater.from(parent.getContext()).
inflate(R.layout.recycler_loading_item, parent, false);
return new LoadingViewHolder(loading);
}
return null;
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof DownloadViewHolder) {
final Download download = parsedDownloads.get(position);
final DownloadViewHolder downloadViewHolder = (DownloadViewHolder) holder;
if (downloadExpandableVisibility.size() != parsedDownloads.size()) {
for (int i = downloadExpandableVisibility.size(); i < parsedDownloads.size(); ++i)
downloadExpandableVisibility.add(false);
}
if (download.getType() == Download.DownloadItemType.DOWNLOADS_CATEGORY) {
downloadViewHolder.downloadRow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(context, DownloadsActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_DOWNLOADS_URL, download.getUrl());
extras.putString(BUNDLE_DOWNLOADS_TITLE, download.getTitle());
intent.putExtras(extras);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
if (downloadExpandableVisibility.get(downloadViewHolder.getAdapterPosition())) {
downloadViewHolder.informationExpandable.setVisibility(View.VISIBLE);
downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up);
} else {
downloadViewHolder.informationExpandable.setVisibility(View.GONE);
downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down);
}
downloadViewHolder.informationExpandableBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final boolean visible = downloadExpandableVisibility.get(downloadViewHolder.
getAdapterPosition());
if (visible) {
downloadViewHolder.informationExpandable.setVisibility(View.GONE);
downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down);
} else {
downloadViewHolder.informationExpandable.setVisibility(View.VISIBLE);
downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up);
}
downloadExpandableVisibility.set(downloadViewHolder.getAdapterPosition(), !visible);
}
});
downloadViewHolder.title.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf"));
if (download.hasSubCategory()) {
String tmp = context.getResources().getString(R.string.fa_folder) + " "
+ download.getTitle();
downloadViewHolder.title.setText(tmp);
} else {
String tmp = context.getResources().getString(R.string.fa_file) + " "
+ download.getTitle();
downloadViewHolder.title.setText(tmp);
}
} else {
//TODO implement download on click
downloadViewHolder.upperLinear.setBackgroundColor(context.getResources().getColor(R.color.background));
downloadViewHolder.informationExpandable.setVisibility(View.VISIBLE);
downloadViewHolder.informationExpandableBtn.setVisibility(View.GONE);
downloadViewHolder.informationExpandableBtn.setEnabled(false);
downloadViewHolder.title.setText(download.getTitle());
}
downloadViewHolder.subTitle.setText(download.getSubTitle());
String tmp = download.getExtraInfo();
if (tmp != null && !Objects.equals(tmp, ""))
downloadViewHolder.extraInfo.setText(tmp);
else downloadViewHolder.extraInfo.setVisibility(View.GONE);
tmp = download.getStatNumbers();
if (tmp != null && !Objects.equals(tmp, ""))
downloadViewHolder.uploaderDate.setText(tmp);
else downloadViewHolder.uploaderDate.setVisibility(View.GONE);
} else if (holder instanceof LoadingViewHolder) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
@Override
public int getItemCount() {
return parsedDownloads.size();
}
private static class DownloadViewHolder extends RecyclerView.ViewHolder {
final LinearLayout upperLinear, downloadRow, informationExpandable;
final TextView title, subTitle, extraInfo, uploaderDate;
final ImageButton informationExpandableBtn;
DownloadViewHolder(View download) {
super(download);
upperLinear = (LinearLayout) download.findViewById(R.id.upper_linear);
downloadRow = (LinearLayout) download.findViewById(R.id.download_row);
informationExpandable = (LinearLayout) download.findViewById(R.id.child_board_expandable);
title = (TextView) download.findViewById(R.id.download_title);
subTitle = (TextView) download.findViewById(R.id.download_sub_title);
extraInfo = (TextView) download.findViewById(R.id.download_extra_info);
uploaderDate = (TextView) download.findViewById(R.id.download_uploader_date);
informationExpandableBtn = (ImageButton) download.findViewById(R.id.download_information_button);
}
}
private static class LoadingViewHolder extends RecyclerView.ViewHolder {
final MaterialProgressBar progressBar;
LoadingViewHolder(View itemView) {
super(itemView);
progressBar = (MaterialProgressBar) itemView.findViewById(R.id.recycler_progress_bar);
}
}
}

36
app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java

@ -248,11 +248,13 @@ public class StatsFragment extends Fragment {
postingActivityByTimeChartXAxis.setGranularity(1f);
LineDataSet postingActivityByTimeDataSet = new LineDataSet(postingActivityByTime, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
postingActivityByTimeDataSet.setFillDrawable(getResources().getDrawable(R.drawable.line_chart_gradient, null));
} else
//noinspection deprecation
postingActivityByTimeDataSet.setFillDrawable(getResources().getDrawable(R.drawable.line_chart_gradient));
if (isAdded()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
postingActivityByTimeDataSet.setFillDrawable(getResources().getDrawable(R.drawable.line_chart_gradient, null));
} else
//noinspection deprecation
postingActivityByTimeDataSet.setFillDrawable(getResources().getDrawable(R.drawable.line_chart_gradient));
}
postingActivityByTimeDataSet.setDrawFilled(true);
postingActivityByTimeDataSet.setDrawCircles(false);
postingActivityByTimeDataSet.setDrawValues(false);
@ -285,11 +287,13 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByPostsChartYAxis.setGranularity(1f);
BarDataSet mostPopularBoardsByPostsDataSet = new BarDataSet(mostPopularBoardsByPosts, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mostPopularBoardsByPostsDataSet.setColors(getResources().getColor(R.color.accent, null));
} else
//noinspection deprecation
mostPopularBoardsByPostsDataSet.setColors(getResources().getColor(R.color.accent));
if (isAdded()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mostPopularBoardsByPostsDataSet.setColors(getResources().getColor(R.color.accent, null));
} else
//noinspection deprecation
mostPopularBoardsByPostsDataSet.setColors(getResources().getColor(R.color.accent));
}
mostPopularBoardsByPostsDataSet.setDrawValues(false);
mostPopularBoardsByPostsDataSet.setValueTextColor(Color.WHITE);
@ -324,11 +328,13 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByActivityChartYAxis.setLabelCount(10, false);
BarDataSet mostPopularBoardsByActivityDataSet = new BarDataSet(mostPopularBoardsByActivity, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mostPopularBoardsByActivityDataSet.setColors(getResources().getColor(R.color.accent, null));
} else
//noinspection deprecation
mostPopularBoardsByActivityDataSet.setColors(getResources().getColor(R.color.accent));
if (isAdded()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mostPopularBoardsByActivityDataSet.setColors(getResources().getColor(R.color.accent, null));
} else
//noinspection deprecation
mostPopularBoardsByActivityDataSet.setColors(getResources().getColor(R.color.accent));
}
mostPopularBoardsByActivityDataSet.setDrawValues(false);
mostPopularBoardsByActivityDataSet.setValueTextColor(Color.WHITE);

11
app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java

@ -187,11 +187,12 @@ public class SummaryFragment extends Fragment {
}
TextView entry = new TextView(this.getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
entry.setTextColor(getResources().getColor(R.color.primary_text, null));
} else {
//noinspection deprecation
entry.setTextColor(getResources().getColor(R.color.primary_text));
if (isAdded()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
entry.setTextColor(getResources().getColor(R.color.primary_text, null));
else
//noinspection deprecation
entry.setTextColor(getResources().getColor(R.color.primary_text));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY));

19
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java

@ -9,7 +9,6 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
@ -63,7 +62,6 @@ public class TopicActivity extends BaseActivity {
*/
public static final String BUNDLE_TOPIC_TITLE = "TOPIC_TITLE";
private static final int PERMISSIONS_REQUEST_CODE = 69;
static boolean readWriteAccepted;
private static TopicTask topicTask;
//About posts
private TopicAdapter topicAdapter;
@ -213,7 +211,7 @@ public class TopicActivity extends BaseActivity {
topicTask.cancel(true);
}
@Override
/*@Override
public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
, @NonNull int[] grantResults) {
switch (permsRequestCode) {
@ -221,18 +219,20 @@ public class TopicActivity extends BaseActivity {
readWriteAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
}
}*/
private void requestPerms() { //Runtime permissions for devices with API >= 23
boolean requestPerms() { //Runtime permissions request for devices with API >= 23
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
checkSelfPermission(PERMISSIONS_STORAGE[0]);
checkSelfPermission(PERMISSIONS_STORAGE[1]);
requestPermissions(PERMISSIONS_STORAGE, PERMISSIONS_REQUEST_CODE);
} else readWriteAccepted = true;
if (checkSelfPermission(PERMISSIONS_STORAGE[0]) == PackageManager.PERMISSION_DENIED ||
checkSelfPermission(PERMISSIONS_STORAGE[1]) == PackageManager.PERMISSION_DENIED) {
requestPermissions(PERMISSIONS_STORAGE, PERMISSIONS_REQUEST_CODE);
return false;
} else return true;
} else return true;
}
//--------------------------------------BOTTOM NAV BAR METHODS----------------------------------
@ -365,6 +365,7 @@ public class TopicActivity extends BaseActivity {
private static final int OTHER_ERROR = 2;
private static final int SAME_PAGE = 3;
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
paginationEnabled(false);

2
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java

@ -243,7 +243,7 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
attached.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (TopicActivity.readWriteAccepted) {
if (((TopicActivity) context).requestPerms()) {
downloadTask = new DownloadTask();
downloadTask.execute(attachedFile);
} else

121
app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java

@ -24,18 +24,20 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.AboutActivity;
import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.downloads.DownloadsActivity;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.session.SessionManager;
import okhttp3.OkHttpClient;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_TITLE;
import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_THUMBNAIL_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_USERNAME;
public abstract class BaseActivity extends AppCompatActivity
{
public abstract class BaseActivity extends AppCompatActivity {
// Client & Cookies
protected static OkHttpClient client;
@ -49,10 +51,10 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(client==null)
if (client == null)
client = BaseApplication.getInstance().getClient(); //must check every time - e.g.
// they become null when app restarts after crash
if(sessionManager==null)
if (sessionManager == null)
sessionManager = BaseApplication.getInstance().getSessionManager();
}
@ -65,27 +67,25 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onPause() {
super.onPause();
if(drawer!=null) //close drawer animation after returning to activity
if (drawer != null) //close drawer animation after returning to activity
drawer.closeDrawer();
}
public static OkHttpClient getClient()
{
public static OkHttpClient getClient() {
return client;
}
public static SessionManager getSessionManager()
{
public static SessionManager getSessionManager() {
return sessionManager;
}
//TODO: move stuff below (?)
//------------------------------------------DRAWER STUFF----------------------------------------
protected static final int HOME_ID=0;
protected static final int DOWNLOADS_ID=1;
protected static final int LOG_ID =2;
protected static final int ABOUT_ID=3;
protected static final int HOME_ID = 0;
protected static final int DOWNLOADS_ID = 1;
protected static final int LOG_ID = 2;
protected static final int ABOUT_ID = 3;
private AccountHeader accountHeader;
private ProfileDrawerItem profileDrawerItem;
@ -96,42 +96,41 @@ public abstract class BaseActivity extends AppCompatActivity
/**
* Call only after initializing Toolbar
*/
protected void createDrawer()
{
protected void createDrawer() {
final int primaryColor = ContextCompat.getColor(this, R.color.iron);
final int selectedPrimaryColor = ContextCompat.getColor(this, R.color.primary_dark);
final int selectedSecondaryColor = ContextCompat.getColor(this, R.color.accent);
//Drawer Icons
homeIcon =new IconicsDrawable(this)
homeIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_home)
.color(primaryColor);
homeIconSelected =new IconicsDrawable(this)
homeIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_home)
.color(selectedSecondaryColor);
downloadsIcon =new IconicsDrawable(this)
downloadsIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_download)
.color(primaryColor);
downloadsIconSelected =new IconicsDrawable(this)
downloadsIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_download)
.color(selectedSecondaryColor);
loginIcon =new IconicsDrawable(this)
loginIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_sign_in)
.color(primaryColor);
logoutIcon =new IconicsDrawable(this)
logoutIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_sign_out)
.color(primaryColor);
aboutIcon =new IconicsDrawable(this)
aboutIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_info_circle)
.color(primaryColor);
aboutIconSelected =new IconicsDrawable(this)
aboutIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_info_circle)
.color(selectedSecondaryColor);
@ -146,7 +145,6 @@ public abstract class BaseActivity extends AppCompatActivity
.withSelectedIcon(homeIconSelected);
if (sessionManager.isLoggedIn()) //When logged in
{
loginLogoutItem = new PrimaryDrawerItem()
@ -164,8 +162,7 @@ public abstract class BaseActivity extends AppCompatActivity
.withName(R.string.downloads)
.withIcon(downloadsIcon)
.withSelectedIcon(downloadsIconSelected);
}
else
} else
loginLogoutItem = new PrimaryDrawerItem()
.withTextColor(primaryColor)
.withSelectedColor(selectedSecondaryColor)
@ -195,12 +192,11 @@ public abstract class BaseActivity extends AppCompatActivity
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
@Override
public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
if(sessionManager.isLoggedIn())
{
if (sessionManager.isLoggedIn()) {
Intent intent = new Intent(BaseActivity.this, ProfileActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_PROFILE_URL, "https://www.thmmy.gr/smf/index.php?action=profile");
if(!sessionManager.hasAvatar())
if (!sessionManager.hasAvatar())
extras.putString(BUNDLE_THUMBNAIL_URL, "");
else
extras.putString(BUNDLE_THUMBNAIL_URL, sessionManager.getAvatarLink());
@ -220,44 +216,38 @@ public abstract class BaseActivity extends AppCompatActivity
DrawerBuilder drawerBuilder = new DrawerBuilder()
.withActivity(this)
.withToolbar(toolbar)
.withDrawerWidthDp((int)BaseApplication.getInstance().getDpWidth()/2)
.withDrawerWidthDp((int) BaseApplication.getInstance().getDpWidth() / 2)
.withSliderBackgroundColor(ContextCompat.getColor(this, R.color.primary_light))
.withAccountHeader(accountHeader)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
@Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
if(drawerItem.equals(HOME_ID))
{
if(!(BaseActivity.this instanceof MainActivity))
{
if (drawerItem.equals(HOME_ID)) {
if (!(BaseActivity.this instanceof MainActivity)) {
Intent i = new Intent(BaseActivity.this, MainActivity.class);
startActivity(i);
}
}
// else if(drawerItem.equals(DOWNLOADS_ID))
// {
// if (sessionManager.isLoggedIn()) //When logged out or if user is guest
// {
// Intent i = new Intent(BaseActivity.this, DownloadsActivity.class);
// startActivity(i);
// }
// }
else if(drawerItem.equals(LOG_ID))
{
} else if (drawerItem.equals(DOWNLOADS_ID)) {
if (sessionManager.isLoggedIn()) //When logged out or if user is guest
{
Intent i = new Intent(BaseActivity.this, DownloadsActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_DOWNLOADS_URL, "");
extras.putString(BUNDLE_DOWNLOADS_TITLE, null);
i.putExtras(extras);
startActivity(i);
}
} else if (drawerItem.equals(LOG_ID)) {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{
Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
}
else
} else
new LogoutTask().execute();
}
else if(drawerItem.equals(ABOUT_ID))
{
if(!(BaseActivity.this instanceof AboutActivity))
{
} else if (drawerItem.equals(ABOUT_ID)) {
if (!(BaseActivity.this instanceof AboutActivity)) {
Intent i = new Intent(BaseActivity.this, AboutActivity.class);
startActivity(i);
}
@ -269,10 +259,10 @@ public abstract class BaseActivity extends AppCompatActivity
}
});
if(sessionManager.isLoggedIn())
drawerBuilder.addDrawerItems(homeItem,downloadsItem,loginLogoutItem,aboutItem);
if (sessionManager.isLoggedIn())
drawerBuilder.addDrawerItems(homeItem, downloadsItem, loginLogoutItem, aboutItem);
else
drawerBuilder.addDrawerItems(homeItem,loginLogoutItem,aboutItem);
drawerBuilder.addDrawerItems(homeItem, loginLogoutItem, aboutItem);
drawer = drawerBuilder.build();
@ -286,10 +276,8 @@ public abstract class BaseActivity extends AppCompatActivity
});
}
protected void updateDrawer()
{
if(drawer!=null)
{
protected void updateDrawer() {
if (drawer != null) {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{
drawer.removeItem(DOWNLOADS_ID);
@ -299,9 +287,7 @@ public abstract class BaseActivity extends AppCompatActivity
.paddingDp(10)
.color(ContextCompat.getColor(this, R.color.primary_light))
.backgroundColor(ContextCompat.getColor(this, R.color.primary)));
}
else
{
} else {
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername()).withIcon(sessionManager.getAvatarLink());
}
@ -313,9 +299,10 @@ public abstract class BaseActivity extends AppCompatActivity
//-------------------------------------------LOGOUT-------------------------------------------------
/**
* Result toast will always display a success, because when user chooses logout all data are
* cleared regardless of the actual outcome
* Result toast will always display a success, because when user chooses logout all data are
* cleared regardless of the actual outcome
*/
protected class LogoutTask extends AsyncTask<Void, Void, Integer> { //Attempt logout
ProgressDialog progressDialog;
@ -324,8 +311,7 @@ public abstract class BaseActivity extends AppCompatActivity
return sessionManager.logout();
}
protected void onPreExecute()
{ //Show a progress dialog until done
protected void onPreExecute() { //Show a progress dialog until done
progressDialog = new ProgressDialog(BaseActivity.this,
R.style.AppTheme_Dark_Dialog);
progressDialog.setCancelable(false);
@ -334,8 +320,7 @@ public abstract class BaseActivity extends AppCompatActivity
progressDialog.show();
}
protected void onPostExecute(Integer result)
{
protected void onPostExecute(Integer result) {
Toast.makeText(getBaseContext(), "Logged out successfully!", Toast.LENGTH_LONG).show();
updateDrawer();
progressDialog.dismiss();

58
app/src/main/java/gr/thmmy/mthmmy/model/Download.java

@ -0,0 +1,58 @@
package gr.thmmy.mthmmy.model;
public class Download {
public enum DownloadItemType {DOWNLOADS_CATEGORY, DOWNLOADS_FILE}
private final String url, title, subTitle, statNumbers, extraInfo;
private final boolean hasSubCategory;
private final DownloadItemType type;
public Download() {
type = null;
url = null;
title = null;
subTitle = null;
statNumbers = null;
hasSubCategory = false;
extraInfo = null;
}
public Download(DownloadItemType type, String url, String title, String subTitle,
String statNumbers, boolean hasSubCategory, String extraInfo) {
this.type = type;
this.url = url;
this.title = title;
this.subTitle = subTitle;
this.statNumbers = statNumbers;
this.hasSubCategory = hasSubCategory;
this.extraInfo = extraInfo;
}
public DownloadItemType getType() {
return type;
}
public String getUrl() {
return url;
}
public String getTitle() {
return title;
}
public String getSubTitle() {
return subTitle;
}
public String getStatNumbers() {
return statNumbers;
}
public String getExtraInfo() {
return extraInfo;
}
public boolean hasSubCategory() {
return hasSubCategory;
}
}

34
app/src/main/java/gr/thmmy/mthmmy/model/LinkTarget.java

@ -72,7 +72,19 @@ public class LinkTarget {
/**
* Link points to a profile.
*/
PROFILE;
PROFILE,
/**
* Link points to a download.
*/
DOWNLOADS_CATEGORY,
/**
* Link points to a download category.
*/
DOWNLOADS_FILE,
/**
* Link points to downloads.
*/
DOWNLOADS;
/**
* This method defines a custom equality check for {@link Target} enums. It does not check
@ -81,19 +93,23 @@ public class LinkTarget {
* cases described below, false otherwise.</p><ul>
* <li>(Everything but {@link #NOT_THMMY}).is({@link #THMMY}) returns true</li>
* <li>{@link #PROFILE_SUMMARY}.is({@link #PROFILE}) returns true</li>
* <li>{@link #PROFILE_LATEST_POSTS}.is({@link #PROFILE}) returns true</li>
* <li>{@link #PROFILE_STATS}.is({@link #PROFILE}) returns true</li>
* <li>{@link #PROFILE}.is({@link #PROFILE_SUMMARY}) returns false</li>
* <li>{@link #PROFILE_LATEST_POSTS}.is({@link #PROFILE}) returns true</li>
* <li>{@link #PROFILE}.is({@link #PROFILE_LATEST_POSTS}) returns false</li>
* <li>{@link #PROFILE}.is({@link #PROFILE_STATS}) returns false</li></ul>
* <li>{@link #PROFILE_STATS}.is({@link #PROFILE}) returns true</li>
* <li>{@link #PROFILE}.is({@link #PROFILE_STATS}) returns false</li>
* <li>{@link #DOWNLOADS_CATEGORY}.is({@link #DOWNLOADS}) returns true</li>
* <li>{@link #DOWNLOADS}.is({@link #DOWNLOADS_CATEGORY}) returns false</li>
* <li>{@link #DOWNLOADS_FILE}.is({@link #DOWNLOADS}) returns true</li>
* <li>{@link #DOWNLOADS}.is({@link #DOWNLOADS_FILE}) returns false</li></ul>
*
* @param other another Target
* @return true if <b>enums</b> are equal, false otherwise
*/
public boolean is(Target other) {
return (this == PROFILE_LATEST_POSTS ||
this == PROFILE_STATS ||
this == PROFILE_SUMMARY) && other == PROFILE
return ((this == PROFILE_LATEST_POSTS || this == PROFILE_STATS || this == PROFILE_SUMMARY)
&& other == PROFILE)
|| ((this == DOWNLOADS_FILE || this == DOWNLOADS_CATEGORY) && other == DOWNLOADS)
|| (this != NOT_THMMY && other == THMMY)
|| this == other;
}
@ -130,6 +146,10 @@ public class LinkTarget {
else return Target.PROFILE_SUMMARY;
} else if (uriString.contains("action=unread"))
return Target.UNREAD_POSTS;
else if (uriString.contains("action=tpmod;dl=item"))
return Target.DOWNLOADS_FILE;
else if (uriString.contains("action=tpmod;dl"))
return Target.DOWNLOADS_CATEGORY;
Report.v(TAG, "Unknown thmmy link found, link: " + uriString);
return Target.UNKNOWN_THMMY;
}

1
app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java

@ -164,7 +164,6 @@ public class ThmmyFile {
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
} catch (IllegalStateException e) {
Report.d(TAG, "External directory not available!", e);
Log.d(TAG, "External directory not available!", e);
throw e;
}

11
app/src/main/res/drawable/ic_file_upload.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zM5,18h14v2L5,20z"/>
</vector>

62
app/src/main/res/layout/activity_downloads.xml

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".activities.downloads.DownloadsActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/ToolbarTheme">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ToolbarTheme">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/downloads_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
android:layout_marginTop="64dp"
android:background="@color/background"
android:scrollbars="none"
tools:context="gr.thmmy.mthmmy.activities.downloads.DownloadsActivity">
</android.support.v7.widget.RecyclerView>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
android:indeterminate="true"
android:visibility="invisible"
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/download_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload"/>
</android.support.design.widget.CoordinatorLayout>

65
app/src/main/res/layout/activity_downloads_row.xml

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/upper_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/card_background"
android:orientation="vertical"
android:paddingEnd="16dp"
android:paddingStart="16dp">
<LinearLayout
android:id="@+id/download_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/download_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:layout_weight="1"
android:textColor="@color/accent"
android:textSize="18sp"/>
<ImageButton
android:id="@+id/download_information_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/card_background"
android:contentDescription="@string/child_board_button"
android:src="@drawable/ic_arrow_drop_down"/>
</LinearLayout>
<LinearLayout
android:id="@+id/child_board_expandable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/download_sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/secondary_text"/>
<TextView
android:id="@+id/download_extra_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/secondary_text"/>
<TextView
android:id="@+id/download_uploader_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/secondary_text"/>
</LinearLayout>
</LinearLayout>

1
app/src/main/res/values/strings.xml

@ -81,4 +81,5 @@
<string name="fa_file_video_o">&#xf1c8;</string>
<string name="fa_lock">&#xf023;</string>
<string name="fa_sticky">&#xf249;</string>
<string name="fa_folder">&#xf07b;</string>
</resources>

Loading…
Cancel
Save