Migrated Profile Package from Butterknife to View Binding (#5591)

* Butterknife to ViewBinding
* code fix to pass all tests
* code cleanup and tests migrated to binding
* fix LoD
This commit is contained in:
Shashank Kumar 2024-03-11 01:14:06 +05:30 committed by GitHub
parent dbe739e755
commit 6ed5deac65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 78 additions and 100 deletions

View file

@ -131,7 +131,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
// If fragment is associated with ProfileActivity, then hide the tabLayout // If fragment is associated with ProfileActivity, then hide the tabLayout
if (getActivity() instanceof ProfileActivity) { if (getActivity() instanceof ProfileActivity) {
((ProfileActivity)getActivity()).tabLayout.setVisibility(View.GONE); ((ProfileActivity)getActivity()).setTabLayoutVisibility(false);
} }
// Else if fragment is associated with MainActivity then hide that tab layout // Else if fragment is associated with MainActivity then hide that tab layout

View file

@ -13,19 +13,16 @@ import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.ViewPagerAdapter;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.ContributionsFragment; import fr.free.nrw.commons.contributions.ContributionsFragment;
import fr.free.nrw.commons.explore.ParentViewPager; import fr.free.nrw.commons.databinding.ActivityProfileBinding;
import fr.free.nrw.commons.profile.achievements.AchievementsFragment; import fr.free.nrw.commons.profile.achievements.AchievementsFragment;
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment; import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment;
import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.theme.BaseActivity;
@ -45,14 +42,7 @@ public class ProfileActivity extends BaseActivity {
private FragmentManager supportFragmentManager; private FragmentManager supportFragmentManager;
@BindView(R.id.viewPager) public ActivityProfileBinding binding;
ParentViewPager viewPager;
@BindView(R.id.tab_layout)
public TabLayout tabLayout;
@BindView(R.id.toolbar)
Toolbar toolbar;
@Inject @Inject
SessionManager sessionManager; SessionManager sessionManager;
@ -70,7 +60,7 @@ public class ProfileActivity extends BaseActivity {
ContributionsFragment contributionsFragment; ContributionsFragment contributionsFragment;
public void setScroll(boolean canScroll){ public void setScroll(boolean canScroll){
viewPager.setCanScroll(canScroll); binding.viewPager.setCanScroll(canScroll);
} }
@Override @Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) { protected void onRestoreInstanceState(final Bundle savedInstanceState) {
@ -85,11 +75,13 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
ButterKnife.bind(this);
setSupportActionBar(toolbar); binding = ActivityProfileBinding.inflate(getLayoutInflater());
toolbar.setNavigationOnClickListener(view -> { setContentView(binding.getRoot());
setSupportActionBar(binding.toolbarBinding.toolbar);
binding.toolbarBinding.toolbar.setNavigationOnClickListener(view -> {
onSupportNavigateUp(); onSupportNavigateUp();
}); });
@ -99,8 +91,8 @@ public class ProfileActivity extends BaseActivity {
supportFragmentManager = getSupportFragmentManager(); supportFragmentManager = getSupportFragmentManager();
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(viewPagerAdapter); binding.viewPager.setAdapter(viewPagerAdapter);
tabLayout.setupWithViewPager(viewPager); binding.tabLayout.setupWithViewPager(binding.viewPager);
setTabs(); setTabs();
} }
@ -257,9 +249,17 @@ public class ProfileActivity extends BaseActivity {
// Checking if MediaDetailPagerFragment is visible, If visible then show ContributionListFragment else close the ProfileActivity // Checking if MediaDetailPagerFragment is visible, If visible then show ContributionListFragment else close the ProfileActivity
if(contributionsFragment != null && contributionsFragment.getMediaDetailPagerFragment() != null && contributionsFragment.getMediaDetailPagerFragment().isVisible()) { if(contributionsFragment != null && contributionsFragment.getMediaDetailPagerFragment() != null && contributionsFragment.getMediaDetailPagerFragment().isVisible()) {
contributionsFragment.backButtonClicked(); contributionsFragment.backButtonClicked();
tabLayout.setVisibility(View.VISIBLE); binding.tabLayout.setVisibility(View.VISIBLE);
}else { }else {
super.onBackPressed(); super.onBackPressed();
} }
} }
/**
* To set the visibility of tab layout
* @param isVisible boolean
*/
public void setTabLayoutVisibility(boolean isVisible) {
binding.tabLayout.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
} }

View file

@ -14,19 +14,14 @@ import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.MergeAdapter; import androidx.recyclerview.widget.MergeAdapter;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.databinding.FragmentLeaderboardBinding;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.profile.ProfileActivity;
@ -44,20 +39,6 @@ import timber.log.Timber;
*/ */
public class LeaderboardFragment extends CommonsDaggerSupportFragment { public class LeaderboardFragment extends CommonsDaggerSupportFragment {
@BindView(R.id.leaderboard_list)
RecyclerView leaderboardListRecyclerView;
@BindView(R.id.progressBar)
ProgressBar progressBar;
@BindView(R.id.category_spinner)
Spinner categorySpinner;
@BindView(R.id.duration_spinner)
Spinner durationSpinner;
@BindView(R.id.scroll)
Button scrollButton;
@Inject @Inject
SessionManager sessionManager; SessionManager sessionManager;
@ -110,6 +91,8 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
private String userName; private String userName;
private FragmentLeaderboardBinding binding;
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -120,19 +103,18 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false); binding = FragmentLeaderboardBinding.inflate(inflater, container, false);
ButterKnife.bind(this, rootView);
hideLayouts(); hideLayouts();
// Leaderboard currently unimplemented in Beta flavor. Skip all API calls and disable menu // Leaderboard currently unimplemented in Beta flavor. Skip all API calls and disable menu
if(ConfigUtils.isBetaFlavour()) { if(ConfigUtils.isBetaFlavour()) {
progressBar.setVisibility(View.GONE); binding.progressBar.setVisibility(View.GONE);
scrollButton.setVisibility(View.GONE); binding.scroll.setVisibility(View.GONE);
return rootView; return binding.getRoot();
} }
progressBar.setVisibility(View.VISIBLE); binding.progressBar.setVisibility(View.VISIBLE);
setSpinners(); setSpinners();
/** /**
@ -152,11 +134,11 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
setLeaderboard(duration, category, limit, offset); setLeaderboard(duration, category, limit, offset);
durationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { binding.durationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
duration = durationValues[durationSpinner.getSelectedItemPosition()]; duration = durationValues[binding.durationSpinner.getSelectedItemPosition()];
refreshLeaderboard(); refreshLeaderboard();
} }
@ -165,10 +147,10 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
} }
}); });
categorySpinner.setOnItemSelectedListener(new OnItemSelectedListener() { binding.categorySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
category = categoryValues[categorySpinner.getSelectedItemPosition()]; category = categoryValues[binding.categorySpinner.getSelectedItemPosition()];
refreshLeaderboard(); refreshLeaderboard();
} }
@ -178,10 +160,10 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
}); });
scrollButton.setOnClickListener(view -> scrollToUserRank()); binding.scroll.setOnClickListener(view -> scrollToUserRank());
return rootView; return binding.getRoot();
} }
@Override @Override
@ -226,9 +208,12 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
if(userRank==0){ if(userRank==0){
Toast.makeText(getContext(),R.string.no_achievements_yet,Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(),R.string.no_achievements_yet,Toast.LENGTH_SHORT).show();
}else { }else {
if (Objects.requireNonNull(leaderboardListRecyclerView.getAdapter()).getItemCount() if (binding == null) {
return;
}
if (Objects.requireNonNull(binding.leaderboardList.getAdapter()).getItemCount()
> userRank + 1) { > userRank + 1) {
leaderboardListRecyclerView.smoothScrollToPosition(userRank + 1); binding.leaderboardList.smoothScrollToPosition(userRank + 1);
} else { } else {
if (viewModel != null) { if (viewModel != null) {
viewModel.refresh(duration, category, userRank + 1, 0); viewModel.refresh(duration, category, userRank + 1, 0);
@ -247,12 +232,12 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
ArrayAdapter<CharSequence> categoryAdapter = ArrayAdapter.createFromResource(getContext(), ArrayAdapter<CharSequence> categoryAdapter = ArrayAdapter.createFromResource(getContext(),
R.array.leaderboard_categories, android.R.layout.simple_spinner_item); R.array.leaderboard_categories, android.R.layout.simple_spinner_item);
categoryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); categoryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
categorySpinner.setAdapter(categoryAdapter); binding.categorySpinner.setAdapter(categoryAdapter);
ArrayAdapter<CharSequence> durationAdapter = ArrayAdapter.createFromResource(getContext(), ArrayAdapter<CharSequence> durationAdapter = ArrayAdapter.createFromResource(getContext(),
R.array.leaderboard_durations, android.R.layout.simple_spinner_item); R.array.leaderboard_durations, android.R.layout.simple_spinner_item);
durationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); durationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
durationSpinner.setAdapter(durationAdapter); binding.durationSpinner.setAdapter(durationAdapter);
} }
/** /**
@ -297,8 +282,8 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
UserDetailAdapter userDetailAdapter= new UserDetailAdapter(response); UserDetailAdapter userDetailAdapter= new UserDetailAdapter(response);
MergeAdapter mergeAdapter = new MergeAdapter(userDetailAdapter, leaderboardListAdapter); MergeAdapter mergeAdapter = new MergeAdapter(userDetailAdapter, leaderboardListAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
leaderboardListRecyclerView.setLayoutManager(linearLayoutManager); binding.leaderboardList.setLayoutManager(linearLayoutManager);
leaderboardListRecyclerView.setAdapter(mergeAdapter); binding.leaderboardList.setAdapter(mergeAdapter);
viewModel.getListLiveData().observe(getViewLifecycleOwner(), leaderboardListAdapter::submitList); viewModel.getListLiveData().observe(getViewLifecycleOwner(), leaderboardListAdapter::submitList);
viewModel.getProgressLoadStatus().observe(getViewLifecycleOwner(), status -> { viewModel.getProgressLoadStatus().observe(getViewLifecycleOwner(), status -> {
if (Objects.requireNonNull(status).equalsIgnoreCase(LOADING)) { if (Objects.requireNonNull(status).equalsIgnoreCase(LOADING)) {
@ -306,7 +291,7 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
} else if (status.equalsIgnoreCase(LOADED)) { } else if (status.equalsIgnoreCase(LOADED)) {
hideProgressBar(); hideProgressBar();
if (scrollToRank) { if (scrollToRank) {
leaderboardListRecyclerView.smoothScrollToPosition(userRank + 1); binding.leaderboardList.smoothScrollToPosition(userRank + 1);
} }
} }
}); });
@ -316,12 +301,12 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
* to hide progressbar * to hide progressbar
*/ */
private void hideProgressBar() { private void hideProgressBar() {
if (progressBar != null) { if (binding != null) {
progressBar.setVisibility(View.GONE); binding.progressBar.setVisibility(View.GONE);
categorySpinner.setVisibility(View.VISIBLE); binding.categorySpinner.setVisibility(View.VISIBLE);
durationSpinner.setVisibility(View.VISIBLE); binding.durationSpinner.setVisibility(View.VISIBLE);
scrollButton.setVisibility(View.VISIBLE); binding.scroll.setVisibility(View.VISIBLE);
leaderboardListRecyclerView.setVisibility(View.VISIBLE); binding.leaderboardList.setVisibility(View.VISIBLE);
} }
} }
@ -329,19 +314,19 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
* to show progressbar * to show progressbar
*/ */
private void showProgressBar() { private void showProgressBar() {
if (progressBar != null) { if (binding != null) {
progressBar.setVisibility(View.VISIBLE); binding.progressBar.setVisibility(View.VISIBLE);
binding.scroll.setVisibility(View.INVISIBLE);
} }
scrollButton.setVisibility(View.INVISIBLE);
} }
/** /**
* used to hide the layouts while fetching results from api * used to hide the layouts while fetching results from api
*/ */
private void hideLayouts(){ private void hideLayouts(){
categorySpinner.setVisibility(View.INVISIBLE); binding.categorySpinner.setVisibility(View.INVISIBLE);
durationSpinner.setVisibility(View.INVISIBLE); binding.durationSpinner.setVisibility(View.INVISIBLE);
leaderboardListRecyclerView.setVisibility(View.INVISIBLE); binding.leaderboardList.setVisibility(View.INVISIBLE);
} }
/** /**
@ -364,7 +349,15 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
*/ */
private void onError() { private void onError() {
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.error_occurred)); ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.error_occurred));
progressBar.setVisibility(View.GONE); if (binding!=null) {
binding.progressBar.setVisibility(View.GONE);
}
} }
@Override
public void onDestroy() {
super.onDestroy();
compositeDisposable.clear();
binding = null;
}
} }

View file

@ -11,6 +11,7 @@
android:orientation="vertical"> android:orientation="vertical">
<include <include
android:id="@+id/toolbarBinding"
layout="@layout/toolbar" layout="@layout/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View file

@ -17,6 +17,7 @@ import fr.free.nrw.commons.location.LocationServiceManager
class TestCommonsApplication : Application() { class TestCommonsApplication : Application() {
private var mockApplicationComponent: CommonsApplicationComponent? = null private var mockApplicationComponent: CommonsApplicationComponent? = null
override fun onCreate() { override fun onCreate() {
if (mockApplicationComponent == null) { if (mockApplicationComponent == null) {
mockApplicationComponent = DaggerCommonsApplicationComponent.builder() mockApplicationComponent = DaggerCommonsApplicationComponent.builder()

View file

@ -18,6 +18,7 @@ import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.createTestClient
import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.databinding.FragmentLeaderboardBinding
import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.profile.ProfileActivity
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment
import fr.free.nrw.commons.profile.leaderboard.LeaderboardListAdapter import fr.free.nrw.commons.profile.leaderboard.LeaderboardListAdapter
@ -47,21 +48,12 @@ class LeaderboardFragmentUnitTests {
private lateinit var fragment: LeaderboardFragment private lateinit var fragment: LeaderboardFragment
private lateinit var fragmentManager: FragmentManager private lateinit var fragmentManager: FragmentManager
private lateinit var context: Context private lateinit var context: Context
private lateinit var view: View
private lateinit var layoutInflater: LayoutInflater private lateinit var layoutInflater: LayoutInflater
@Mock
private lateinit var progressBar: ProgressBar
@Mock
private lateinit var spinner: Spinner
@Mock @Mock
private lateinit var viewModel: LeaderboardListViewModel private lateinit var viewModel: LeaderboardListViewModel
@Mock
private lateinit var recyclerView: RecyclerView
@Mock @Mock
private lateinit var adapter: LeaderboardListAdapter private lateinit var adapter: LeaderboardListAdapter
@ -71,12 +63,11 @@ class LeaderboardFragmentUnitTests {
@Mock @Mock
private lateinit var account: Account private lateinit var account: Account
@Mock
private lateinit var button: Button
@Mock @Mock
private lateinit var parentView: ViewGroup private lateinit var parentView: ViewGroup
private lateinit var binding: FragmentLeaderboardBinding
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
@ -92,15 +83,9 @@ class LeaderboardFragmentUnitTests {
fragmentTransaction.commitNowAllowingStateLoss() fragmentTransaction.commitNowAllowingStateLoss()
layoutInflater = LayoutInflater.from(activity) layoutInflater = LayoutInflater.from(activity)
view = LayoutInflater.from(activity) binding = FragmentLeaderboardBinding.inflate(layoutInflater)
.inflate(R.layout.fragment_leaderboard, null) as View
Whitebox.setInternalState(fragment, "progressBar", progressBar)
Whitebox.setInternalState(fragment, "categorySpinner", spinner)
Whitebox.setInternalState(fragment, "durationSpinner", spinner)
Whitebox.setInternalState(fragment, "viewModel", viewModel) Whitebox.setInternalState(fragment, "viewModel", viewModel)
Whitebox.setInternalState(fragment, "scrollButton", button)
Whitebox.setInternalState(fragment, "leaderboardListRecyclerView", recyclerView)
Whitebox.setInternalState(fragment, "mView", parentView) Whitebox.setInternalState(fragment, "mView", parentView)
} }
@ -140,7 +125,6 @@ class LeaderboardFragmentUnitTests {
@Throws(Exception::class) @Throws(Exception::class)
fun testScrollToUserRankCaseNonZeroTrue() { fun testScrollToUserRankCaseNonZeroTrue() {
Whitebox.setInternalState(fragment, "userRank", 1) Whitebox.setInternalState(fragment, "userRank", 1)
`when`(recyclerView.adapter).thenReturn(adapter)
`when`(adapter.itemCount).thenReturn(3) `when`(adapter.itemCount).thenReturn(3)
val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( val method: Method = LeaderboardFragment::class.java.getDeclaredMethod(
"scrollToUserRank" "scrollToUserRank"
@ -153,7 +137,6 @@ class LeaderboardFragmentUnitTests {
@Throws(Exception::class) @Throws(Exception::class)
fun testScrollToUserRankCaseNonZeroFalse() { fun testScrollToUserRankCaseNonZeroFalse() {
Whitebox.setInternalState(fragment, "userRank", 1) Whitebox.setInternalState(fragment, "userRank", 1)
`when`(recyclerView.adapter).thenReturn(adapter)
`when`(adapter.itemCount).thenReturn(1) `when`(adapter.itemCount).thenReturn(1)
val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( val method: Method = LeaderboardFragment::class.java.getDeclaredMethod(
"scrollToUserRank" "scrollToUserRank"

View file

@ -109,7 +109,7 @@ class ProfileActivityTest {
} }
@Test @Test
fun testToolbarNotNull() { fun testToolbarNotNull() {
val toolbar = activity.findViewById<Toolbar>(R.id.toolbar) val toolbar = activity.binding.toolbarBinding.toolbar
Assert.assertNotNull(toolbar) Assert.assertNotNull(toolbar)
} }