Add contributin list fragment

This commit is contained in:
neslihanturan 2018-09-07 19:30:50 +03:00
parent 738db8306f
commit 095082248c
3 changed files with 168 additions and 251 deletions

View file

@ -22,7 +22,7 @@ import fr.free.nrw.commons.upload.UploadService;
import static android.content.ContentResolver.requestSync;
public class ContributionsActivity extends AuthenticatedActivity {
public class ContributionsActivity extends AuthenticatedActivity implements FragmentManager.OnBackStackChangedListener {
@Inject
SessionManager sessionManager;
@ -178,6 +178,11 @@ public class ContributionsActivity extends AuthenticatedActivity {
}
}
@Override
public void onBackStackChanged() {
}
public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter {
FragmentManager fragmentManager;
private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.contributions;
import android.app.FragmentManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@ -12,6 +11,7 @@ import android.graphics.PorterDuff;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
@ -30,6 +30,7 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javax.inject.Inject;
import javax.inject.Named;
@ -44,8 +45,10 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.nearby.NearbyPlaces;
import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.upload.UploadService;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
@ -75,7 +78,8 @@ public class ContributionsFragment
private Cursor allContributions;
private UploadService uploadService;
private boolean isUploadServiceConnected;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
CountDownLatch waitForContributionsListFragment = new CountDownLatch(1);
public TextView numberOfUploads;
public ProgressBar numberOfUploadsProgressBar;
@ -332,6 +336,11 @@ public class ContributionsFragment
return contributionsListFragment.getAdapter().getCount();
}
@Override
public void notifyDatasetChanged() {
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
Adapter adapter = contributionsListFragment.getAdapter();
@ -354,12 +363,41 @@ public class ContributionsFragment
@SuppressWarnings("ConstantConditions")
private void setUploadCount() {
if (getChildFragmentManager().findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG) != null) {
// Means Media Details Fragment is active
// TODO: Neslihan ContributionListViewUtils.setIndicatorVisibility(numberOfUploads, numberOfUploadsProgressBar,false, true);
} else {
// Means Contribution List Fragment is visible to user
// TODO: Neslihan ContributionListViewUtils.setIndicatorVisibility(numberOfUploads, numberOfUploadsProgressBar,false, false);
}
compositeDisposable.add(mediaWikiApi
.getUploadCount(((ContributionsActivity)getActivity()).sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::displayUploadCount,
t -> Timber.e(t, "Fetching upload count failed")
));
}
private void displayUploadCount(Integer uploadCount) {
if (getActivity().isFinishing()
|| numberOfUploads == null
|| getResources() == null) {
return;
}
numberOfUploads.setText(getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount, uploadCount));
if (getChildFragmentManager().findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG) != null) {
// Means Media Details Fragment is active
// TODO: Neslihan ContributionListViewUtils.setIndicatorVisibility(numberOfUploads, numberOfUploadsProgressBar,false, true);
} else {
// Means Contribution List Fragment is visible to user
// TODO: Neslihan ContributionListViewUtils.setIndicatorVisibility(numberOfUploads, numberOfUploadsProgressBar,true, false);
}
}
public void betaSetUploadCount(int betaUploadCount) {
@ -381,5 +419,35 @@ public class ContributionsFragment
public void updateNearbyNotification(List<NearbyPlaces> nearbyPlaces) {
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
boolean mediaDetailsVisible = mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible();
outState.putBoolean("mediaDetailsVisible", mediaDetailsVisible);
}
@Override
public void onResume() {
super.onResume();
boolean isSettingsChanged = prefs.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
prefs.edit().putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false).apply();
if (isSettingsChanged) {
refreshSource();
}
}
@Override
public void onDestroy() {
compositeDisposable.clear();
getChildFragmentManager().removeOnBackStackChangedListener(this);
super.onDestroy();
if (isUploadServiceConnected) {
if (getActivity() != null) {
getActivity().unbindService(uploadServiceConnection);
}
}
}
}

View file

@ -1,44 +1,32 @@
package fr.free.nrw.commons.contributions;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.nearby.NearbyActivity;
import timber.log.Timber;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.Activity.RESULT_OK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.View.GONE;
/**
* Created by root on 01.06.2018.
*/
public class ContributionsListFragment extends CommonsDaggerSupportFragment {
@BindView(R.id.contributionsList)
@ -47,252 +35,108 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
TextView waitingMessage;
@BindView(R.id.loadingContributionsProgressBar)
ProgressBar progressBar;
@BindView(R.id.fab_plus)
FloatingActionButton fabPlus;
@BindView(R.id.fab_camera)
FloatingActionButton fabCamera;
@BindView(R.id.fab_galery)
FloatingActionButton fabGalery;
@Inject
@Named("prefs")
SharedPreferences prefs;
@Inject
@Named("default_preferences")
SharedPreferences defaultPrefs;
private ContributionController controller;
private Animation fab_close;
private Animation fab_open;
private Animation rotate_forward;
private Animation rotate_backward;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_contributions_list, container, false);
ButterKnife.bind(this, v);
private boolean isFabOpen = false;
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
if (savedInstanceState != null) {
Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position"));
contributionsList.setSelection(savedInstanceState.getInt("grid-position"));
}
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contributions_list, container, false);
ButterKnife.bind(this, view);
//TODO: Should this be in onResume?
String lastModified = prefs.getString("lastSyncTimestamp", "");
Timber.d("Last Sync Timestamp: %s", lastModified);
if (lastModified.equals("")) {
waitingMessage.setVisibility(View.VISIBLE);
} else {
waitingMessage.setVisibility(GONE);
}
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getParentFragment());
changeProgressBarVisibility(true);
return v;
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initializeAnimations();
setListeners();
}
private void initializeAnimations() {
fab_open = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_open);
fab_close = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_close);
rotate_forward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_forward);
rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward);
}
private void setListeners() {
fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
}
private void animateFAB(boolean isFabOpen) {
this.isFabOpen = !isFabOpen;
if (fabPlus.isShown()){
if (isFabOpen) {
fabPlus.startAnimation(rotate_backward);
fabCamera.startAnimation(fab_close);
fabGalery.startAnimation(fab_close);
fabCamera.hide();
fabGalery.hide();
} else {
fabPlus.startAnimation(rotate_forward);
fabCamera.startAnimation(fab_open);
fabGalery.startAnimation(fab_open);
fabCamera.show();
fabGalery.show();
}
this.isFabOpen=!isFabOpen;
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
ContributionsFragment parentFragment = (ContributionsFragment)getParentFragment();
parentFragment.waitForContributionsListFragment.countDown();
}
/**
* Responsible to set progress bar invisible and visible
* @param isVisible True when contributions list should be hidden.
*/
public void changeProgressBarVisibility(boolean isVisible) {
this.progressBar.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
/**
* Clears sync message displayed with progress bar before contributions list became visible
*/
protected void clearSyncMessage() {
waitingMessage.setVisibility(GONE);
}
public ListAdapter getAdapter() {
return contributionsList.getAdapter();
}
/**
* Sets adapter to contributions list. If beta mode, sets upload count for beta explicitly.
* @param adapter List adapter for uploads of contributor
*/
public void setAdapter(ListAdapter adapter) {
this.contributionsList.setAdapter(adapter);
if(BuildConfig.FLAVOR.equalsIgnoreCase("beta")){
// TODO: Neslihan open later ((ContributionsActivity) getActivity()).betaSetUploadCount(adapter.getCount());
//TODO: add betaSetUploadCount method
((ContributionsFragment) getParentFragment()).betaSetUploadCount(adapter.getCount());
}
}
public void changeProgressBarVisibility(boolean isVisible) {
this.progressBar.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (outState == null) {
outState = new Bundle();
}
super.onSaveInstanceState(outState);
controller.saveState(outState);
outState.putInt("grid-position", contributionsList.getFirstVisiblePosition());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//FIXME: must get the file data for Google Photos when receive the intent answer, in the onActivityResult method
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
if (requestCode == ContributionController.SELECT_FROM_CAMERA) {
// If coming from camera, pass null as uri. Because camera photos get saved to a
// fixed directory
controller.handleImagePicked(requestCode, null, false, null);
} else {
controller.handleImagePicked(requestCode, data.getData(), false, null);
}
} else {
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_from_gallery:
//Gallery crashes before reach ShareActivity screen so must implement permissions check here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(),
READ_EXTERNAL_STORAGE)
!= PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.read_storage_permission_rationale))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
dialog.dismiss();
})
.setNegativeButton(android.R.string.cancel, null)
.create()
.show();
} else {
// No explanation needed, we can request the permission.
requestPermissions(new String[]{READ_EXTERNAL_STORAGE},
1);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
controller.startGalleryPick();
return true;
}
} else {
controller.startGalleryPick();
return true;
}
return true;
case R.id.menu_from_camera:
boolean useExtStorage = defaultPrefs.getBoolean("useExternalStorage", true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.write_storage_permission_rationale))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 3);
dialog.dismiss();
})
.setNegativeButton(android.R.string.cancel, null)
.create()
.show();
} else {
// No explanation needed, we can request the permission.
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
3);
// MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
controller.startCameraCapture();
return true;
}
} else {
controller.startCameraCapture();
return true;
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult: req code = " + " perm = "
+ Arrays.toString(permissions) + " grant =" + Arrays.toString(grantResults));
switch (requestCode) {
// 1 = Storage allowed when gallery selected
case 1: {
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Call controller.startGalleryPick()");
controller.startGalleryPick();
}
}
break;
// 2 = Location allowed when 'nearby places' selected
case 2: {
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Location permission granted");
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
startActivity(nearbyIntent);
}
}
break;
case 3: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Timber.d("Call controller.startCameraCapture()");
controller.startCameraCapture();
}
}
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_contributions_list, menu);
if (!deviceHasCamera()) {
menu.findItem(R.id.menu_from_camera).setEnabled(false);
}
}
public boolean deviceHasCamera() {
PackageManager pm = getContext().getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = new ContributionController(this);
setHasOptionsMenu(true);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
controller.loadState(savedInstanceState);
}
protected void clearSyncMessage() {
waitingMessage.setVisibility(GONE);
}
public interface SourceRefresher {
void refreshSource();
}