1
0
mirror of https://codeberg.org/gitnex/GitNex synced 2024-12-23 00:48:57 +01:00

Most visited repositories (#1178)

This is an app only feature and could be useful to some users including me.

Co-authored-by: M M Arif <mmarif@swatian.com>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1178
This commit is contained in:
M M Arif 2022-08-07 22:03:17 +02:00
parent 23df83110d
commit 387089fb26
23 changed files with 411 additions and 37 deletions

View File

@ -108,7 +108,7 @@ dependencies {
implementation "androidx.work:work-runtime:$work_version"
implementation "io.mikael:urlbuilder:2.0.9"
implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.2.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'com.github.chrisvest:stormpot:2.4.2'
implementation 'androidx.browser:browser:1.4.0'

View File

@ -39,6 +39,7 @@ import org.mian.gitnex.fragments.BottomSheetDraftsFragment;
import org.mian.gitnex.fragments.BottomSheetMyIssuesFilterFragment;
import org.mian.gitnex.fragments.DraftsFragment;
import org.mian.gitnex.fragments.ExploreFragment;
import org.mian.gitnex.fragments.MostVisitedReposFragment;
import org.mian.gitnex.fragments.MyIssuesFragment;
import org.mian.gitnex.fragments.MyProfileFragment;
import org.mian.gitnex.fragments.MyRepositoriesFragment;
@ -145,6 +146,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
else if(fragmentById instanceof MyProfileFragment) {
toolbarTitle.setText(getResources().getString(R.string.navProfile));
}
else if(fragmentById instanceof MostVisitedReposFragment) {
toolbarTitle.setText(getResources().getString(R.string.navMostVisited));
}
else if(fragmentById instanceof DraftsFragment) {
toolbarTitle.setText(getResources().getString(R.string.titleDrafts));
}
@ -592,6 +596,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
toolbarTitle.setText(getResources().getString(R.string.navMyIssues));
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new MyIssuesFragment()).commit();
}
else if(id == R.id.nav_most_visited) {
toolbarTitle.setText(getResources().getString(R.string.navMostVisited));
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new MostVisitedReposFragment()).commit();
}
drawer.closeDrawer(GravityCompat.START);
return true;

View File

@ -62,7 +62,6 @@ public class DraftsAdapter extends RecyclerView.Adapter<DraftsAdapter.DraftsView
DraftsApi draftsApi = BaseApi.getInstance(context, DraftsApi.class);
assert draftsApi != null;
draftsApi.deleteSingleDraft(getDraftId);
});
itemView.setOnClickListener(itemEdit -> {
@ -98,9 +97,7 @@ public class DraftsAdapter extends RecyclerView.Adapter<DraftsAdapter.DraftsView
});
bottomSheetReplyFragment.show(fragmentManager, "replyBottomSheet");
});
}
}
public DraftsAdapter(Context ctx, FragmentManager fragmentManager, List<DraftWithRepository> draftsListMain) {
@ -152,9 +149,14 @@ public class DraftsAdapter extends RecyclerView.Adapter<DraftsAdapter.DraftsView
return draftsList.size();
}
@SuppressLint("NotifyDataSetChanged")
public void notifyDataChanged() {
notifyDataSetChanged();
}
public void updateList(List<DraftWithRepository> list) {
draftsList = list;
notifyDataSetChanged();
notifyDataChanged();
}
}

View File

@ -117,14 +117,21 @@ public class ExploreIssuesAdapter extends RecyclerView.Adapter<RecyclerView.View
RepositoryContext repo = new RepositoryContext(repoOwner, repoName, context);
repo.saveToDB(context);
Intent intentIssueDetail = new IssueContext(issue, repo).getIntent(context, IssueDetailActivity.class);
intentIssueDetail.putExtra("openedFromLink", "true");
itemView.setOnClickListener(v -> context.startActivity(intentIssueDetail));
frameLabels.setOnClickListener(v -> context.startActivity(intentIssueDetail));
frameLabelsDots.setOnClickListener(v -> context.startActivity(intentIssueDetail));
itemView.setOnClickListener(v -> {
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
frameLabels.setOnClickListener(v -> {
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
frameLabelsDots.setOnClickListener(v -> {
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
}, 200);
issueAssigneeAvatar.setOnClickListener(v -> {

View File

@ -116,7 +116,7 @@ public class ExploreRepositoriesAdapter extends RecyclerView.Adapter<RecyclerVie
orgName.setText(userRepositories.getFullName().split("/")[0]);
repoName.setText(userRepositories.getFullName().split("/")[1]);
repoStars.setText(String.valueOf(userRepositories.getStarsCount()));
repoStars.setText(AppUtil.numberFormatter(userRepositories.getStarsCount()));
ColorGenerator generator = ColorGenerator.Companion.getMATERIAL();
int color = generator.getColor(userRepositories.getName());

View File

@ -0,0 +1,103 @@
package org.mian.gitnex.adapters;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.RepoDetailActivity;
import org.mian.gitnex.database.models.Repository;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import java.util.List;
/**
* @author M M Arif
*/
public class MostVisitedReposAdapter extends RecyclerView.Adapter<MostVisitedReposAdapter.MostVisitedViewHolder> {
private List<Repository> mostVisitedReposList;
static class MostVisitedViewHolder extends RecyclerView.ViewHolder {
private Repository repository;
private final ImageView image;
private final TextView repoName;
private final TextView orgName;
private final TextView mostVisited;
private MostVisitedViewHolder(View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
repoName = itemView.findViewById(R.id.repo_name);
orgName = itemView.findViewById(R.id.org_name);
mostVisited = itemView.findViewById(R.id.most_visited);
itemView.setOnClickListener(v -> {
Context context = v.getContext();
RepositoryContext repositoryContext = new RepositoryContext(repository.getRepositoryOwner(), repository.getRepositoryName(), context);
Intent intent = repositoryContext.getIntent(context, RepoDetailActivity.class);
context.startActivity(intent);
});
}
}
public MostVisitedReposAdapter(List<Repository> reposListMain) {
this.mostVisitedReposList = reposListMain;
}
@NonNull
@Override
public MostVisitedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_most_visited_repos, parent, false);
return new MostVisitedViewHolder(v);
}
@SuppressLint("DefaultLocale")
@Override
public void onBindViewHolder(@NonNull MostVisitedViewHolder holder, int position) {
Repository currentItem = mostVisitedReposList.get(position);
holder.repository = currentItem;
ColorGenerator generator = ColorGenerator.Companion.getMATERIAL();
int color = generator.getColor(currentItem.getRepositoryOwner());
String firstCharacter = String.valueOf(currentItem.getRepositoryOwner().charAt(0));
TextDrawable drawable = TextDrawable.builder().beginConfig().useFont(Typeface.DEFAULT).fontSize(18).toUpperCase().width(28).height(28).endConfig().buildRoundRect(firstCharacter, color, 14);
holder.image.setImageDrawable(drawable);
holder.orgName.setText(currentItem.getRepositoryOwner());
holder.repoName.setText(currentItem.getRepositoryName());
holder.mostVisited.setText(AppUtil.numberFormatter(currentItem.getMostVisited()));
}
@Override
public int getItemCount() {
return mostVisitedReposList.size();
}
@SuppressLint("NotifyDataSetChanged")
public void notifyDataChanged() {
notifyDataSetChanged();
}
public void updateList(List<Repository> list) {
mostVisitedReposList = list;
notifyDataChanged();
}
}

View File

@ -109,7 +109,7 @@ public class RepoForksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
this.userRepositories = forksModel;
orgName.setText(forksModel.getFullName().split("/")[0]);
repoName.setText(forksModel.getFullName().split("/")[1]);
repoStars.setText(String.valueOf(forksModel.getStarsCount()));
repoStars.setText(AppUtil.numberFormatter(forksModel.getStarsCount()));
ColorGenerator generator = ColorGenerator.Companion.getMATERIAL();
int color = generator.getColor(forksModel.getName());
@ -175,7 +175,6 @@ public class RepoForksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
Context context = v.getContext();
String[] parts = userRepositories.getFullName().split("/");
RepositoryContext repo = new RepositoryContext(userRepositories, context);
repo.saveToDB(context);
Intent intent = repo.getIntent(context, RepoDetailActivity.class);
@ -184,7 +183,6 @@ public class RepoForksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
});
}
}
public void setMoreDataAvailable(boolean moreDataAvailable) {

View File

@ -127,7 +127,7 @@ public class ReposListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
String timeFormat = tinyDb.getString("dateFormat", "pretty");
orgName.setText(repositories.getFullName().split("/")[0]);
repoName.setText(repositories.getFullName().split("/")[1]);
repoStars.setText(String.valueOf(repositories.getStarsCount()));
repoStars.setText(AppUtil.numberFormatter(repositories.getStarsCount()));
ColorGenerator generator = ColorGenerator.Companion.getMATERIAL();
int color = generator.getColor(repositories.getName());

View File

@ -19,12 +19,13 @@ public class RepositoriesApi extends BaseApi {
repositoriesDao = gitnexDatabase.repositoriesDao();
}
public long insertRepository(int repoAccountId, String repositoryOwner, String repositoryName) {
public long insertRepository(int repoAccountId, String repositoryOwner, String repositoryName, int mostVisited) {
Repository repository = new Repository();
repository.setRepoAccountId(repoAccountId);
repository.setRepositoryOwner(repositoryOwner);
repository.setRepositoryName(repositoryName);
repository.setMostVisited(mostVisited);
return insertRepositoryAsyncTask(repository);
}
@ -69,4 +70,11 @@ public class RepositoriesApi extends BaseApi {
executorService.execute(() -> repositoriesDao.deleteRepository(repositoryId));
}
public void updateRepositoryMostVisited(int mostVisited, int repositoryId) {
executorService.execute(() -> repositoriesDao.updateRepositoryMostVisited(mostVisited, repositoryId));
}
public LiveData<List<Repository>> fetchAllMostVisited(int repoAccountId) {
return repositoriesDao.fetchAllMostVisited(repoAccountId);
}
}

View File

@ -44,4 +44,9 @@ public interface RepositoriesDao {
@Query("DELETE FROM Repositories WHERE repoAccountId = :repoAccountId")
void deleteRepositoriesByAccount(int repoAccountId);
@Query("UPDATE Repositories SET mostVisited = :mostVisited WHERE repositoryId = :repositoryId")
void updateRepositoryMostVisited(int mostVisited, int repositoryId);
@Query("SELECT * FROM Repositories WHERE mostVisited > 0 AND repoAccountId = :repoAccountId ORDER BY mostVisited DESC LIMIT 50")
LiveData<List<Repository>> fetchAllMostVisited(int repoAccountId);
}

View File

@ -19,11 +19,11 @@ import org.mian.gitnex.database.models.UserAccount;
*/
@Database(entities = {Draft.class, Repository.class, UserAccount.class},
version = 5, exportSchema = false)
version = 6, exportSchema = false)
public abstract class GitnexDatabase extends RoomDatabase {
private static final String DB_NAME = "gitnex";
private static GitnexDatabase gitnexDatabase;
private static volatile GitnexDatabase gitnexDatabase;
public abstract DraftsDao draftsDao();
public abstract RepositoriesDao repositoriesDao();
@ -61,6 +61,14 @@ public abstract class GitnexDatabase extends RoomDatabase {
}
};
private static final Migration MIGRATION_5_6 = new Migration(5, 6) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'Repositories' ADD COLUMN 'mostVisited' INTEGER NOT NULL DEFAULT 0");
}
};
public static GitnexDatabase getDatabaseInstance(Context context) {
if (gitnexDatabase == null) {
@ -68,9 +76,9 @@ public abstract class GitnexDatabase extends RoomDatabase {
if(gitnexDatabase == null) {
gitnexDatabase = Room.databaseBuilder(context, GitnexDatabase.class, DB_NAME)
// .fallbackToDestructiveMigration()
//.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
.build();
}

View File

@ -24,6 +24,7 @@ public class Repository implements Serializable {
private int repoAccountId;
private String repositoryOwner;
private String repositoryName;
private int mostVisited;
public int getRepositoryId() {
return repositoryId;
@ -56,4 +57,12 @@ public class Repository implements Serializable {
public void setRepositoryName(String repositoryName) {
this.repositoryName = repositoryName;
}
public int getMostVisited() {
return mostVisited;
}
public void setMostVisited(int mostVisited) {
this.mostVisited = mostVisited;
}
}

View File

@ -85,7 +85,6 @@ public class DraftsFragment extends Fragment {
fetchDataAsync(currentActiveAccountId);
return fragmentDraftsBinding.getRoot();
}
private void fetchDataAsync(int accountId) {
@ -99,18 +98,14 @@ public class DraftsFragment extends Fragment {
draftsList_.clear();
noData.setVisibility(View.GONE);
draftsList_.addAll(drafts);
adapter.notifyDataSetChanged();
adapter.notifyDataChanged();
mRecyclerView.setAdapter(adapter);
}
else {
noData.setVisibility(View.VISIBLE);
}
});
}
@Override
@ -126,9 +121,8 @@ public class DraftsFragment extends Fragment {
BaseApi.getInstance(ctx, DraftsApi.class).deleteAllDrafts(accountId);
draftsList_.clear();
adapter.notifyDataSetChanged();
adapter.notifyDataChanged();
Toasty.success(ctx, getResources().getString(R.string.draftsDeleteSuccess));
}
else {
Toasty.warning(ctx, getResources().getString(R.string.draftsListEmpty));
@ -160,10 +154,8 @@ public class DraftsFragment extends Fragment {
filter(newText);
return false;
}
});
}
private void filter(String text) {
@ -184,5 +176,4 @@ public class DraftsFragment extends Fragment {
adapter.updateList(arr);
}
}

View File

@ -0,0 +1,103 @@
package org.mian.gitnex.fragments;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.MainActivity;
import org.mian.gitnex.adapters.MostVisitedReposAdapter;
import org.mian.gitnex.database.api.BaseApi;
import org.mian.gitnex.database.api.RepositoriesApi;
import org.mian.gitnex.database.models.Repository;
import org.mian.gitnex.databinding.FragmentDraftsBinding;
import org.mian.gitnex.helpers.TinyDB;
import java.util.ArrayList;
import java.util.List;
/**
* @author M M Arif
*/
public class MostVisitedReposFragment extends Fragment {
private Context ctx;
private MostVisitedReposAdapter adapter;
private RecyclerView mRecyclerView;
private RepositoriesApi repositoriesApi;
private TextView noData;
private List<Repository> mostVisitedReposList;
private int currentActiveAccountId;
private SwipeRefreshLayout swipeRefresh;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentDraftsBinding fragmentDraftsBinding = FragmentDraftsBinding.inflate(inflater, container, false);
ctx = getContext();
setHasOptionsMenu(true);
((MainActivity) requireActivity()).setActionBarTitle(getResources().getString(R.string.navMostVisited));
TinyDB tinyDb = TinyDB.getInstance(ctx);
mostVisitedReposList = new ArrayList<>();
repositoriesApi = BaseApi.getInstance(ctx, RepositoriesApi.class);
noData = fragmentDraftsBinding.noData;
mRecyclerView = fragmentDraftsBinding.recyclerView;
swipeRefresh = fragmentDraftsBinding.pullToRefresh;
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(ctx));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(),
DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(dividerItemDecoration);
adapter = new MostVisitedReposAdapter(mostVisitedReposList);
currentActiveAccountId = tinyDb.getInt("currentActiveAccountId");
swipeRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> {
mostVisitedReposList.clear();
fetchDataAsync(currentActiveAccountId);
}, 250));
fetchDataAsync(currentActiveAccountId);
return fragmentDraftsBinding.getRoot();
}
private void fetchDataAsync(int accountId) {
repositoriesApi.fetchAllMostVisited(accountId).observe(getViewLifecycleOwner(), mostVisitedRepos -> {
swipeRefresh.setRefreshing(false);
assert mostVisitedRepos != null;
if(mostVisitedRepos.size() > 0) {
mostVisitedReposList.clear();
noData.setVisibility(View.GONE);
mostVisitedReposList.addAll(mostVisitedRepos);
adapter.notifyDataChanged();
mRecyclerView.setAdapter(adapter);
}
else {
noData.setVisibility(View.VISIBLE);
}
});
}
}

View File

@ -34,6 +34,7 @@ import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
@ -456,6 +457,27 @@ public class AppUtil {
}
/**
* Pretty number format
* Example, 1200 = 1.2k
*/
public static String numberFormatter(Number number) {
char[] suffix = {' ', 'k', 'M', 'B', 'T'};
long numValue = number.longValue();
int value = (int) Math.floor(Math.log10(numValue));
int base = value / 3;
if(value >= 3 && base < suffix.length) {
return new DecimalFormat("#0.0").format(numValue / Math.pow(10, base * 3)) + suffix[base];
}
if(base >= suffix.length) {
return new DecimalFormat("#0").format(numValue / Math.pow(10, base * 2)) + suffix[4];
}
else {
return new DecimalFormat("#,##0").format(numValue);
}
}
/*
* check for ghost/restricted users/profiles
*/
public static Boolean checkGhostUsers(String str) {

View File

@ -33,7 +33,6 @@ public class RepositoryContext implements Serializable {
OPEN,
CLOSED;
@NonNull
@Override
public String toString() {
@ -243,16 +242,17 @@ public class RepositoryContext implements Serializable {
RepositoriesApi repositoryData = BaseApi.getInstance(context, RepositoriesApi.class);
assert repositoryData != null;
Integer count = repositoryData.checkRepository(currentActiveAccountId, getOwner(), getName());
Repository getMostVisitedValue = repositoryData.getRepository(currentActiveAccountId, getOwner(), getName());
if(count == 0) {
long id = repositoryData.insertRepository(currentActiveAccountId, getOwner(), getName());
if(getMostVisitedValue == null) {
long id = repositoryData.insertRepository(currentActiveAccountId, getOwner(), getName(), 1);
setRepositoryId((int) id);
return (int) id;
}
else {
Repository data = repositoryData.getRepository(currentActiveAccountId, getOwner(), getName());
setRepositoryId(data.getRepositoryId());
repositoryData.updateRepositoryMostVisited(getMostVisitedValue.getMostVisited() + 1, data.getRepositoryId());
return data.getRepositoryId();
}
}

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,17l6,-6l4,4l8,-8"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M14,7l7,0l0,7"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
</vector>

View File

@ -70,7 +70,7 @@
android:layout_marginBottom="@dimen/dimen8dp"
android:hint="@string/protocol"
app:endIconTint="?attr/iconsColor"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu">
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
<AutoCompleteTextView
android:id="@+id/httpsSpinner"

View File

@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:text="@string/draftsListEmpty"
android:text="@string/noDataFound"
android:textColor="?attr/primaryTextColor"
android:gravity="center"
android:textSize="20sp"

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:orientation="vertical"
android:padding="@dimen/dimen16dp">
<LinearLayout
android:id="@+id/org_info_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/image"
android:layout_width="@dimen/dimen24dp"
android:layout_height="@dimen/dimen24dp"
android:layout_marginStart="@dimen/dimen0dp"
android:layout_marginEnd="@dimen/dimen10dp"
android:contentDescription="@string/repoContentAvatar"
android:src="@drawable/ic_android" />
<TextView
android:id="@+id/org_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp"
tools:text="@string/orgName" />
</LinearLayout>
<TextView
android:id="@+id/repo_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/repoName"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp"
android:textStyle="bold" />
<View
android:id="@+id/spacer_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/dimen8dp" />
<LinearLayout
android:id="@+id/repo_info_stars_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="@dimen/dimen18dp"
android:layout_height="@dimen/dimen18dp"
android:layout_marginStart="@dimen/dimen0dp"
android:layout_marginEnd="@dimen/dimen6dp"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_trending"
app:tint="?attr/iconsColor" />
<TextView
android:id="@+id/most_visited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp"
tools:text="@string/repoStars" />
</LinearLayout>
</LinearLayout>

View File

@ -38,6 +38,10 @@
android:icon="@drawable/ic_search"
android:title="@string/pageTitleExplore" />
<item android:id="@+id/nav_most_visited"
android:icon="@drawable/ic_trending"
android:title="@string/navMostVisited" />
<item android:id="@+id/nav_comments_draft"
android:icon="@drawable/ic_drafts"
android:title="@string/titleDrafts" />

View File

@ -9,6 +9,7 @@
<dimen name="dimen10dp">10dp</dimen>
<dimen name="dimen12dp">12dp</dimen>
<dimen name="dimen16dp">16dp</dimen>
<dimen name="dimen18dp">18dp</dimen>
<dimen name="dimen20dp">20dp</dimen>
<dimen name="dimen24dp">24dp</dimen>
<dimen name="dimen26dp">26dp</dimen>

View File

@ -31,6 +31,7 @@
<string name="navLogout">Logout</string>
<string name="navAdministration">Administration</string>
<string name="navMyIssues">My Issues</string>
<string name="navMostVisited">Most Visited Repos</string>
<!-- menu items -->
<!-- page titles -->