mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
In app feedback (#4845)
* rebase * Progress * Completed UI and POST Request * removed invalid string resource * Removed unused code & Added string resources * Resolved Code style issues * Javadoc for getters & setters * Codestyle fixes * Minor Fixes * wip * Tests * Comments * Fixed Tests * Minor changes * minor change * Comments * Minor Fixes * fixed tests * Removed Butterknife * Fixed tests * Removed Unecessary strings * Minor chnages * Minor fix * Minor changes * Minor changes * Implemented Suggestions * Removed Redundant Toast
This commit is contained in:
parent
00760ba1c6
commit
fa0370438b
12 changed files with 777 additions and 5 deletions
|
|
@ -0,0 +1,95 @@
|
|||
package fr.free.nrw.commons.feedback;
|
||||
|
||||
import android.content.Context;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.feedback.model.Feedback;
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Creates a wikimedia recognizable format
|
||||
* from feedback information
|
||||
*/
|
||||
public class FeedbackContentCreator {
|
||||
private StringBuilder stringBuilder;
|
||||
private Feedback feedback;
|
||||
private Context context;
|
||||
|
||||
public FeedbackContentCreator(Context context, Feedback feedback) {
|
||||
this.feedback = feedback;
|
||||
this.context = context;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the string buffer object to append content from feedback object
|
||||
*/
|
||||
public void init() {
|
||||
// Localization is not needed here, because this ends up on a page where developers read the feedback, so English is the most convenient.
|
||||
|
||||
stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("== ");
|
||||
stringBuilder.append("Feedback from ~~~ for version ");
|
||||
stringBuilder.append(feedback.getVersion());
|
||||
stringBuilder.append(" ==");
|
||||
stringBuilder.append("\n");
|
||||
stringBuilder.append(feedback.getTitle());
|
||||
stringBuilder.append("\n");
|
||||
stringBuilder.append("\n");
|
||||
if (feedback.getApiLevel() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.api_level));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getApiLevel());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
if (feedback.getAndroidVersion() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.android_version));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getAndroidVersion());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
if (feedback.getDeviceManufacturer() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.device_manufacturer));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getDeviceManufacturer());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
if (feedback.getDeviceModel() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.device_model));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getDeviceModel());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
if (feedback.getDevice() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.device_name));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getDevice());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
if (feedback.getNetworkType() != null) {
|
||||
stringBuilder.append("* ");
|
||||
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
|
||||
Locale.ENGLISH).getString(R.string.network_type));
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(feedback.getNetworkType());
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
stringBuilder.append("~~~~");
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package fr.free.nrw.commons.feedback;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Toast;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.databinding.DialogFeedbackBinding;
|
||||
import fr.free.nrw.commons.feedback.model.Feedback;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.DeviceInfoUtil;
|
||||
|
||||
/**
|
||||
* Feedback dialog that asks user for message and
|
||||
* other device specifications
|
||||
*/
|
||||
public class FeedbackDialog extends Dialog {
|
||||
DialogFeedbackBinding dialogFeedbackBinding;
|
||||
|
||||
private OnFeedbackSubmitCallback onFeedbackSubmitCallback;
|
||||
|
||||
public FeedbackDialog(Context context, OnFeedbackSubmitCallback onFeedbackSubmitCallback) {
|
||||
super(context);
|
||||
this.onFeedbackSubmitCallback = onFeedbackSubmitCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
dialogFeedbackBinding = DialogFeedbackBinding.inflate(getLayoutInflater());
|
||||
final View view = dialogFeedbackBinding.getRoot();
|
||||
setContentView(view);
|
||||
dialogFeedbackBinding.btnSubmitFeedback.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
submitFeedback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When the button is clicked, it will create a feedback object
|
||||
* and give a callback to calling activity/fragment
|
||||
*/
|
||||
void submitFeedback() {
|
||||
if(dialogFeedbackBinding.feedbackItemEditText.getText().toString().equals("")) {
|
||||
dialogFeedbackBinding.feedbackItemEditText.setError(getContext().getString(R.string.enter_description));
|
||||
return;
|
||||
}
|
||||
String appVersion = ConfigUtils.getVersionNameWithSha(getContext());
|
||||
String androidVersion = dialogFeedbackBinding.androidVersionCheckbox.isChecked() ? DeviceInfoUtil.getAndroidVersion() : null;
|
||||
String apiLevel = dialogFeedbackBinding.apiLevelCheckbox.isChecked() ? DeviceInfoUtil.getAPILevel() : null;
|
||||
String deviceManufacturer = dialogFeedbackBinding.deviceManufacturerCheckbox.isChecked() ? DeviceInfoUtil.getDeviceManufacturer() : null;
|
||||
String deviceModel = dialogFeedbackBinding.deviceModelCheckbox.isChecked() ? DeviceInfoUtil.getDeviceModel() : null;
|
||||
String deviceName = dialogFeedbackBinding.deviceNameCheckbox.isChecked() ? DeviceInfoUtil.getDevice() : null;
|
||||
String networkType = dialogFeedbackBinding.networkTypeCheckbox.isChecked() ? DeviceInfoUtil.getConnectionType(getContext()).toString() : null;
|
||||
Feedback feedback = new Feedback(appVersion, apiLevel
|
||||
, dialogFeedbackBinding.feedbackItemEditText.getText().toString()
|
||||
, androidVersion, deviceModel, deviceManufacturer, deviceName, networkType);
|
||||
onFeedbackSubmitCallback.onFeedbackSubmit(feedback);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package fr.free.nrw.commons.feedback;
|
||||
|
||||
import fr.free.nrw.commons.feedback.model.Feedback;
|
||||
|
||||
/**
|
||||
* This interface is used to provide callback
|
||||
* from Feedback dialog whenever submit button is clicked
|
||||
*/
|
||||
public interface OnFeedbackSubmitCallback {
|
||||
|
||||
/**
|
||||
* callback function, called when user clicks on submit
|
||||
*/
|
||||
void onFeedbackSubmit(Feedback feedback);
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
package fr.free.nrw.commons.feedback.model;
|
||||
|
||||
/**
|
||||
* Pojo class for storing information that are required while uploading a feedback
|
||||
*/
|
||||
public class Feedback {
|
||||
/**
|
||||
* Version of app
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* API level of user's phone
|
||||
*/
|
||||
private String apiLevel;
|
||||
/**
|
||||
* Title/Description entered by user
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* Android version of user's device
|
||||
*/
|
||||
private String androidVersion;
|
||||
/**
|
||||
* Device Model of user's device
|
||||
*/
|
||||
private String deviceModel;
|
||||
/**
|
||||
* Device manufacturer name
|
||||
*/
|
||||
private String deviceManufacturer;
|
||||
/**
|
||||
* Device name stored on user's device
|
||||
*/
|
||||
private String device;
|
||||
/**
|
||||
* network type user is having (Ex: Wifi)
|
||||
*/
|
||||
private String networkType;
|
||||
|
||||
public Feedback(final String version, final String apiLevel, final String title, final String androidVersion,
|
||||
final String deviceModel, final String deviceManufacturer, final String device, final String networkType
|
||||
) {
|
||||
this.version = version;
|
||||
this.apiLevel = apiLevel;
|
||||
this.title = title;
|
||||
this.androidVersion = androidVersion;
|
||||
this.deviceModel = deviceModel;
|
||||
this.deviceManufacturer = deviceManufacturer;
|
||||
this.device = device;
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version from which this piece of feedback is being sent.
|
||||
* Ex: 3.0.1
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version of app to given version
|
||||
*/
|
||||
public void setVersion(final String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets api level of device
|
||||
* Ex: 28
|
||||
*/
|
||||
public String getApiLevel() {
|
||||
return apiLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets api level value to given value
|
||||
*/
|
||||
public void setApiLevel(final String apiLevel) {
|
||||
this.apiLevel = apiLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets feedback text entered by user
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets feedback text
|
||||
*/
|
||||
public void setTitle(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets android version of device
|
||||
* Ex: 9
|
||||
*/
|
||||
public String getAndroidVersion() {
|
||||
return androidVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets value of android version
|
||||
*/
|
||||
public void setAndroidVersion(final String androidVersion) {
|
||||
this.androidVersion = androidVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* get device model of current device
|
||||
* Ex: Redmi 6 Pro
|
||||
*/
|
||||
public String getDeviceModel() {
|
||||
return deviceModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets value of device model to a given value
|
||||
*/
|
||||
public void setDeviceModel(final String deviceModel) {
|
||||
this.deviceModel = deviceModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* get device manufacturer of user's device
|
||||
* Ex: Redmi
|
||||
*/
|
||||
public String getDeviceManufacturer() {
|
||||
return deviceManufacturer;
|
||||
}
|
||||
|
||||
/**
|
||||
* set device manufacturer value to a given value
|
||||
*/
|
||||
public void setDeviceManufacturer(final String deviceManufacturer) {
|
||||
this.deviceManufacturer = deviceManufacturer;
|
||||
}
|
||||
|
||||
/**
|
||||
* get device name of user's device
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets device name value to a given value
|
||||
*/
|
||||
public void setDevice(final String device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
/**
|
||||
* get network type of user's network
|
||||
* Ex: wifi
|
||||
*/
|
||||
public String getNetworkType() {
|
||||
return networkType;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets network type to a given value
|
||||
*/
|
||||
public void setNetworkType(final String networkType) {
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,13 +24,23 @@ import fr.free.nrw.commons.BuildConfig;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.WelcomeActivity;
|
||||
import fr.free.nrw.commons.actions.PageEditClient;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.feedback.FeedbackContentCreator;
|
||||
import fr.free.nrw.commons.feedback.model.Feedback;
|
||||
import fr.free.nrw.commons.feedback.FeedbackDialog;
|
||||
import fr.free.nrw.commons.feedback.OnFeedbackSubmitCallback;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.logging.CommonsLogSender;
|
||||
import fr.free.nrw.commons.profile.ProfileActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import timber.log.Timber;
|
||||
|
|
@ -47,6 +57,10 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
|||
@Inject @Named("default_preferences")
|
||||
JsonKvStore store;
|
||||
|
||||
@Inject
|
||||
@Named("commons-page-edit")
|
||||
PageEditClient pageEditClient;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
|
|
@ -104,7 +118,44 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
|||
|
||||
@OnClick(R.id.more_feedback)
|
||||
public void onFeedbackClicked() {
|
||||
showAlertDialog();
|
||||
showFeedbackDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and shows a dialog asking feedback from users
|
||||
*/
|
||||
private void showFeedbackDialog() {
|
||||
new FeedbackDialog(getContext(), new OnFeedbackSubmitCallback() {
|
||||
@Override
|
||||
public void onFeedbackSubmit(Feedback feedback) {
|
||||
uploadFeedback(feedback);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads feedback data on the server
|
||||
*/
|
||||
void uploadFeedback(Feedback feedback) {
|
||||
FeedbackContentCreator feedbackContentCreator = new FeedbackContentCreator(getContext(), feedback);
|
||||
|
||||
Single<Boolean> single =
|
||||
pageEditClient.prependEdit("Commons:Mobile_app/Feedback", feedbackContentCreator.toString(), "Summary")
|
||||
.flatMapSingle(result -> Single.just(result))
|
||||
.firstOrError();
|
||||
|
||||
Single.defer((Callable<SingleSource<Boolean>>) () -> single)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(aBoolean -> {
|
||||
if (aBoolean) {
|
||||
Toast.makeText(getContext(), getString(R.string.thanks_feedback), Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), getString(R.string.error_feedback),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue