mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +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
	
	 Devarsh Mavani
						Devarsh Mavani