diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 9c3c3327..61922179 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -34,7 +34,7 @@ import org.mian.gitnex.database.models.UserAccount; import org.mian.gitnex.fragments.AdministrationFragment; import org.mian.gitnex.fragments.BottomSheetDraftsFragment; import org.mian.gitnex.fragments.DraftsFragment; -import org.mian.gitnex.fragments.ExploreRepositoriesFragment; +import org.mian.gitnex.fragments.ExploreFragment; import org.mian.gitnex.fragments.MyRepositoriesFragment; import org.mian.gitnex.fragments.NotificationsFragment; import org.mian.gitnex.fragments.OrganizationsFragment; @@ -176,7 +176,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig else if(fragmentById instanceof OrganizationsFragment) { toolbarTitle.setText(getResources().getString(R.string.pageTitleOrganizations)); } - else if(fragmentById instanceof ExploreRepositoriesFragment) { + else if(fragmentById instanceof ExploreFragment) { toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); } else if(fragmentById instanceof NotificationsFragment) { @@ -305,6 +305,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig toolbarTitle.setText(getResources().getString(R.string.pageTitleProfile)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ProfileFragment()).commit(); + navigationView.setCheckedItem(R.id.nav_profile); drawer.closeDrawers(); }); @@ -384,7 +385,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig case 5: toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreRepositoriesFragment()).commit(); + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreFragment()).commit(); navigationView.setCheckedItem(R.id.nav_explore); break; @@ -550,7 +551,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig case R.id.nav_explore: toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreRepositoriesFragment()).commit(); + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreFragment()).commit(); break; case R.id.nav_notifications: diff --git a/app/src/main/java/org/mian/gitnex/adapters/SearchIssuesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/SearchIssuesAdapter.java new file mode 100644 index 00000000..379029e4 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/SearchIssuesAdapter.java @@ -0,0 +1,172 @@ +package org.mian.gitnex.adapters; + +import android.content.Context; +import android.content.Intent; +import android.text.Html; +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 org.mian.gitnex.R; +import org.mian.gitnex.activities.IssueDetailActivity; +import org.mian.gitnex.clients.PicassoService; +import org.mian.gitnex.database.api.RepositoriesApi; +import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.helpers.ClickListener; +import org.mian.gitnex.helpers.RoundedTransformation; +import org.mian.gitnex.helpers.TimeHelper; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.models.Issues; +import org.ocpsoft.prettytime.PrettyTime; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +/** + * Author M M Arif + */ + +public class SearchIssuesAdapter extends RecyclerView.Adapter { + + private List searchedList; + private Context mCtx; + private TinyDB tinyDb; + + public SearchIssuesAdapter(List dataList, Context mCtx) { + + this.mCtx = mCtx; + this.searchedList = dataList; + this.tinyDb = new TinyDB(mCtx); + } + + class SearchViewHolder extends RecyclerView.ViewHolder { + + private TextView issueNumber; + private ImageView issueAssigneeAvatar; + private TextView issueTitle; + private TextView issueCreatedTime; + private TextView issueCommentsCount; + private TextView repoFullName; + + private SearchViewHolder(View itemView) { + + super(itemView); + + issueNumber = itemView.findViewById(R.id.issueNumber); + issueAssigneeAvatar = itemView.findViewById(R.id.assigneeAvatar); + issueTitle = itemView.findViewById(R.id.issueTitle); + issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount); + issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime); + repoFullName = itemView.findViewById(R.id.repoFullName); + + issueTitle.setOnClickListener(v -> { + + Context context = v.getContext(); + + Intent intent = new Intent(context, IssueDetailActivity.class); + intent.putExtra("issueNumber", issueNumber.getText()); + + tinyDb.putString("issueNumber", issueNumber.getText().toString()); + tinyDb.putString("issueType", "Issue"); + + tinyDb.putString("repoFullName", repoFullName.getText().toString()); + + String[] parts = repoFullName.getText().toString().split("/"); + final String repoOwner = parts[0]; + final String repoName = parts[1]; + + int currentActiveAccountId = tinyDb.getInt("currentActiveAccountId"); + RepositoriesApi repositoryData = new RepositoriesApi(context); + + Integer count = repositoryData.checkRepository(currentActiveAccountId, repoOwner, repoName); + + if(count == 0) { + + long id = repositoryData.insertRepository(currentActiveAccountId, repoOwner, repoName); + tinyDb.putLong("repositoryId", id); + + } + else { + + Repository data = repositoryData.getRepository(currentActiveAccountId, repoOwner, repoName); + tinyDb.putLong("repositoryId", data.getRepositoryId()); + + } + + context.startActivity(intent); + }); + } + + } + + @NonNull + @Override + public SearchIssuesAdapter.SearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_issues, parent, false); + return new SearchIssuesAdapter.SearchViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull final SearchIssuesAdapter.SearchViewHolder holder, int position) { + + Issues currentItem = searchedList.get(position); + + String locale = tinyDb.getString("locale"); + String timeFormat = tinyDb.getString("dateFormat"); + + if(!currentItem.getUser().getFull_name().equals("")) { + holder.issueAssigneeAvatar.setOnClickListener(new ClickListener(mCtx.getResources().getString(R.string.issueCreator) + currentItem.getUser().getFull_name(), mCtx)); + } + else { + holder.issueAssigneeAvatar.setOnClickListener(new ClickListener(mCtx.getResources().getString(R.string.issueCreator) + currentItem.getUser().getLogin(), mCtx)); + } + + PicassoService + .getInstance(mCtx).get().load(currentItem.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(holder.issueAssigneeAvatar); + + String issueNumber_ = "" + currentItem.getRepository().getFull_name() + mCtx.getResources().getString(R.string.hash) + currentItem.getNumber() + ""; + holder.issueTitle.setText(Html.fromHtml(issueNumber_ + " " + currentItem.getTitle())); + + holder.issueNumber.setText(String.valueOf(currentItem.getNumber())); + holder.issueCommentsCount.setText(String.valueOf(currentItem.getComments())); + holder.repoFullName.setText(currentItem.getRepository().getFull_name()); + + switch(timeFormat) { + case "pretty": { + PrettyTime prettyTime = new PrettyTime(new Locale(locale)); + String createdTime = prettyTime.format(currentItem.getCreated_at()); + holder.issueCreatedTime.setText(createdTime); + holder.issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(currentItem.getCreated_at()), mCtx)); + break; + } + case "normal": { + DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + mCtx.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); + String createdTime = formatter.format(currentItem.getCreated_at()); + holder.issueCreatedTime.setText(createdTime); + break; + } + case "normal1": { + DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + mCtx.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); + String createdTime = formatter.format(currentItem.getCreated_at()); + holder.issueCreatedTime.setText(createdTime); + break; + } + } + } + + @Override + public int getItemCount() { + + return searchedList.size(); + } + + public void notifyDataChanged() { + + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java new file mode 100644 index 00000000..47d3c23e --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java @@ -0,0 +1,129 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; +import com.google.android.material.tabs.TabLayout; +import org.mian.gitnex.R; +import org.mian.gitnex.activities.MainActivity; +import org.mian.gitnex.helpers.TinyDB; + +/** + * Author M M Arif + */ + +public class ExploreFragment extends Fragment { + + private Context ctx; + private TinyDB tinyDB; + + private int tabsCount; + public ViewPager mViewPager; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_explore, container,false); + ctx = getContext(); + tinyDB = new TinyDB(ctx); + + ((MainActivity) requireActivity()).setActionBarTitle(getResources().getString(R.string.navExplore)); + + TabLayout tabLayout = v.findViewById(R.id.tabsExplore); + + ViewGroup viewGroup = (ViewGroup) tabLayout.getChildAt(0); + tabsCount = viewGroup.getChildCount(); + + Typeface myTypeface; + + switch(tinyDB.getInt("customFontId", -1)) { + + case 0: + myTypeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/roboto.ttf"); + break; + + case 2: + myTypeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/sourcecodeproregular.ttf"); + break; + + default: + myTypeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/manroperegular.ttf"); + break; + + } + + for(int j = 0; j < tabsCount; j++) { + + ViewGroup vgTab = (ViewGroup) viewGroup.getChildAt(j); + int tabChildCount = vgTab.getChildCount(); + + for(int i = 0; i < tabChildCount; i++) { + + View tabViewChild = vgTab.getChildAt(i); + + if(tabViewChild instanceof TextView) { + ((TextView) tabViewChild).setTypeface(myTypeface); + } + } + } + + mViewPager = v.findViewById(R.id.containerExplore); + + mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); + tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); + + SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager()); + mViewPager.setAdapter(mSectionsPagerAdapter); + + return v; + } + + public class SectionsPagerAdapter extends FragmentPagerAdapter { + + SectionsPagerAdapter(FragmentManager fm) { + + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NonNull + @Override + public Fragment getItem(int position) { + + Fragment fragment = null; + + switch(position) { + + case 0: // Repositories + fragment = new ExploreRepositoriesFragment(); + break; + + case 1: // Issues + fragment = new SearchIssuesFragment(); + break; + } + + assert fragment != null; + return fragment; + + } + + @Override + public int getCount() { + + return tabsCount; + } + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java index 91323f7f..ea15a89f 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java @@ -27,7 +27,6 @@ import org.mian.gitnex.interfaces.ApiInterface; import org.mian.gitnex.models.Milestones; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -104,7 +103,7 @@ public class MilestonesFragment extends Fragment { }, 50)); - ((RepoDetailActivity) Objects.requireNonNull(getActivity())).setFragmentRefreshListenerMilestone(milestoneState -> { + ((RepoDetailActivity) requireActivity()).setFragmentRefreshListenerMilestone(milestoneState -> { if(milestoneState.equals("closed")) { menu.getItem(1).setIcon(R.drawable.ic_filter_closed); diff --git a/app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java new file mode 100644 index 00000000..72ba3582 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java @@ -0,0 +1,192 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import org.mian.gitnex.adapters.SearchIssuesAdapter; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.FragmentSearchIssuesBinding; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.InfiniteScrollListener; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.models.Issues; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Author M M Arif + */ + +public class SearchIssuesFragment extends Fragment { + + private Context ctx; + private TinyDB tinyDb; + private FragmentSearchIssuesBinding viewBinding; + private SearchIssuesAdapter adapter; + private List dataList; + + private String instanceUrl; + private String loginUid; + private String instanceToken; + + private int apiCallCurrentValue = 10; + private int pageCurrentIndex = 1; + private String type = "issues"; + private String state = "open"; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + viewBinding = FragmentSearchIssuesBinding.inflate(inflater, container, false); + setHasOptionsMenu(true); + + ctx = getContext(); + tinyDb = new TinyDB(getContext()); + + instanceUrl = tinyDb.getString("instanceUrl"); + loginUid = tinyDb.getString("loginUid"); + instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + + dataList = new ArrayList<>(); + adapter = new SearchIssuesAdapter(dataList, ctx); + + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(ctx); + + viewBinding.recyclerViewSearchIssues.setHasFixedSize(true); + viewBinding.recyclerViewSearchIssues.setLayoutManager(linearLayoutManager); + viewBinding.recyclerViewSearchIssues.setAdapter(adapter); + + viewBinding.searchKeyword.setOnEditorActionListener((v1, actionId, event) -> { + + if(actionId == EditorInfo.IME_ACTION_SEND) { + + if(!Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString().equals("")) { + + InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(viewBinding.searchKeyword.getWindowToken(), 0); + + pageCurrentIndex = 1; + apiCallCurrentValue = 10; + viewBinding.progressBar.setVisibility(View.VISIBLE); + loadData(false, viewBinding.searchKeyword.getText().toString()); + } + } + + return false; + }); + + viewBinding.recyclerViewSearchIssues.addOnScrollListener(new InfiniteScrollListener(pageCurrentIndex, linearLayoutManager) { + + @Override + public void onScrolledToEnd(int firstVisibleItemPosition) { + + pageCurrentIndex++; + loadData(true, Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString()); + + } + }); + + viewBinding.pullToRefresh.setOnRefreshListener(() -> { + + pageCurrentIndex = 1; + apiCallCurrentValue = 10; + loadData(false, Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString()); + + }); + + loadData(false, ""); + + return viewBinding.getRoot(); + } + + private void loadData(boolean append, String searchKeyword) { + + viewBinding.noData.setVisibility(View.GONE); + + int apiCallDefaultLimit = 10; + if(apiCallDefaultLimit > apiCallCurrentValue) { + return; + } + + if(pageCurrentIndex == 1 || !append) { + + dataList.clear(); + adapter.notifyDataSetChanged(); + viewBinding.pullToRefresh.setRefreshing(false); + viewBinding.progressBar.setVisibility(View.VISIBLE); + } + else { + + viewBinding.loadingMoreView.setVisibility(View.VISIBLE); + } + + Call> call = RetrofitClient.getInstance(instanceUrl, getContext()).getApiInterface().queryIssues( + Authorization.returnAuthentication(getContext(), loginUid, instanceToken), searchKeyword, type, state, pageCurrentIndex); + + call.enqueue(new Callback>() { + + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + + if(response.code() == 200) { + + assert response.body() != null; + apiCallCurrentValue = response.body().size(); + + Log.e("searchIssues", String.valueOf(apiCallCurrentValue)); + + if(!append) { + + dataList.clear(); + } + + dataList.addAll(response.body()); + adapter.notifyDataSetChanged(); + + } + else { + + dataList.clear(); + adapter.notifyDataChanged(); + viewBinding.noData.setVisibility(View.VISIBLE); + + } + + onCleanup(); + + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + + Log.e("onFailure", Objects.requireNonNull(t.getMessage())); + onCleanup(); + + } + + private void onCleanup() { + + AppUtil.setMultiVisibility(View.GONE, viewBinding.loadingMoreView, viewBinding.progressBar); + + if(dataList.isEmpty()) { + + viewBinding.noData.setVisibility(View.VISIBLE); + } + } + }); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/ClickListener.java b/app/src/main/java/org/mian/gitnex/helpers/ClickListener.java index 8c950a95..78a298ff 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ClickListener.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ClickListener.java @@ -3,7 +3,6 @@ package org.mian.gitnex.helpers; import android.content.Context; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import org.mian.gitnex.R; @@ -18,17 +17,16 @@ public class ClickListener implements View.OnClickListener { private Context mCtx; public ClickListener(String infoText, Context mCtx) { + this.infoText = infoText; this.mCtx = mCtx; } @Override - public void onClick(View v) - { + public void onClick(View v) { LayoutInflater inflater1 = LayoutInflater.from(mCtx); - View layout = inflater1.inflate(R.layout.custom_toast_success, - (ViewGroup) v.findViewById(R.id.custom_toast_container)); + View layout = inflater1.inflate(R.layout.custom_toast_info, v.findViewById(R.id.custom_toast_container)); TextView text = layout.findViewById(R.id.toastText); text.setText(infoText); diff --git a/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java b/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java index 379cbf8f..78acb25e 100644 --- a/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java +++ b/app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java @@ -268,6 +268,9 @@ public interface ApiInterface { @GET("repos/search") // get all the repos which match the query string Call queryRepos(@Header("Authorization") String token, @Query("q") String searchKeyword, @Query("private") Boolean repoTypeInclude, @Query("sort") String sort, @Query("order") String order, @Query("topic") boolean topic, @Query("includeDesc") boolean includeDesc, @Query("template") boolean template, @Query("archived") boolean archived, @Query("is_private") boolean is_private, @Query("limit") int limit); + @GET("repos/issues/search") // get all the issues which match the query string + Call> queryIssues(@Header("Authorization") String token, @Query("q") String searchKeyword, @Query("type") String type, @Query("state") String state, @Query("page") int page); + @POST("repos/{owner}/{repo}/contents/{file}") // create new file Call createNewFile(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("file") String fileName, @Body NewFile jsonStr); diff --git a/app/src/main/java/org/mian/gitnex/models/Issues.java b/app/src/main/java/org/mian/gitnex/models/Issues.java index 940592a8..b5269176 100644 --- a/app/src/main/java/org/mian/gitnex/models/Issues.java +++ b/app/src/main/java/org/mian/gitnex/models/Issues.java @@ -27,6 +27,7 @@ public class Issues { private pullRequestObject pull_request; private milestoneObject milestone; private List assignees; + private repositoryObject repository; public Issues(String body) { this.body = body; @@ -193,6 +194,35 @@ public class Issues { } } + public static class repositoryObject { + + private int id; + private String full_name; + private String name; + private String owner; + + public int getId() { + + return id; + } + + public String getFull_name() { + + return full_name; + } + + public String getName() { + + return name; + } + + public String getOwner() { + + return owner; + } + + } + public int getId() { return id; } @@ -267,4 +297,9 @@ public class Issues { this.html_url = html_url; } + public repositoryObject getRepository() { + + return repository; + } + } diff --git a/app/src/main/res/layout/fragment_explore.xml b/app/src/main/res/layout/fragment_explore.xml new file mode 100644 index 00000000..652f3dad --- /dev/null +++ b/app/src/main/res/layout/fragment_explore.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_search_issues.xml b/app/src/main/res/layout/fragment_search_issues.xml new file mode 100644 index 00000000..9da9eda8 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_issues.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_issues.xml b/app/src/main/res/layout/list_issues.xml index 6cb864ac..ce2d427d 100644 --- a/app/src/main/res/layout/list_issues.xml +++ b/app/src/main/res/layout/list_issues.xml @@ -12,6 +12,12 @@ android:layout_height="wrap_content" android:visibility="invisible" /> + + Logout Explore Administration + Search Issues