From c571e6cb95ec667e39f55224550481dbfafe3e8c Mon Sep 17 00:00:00 2001 From: qwerty287 Date: Sun, 8 May 2022 20:06:41 +0200 Subject: [PATCH] Add support for unadopted repo management (#1125) Support unadopted repos and allow to adopt and delete them. Only usable for admins. Co-authored-by: qwerty287 Co-authored-by: M M Arif Co-authored-by: M M Arif Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1125 Reviewed-by: M M Arif Co-authored-by: qwerty287 Co-committed-by: qwerty287 --- app/src/main/AndroidManifest.xml | 3 + .../activities/AdminCronTasksActivity.java | 20 +- .../AdminUnadoptedReposActivity.java | 115 ++++++++++ .../adapters/AdminUnadoptedReposAdapter.java | 201 ++++++++++++++++++ .../mian/gitnex/adapters/LabelsAdapter.java | 2 +- .../UserSearchForTeamMemberAdapter.java | 2 - .../fragments/AdministrationFragment.java | 12 +- .../gitnex/fragments/ReleasesFragment.java | 6 +- .../java/org/mian/gitnex/helpers/TinyDB.java | 3 - .../AdminUnadoptedReposViewModel.java | 78 +++++++ .../res/layout/activity_admin_cron_tasks.xml | 19 +- .../res/layout/fragment_administration.xml | 149 +++++++++++-- .../res/layout/list_admin_unadopted_repos.xml | 17 ++ app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/themes.xml | 2 + 15 files changed, 583 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/activities/AdminUnadoptedReposActivity.java create mode 100644 app/src/main/java/org/mian/gitnex/adapters/AdminUnadoptedReposAdapter.java create mode 100644 app/src/main/java/org/mian/gitnex/viewmodels/AdminUnadoptedReposViewModel.java create mode 100644 app/src/main/res/layout/list_admin_unadopted_repos.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ac021f42..f3b4b077 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,6 +40,9 @@ + diff --git a/app/src/main/java/org/mian/gitnex/activities/AdminCronTasksActivity.java b/app/src/main/java/org/mian/gitnex/activities/AdminCronTasksActivity.java index 267ab464..ecbdfa65 100644 --- a/app/src/main/java/org/mian/gitnex/activities/AdminCronTasksActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/AdminCronTasksActivity.java @@ -11,6 +11,7 @@ import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import org.mian.gitnex.adapters.AdminCronTasksAdapter; import org.mian.gitnex.databinding.ActivityAdminCronTasksBinding; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.viewmodels.AdminCronTasksViewModel; /** @@ -25,8 +26,8 @@ public class AdminCronTasksActivity extends BaseActivity { private ActivityAdminCronTasksBinding activityAdminCronTasksBinding; - public static final int PAGE = 1; - public static final int LIMIT = 50; + private final int PAGE = 1; + private int resultLimit; @Override public void onCreate(Bundle savedInstanceState) { @@ -37,6 +38,7 @@ public class AdminCronTasksActivity extends BaseActivity { setContentView(activityAdminCronTasksBinding.getRoot()); adminCronTasksViewModel = new ViewModelProvider(this).get(AdminCronTasksViewModel.class); + resultLimit = Constants.getCurrentResultLimit(ctx); initCloseListener(); activityAdminCronTasksBinding.close.setOnClickListener(onClickListener); @@ -52,8 +54,9 @@ public class AdminCronTasksActivity extends BaseActivity { activityAdminCronTasksBinding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { + activityAdminCronTasksBinding.progressBar.setVisibility(View.VISIBLE); activityAdminCronTasksBinding.pullToRefresh.setRefreshing(false); - adminCronTasksViewModel.loadCronTasksList(ctx, PAGE, LIMIT); + adminCronTasksViewModel.loadCronTasksList(ctx, PAGE, resultLimit); }, 500)); @@ -62,26 +65,19 @@ public class AdminCronTasksActivity extends BaseActivity { private void fetchDataAsync(Context ctx) { - AdminCronTasksViewModel cronTasksViewModel = new ViewModelProvider(this).get(AdminCronTasksViewModel.class); - - cronTasksViewModel.getCronTasksList(ctx, PAGE, LIMIT).observe(this, cronTasksListMain -> { + adminCronTasksViewModel.getCronTasksList(ctx, PAGE, resultLimit).observe(this, cronTasksListMain -> { adapter = new AdminCronTasksAdapter(cronTasksListMain); if(adapter.getItemCount() > 0) { - - activityAdminCronTasksBinding.recyclerView.setVisibility(View.VISIBLE); activityAdminCronTasksBinding.recyclerView.setAdapter(adapter); activityAdminCronTasksBinding.noData.setVisibility(View.GONE); + activityAdminCronTasksBinding.progressBar.setVisibility(View.GONE); } else { - - activityAdminCronTasksBinding.recyclerView.setVisibility(View.GONE); activityAdminCronTasksBinding.noData.setVisibility(View.VISIBLE); } - }); - } private void initCloseListener() { diff --git a/app/src/main/java/org/mian/gitnex/activities/AdminUnadoptedReposActivity.java b/app/src/main/java/org/mian/gitnex/activities/AdminUnadoptedReposActivity.java new file mode 100644 index 00000000..b297ef2c --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/activities/AdminUnadoptedReposActivity.java @@ -0,0 +1,115 @@ +package org.mian.gitnex.activities; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import androidx.appcompat.widget.Toolbar; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import org.mian.gitnex.R; +import org.mian.gitnex.adapters.AdminUnadoptedReposAdapter; +import org.mian.gitnex.databinding.ActivityAdminCronTasksBinding; +import org.mian.gitnex.helpers.Constants; +import org.mian.gitnex.viewmodels.AdminUnadoptedReposViewModel; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author M M Arif + * @author qwerty287 + */ + +public class AdminUnadoptedReposActivity extends BaseActivity { + + private AdminUnadoptedReposViewModel viewModel; + private View.OnClickListener onClickListener; + private AdminUnadoptedReposAdapter adapter; + + private ActivityAdminCronTasksBinding binding; + + private int PAGE = 1; + private int resultLimit; + private boolean reload = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + binding = ActivityAdminCronTasksBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + viewModel = new ViewModelProvider(this).get(AdminUnadoptedReposViewModel.class); + + resultLimit = Constants.getCurrentResultLimit(ctx); + initCloseListener(); + binding.close.setOnClickListener(onClickListener); + + Toolbar toolbar = binding.toolbar; + setSupportActionBar(toolbar); + + binding.toolbarTitle.setText(R.string.unadoptedRepos); + + binding.recyclerView.setHasFixedSize(true); + binding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(binding.recyclerView.getContext(), + DividerItemDecoration.VERTICAL); + binding.recyclerView.addItemDecoration(dividerItemDecoration); + + binding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { + + binding.pullToRefresh.setRefreshing(false); + PAGE = 1; + binding.progressBar.setVisibility(View.VISIBLE); + reload = true; + viewModel.loadRepos(ctx, PAGE, resultLimit, null); + + }, 500)); + + adapter = new AdminUnadoptedReposAdapter(new ArrayList<>(), () -> { + PAGE = 1; + binding.progressBar.setVisibility(View.VISIBLE); + reload = true; + viewModel.loadRepos(ctx, PAGE, resultLimit, null); + }, () -> { + PAGE += 1; + binding.progressBar.setVisibility(View.VISIBLE); + viewModel.loadRepos(ctx, PAGE, resultLimit, null); + }, binding); + + binding.recyclerView.setAdapter(adapter); + + fetchDataAsync(ctx); + } + + private void fetchDataAsync(Context ctx) { + + AtomicInteger prevSize = new AtomicInteger(); + + viewModel.getUnadoptedRepos(ctx, PAGE, resultLimit, null).observe(this, list -> { + + binding.progressBar.setVisibility(View.GONE); + + boolean hasMore = reload || list.size() > prevSize.get(); + reload = false; + + prevSize.set(list.size()); + + if(list.size() > 0) { + adapter.updateList(list); + adapter.setHasMore(hasMore); + binding.noData.setVisibility(View.GONE); + } + else { + binding.noData.setVisibility(View.VISIBLE); + } + }); + } + + private void initCloseListener() { + onClickListener = view -> finish(); + } +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/AdminUnadoptedReposAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AdminUnadoptedReposAdapter.java new file mode 100644 index 00000000..060b7b2e --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/AdminUnadoptedReposAdapter.java @@ -0,0 +1,201 @@ +package org.mian.gitnex.adapters; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.RecyclerView; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.ActivityAdminCronTasksBinding; +import org.mian.gitnex.helpers.AlertDialogs; +import org.mian.gitnex.helpers.Toasty; +import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; + +/** + * @author M M Arif + * @author qwerty287 + */ + +public class AdminUnadoptedReposAdapter extends RecyclerView.Adapter { + + private List repos; + private final Runnable updateList; + private final Runnable loadMoreListener; + private boolean isLoading = false, hasMore = true; + private final ActivityAdminCronTasksBinding activityAdminCronTasksBinding; + + class UnadoptedViewHolder extends RecyclerView.ViewHolder { + + private String repoName; + private final TextView name; + + private UnadoptedViewHolder(View itemView) { + + super(itemView); + Context ctx = itemView.getContext(); + + name = itemView.findViewById(R.id.repo_name); + + itemView.setOnClickListener(taskInfo -> { + String[] repoSplit = repoName.split("/"); + new AlertDialog.Builder(ctx) + .setTitle(repoName).setMessage(ctx.getString(R.string.unadoptedReposMessage, repoSplit[1], repoSplit[0])) + .setNeutralButton(R.string.close, null) + .setPositiveButton(R.string.menuDeleteText, ((dialog, which) -> delete(ctx, repoName))) + .setNegativeButton(R.string.adoptRepo, ((dialog, which) -> adopt(ctx, repoName, getBindingAdapterPosition()))).show(); + }); + } + } + + public AdminUnadoptedReposAdapter(List list, Runnable updateList, Runnable loadMore, ActivityAdminCronTasksBinding activityAdminCronTasksBinding) { + this.repos = list; + this.updateList = updateList; + this.loadMoreListener = loadMore; + this.activityAdminCronTasksBinding = activityAdminCronTasksBinding; + } + + @NonNull + @Override + public UnadoptedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_admin_unadopted_repos, parent, false); + return new UnadoptedViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull UnadoptedViewHolder holder, int position) { + if(position >= getItemCount() - 1 && hasMore && !isLoading && loadMoreListener != null) { + isLoading = true; + loadMoreListener.run(); + } + + String currentItem = repos.get(position); + + holder.repoName = currentItem; + holder.name.setText(currentItem); + } + + private void updateAdapter(int position) { + repos.remove(position); + notifyItemRemoved(position); + notifyItemRangeChanged(position, repos.size()); + } + + private void delete(final Context ctx, final String name) { + + String[] repoSplit = name.split("/"); + + Call call = RetrofitClient + .getApiInterface(ctx) + .adminDeleteUnadoptedRepository(repoSplit[0], repoSplit[1]); + + call.enqueue(new Callback<>() { + + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + + switch(response.code()) { + + case 204: + updateList.run(); + Toasty.success(ctx, ctx.getString(R.string.repoDeletionSuccess)); + break; + + case 401: + AlertDialogs.authorizationTokenRevokedDialog(ctx); + break; + + case 403: + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + break; + + case 404: + Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + break; + + default: + Toasty.error(ctx, ctx.getString(R.string.genericError)); + + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } + + private void adopt(final Context ctx, final String name, int position) { + + String[] repoSplit = name.split("/"); + + Call call = RetrofitClient + .getApiInterface(ctx) + .adminAdoptRepository(repoSplit[0], repoSplit[1]); + + call.enqueue(new Callback<>() { + + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + + switch(response.code()) { + + case 204: + updateAdapter(position); + if(getItemCount() == 0) { + activityAdminCronTasksBinding.noData.setVisibility(View.VISIBLE); + } + Toasty.success(ctx, ctx.getString(R.string.repoAdopted, name)); + break; + + case 401: + AlertDialogs.authorizationTokenRevokedDialog(ctx); + break; + + case 403: + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + break; + + case 404: + Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + break; + + default: + Toasty.error(ctx, ctx.getString(R.string.genericError)); + + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } + + @Override + public int getItemCount() { + return repos.size(); + } + + @SuppressLint("NotifyDataSetChanged") + public void updateList(List list) { + this.repos = list; + notifyDataSetChanged(); + } + + public void setHasMore(boolean hasMore) { + this.hasMore = hasMore; + isLoading = false; + } +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java index e6c68030..5cc1defb 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java @@ -52,7 +52,7 @@ public class LabelsAdapter extends RecyclerView.Adapter { diff --git a/app/src/main/java/org/mian/gitnex/adapters/UserSearchForTeamMemberAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/UserSearchForTeamMemberAdapter.java index f89ec84e..c93b694e 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/UserSearchForTeamMemberAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/UserSearchForTeamMemberAdapter.java @@ -9,11 +9,9 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; import org.gitnex.tea4j.v2.models.User; import org.mian.gitnex.R; -import org.mian.gitnex.actions.TeamActions; import org.mian.gitnex.activities.BaseActivity; import org.mian.gitnex.activities.ProfileActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/fragments/AdministrationFragment.java b/app/src/main/java/org/mian/gitnex/fragments/AdministrationFragment.java index 0cd5a943..213249f8 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/AdministrationFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/AdministrationFragment.java @@ -1,6 +1,5 @@ package org.mian.gitnex.fragments; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; @@ -11,29 +10,30 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import org.mian.gitnex.activities.AdminCronTasksActivity; import org.mian.gitnex.activities.AdminGetUsersActivity; +import org.mian.gitnex.activities.AdminUnadoptedReposActivity; import org.mian.gitnex.activities.BaseActivity; import org.mian.gitnex.databinding.FragmentAdministrationBinding; /** - * Author M M Arif + * @author M M Arif */ public class AdministrationFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - Context ctx = getContext(); FragmentAdministrationBinding fragmentAdministrationBinding = FragmentAdministrationBinding.inflate(inflater, container, false); - fragmentAdministrationBinding.adminUsers.setOnClickListener(v1 -> startActivity(new Intent(getContext(), AdminGetUsersActivity.class))); + fragmentAdministrationBinding.systemUsersFrame.setOnClickListener(v1 -> startActivity(new Intent(getContext(), AdminGetUsersActivity.class))); // if gitea version is greater/equal(1.13.0) than user installed version (installed.higherOrEqual(compareVer)) if(((BaseActivity) requireActivity()).getAccount().requiresVersion("1.13.0")) { - fragmentAdministrationBinding.adminCron.setVisibility(View.VISIBLE); + fragmentAdministrationBinding.adminCronFrame.setVisibility(View.VISIBLE); } - fragmentAdministrationBinding.adminCron.setOnClickListener(v1 -> startActivity(new Intent(getContext(), AdminCronTasksActivity.class))); + fragmentAdministrationBinding.adminCronFrame.setOnClickListener(v1 -> startActivity(new Intent(getContext(), AdminCronTasksActivity.class))); + fragmentAdministrationBinding.unadoptedReposFrame.setOnClickListener(v1 -> startActivity(new Intent(getContext(), AdminUnadoptedReposActivity.class))); String action = requireActivity().getIntent().getStringExtra("giteaAdminAction"); if(action != null) { diff --git a/app/src/main/java/org/mian/gitnex/fragments/ReleasesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ReleasesFragment.java index 5339382a..1f60cdd6 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ReleasesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ReleasesFragment.java @@ -45,7 +45,11 @@ import java.util.Objects; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; -import okhttp3.*; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; /** * @author M M Arif diff --git a/app/src/main/java/org/mian/gitnex/helpers/TinyDB.java b/app/src/main/java/org/mian/gitnex/helpers/TinyDB.java index 8d01a05a..c5e52340 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/TinyDB.java +++ b/app/src/main/java/org/mian/gitnex/helpers/TinyDB.java @@ -5,12 +5,9 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.os.Environment; -import android.text.TextUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Map; public class TinyDB { diff --git a/app/src/main/java/org/mian/gitnex/viewmodels/AdminUnadoptedReposViewModel.java b/app/src/main/java/org/mian/gitnex/viewmodels/AdminUnadoptedReposViewModel.java new file mode 100644 index 00000000..fa9d434d --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/viewmodels/AdminUnadoptedReposViewModel.java @@ -0,0 +1,78 @@ +package org.mian.gitnex.viewmodels; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.helpers.AlertDialogs; +import org.mian.gitnex.helpers.Toasty; +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 + * @author qwerty287 + */ + +public class AdminUnadoptedReposViewModel extends ViewModel { + + private MutableLiveData> tasksList; + + public LiveData> getUnadoptedRepos(Context ctx, int page, int limit, String query) { + + tasksList = new MutableLiveData<>(); + loadRepos(ctx, page, limit, query); + + return tasksList; + } + + public void loadRepos(final Context ctx, final int page, int limit, String query) { + + Call> call = RetrofitClient + .getApiInterface(ctx) + .adminUnadoptedList(page, limit, query); + + call.enqueue(new Callback<>() { + + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + + if(response.isSuccessful()) { + if(page <= 1 || tasksList.getValue() == null) { + tasksList.postValue(response.body()); + } else { + List repos = new ArrayList<>(Objects.requireNonNull(tasksList.getValue())); + assert response.body() != null; + repos.addAll(response.body()); + tasksList.postValue(repos); + } + } + else if(response.code() == 401) { + AlertDialogs.authorizationTokenRevokedDialog(ctx); + } + else if(response.code() == 403) { + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + } + else if(response.code() == 404) { + Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + } + else { + Toasty.error(ctx, ctx.getString(R.string.genericError)); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } +} diff --git a/app/src/main/res/layout/activity_admin_cron_tasks.xml b/app/src/main/res/layout/activity_admin_cron_tasks.xml index a5592d87..92f67c41 100644 --- a/app/src/main/res/layout/activity_admin_cron_tasks.xml +++ b/app/src/main/res/layout/activity_admin_cron_tasks.xml @@ -1,5 +1,5 @@ - + + + android:layout_height="wrap_content" + android:layout_below="@+id/progress_bar"> + android:visibility="gone" /> - + diff --git a/app/src/main/res/layout/fragment_administration.xml b/app/src/main/res/layout/fragment_administration.xml index d7f651aa..d3806d3c 100644 --- a/app/src/main/res/layout/fragment_administration.xml +++ b/app/src/main/res/layout/fragment_administration.xml @@ -11,21 +11,51 @@ + android:layout_height="wrap_content" + android:orientation="vertical"> - + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="16dp"> + + + + + + + + + + + + - + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="16dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_admin_unadopted_repos.xml b/app/src/main/res/layout/list_admin_unadopted_repos.xml new file mode 100644 index 00000000..2834afad --- /dev/null +++ b/app/src/main/res/layout/list_admin_unadopted_repos.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c404cd97..bfc70ca6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -762,4 +762,8 @@ Follow system (Light/Dark) Follow system (Light/Pitch Black) Fork of: %s + Adopt + Adopted repository %s + Unadopted Repositories + - Adopt will add repository %s to organization/user %s.\n- Delete will remove it from the system. diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 672454ac..69007921 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -247,6 +247,7 @@