mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Migrated AboutActivity from Java to Kotlin (#6158)
* Rename Constants to Follow Kotlin Naming Conventions >This PR refactors constant names in the project to adhere to Kotlin's UPPERCASE_SNAKE_CASE naming convention, improving code readability and maintaining consistency across the codebase. >Renamed the following constants in LoginActivity: >saveProgressDialog → SAVE_PROGRESS_DIALOG >saveErrorMessage → SAVE_ERROR_MESSAGE >saveUsername → SAVE_USERNAME >savePassword → SAVE_PASSWORD >Updated all references to these constants throughout the project. * Update Project_Default.xml * Refactor variable names to adhere to naming conventions Renamed variables to use camel case: -UPLOAD_COUNT_THRESHOLD → uploadCountThreshold -REVERT_PERCENTAGE_FOR_MESSAGE → revertPercentageForMessage -REVERT_SHARED_PREFERENCE → revertSharedPreference -UPLOAD_SHARED_PREFERENCE → uploadSharedPreference Renamed variables with uppercase initials to lowercase for alignment with Kotlin conventions: -Latitude → latitude -Longitude → longitude -Accuracy → accuracy Refactored the following variable names: -NUMBER_OF_QUESTIONS → numberOfQuestions -MULTIPLIER_TO_GET_PERCENTAGE → multiplierToGetPercentage * Refactor Dialog View Initialization with Null-Safe Calls This PR refactors the dialog setup code in CustomSelectorActivity to improve safety and readability by replacing explicit casts with null-safe generic calls for findViewById. >Replaced explicit casting (as Button and as TextView) with the generic findViewById<T>() method for improved type safety. >Added null-safety (?.) to avoid potential crashes if a view is not found in the dialog layout. why changed:- >Prevents runtime crashes caused by NullPointerException when a view is missing in the layout. * Refactor Unit Test: Replace Unsafe Casting with Type-Safe Mocking for findViewById >PR refactors the unit test for NearbyParentFragment by replacing unsafe casting in the findViewById mocking statements with type-safe >Ensured all findViewById mocks now use a consistent, type-safe format (findViewById<View>(...)) to reduce verbosity and potential casting errors. >Verified the functionality of prepareViewsForSheetPosition remains unchanged, ensuring no regression in test behavior. * Update NearbyParentFragmentUnitTest.kt * Refactor: Rename Constants to Follow CamelCase Naming Convention >Updated all constant variable names to follow the camelCase naming convention, removing underscores in the middle or end. >Ensured variable names remain descriptive and align with code readability best practices. * Replace private val with const val for URL constants in QuizController * Renaming the constant to use UPPER_SNAKE_CASE * Renaming the constant to use UPPER_SNAKE_CASE * Update Done * **Refactor: Convert `minimumThresholdForSwipe` to a compile-time constant** * Convert AboutActivity from Java to Kotlin This PR converts the AboutActivity class from Java to Kotlin >Testing: >Verified all functionalities of the AboutActivity, including toolbar setup, intent launches, and dialog interactions, to ensure behavior remains consistent post-conversion. >Successfully ran unit tests for AboutActivity to confirm the correctness of methods and logic. * Thank you for the suggestion! Since these methods all take a single View parameter, replacing them with method references is a great way to simplify the code and improve readability. I'll updated the code accordingly. Added a TODO in the code as a reminder to refactor this in the future. --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
36f844a709
commit
5d4474ead6
2 changed files with 209 additions and 187 deletions
|
|
@ -1,187 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.databinding.ActivityAboutBinding;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents about screen of this app
|
||||
*/
|
||||
public class AboutActivity extends BaseActivity {
|
||||
|
||||
/*
|
||||
This View Binding class is auto-generated for each xml file. The format is usually the name
|
||||
of the file with PascalCasing (The underscore characters will be ignored).
|
||||
More information is available at https://developer.android.com/topic/libraries/view-binding
|
||||
*/
|
||||
private ActivityAboutBinding binding;
|
||||
|
||||
/**
|
||||
* This method helps in the creation About screen
|
||||
*
|
||||
* @param savedInstanceState Data bundle
|
||||
*/
|
||||
@Override
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
/*
|
||||
Instead of just setting the view with the xml file. We need to use View Binding class.
|
||||
*/
|
||||
binding = ActivityAboutBinding.inflate(getLayoutInflater());
|
||||
final View view = binding.getRoot();
|
||||
setContentView(view);
|
||||
|
||||
setSupportActionBar(binding.toolbarBinding.toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
final String aboutText = getString(R.string.about_license);
|
||||
/*
|
||||
We can then access all the views by just using the id names like this.
|
||||
camelCasing is used with underscore characters being ignored.
|
||||
*/
|
||||
binding.aboutLicense.setHtmlText(aboutText);
|
||||
|
||||
@SuppressLint("StringFormatMatches")
|
||||
String improveText = String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL);
|
||||
binding.aboutImprove.setHtmlText(improveText);
|
||||
binding.aboutVersion.setText(ConfigUtils.getVersionNameWithSha(getApplicationContext()));
|
||||
|
||||
Utils.setUnderlinedText(binding.aboutFaq, R.string.about_faq, getApplicationContext());
|
||||
Utils.setUnderlinedText(binding.aboutRateUs, R.string.about_rate_us, getApplicationContext());
|
||||
Utils.setUnderlinedText(binding.aboutUserGuide, R.string.user_guide, getApplicationContext());
|
||||
Utils.setUnderlinedText(binding.aboutPrivacyPolicy, R.string.about_privacy_policy, getApplicationContext());
|
||||
Utils.setUnderlinedText(binding.aboutTranslate, R.string.about_translate, getApplicationContext());
|
||||
Utils.setUnderlinedText(binding.aboutCredits, R.string.about_credits, getApplicationContext());
|
||||
|
||||
/*
|
||||
To set listeners, we can create a separate method and use lambda syntax.
|
||||
*/
|
||||
binding.facebookLaunchIcon.setOnClickListener(this::launchFacebook);
|
||||
binding.githubLaunchIcon.setOnClickListener(this::launchGithub);
|
||||
binding.websiteLaunchIcon.setOnClickListener(this::launchWebsite);
|
||||
binding.aboutRateUs.setOnClickListener(this::launchRatings);
|
||||
binding.aboutCredits.setOnClickListener(this::launchCredits);
|
||||
binding.aboutPrivacyPolicy.setOnClickListener(this::launchPrivacyPolicy);
|
||||
binding.aboutUserGuide.setOnClickListener(this::launchUserGuide);
|
||||
binding.aboutFaq.setOnClickListener(this::launchFrequentlyAskedQuesions);
|
||||
binding.aboutTranslate.setOnClickListener(this::launchTranslate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void launchFacebook(View view) {
|
||||
Intent intent;
|
||||
try {
|
||||
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL));
|
||||
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME);
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL));
|
||||
}
|
||||
}
|
||||
|
||||
public void launchGithub(View view) {
|
||||
Intent intent;
|
||||
try {
|
||||
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL));
|
||||
intent.setPackage(Urls.GITHUB_PACKAGE_NAME);
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL));
|
||||
}
|
||||
}
|
||||
|
||||
public void launchWebsite(View view) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL));
|
||||
}
|
||||
|
||||
public void launchRatings(View view){
|
||||
Utils.rateApp(this);
|
||||
}
|
||||
|
||||
public void launchCredits(View view) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL));
|
||||
}
|
||||
|
||||
public void launchUserGuide(View view) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL));
|
||||
}
|
||||
|
||||
public void launchPrivacyPolicy(View view) {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL));
|
||||
}
|
||||
|
||||
public void launchFrequentlyAskedQuesions(View view) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_about, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.share_app_icon:
|
||||
String shareText = String.format(getString(R.string.share_text), Urls.PLAY_STORE_URL_PREFIX + this.getPackageName());
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
|
||||
sendIntent.setType("text/plain");
|
||||
startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via)));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void launchTranslate(View view) {
|
||||
@NonNull List<String> sortedLocalizedNamesRef = CommonsApplication.getInstance().getLanguageLookUpTable().getCanonicalNames();
|
||||
Collections.sort(sortedLocalizedNamesRef);
|
||||
final ArrayAdapter<String> languageAdapter = new ArrayAdapter<>(AboutActivity.this,
|
||||
android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef);
|
||||
final Spinner spinner = new Spinner(AboutActivity.this);
|
||||
spinner.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
spinner.setAdapter(languageAdapter);
|
||||
spinner.setGravity(17);
|
||||
spinner.setPadding(50,0,0,0);
|
||||
|
||||
Runnable positiveButtonRunnable = () -> {
|
||||
String langCode = CommonsApplication.getInstance().getLanguageLookUpTable().getCodes().get(spinner.getSelectedItemPosition());
|
||||
Utils.handleWebUrl(AboutActivity.this, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode));
|
||||
};
|
||||
DialogUtil.showAlertDialog(this,
|
||||
getString(R.string.about_translate_title),
|
||||
getString(R.string.about_translate_message),
|
||||
getString(R.string.about_translate_proceed),
|
||||
getString(R.string.about_translate_cancel),
|
||||
positiveButtonRunnable,
|
||||
() -> {},
|
||||
spinner
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
209
app/src/main/java/fr/free/nrw/commons/AboutActivity.kt
Normal file
209
app/src/main/java/fr/free/nrw/commons/AboutActivity.kt
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Spinner
|
||||
import fr.free.nrw.commons.CommonsApplication.Companion.instance
|
||||
import fr.free.nrw.commons.databinding.ActivityAboutBinding
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* Represents about screen of this app
|
||||
*/
|
||||
class AboutActivity : BaseActivity() {
|
||||
/*
|
||||
This View Binding class is auto-generated for each xml file. The format is usually the name
|
||||
of the file with PascalCasing (The underscore characters will be ignored).
|
||||
More information is available at https://developer.android.com/topic/libraries/view-binding
|
||||
*/
|
||||
private var binding: ActivityAboutBinding? = null
|
||||
|
||||
/**
|
||||
* This method helps in the creation About screen
|
||||
*
|
||||
* @param savedInstanceState Data bundle
|
||||
*/
|
||||
@SuppressLint("StringFormatInvalid") //TODO:
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
/*
|
||||
Instead of just setting the view with the xml file. We need to use View Binding class.
|
||||
*/
|
||||
binding = ActivityAboutBinding.inflate(layoutInflater)
|
||||
val view: View = binding!!.root
|
||||
setContentView(view)
|
||||
|
||||
setSupportActionBar(binding!!.toolbarBinding.toolbar)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
val aboutText = getString(R.string.about_license)
|
||||
/*
|
||||
We can then access all the views by just using the id names like this.
|
||||
camelCasing is used with underscore characters being ignored.
|
||||
*/
|
||||
binding!!.aboutLicense.setHtmlText(aboutText)
|
||||
|
||||
@SuppressLint("StringFormatMatches") // TODO:
|
||||
val improveText =
|
||||
String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL)
|
||||
binding!!.aboutImprove.setHtmlText(improveText)
|
||||
binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha()
|
||||
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutFaq, R.string.about_faq,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutRateUs, R.string.about_rate_us,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutUserGuide, R.string.user_guide,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutPrivacyPolicy, R.string.about_privacy_policy,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutTranslate, R.string.about_translate,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutCredits, R.string.about_credits,
|
||||
applicationContext
|
||||
)
|
||||
|
||||
/*
|
||||
To set listeners, we can create a separate method and use lambda syntax.
|
||||
*/
|
||||
binding!!.facebookLaunchIcon.setOnClickListener(::launchFacebook)
|
||||
binding!!.githubLaunchIcon.setOnClickListener(::launchGithub)
|
||||
binding!!.websiteLaunchIcon.setOnClickListener(::launchWebsite)
|
||||
binding!!.aboutRateUs.setOnClickListener(::launchRatings)
|
||||
binding!!.aboutCredits.setOnClickListener(::launchCredits)
|
||||
binding!!.aboutPrivacyPolicy.setOnClickListener(::launchPrivacyPolicy)
|
||||
binding!!.aboutUserGuide.setOnClickListener(::launchUserGuide)
|
||||
binding!!.aboutFaq.setOnClickListener(::launchFrequentlyAskedQuesions)
|
||||
binding!!.aboutTranslate.setOnClickListener(::launchTranslate)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
fun launchFacebook(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL))
|
||||
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL))
|
||||
}
|
||||
}
|
||||
|
||||
fun launchGithub(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
intent.setPackage(Urls.GITHUB_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
}
|
||||
}
|
||||
|
||||
fun launchWebsite(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL))
|
||||
}
|
||||
|
||||
fun launchRatings(view: View?) {
|
||||
Utils.rateApp(this)
|
||||
}
|
||||
|
||||
fun launchCredits(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL))
|
||||
}
|
||||
|
||||
fun launchUserGuide(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL))
|
||||
}
|
||||
|
||||
fun launchPrivacyPolicy(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
}
|
||||
|
||||
fun launchFrequentlyAskedQuesions(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val inflater = menuInflater
|
||||
inflater.inflate(R.menu.menu_about, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.share_app_icon -> {
|
||||
val shareText = String.format(
|
||||
getString(R.string.share_text),
|
||||
Urls.PLAY_STORE_URL_PREFIX + this.packageName
|
||||
)
|
||||
val sendIntent = Intent()
|
||||
sendIntent.setAction(Intent.ACTION_SEND)
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText)
|
||||
sendIntent.setType("text/plain")
|
||||
startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via)))
|
||||
return true
|
||||
}
|
||||
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
fun launchTranslate(view: View?) {
|
||||
val sortedLocalizedNamesRef = instance.languageLookUpTable!!.getCanonicalNames()
|
||||
Collections.sort(sortedLocalizedNamesRef)
|
||||
val languageAdapter = ArrayAdapter(
|
||||
this@AboutActivity,
|
||||
android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef
|
||||
)
|
||||
val spinner = Spinner(this@AboutActivity)
|
||||
spinner.layoutParams =
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
spinner.adapter = languageAdapter
|
||||
spinner.gravity = 17
|
||||
spinner.setPadding(50, 0, 0, 0)
|
||||
|
||||
val positiveButtonRunnable = Runnable {
|
||||
val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition]
|
||||
Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode))
|
||||
}
|
||||
showAlertDialog(
|
||||
this,
|
||||
getString(R.string.about_translate_title),
|
||||
getString(R.string.about_translate_message),
|
||||
getString(R.string.about_translate_proceed),
|
||||
getString(R.string.about_translate_cancel),
|
||||
positiveButtonRunnable,
|
||||
{},
|
||||
spinner
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue